blob: dc67a17f9d5e368d3296e88f2c1c34ba1c3267d1 [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;
18import com.google.common.base.Function;
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;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010026import com.google.devtools.build.lib.analysis.BlazeDirectories;
27import com.google.devtools.build.lib.analysis.BlazeVersionInfo;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010028import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
Ulf Adams15a23b92016-08-09 11:46:00 +000029import com.google.devtools.build.lib.analysis.ServerDirectories;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010030import com.google.devtools.build.lib.analysis.config.BinTools;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010031import com.google.devtools.build.lib.analysis.config.BuildOptions;
32import com.google.devtools.build.lib.analysis.config.ConfigurationFactory;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010033import com.google.devtools.build.lib.events.Event;
34import com.google.devtools.build.lib.events.OutputFilter;
Janak Ramakrishnane9cd0f32016-04-29 22:46:16 +000035import com.google.devtools.build.lib.flags.CommandNameCache;
Nathan Harmata42fb5602016-05-25 20:32:08 +000036import com.google.devtools.build.lib.packages.Package;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010037import com.google.devtools.build.lib.packages.PackageFactory;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010038import com.google.devtools.build.lib.packages.RuleClassProvider;
Nathan Harmatad4803012015-09-08 20:03:22 +000039import com.google.devtools.build.lib.profiler.AutoProfiler;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010040import com.google.devtools.build.lib.profiler.MemoryProfiler;
41import com.google.devtools.build.lib.profiler.ProfilePhase;
42import com.google.devtools.build.lib.profiler.Profiler;
43import com.google.devtools.build.lib.profiler.Profiler.ProfiledTaskKinds;
44import com.google.devtools.build.lib.profiler.ProfilerTask;
Nathan Harmataf0cc5b82016-03-18 14:56:28 +000045import com.google.devtools.build.lib.query2.AbstractBlazeQueryEnvironment;
46import com.google.devtools.build.lib.query2.QueryEnvironmentFactory;
Ulf Adams6bfd2c52016-08-29 08:00:39 +000047import com.google.devtools.build.lib.query2.engine.QueryEnvironment.QueryFunction;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010048import com.google.devtools.build.lib.query2.output.OutputFormatter;
49import com.google.devtools.build.lib.rules.test.CoverageReportActionFactory;
Lukacs Berkice1445f2016-04-19 15:52:55 +000050import com.google.devtools.build.lib.runtime.BlazeCommandDispatcher.LockingMode;
Ulf Adams6bfd2c52016-08-29 08:00:39 +000051import com.google.devtools.build.lib.runtime.commands.InfoItem;
Luis Fernando Pino Duqueb1b28b62016-02-25 14:25:19 +000052import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010053import com.google.devtools.build.lib.server.RPCServer;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010054import com.google.devtools.build.lib.server.signal.InterruptSignalHandler;
Lukacs Berki74dcfee2016-07-04 12:57:25 +000055import com.google.devtools.build.lib.shell.JavaSubprocessFactory;
56import com.google.devtools.build.lib.shell.Subprocess;
57import com.google.devtools.build.lib.shell.SubprocessBuilder;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010058import com.google.devtools.build.lib.util.AbruptExitException;
59import com.google.devtools.build.lib.util.BlazeClock;
60import com.google.devtools.build.lib.util.Clock;
61import com.google.devtools.build.lib.util.ExitCode;
62import com.google.devtools.build.lib.util.LoggingUtil;
63import com.google.devtools.build.lib.util.OS;
64import com.google.devtools.build.lib.util.OsUtils;
Mark Schaller6df81792015-12-10 18:47:47 +000065import com.google.devtools.build.lib.util.Preconditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010066import com.google.devtools.build.lib.util.ThreadUtils;
67import com.google.devtools.build.lib.util.io.OutErr;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010068import com.google.devtools.build.lib.vfs.FileSystem;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010069import com.google.devtools.build.lib.vfs.JavaIoFileSystem;
70import com.google.devtools.build.lib.vfs.Path;
71import com.google.devtools.build.lib.vfs.PathFragment;
72import com.google.devtools.build.lib.vfs.UnixFileSystem;
Lukacs Berki58dbb7f2016-01-29 11:56:29 +000073import com.google.devtools.build.lib.vfs.WindowsFileSystem;
Lukacs Berki74dcfee2016-07-04 12:57:25 +000074import com.google.devtools.build.lib.windows.WindowsSubprocessFactory;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010075import com.google.devtools.common.options.Option;
76import com.google.devtools.common.options.OptionPriority;
77import com.google.devtools.common.options.OptionsBase;
78import com.google.devtools.common.options.OptionsClassProvider;
79import com.google.devtools.common.options.OptionsParser;
80import com.google.devtools.common.options.OptionsParsingException;
81import com.google.devtools.common.options.OptionsProvider;
82import com.google.devtools.common.options.TriState;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010083import java.io.BufferedOutputStream;
84import java.io.IOException;
85import java.io.OutputStream;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010086import java.lang.reflect.Field;
87import java.util.ArrayList;
88import java.util.Arrays;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010089import java.util.Date;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010090import java.util.Iterator;
Ulf Adams47cb9162015-09-18 08:12:30 +000091import java.util.LinkedHashMap;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010092import java.util.List;
Ulf Adamsb5a06f332016-06-21 14:55:49 +000093import java.util.Locale;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010094import java.util.Map;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010095import java.util.UUID;
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +000096import java.util.concurrent.Future;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010097import java.util.concurrent.TimeUnit;
98import java.util.concurrent.atomic.AtomicInteger;
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +000099import java.util.logging.Handler;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100100import java.util.logging.Level;
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +0000101import java.util.logging.LogRecord;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100102import java.util.logging.Logger;
103import java.util.regex.Pattern;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100104import javax.annotation.Nullable;
105
106/**
Ulf Adams633f5392015-09-15 11:13:08 +0000107 * The BlazeRuntime class encapsulates the immutable configuration of the current instance. These
108 * runtime settings and services are available to most parts of any Blaze application for the
109 * duration of the batch run or server lifetime.
110 *
111 * <p>The parts specific to the current command are stored in {@link CommandEnvironment}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100112 */
113public final class BlazeRuntime {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100114 private static final Pattern suppressFromLog = Pattern.compile(".*(auth|pass|cookie).*",
115 Pattern.CASE_INSENSITIVE);
116
117 private static final Logger LOG = Logger.getLogger(BlazeRuntime.class.getName());
118
Ulf Adams50e7db62015-10-20 09:14:16 +0000119 private final Iterable<BlazeModule> blazeModules;
120 private final Map<String, BlazeCommand> commandMap = new LinkedHashMap<>();
121 private final Clock clock;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100122
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100123 private final PackageFactory packageFactory;
124 private final ConfigurationFactory configurationFactory;
125 private final ConfiguredRuleClassProvider ruleClassProvider;
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000126 // For bazel info.
127 private final ImmutableMap<String, InfoItem> infoItems;
128 // For bazel query.
129 private final QueryEnvironmentFactory queryEnvironmentFactory;
130 private final ImmutableList<QueryFunction> queryFunctions;
Ulf Adams54eeac52016-08-29 09:07:35 +0000131 private final ImmutableList<OutputFormatter> queryOutputFormatters;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100132
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100133 private final AtomicInteger storedExitCode = new AtomicInteger();
134
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100135 // We pass this through here to make it available to the MasterLogWriter.
136 private final OptionsProvider startupOptionsProvider;
137
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100138 private final ProjectFile.Provider projectFileProvider;
Luis Fernando Pino Duqueb1b28b62016-02-25 14:25:19 +0000139 @Nullable
140 private final InvocationPolicy invocationPolicy;
Janak Ramakrishnan1d899602016-04-28 03:48:55 +0000141 private final String defaultsPackageContent;
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000142 private final SubscriberExceptionHandler eventBusExceptionHandler;
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +0000143 private final String productName;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100144
Ulf Adams50e7db62015-10-20 09:14:16 +0000145 // Workspace state (currently exactly one workspace per server)
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000146 private BlazeWorkspace workspace;
Ulf Adams50e7db62015-10-20 09:14:16 +0000147
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000148 private BlazeRuntime(
Nathan Harmataf0cc5b82016-03-18 14:56:28 +0000149 QueryEnvironmentFactory queryEnvironmentFactory,
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000150 ImmutableList<QueryFunction> queryFunctions,
Ulf Adams54eeac52016-08-29 09:07:35 +0000151 ImmutableList<OutputFormatter> queryOutputFormatters,
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000152 PackageFactory pkgFactory,
153 ConfiguredRuleClassProvider ruleClassProvider,
154 ConfigurationFactory configurationFactory,
155 ImmutableMap<String, InfoItem> infoItems,
156 Clock clock,
157 OptionsProvider startupOptionsProvider,
158 Iterable<BlazeModule> blazeModules,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100159 SubscriberExceptionHandler eventBusExceptionHandler,
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000160 ProjectFile.Provider projectFileProvider,
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000161 InvocationPolicy invocationPolicy,
162 Iterable<BlazeCommand> commands,
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +0000163 String productName) {
Ulf Adams50e7db62015-10-20 09:14:16 +0000164 // Server state
165 this.blazeModules = blazeModules;
166 overrideCommands(commands);
167
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100168 this.packageFactory = pkgFactory;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100169 this.projectFileProvider = projectFileProvider;
Luis Fernando Pino Duqueb1b28b62016-02-25 14:25:19 +0000170 this.invocationPolicy = invocationPolicy;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100171
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100172 this.ruleClassProvider = ruleClassProvider;
173 this.configurationFactory = configurationFactory;
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000174 this.infoItems = infoItems;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100175 this.clock = clock;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100176 this.startupOptionsProvider = startupOptionsProvider;
Nathan Harmataf0cc5b82016-03-18 14:56:28 +0000177 this.queryEnvironmentFactory = queryEnvironmentFactory;
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000178 this.queryFunctions = queryFunctions;
Ulf Adams54eeac52016-08-29 09:07:35 +0000179 this.queryOutputFormatters = queryOutputFormatters;
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000180 this.eventBusExceptionHandler = eventBusExceptionHandler;
Janak Ramakrishnan1d899602016-04-28 03:48:55 +0000181
182 this.defaultsPackageContent =
183 ruleClassProvider.getDefaultsPackageContent(getInvocationPolicy());
Janak Ramakrishnane9cd0f32016-04-29 22:46:16 +0000184 CommandNameCache.CommandNameCacheInstance.INSTANCE.setCommandNameCache(
185 new CommandNameCacheImpl(getCommandMap()));
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +0000186 this.productName = productName;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100187 }
188
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000189 public void initWorkspace(BlazeDirectories directories, BinTools binTools)
190 throws AbruptExitException {
Ulf Adamsea46dfc2016-08-10 08:44:19 +0000191 Preconditions.checkState(this.workspace == null);
Ulf Adamsde14ade2016-10-14 14:20:31 +0000192 WorkspaceBuilder builder = new WorkspaceBuilder(directories, binTools);
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000193 for (BlazeModule module : blazeModules) {
Ulf Adamse8d9c4c2016-07-05 08:13:41 +0000194 module.workspaceInit(directories, builder);
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000195 }
Ulf Adamse8d9c4c2016-07-05 08:13:41 +0000196 this.workspace = builder.build(
197 this, packageFactory, ruleClassProvider, getProductName(), eventBusExceptionHandler);
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000198 }
199
Ulf Adams33644a42016-08-10 11:32:41 +0000200 @Nullable public CoverageReportActionFactory getCoverageReportActionFactory(
201 OptionsClassProvider commandOptions) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100202 CoverageReportActionFactory firstFactory = null;
203 for (BlazeModule module : blazeModules) {
Ulf Adams33644a42016-08-10 11:32:41 +0000204 CoverageReportActionFactory factory = module.getCoverageReportFactory(commandOptions);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100205 if (factory != null) {
206 Preconditions.checkState(firstFactory == null,
207 "only one Blaze Module can have a Coverage Report Factory");
208 firstFactory = factory;
209 }
210 }
211 return firstFactory;
212 }
213
214 /**
Ulf Adams47cb9162015-09-18 08:12:30 +0000215 * Adds the given command under the given name to the map of commands.
216 *
217 * @throws AssertionError if the name is already used by another command.
218 */
219 private void addCommand(BlazeCommand command) {
220 String name = command.getClass().getAnnotation(Command.class).name();
221 if (commandMap.containsKey(name)) {
222 throw new IllegalStateException("Command name or alias " + name + " is already used.");
223 }
224 commandMap.put(name, command);
225 }
226
227 final void overrideCommands(Iterable<BlazeCommand> commands) {
228 commandMap.clear();
229 for (BlazeCommand command : commands) {
230 addCommand(command);
231 }
Ulf Adams47cb9162015-09-18 08:12:30 +0000232 }
233
Michael Staib6e5e8fb2016-10-04 21:26:37 +0000234 /**
235 * Initializes a CommandEnvironment to execute a command in this server.
236 *
237 * <p>This method should be called from the "main" thread on which the command will execute;
238 * that thread will receive interruptions if a module requests an early exit.
239 */
Ulf Adams633f5392015-09-15 11:13:08 +0000240 public CommandEnvironment initCommand() {
Ulf Adams69cc0032016-03-30 13:52:25 +0000241 return workspace.initCommand();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100242 }
243
Luis Fernando Pino Duqueb1b28b62016-02-25 14:25:19 +0000244 @Nullable
245 public InvocationPolicy getInvocationPolicy() {
246 return invocationPolicy;
247 }
248
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100249 /**
250 * Conditionally enable profiling.
251 */
Ulf Adams633f5392015-09-15 11:13:08 +0000252 private final boolean initProfiler(CommandEnvironment env, CommonCommandOptions options,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100253 UUID buildID, long execStartTimeNanos) {
254 OutputStream out = null;
255 boolean recordFullProfilerData = false;
256 ProfiledTaskKinds profiledTasks = ProfiledTaskKinds.NONE;
257
258 try {
259 if (options.profilePath != null) {
Ulf Adamsab43b972016-03-30 12:23:50 +0000260 Path profilePath = env.getWorkspace().getRelative(options.profilePath);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100261
262 recordFullProfilerData = options.recordFullProfilerData;
263 out = new BufferedOutputStream(profilePath.getOutputStream(), 1024 * 1024);
Ulf Adams633f5392015-09-15 11:13:08 +0000264 env.getReporter().handle(Event.info("Writing profile data to '" + profilePath + "'"));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100265 profiledTasks = ProfiledTaskKinds.ALL;
266 } else if (options.alwaysProfileSlowOperations) {
267 recordFullProfilerData = false;
268 out = null;
269 profiledTasks = ProfiledTaskKinds.SLOWEST;
270 }
271 if (profiledTasks != ProfiledTaskKinds.NONE) {
272 Profiler.instance().start(profiledTasks, out,
Luis Fernando Pino Duquebe102182016-05-23 14:03:55 +0000273 getProductName() + " profile for " + env.getOutputBase() + " at " + new Date()
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100274 + ", build ID: " + buildID,
275 recordFullProfilerData, clock, execStartTimeNanos);
276 return true;
277 }
278 } catch (IOException e) {
Ulf Adams633f5392015-09-15 11:13:08 +0000279 env.getReporter().handle(Event.error("Error while creating profile file: " + e.getMessage()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100280 }
281 return false;
282 }
283
Ulf Adamsab43b972016-03-30 12:23:50 +0000284 public BlazeWorkspace getWorkspace() {
285 return workspace;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100286 }
287
288 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100289 * The directory in which blaze stores the server state - that is, the socket
290 * file and a log.
291 */
Ulf Adams94b72db2016-03-30 11:58:37 +0000292 private Path getServerDirectory() {
Ulf Adamsab43b972016-03-30 12:23:50 +0000293 return getWorkspace().getDirectories().getOutputBase().getChild("server");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100294 }
295
Michajlo Matijkiwaf79a322016-09-16 15:44:35 +0000296 public boolean writeCommandLog() {
297 return startupOptionsProvider.getOptions(BlazeServerStartupOptions.class).writeCommandLog;
298 }
299
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100300 /**
Nathan Harmataf0cc5b82016-03-18 14:56:28 +0000301 * Returns the {@link QueryEnvironmentFactory} that should be used to create a
302 * {@link AbstractBlazeQueryEnvironment}, whenever one is needed.
303 */
304 public QueryEnvironmentFactory getQueryEnvironmentFactory() {
305 return queryEnvironmentFactory;
306 }
307
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000308 public ImmutableList<QueryFunction> getQueryFunctions() {
309 return queryFunctions;
310 }
311
Ulf Adams54eeac52016-08-29 09:07:35 +0000312 public ImmutableList<OutputFormatter> getQueryOutputFormatters() {
313 return queryOutputFormatters;
314 }
315
Nathan Harmataf0cc5b82016-03-18 14:56:28 +0000316 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100317 * Returns the package factory.
318 */
319 public PackageFactory getPackageFactory() {
320 return packageFactory;
321 }
322
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100323 /**
324 * Returns the rule class provider.
325 */
326 public ConfiguredRuleClassProvider getRuleClassProvider() {
327 return ruleClassProvider;
328 }
329
Ulf Adams6bfd2c52016-08-29 08:00:39 +0000330 public ImmutableMap<String, InfoItem> getInfoItems() {
331 return infoItems;
332 }
333
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100334 public Iterable<BlazeModule> getBlazeModules() {
335 return blazeModules;
336 }
337
338 @SuppressWarnings("unchecked")
339 public <T extends BlazeModule> T getBlazeModule(Class<T> moduleClass) {
340 for (BlazeModule module : blazeModules) {
341 if (module.getClass() == moduleClass) {
342 return (T) module;
343 }
344 }
345
346 return null;
347 }
348
349 public ConfigurationFactory getConfigurationFactory() {
350 return configurationFactory;
351 }
352
353 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100354 * Returns a provider for project file objects. Can be null if no such provider was set by any of
355 * the modules.
356 */
357 @Nullable
358 public ProjectFile.Provider getProjectFileProvider() {
359 return projectFileProvider;
360 }
361
362 /**
363 * Hook method called by the BlazeCommandDispatcher prior to the dispatch of
364 * each command.
365 *
366 * @param options The CommonCommandOptions used by every command.
367 * @throws AbruptExitException if this command is unsuitable to be run as specified
368 */
Ulf Adams50e7db62015-10-20 09:14:16 +0000369 void beforeCommand(CommandEnvironment env, CommonCommandOptions options, long execStartTimeNanos)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100370 throws AbruptExitException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100371 // Conditionally enable profiling
372 // We need to compensate for launchTimeNanos (measurements taken outside of the jvm).
373 long startupTimeNanos = options.startupTime * 1000000L;
Ulf Adams3815b4c2015-09-18 07:34:13 +0000374 if (initProfiler(env, options, env.getCommandId(), execStartTimeNanos - startupTimeNanos)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100375 Profiler profiler = Profiler.instance();
376
377 // Instead of logEvent() we're calling the low level function to pass the timings we took in
378 // the launcher. We're setting the INIT phase marker so that it follows immediately the LAUNCH
379 // phase.
380 profiler.logSimpleTaskDuration(execStartTimeNanos - startupTimeNanos, 0, ProfilerTask.PHASE,
381 ProfilePhase.LAUNCH.description);
382 profiler.logSimpleTaskDuration(execStartTimeNanos, 0, ProfilerTask.PHASE,
383 ProfilePhase.INIT.description);
384 }
385
386 if (options.memoryProfilePath != null) {
Ulf Adamsc5855302015-10-20 08:46:38 +0000387 Path memoryProfilePath = env.getWorkingDirectory().getRelative(options.memoryProfilePath);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100388 try {
389 MemoryProfiler.instance().start(memoryProfilePath.getOutputStream());
390 } catch (IOException e) {
Ulf Adams633f5392015-09-15 11:13:08 +0000391 env.getReporter().handle(
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100392 Event.error("Error while creating memory profile file: " + e.getMessage()));
393 }
394 }
395
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100396 // Initialize exit code to dummy value for afterCommand.
397 storedExitCode.set(ExitCode.RESERVED.getNumericExitCode());
398 }
399
400 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100401 * Posts the {@link CommandCompleteEvent}, so that listeners can tidy up. Called by {@link
402 * #afterCommand}, and by BugReport when crashing from an exception in an async thread.
403 */
Janak Ramakrishnan39a55132016-05-23 21:55:20 +0000404 void notifyCommandComplete(int exitCode) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100405 if (!storedExitCode.compareAndSet(ExitCode.RESERVED.getNumericExitCode(), exitCode)) {
406 // This command has already been called, presumably because there is a race between the main
407 // thread and a worker thread that crashed. Don't try to arbitrate the dispute. If the main
408 // thread won the race (unlikely, but possible), this may be incorrectly logged as a success.
409 return;
410 }
Janak Ramakrishnana50b7b72016-06-02 21:46:40 +0000411 workspace.getSkyframeExecutor().getEventBus().post(new CommandCompleteEvent(exitCode));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100412 }
413
414 /**
415 * Hook method called by the BlazeCommandDispatcher after the dispatch of each
416 * command.
417 */
418 @VisibleForTesting
Ulf Adams633f5392015-09-15 11:13:08 +0000419 public void afterCommand(CommandEnvironment env, int exitCode) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100420 // Remove any filters that the command might have added to the reporter.
Ulf Adams633f5392015-09-15 11:13:08 +0000421 env.getReporter().setOutputFilter(OutputFilter.OUTPUT_EVERYTHING);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100422
423 notifyCommandComplete(exitCode);
424
425 for (BlazeModule module : blazeModules) {
426 module.afterCommand();
427 }
428
Ulf Adamsab43b972016-03-30 12:23:50 +0000429 env.getBlazeWorkspace().clearEventBus();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100430
431 try {
432 Profiler.instance().stop();
433 MemoryProfiler.instance().stop();
434 } catch (IOException e) {
Ulf Adams633f5392015-09-15 11:13:08 +0000435 env.getReporter().handle(Event.error("Error while writing profile file: " + e.getMessage()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100436 }
437 }
438
439 // Make sure we keep a strong reference to this logger, so that the
440 // configuration isn't lost when the gc kicks in.
441 private static Logger templateLogger = Logger.getLogger("com.google.devtools.build");
442
443 /**
444 * Configures "com.google.devtools.build.*" loggers to the given
445 * {@code level}. Note: This code relies on static state.
446 */
447 public static void setupLogging(Level level) {
448 templateLogger.setLevel(level);
449 templateLogger.info("Log level: " + templateLogger.getLevel());
450 }
451
452 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100453 * Returns the Clock-instance used for the entire build. Before,
454 * individual classes (such as Profiler) used to specify the type
455 * of clock (e.g. EpochClock) they wanted to use. This made it
456 * difficult to get Blaze working on Windows as some of the clocks
457 * available for Linux aren't (directly) available on Windows.
458 * Setting the Blaze-wide clock upon construction of BlazeRuntime
459 * allows injecting whatever Clock instance should be used from
460 * BlazeMain.
461 *
462 * @return The Blaze-wide clock
463 */
464 public Clock getClock() {
465 return clock;
466 }
467
468 public OptionsProvider getStartupOptionsProvider() {
469 return startupOptionsProvider;
470 }
471
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100472 public Map<String, BlazeCommand> getCommandMap() {
473 return commandMap;
474 }
475
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100476 public void shutdown() {
477 for (BlazeModule module : blazeModules) {
478 module.blazeShutdown();
479 }
480 }
481
482 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100483 * Returns the defaults package for the default settings. Should only be called by commands that
484 * do <i>not</i> process {@link BuildOptions}, since build options can alter the contents of the
485 * defaults package, which will not be reflected here.
486 */
487 public String getDefaultsPackageContent() {
Janak Ramakrishnan1d899602016-04-28 03:48:55 +0000488 return defaultsPackageContent;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100489 }
490
491 /**
492 * Returns the defaults package for the given options taken from an optionsProvider.
493 */
494 public String getDefaultsPackageContent(OptionsClassProvider optionsProvider) {
495 return ruleClassProvider.getDefaultsPackageContent(optionsProvider);
496 }
497
498 /**
499 * Creates a BuildOptions class for the given options taken from an optionsProvider.
500 */
501 public BuildOptions createBuildOptions(OptionsClassProvider optionsProvider) {
502 return ruleClassProvider.createBuildOptions(optionsProvider);
503 }
504
505 /**
506 * An EventBus exception handler that will report the exception to a remote server, if a
507 * handler is registered.
508 */
509 public static final class RemoteExceptionHandler implements SubscriberExceptionHandler {
510 @Override
511 public void handleException(Throwable exception, SubscriberExceptionContext context) {
Eric Fellheimere95a80a2015-02-07 00:40:26 +0000512 LOG.log(Level.SEVERE, "Failure in EventBus subscriber", exception);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100513 LoggingUtil.logToRemote(Level.SEVERE, "Failure in EventBus subscriber.", exception);
514 }
515 }
516
517 /**
518 * An EventBus exception handler that will call BugReport.handleCrash exiting
519 * the current thread.
520 */
521 public static final class BugReportingExceptionHandler implements SubscriberExceptionHandler {
522 @Override
523 public void handleException(Throwable exception, SubscriberExceptionContext context) {
524 BugReport.handleCrash(exception);
525 }
526 }
527
528 /**
529 * Main method for the Blaze server startup. Note: This method logs
530 * exceptions to remote servers. Do not add this to a unittest.
531 */
532 public static void main(Iterable<Class<? extends BlazeModule>> moduleClasses, String[] args) {
533 setupUncaughtHandler(args);
534 List<BlazeModule> modules = createModules(moduleClasses);
Googlerc8c64e72015-03-23 23:22:18 +0000535 // blaze.cc will put --batch first if the user set it.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100536 if (args.length >= 1 && args[0].equals("--batch")) {
537 // Run Blaze in batch mode.
538 System.exit(batchMain(modules, args));
539 }
540 LOG.info("Starting Blaze server with args " + Arrays.toString(args));
541 try {
542 // Run Blaze in server mode.
543 System.exit(serverMain(modules, OutErr.SYSTEM_OUT_ERR, args));
544 } catch (RuntimeException | Error e) { // A definite bug...
545 BugReport.printBug(OutErr.SYSTEM_OUT_ERR, e);
546 BugReport.sendBugReport(e, Arrays.asList(args));
547 System.exit(ExitCode.BLAZE_INTERNAL_ERROR.getNumericExitCode());
548 throw e; // Shouldn't get here.
549 }
550 }
551
552 @VisibleForTesting
553 public static List<BlazeModule> createModules(
554 Iterable<Class<? extends BlazeModule>> moduleClasses) {
555 ImmutableList.Builder<BlazeModule> result = ImmutableList.builder();
556 for (Class<? extends BlazeModule> moduleClass : moduleClasses) {
557 try {
Ulf Adams653a16a2016-08-11 12:39:46 +0000558 BlazeModule module = moduleClass.getConstructor().newInstance();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100559 result.add(module);
560 } catch (Throwable e) {
561 throw new IllegalStateException("Cannot instantiate module " + moduleClass.getName(), e);
562 }
563 }
564
565 return result.build();
566 }
567
568 /**
569 * Generates a string form of a request to be written to the logs,
570 * filtering the user environment to remove anything that looks private.
571 * The current filter criteria removes any variable whose name includes
572 * "auth", "pass", or "cookie".
573 *
574 * @param requestStrings
575 * @return the filtered request to write to the log.
576 */
577 @VisibleForTesting
578 public static String getRequestLogString(List<String> requestStrings) {
579 StringBuilder buf = new StringBuilder();
580 buf.append('[');
581 String sep = "";
582 for (String s : requestStrings) {
583 buf.append(sep);
584 if (s.startsWith("--client_env")) {
585 int varStart = "--client_env=".length();
586 int varEnd = s.indexOf('=', varStart);
587 String varName = s.substring(varStart, varEnd);
588 if (suppressFromLog.matcher(varName).matches()) {
589 buf.append("--client_env=");
590 buf.append(varName);
591 buf.append("=__private_value_removed__");
592 } else {
593 buf.append(s);
594 }
595 } else {
596 buf.append(s);
597 }
598 sep = ", ";
599 }
600 buf.append(']');
601 return buf.toString();
602 }
603
604 /**
605 * Command line options split in to two parts: startup options and everything else.
606 */
607 @VisibleForTesting
608 static class CommandLineOptions {
609 private final List<String> startupArgs;
610 private final List<String> otherArgs;
611
612 CommandLineOptions(List<String> startupArgs, List<String> otherArgs) {
613 this.startupArgs = ImmutableList.copyOf(startupArgs);
614 this.otherArgs = ImmutableList.copyOf(otherArgs);
615 }
616
617 public List<String> getStartupArgs() {
618 return startupArgs;
619 }
620
621 public List<String> getOtherArgs() {
622 return otherArgs;
623 }
624 }
625
626 /**
627 * Splits given arguments into two lists - arguments matching options defined in this class
628 * and everything else, while preserving order in each list.
629 */
630 static CommandLineOptions splitStartupOptions(
631 Iterable<BlazeModule> modules, String... args) {
632 List<String> prefixes = new ArrayList<>();
633 List<Field> startupFields = Lists.newArrayList();
634 for (Class<? extends OptionsBase> defaultOptions
635 : BlazeCommandUtils.getStartupOptions(modules)) {
636 startupFields.addAll(ImmutableList.copyOf(defaultOptions.getFields()));
637 }
638
639 for (Field field : startupFields) {
640 if (field.isAnnotationPresent(Option.class)) {
641 prefixes.add("--" + field.getAnnotation(Option.class).name());
642 if (field.getType() == boolean.class || field.getType() == TriState.class) {
643 prefixes.add("--no" + field.getAnnotation(Option.class).name());
644 }
645 }
646 }
647
648 List<String> startupArgs = new ArrayList<>();
649 List<String> otherArgs = Lists.newArrayList(args);
650
651 for (Iterator<String> argi = otherArgs.iterator(); argi.hasNext(); ) {
652 String arg = argi.next();
653 if (!arg.startsWith("--")) {
654 break; // stop at command - all startup options would be specified before it.
655 }
656 for (String prefix : prefixes) {
657 if (arg.startsWith(prefix)) {
658 startupArgs.add(arg);
659 argi.remove();
660 break;
661 }
662 }
663 }
664 return new CommandLineOptions(startupArgs, otherArgs);
665 }
666
667 private static void captureSigint() {
668 final Thread mainThread = Thread.currentThread();
669 final AtomicInteger numInterrupts = new AtomicInteger();
670
671 final Runnable interruptWatcher = new Runnable() {
672 @Override
673 public void run() {
674 int count = 0;
675 // Not an actual infinite loop because it's run in a daemon thread.
676 while (true) {
677 count++;
678 Uninterruptibles.sleepUninterruptibly(10, TimeUnit.SECONDS);
679 LOG.warning("Slow interrupt number " + count + " in batch mode");
680 ThreadUtils.warnAboutSlowInterrupt();
681 }
682 }
683 };
684
685 new InterruptSignalHandler() {
686 @Override
Janak Ramakrishnan70c57902016-03-10 00:58:59 +0000687 protected void onSignal() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100688 LOG.info("User interrupt");
689 OutErr.SYSTEM_OUT_ERR.printErrLn("Blaze received an interrupt");
690 mainThread.interrupt();
691
692 int curNumInterrupts = numInterrupts.incrementAndGet();
693 if (curNumInterrupts == 1) {
694 Thread interruptWatcherThread = new Thread(interruptWatcher, "interrupt-watcher");
695 interruptWatcherThread.setDaemon(true);
696 interruptWatcherThread.start();
697 } else if (curNumInterrupts == 2) {
698 LOG.warning("Second --batch interrupt: Reverting to JVM SIGINT handler");
699 uninstall();
700 }
701 }
702 };
703 }
704
705 /**
706 * A main method that runs blaze commands in batch mode. The return value indicates the desired
707 * exit status of the program.
708 */
709 private static int batchMain(Iterable<BlazeModule> modules, String[] args) {
710 captureSigint();
711 CommandLineOptions commandLineOptions = splitStartupOptions(modules, args);
712 LOG.info("Running Blaze in batch mode with startup args "
713 + commandLineOptions.getStartupArgs());
714
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100715 BlazeRuntime runtime;
716 try {
Ulf Adams7db73d32016-08-10 12:41:02 +0000717 runtime = newRuntime(modules, commandLineOptions.getStartupArgs());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100718 } catch (OptionsParsingException e) {
719 OutErr.SYSTEM_OUT_ERR.printErr(e.getMessage());
720 return ExitCode.COMMAND_LINE_ERROR.getNumericExitCode();
721 } catch (AbruptExitException e) {
722 OutErr.SYSTEM_OUT_ERR.printErr(e.getMessage());
723 return e.getExitCode().getNumericExitCode();
724 }
725
Ulf Adams47cb9162015-09-18 08:12:30 +0000726 BlazeCommandDispatcher dispatcher = new BlazeCommandDispatcher(runtime);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100727
728 try {
729 LOG.info(getRequestLogString(commandLineOptions.getOtherArgs()));
730 return dispatcher.exec(commandLineOptions.getOtherArgs(), OutErr.SYSTEM_OUT_ERR,
Lukacs Berkice1445f2016-04-19 15:52:55 +0000731 LockingMode.ERROR_OUT, "batch client", runtime.getClock().currentTimeMillis());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100732 } catch (BlazeCommandDispatcher.ShutdownBlazeServerException e) {
733 return e.getExitStatus();
Lukacs Berkice1445f2016-04-19 15:52:55 +0000734 } catch (InterruptedException e) {
735 // This is almost main(), so it's okay to just swallow it. We are exiting soon.
736 return ExitCode.INTERRUPTED.getNumericExitCode();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100737 } finally {
738 runtime.shutdown();
739 dispatcher.shutdown();
740 }
741 }
742
743 /**
744 * A main method that does not send email. The return value indicates the desired exit status of
745 * the program.
746 */
747 private static int serverMain(Iterable<BlazeModule> modules, OutErr outErr, String[] args) {
Lukacs Berkif7633792016-04-21 12:38:29 +0000748 InterruptSignalHandler sigintHandler = null;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100749 try {
Lukacs Berkif7633792016-04-21 12:38:29 +0000750 final RPCServer blazeServer = createBlazeRPCServer(modules, Arrays.asList(args));
751
752 // Register the signal handler.
753 sigintHandler = new InterruptSignalHandler() {
754 @Override
755 protected void onSignal() {
756 LOG.severe("User interrupt");
757 blazeServer.interrupt();
758 }
759 };
760
Lukacs Berkibb0dac72016-04-19 07:21:19 +0000761 blazeServer.serve();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100762 return ExitCode.SUCCESS.getNumericExitCode();
763 } catch (OptionsParsingException e) {
764 outErr.printErr(e.getMessage());
765 return ExitCode.COMMAND_LINE_ERROR.getNumericExitCode();
766 } catch (IOException e) {
767 outErr.printErr("I/O Error: " + e.getMessage());
768 return ExitCode.BUILD_FAILURE.getNumericExitCode();
769 } catch (AbruptExitException e) {
770 outErr.printErr(e.getMessage());
771 return e.getExitCode().getNumericExitCode();
Lukacs Berkif7633792016-04-21 12:38:29 +0000772 } finally {
773 if (sigintHandler != null) {
774 sigintHandler.uninstall();
775 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100776 }
777 }
778
779 private static FileSystem fileSystemImplementation() {
Lukacs Berki28219722016-06-30 12:34:39 +0000780 if ("0".equals(System.getProperty("io.bazel.EnableJni"))) {
Damien Martin-Guillerez8c04e9e2016-01-18 12:53:39 +0000781 // Ignore UnixFileSystem, to be used for bootstrapping.
Dmitry Lomovbe939512016-02-02 20:26:25 +0000782 return OS.getCurrent() == OS.WINDOWS ? new WindowsFileSystem() : new JavaIoFileSystem();
Damien Martin-Guillerez8c04e9e2016-01-18 12:53:39 +0000783 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100784 // The JNI-based UnixFileSystem is faster, but on Windows it is not available.
Lukacs Berki58dbb7f2016-01-29 11:56:29 +0000785 return OS.getCurrent() == OS.WINDOWS ? new WindowsFileSystem() : new UnixFileSystem();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100786 }
787
Lukacs Berki74dcfee2016-07-04 12:57:25 +0000788 private static Subprocess.Factory subprocessFactoryImplementation() {
789 if (!"0".equals(System.getProperty("io.bazel.EnableJni")) && OS.getCurrent() == OS.WINDOWS) {
790 return WindowsSubprocessFactory.INSTANCE;
791 } else {
792 return JavaSubprocessFactory.INSTANCE;
793 }
794 }
795
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100796 /**
797 * Creates and returns a new Blaze RPCServer. Call {@link RPCServer#serve()} to start the server.
798 */
Lukacs Berkibb0dac72016-04-19 07:21:19 +0000799 private static RPCServer createBlazeRPCServer(
800 Iterable<BlazeModule> modules, List<String> args)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100801 throws IOException, OptionsParsingException, AbruptExitException {
Ulf Adams7db73d32016-08-10 12:41:02 +0000802 BlazeRuntime runtime = newRuntime(modules, args);
Lukacs Berkibb0dac72016-04-19 07:21:19 +0000803 BlazeCommandDispatcher dispatcher = new BlazeCommandDispatcher(runtime);
Lukacs Berki8b3b9182016-04-14 08:29:05 +0000804 CommandExecutor commandExecutor = new CommandExecutor(runtime, dispatcher);
Lukacs Berkibb0dac72016-04-19 07:21:19 +0000805
Ulf Adams7db73d32016-08-10 12:41:02 +0000806 BlazeServerStartupOptions startupOptions =
807 runtime.getStartupOptionsProvider().getOptions(BlazeServerStartupOptions.class);
Lukacs Berki3a3c4832016-10-06 14:20:04 +0000808 try {
809 // This is necessary so that Bazel kind of works during bootstrapping, at which time the
810 // gRPC server is not compiled in so that we don't need gRPC for bootstrapping.
811 Class<?> factoryClass = Class.forName(
812 "com.google.devtools.build.lib.server.GrpcServerImpl$Factory");
813 RPCServer.Factory factory = (RPCServer.Factory) factoryClass.getConstructor().newInstance();
814 return factory.create(commandExecutor, runtime.getClock(),
815 startupOptions.commandPort, runtime.getServerDirectory(),
816 startupOptions.maxIdleSeconds);
817 } catch (ReflectiveOperationException | IllegalArgumentException e) {
818 throw new AbruptExitException("gRPC server not compiled in", ExitCode.BLAZE_INTERNAL_ERROR);
Lukacs Berki8b3b9182016-04-14 08:29:05 +0000819 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100820 }
821
822 private static Function<String, String> sourceFunctionForMap(final Map<String, String> map) {
823 return new Function<String, String>() {
824 @Override
825 public String apply(String input) {
826 if (!map.containsKey(input)) {
827 return "default";
828 }
829
830 if (map.get(input).isEmpty()) {
831 return "command line";
832 }
833
834 return map.get(input);
835 }
836 };
837 }
838
839 /**
840 * Parses the command line arguments into a {@link OptionsParser} object.
841 *
842 * <p>This function needs to parse the --option_sources option manually so that the real option
843 * parser can set the source for every option correctly. If that cannot be parsed or is missing,
844 * we just report an unknown source for every startup option.
845 */
846 private static OptionsProvider parseOptions(
847 Iterable<BlazeModule> modules, List<String> args) throws OptionsParsingException {
Ulf Adams7e71f892016-08-10 09:06:50 +0000848 ImmutableList<Class<? extends OptionsBase>> optionClasses =
849 BlazeCommandUtils.getStartupOptions(modules);
850
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100851 // First parse the command line so that we get the option_sources argument
852 OptionsParser parser = OptionsParser.newOptionsParser(optionClasses);
853 parser.setAllowResidue(false);
854 parser.parse(OptionPriority.COMMAND_LINE, null, args);
855 Function<? super String, String> sourceFunction =
856 sourceFunctionForMap(parser.getOptions(BlazeServerStartupOptions.class).optionSources);
857
858 // Then parse the command line again, this time with the correct option sources
859 parser = OptionsParser.newOptionsParser(optionClasses);
860 parser.setAllowResidue(false);
861 parser.parseWithSourceFunction(OptionPriority.COMMAND_LINE, sourceFunction, args);
862 return parser;
863 }
864
865 /**
866 * Creates a new blaze runtime, given the install and output base directories.
867 *
868 * <p>Note: This method can and should only be called once per startup, as it also creates the
869 * filesystem object that will be used for the runtime. So it should only ever be called from the
870 * main method of the Blaze program.
871 *
Ulf Adams7db73d32016-08-10 12:41:02 +0000872 * @param args Blaze startup options.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100873 *
874 * @return a new BlazeRuntime instance initialized with the given filesystem and directories, and
875 * an error string that, if not null, describes a fatal initialization failure that makes
876 * this runtime unsuitable for real commands
877 */
Ulf Adams7db73d32016-08-10 12:41:02 +0000878 private static BlazeRuntime newRuntime(Iterable<BlazeModule> blazeModules, List<String> args)
879 throws AbruptExitException, OptionsParsingException {
880 OptionsProvider options = parseOptions(blazeModules, args);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100881 for (BlazeModule module : blazeModules) {
882 module.globalInit(options);
883 }
884
885 BlazeServerStartupOptions startupOptions = options.getOptions(BlazeServerStartupOptions.class);
Ulf Adamsb5a06f332016-06-21 14:55:49 +0000886 String productName = startupOptions.productName.toLowerCase(Locale.US);
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +0000887
Janak Ramakrishnan19fde1f2016-05-23 21:20:16 +0000888 if (startupOptions.oomMoreEagerlyThreshold != 100) {
Janak Ramakrishnan8cc772e2016-03-23 17:26:12 +0000889 new RetainedHeapLimiter(startupOptions.oomMoreEagerlyThreshold).install();
Janak Ramakrishnan70c57902016-03-10 00:58:59 +0000890 }
Janak Ramakrishnan19fde1f2016-05-23 21:20:16 +0000891 if (startupOptions.oomMoreEagerly) {
892 new OomSignalHandler();
893 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100894 PathFragment workspaceDirectory = startupOptions.workspaceDirectory;
895 PathFragment installBase = startupOptions.installBase;
896 PathFragment outputBase = startupOptions.outputBase;
897
898 OsUtils.maybeForceJNI(installBase); // Must be before first use of JNI.
899
900 // From the point of view of the Java program --install_base and --output_base
901 // are mandatory options, despite the comment in their declarations.
902 if (installBase == null || !installBase.isAbsolute()) { // (includes "" default case)
903 throw new IllegalArgumentException(
904 "Bad --install_base option specified: '" + installBase + "'");
905 }
906 if (outputBase != null && !outputBase.isAbsolute()) { // (includes "" default case)
907 throw new IllegalArgumentException(
908 "Bad --output_base option specified: '" + outputBase + "'");
909 }
910
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100911 FileSystem fs = null;
912 for (BlazeModule module : blazeModules) {
Ulf Adams124bd0a2016-08-10 13:10:46 +0000913 FileSystem moduleFs = module.getFileSystem(options);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100914 if (moduleFs != null) {
915 Preconditions.checkState(fs == null, "more than one module returns a file system");
916 fs = moduleFs;
917 }
918 }
919
920 if (fs == null) {
921 fs = fileSystemImplementation();
922 }
Lukacs Berki74dcfee2016-07-04 12:57:25 +0000923
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100924 Path.setFileSystemForSerialization(fs);
Lukacs Berki74dcfee2016-07-04 12:57:25 +0000925 SubprocessBuilder.setSubprocessFactory(subprocessFactoryImplementation());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100926
927 Path installBasePath = fs.getPath(installBase);
928 Path outputBasePath = fs.getPath(outputBase);
929 Path workspaceDirectoryPath = null;
930 if (!workspaceDirectory.equals(PathFragment.EMPTY_FRAGMENT)) {
931 workspaceDirectoryPath = fs.getPath(workspaceDirectory);
932 }
933
Ulf Adams15a23b92016-08-09 11:46:00 +0000934 ServerDirectories serverDirectories =
935 new ServerDirectories(installBasePath, outputBasePath, startupOptions.installMD5);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100936 Clock clock = BlazeClock.instance();
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +0000937 BlazeRuntime.Builder runtimeBuilder = new BlazeRuntime.Builder()
938 .setProductName(productName)
Ulf Adams15a23b92016-08-09 11:46:00 +0000939 .setServerDirectories(serverDirectories)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100940 .setStartupOptionsProvider(options)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100941 .setClock(clock)
942 // TODO(bazel-team): Make BugReportingExceptionHandler the default.
943 // See bug "Make exceptions in EventBus subscribers fatal"
944 .setEventBusExceptionHandler(
945 startupOptions.fatalEventBusExceptions || !BlazeVersionInfo.instance().isReleasedBlaze()
946 ? new BlazeRuntime.BugReportingExceptionHandler()
947 : new BlazeRuntime.RemoteExceptionHandler());
948
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +0000949 if (System.getenv("TEST_TMPDIR") != null
950 && System.getenv("NO_CRASH_ON_LOGGING_IN_TEST") == null) {
951 LoggingUtil.installRemoteLogger(getTestCrashLogger());
952 }
953
Ulf Adams345e15e2016-07-07 13:27:28 +0000954 runtimeBuilder.addBlazeModule(new BuiltinCommandModule());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100955 for (BlazeModule blazeModule : blazeModules) {
956 runtimeBuilder.addBlazeModule(blazeModule);
957 }
958
959 BlazeRuntime runtime = runtimeBuilder.build();
Ulf Adamsea46dfc2016-08-10 08:44:19 +0000960
961 BlazeDirectories directories =
962 new BlazeDirectories(
963 serverDirectories, workspaceDirectoryPath, startupOptions.deepExecRoot, productName);
964 BinTools binTools;
965 try {
966 binTools = BinTools.forProduction(directories);
967 } catch (IOException e) {
968 throw new AbruptExitException(
969 "Cannot enumerate embedded binaries: " + e.getMessage(),
970 ExitCode.LOCAL_ENVIRONMENTAL_ERROR);
971 }
972 runtime.initWorkspace(directories, binTools);
973
Nathan Harmatad4803012015-09-08 20:03:22 +0000974 AutoProfiler.setClock(runtime.getClock());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100975 BugReport.setRuntime(runtime);
976 return runtime;
977 }
978
979 /**
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +0000980 * Returns a logger that crashes as soon as it's written to, since tests should not cause events
981 * that would be logged.
982 */
983 @VisibleForTesting
984 public static Future<Logger> getTestCrashLogger() {
985 Logger crashLogger = Logger.getAnonymousLogger();
986 crashLogger.addHandler(
987 new Handler() {
988 @Override
989 public void publish(LogRecord record) {
Michajlo Matijkiwf5cc0b22016-01-20 17:44:44 +0000990 System.err.println("Remote logging disabled for testing, forcing abrupt shutdown.");
991 System.err.printf("%s#%s: %s\n",
992 record.getSourceClassName(),
993 record.getSourceMethodName(),
994 record.getMessage());
995
Janak Ramakrishnana88e65b2015-09-17 15:07:26 +0000996 Throwable e = record.getThrown();
Michajlo Matijkiwf5cc0b22016-01-20 17:44:44 +0000997 if (e != null) {
998 e.printStackTrace();
Janak Ramakrishnana88e65b2015-09-17 15:07:26 +0000999 }
Michajlo Matijkiwf5cc0b22016-01-20 17:44:44 +00001000
1001 Runtime.getRuntime().halt(ExitCode.BLAZE_INTERNAL_ERROR.getNumericExitCode());
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +00001002 }
1003
1004 @Override
1005 public void flush() {
1006 throw new IllegalStateException();
1007 }
1008
1009 @Override
1010 public void close() {
1011 throw new IllegalStateException();
1012 }
1013 });
1014 return Futures.immediateFuture(crashLogger);
1015 }
1016
1017 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001018 * Make sure async threads cannot be orphaned. This method makes sure bugs are reported to
1019 * telemetry and the proper exit code is reported.
1020 */
1021 private static void setupUncaughtHandler(final String[] args) {
Janak Ramakrishnanb6582fa2016-03-14 16:19:40 +00001022 Thread.setDefaultUncaughtExceptionHandler(
1023 new Thread.UncaughtExceptionHandler() {
1024 @Override
1025 public void uncaughtException(Thread thread, Throwable throwable) {
1026 BugReport.handleCrash(throwable, args);
1027 }
1028 });
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001029 }
1030
Luis Fernando Pino Duquebe102182016-05-23 14:03:55 +00001031 public String getProductName() {
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +00001032 return productName;
Luis Fernando Pino Duquebe102182016-05-23 14:03:55 +00001033 }
1034
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001035 /**
1036 * A builder for {@link BlazeRuntime} objects. The only required fields are the {@link
1037 * BlazeDirectories}, and the {@link RuleClassProvider} (except for testing). All other fields
1038 * have safe default values.
1039 *
1040 * <p>If a {@link ConfigurationFactory} is set, then the builder ignores the host system flag.
1041 * <p>The default behavior of the BlazeRuntime's EventBus is to exit when a subscriber throws
1042 * an exception. Please plan appropriately.
1043 */
1044 public static class Builder {
Ulf Adams15a23b92016-08-09 11:46:00 +00001045 private ServerDirectories serverDirectories;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001046 private Clock clock;
1047 private OptionsProvider startupOptionsProvider;
Ulf Adams47cb9162015-09-18 08:12:30 +00001048 private final List<BlazeModule> blazeModules = new ArrayList<>();
Ulf Adams8b56c8c2016-04-12 13:45:56 +00001049 private SubscriberExceptionHandler eventBusExceptionHandler = new RemoteExceptionHandler();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001050 private UUID instanceId;
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +00001051 private String productName;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001052
1053 public BlazeRuntime build() throws AbruptExitException {
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +00001054 Preconditions.checkNotNull(productName);
Ulf Adams15a23b92016-08-09 11:46:00 +00001055 Preconditions.checkNotNull(serverDirectories);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001056 Preconditions.checkNotNull(startupOptionsProvider);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001057 Clock clock = (this.clock == null) ? BlazeClock.instance() : this.clock;
1058 UUID instanceId = (this.instanceId == null) ? UUID.randomUUID() : this.instanceId;
1059
1060 Preconditions.checkNotNull(clock);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001061
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001062 for (BlazeModule module : blazeModules) {
1063 module.blazeStartup(startupOptionsProvider,
Ulf Adams15a23b92016-08-09 11:46:00 +00001064 BlazeVersionInfo.instance(), instanceId, serverDirectories, clock);
Ulf Adams8b56c8c2016-04-12 13:45:56 +00001065 }
Ulf Adams345e15e2016-07-07 13:27:28 +00001066 ServerBuilder serverBuilder = new ServerBuilder();
Ulf Adams54eeac52016-08-29 09:07:35 +00001067 serverBuilder.addQueryOutputFormatters(OutputFormatter.getDefaultFormatters());
Ulf Adams8b56c8c2016-04-12 13:45:56 +00001068 for (BlazeModule module : blazeModules) {
Ulf Adams345e15e2016-07-07 13:27:28 +00001069 module.serverInit(startupOptionsProvider, serverBuilder);
Nathan Harmataf0cc5b82016-03-18 14:56:28 +00001070 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001071
1072 ConfiguredRuleClassProvider.Builder ruleClassBuilder =
1073 new ConfiguredRuleClassProvider.Builder();
1074 for (BlazeModule module : blazeModules) {
1075 module.initializeRuleClasses(ruleClassBuilder);
1076 }
1077
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001078 ConfiguredRuleClassProvider ruleClassProvider = ruleClassBuilder.build();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001079
1080 List<PackageFactory.EnvironmentExtension> extensions = new ArrayList<>();
1081 for (BlazeModule module : blazeModules) {
1082 extensions.add(module.getPackageEnvironmentExtension());
1083 }
1084
Nathan Harmata42fb5602016-05-25 20:32:08 +00001085 Package.Builder.Helper packageBuilderHelper = null;
1086 for (BlazeModule module : blazeModules) {
Nathan Harmatacaa000a2016-06-07 17:46:19 +00001087 Package.Builder.Helper candidateHelper =
Ulf Adams15a23b92016-08-09 11:46:00 +00001088 module.getPackageBuilderHelper(ruleClassProvider, serverDirectories.getFileSystem());
Nathan Harmata42fb5602016-05-25 20:32:08 +00001089 if (candidateHelper != null) {
1090 Preconditions.checkState(packageBuilderHelper == null,
1091 "more than one module defines a package builder helper");
1092 packageBuilderHelper = candidateHelper;
1093 }
1094 }
1095 if (packageBuilderHelper == null) {
1096 packageBuilderHelper = Package.Builder.DefaultHelper.INSTANCE;
1097 }
1098
Janak Ramakrishnan02498412016-05-11 22:28:43 +00001099 PackageFactory packageFactory =
1100 new PackageFactory(
1101 ruleClassProvider,
Ulf Adams345e15e2016-07-07 13:27:28 +00001102 ruleClassBuilder.getPlatformRegexps(),
1103 serverBuilder.getAttributeContainerFactory(),
Janak Ramakrishnan02498412016-05-11 22:28:43 +00001104 extensions,
Nathan Harmata42fb5602016-05-25 20:32:08 +00001105 BlazeVersionInfo.instance().getVersion(),
1106 packageBuilderHelper);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001107
Ulf Adams345e15e2016-07-07 13:27:28 +00001108 ConfigurationFactory configurationFactory =
1109 new ConfigurationFactory(
1110 ruleClassProvider.getConfigurationCollectionFactory(),
1111 ruleClassProvider.getConfigurationFragments());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001112
1113 ProjectFile.Provider projectFileProvider = null;
1114 for (BlazeModule module : blazeModules) {
1115 ProjectFile.Provider candidate = module.createProjectFileProvider();
1116 if (candidate != null) {
1117 Preconditions.checkState(projectFileProvider == null,
1118 "more than one module defines a project file provider");
1119 projectFileProvider = candidate;
1120 }
1121 }
1122
Ulf Adamsea46dfc2016-08-10 08:44:19 +00001123 return new BlazeRuntime(
1124 serverBuilder.getQueryEnvironmentFactory(),
Ulf Adams6bfd2c52016-08-29 08:00:39 +00001125 serverBuilder.getQueryFunctions(),
Ulf Adams54eeac52016-08-29 09:07:35 +00001126 serverBuilder.getQueryOutputFormatters(),
Ulf Adamsea46dfc2016-08-10 08:44:19 +00001127 packageFactory,
1128 ruleClassProvider,
1129 configurationFactory,
Ulf Adams6bfd2c52016-08-29 08:00:39 +00001130 serverBuilder.getInfoItems(),
Ulf Adamsea46dfc2016-08-10 08:44:19 +00001131 clock,
1132 startupOptionsProvider,
1133 ImmutableList.copyOf(blazeModules),
1134 eventBusExceptionHandler,
1135 projectFileProvider,
1136 serverBuilder.getInvocationPolicy(),
1137 serverBuilder.getCommands(),
1138 productName);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001139 }
1140
Luis Fernando Pino Duque623cdf82016-05-31 16:21:46 +00001141 public Builder setProductName(String productName) {
1142 this.productName = productName;
1143 return this;
1144 }
1145
Ulf Adams15a23b92016-08-09 11:46:00 +00001146 public Builder setServerDirectories(ServerDirectories serverDirectories) {
1147 this.serverDirectories = serverDirectories;
1148 return this;
1149 }
1150
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001151 public Builder setClock(Clock clock) {
1152 this.clock = clock;
1153 return this;
1154 }
1155
1156 public Builder setStartupOptionsProvider(OptionsProvider startupOptionsProvider) {
1157 this.startupOptionsProvider = startupOptionsProvider;
1158 return this;
1159 }
1160
1161 public Builder addBlazeModule(BlazeModule blazeModule) {
1162 blazeModules.add(blazeModule);
1163 return this;
1164 }
1165
1166 public Builder setInstanceId(UUID id) {
1167 instanceId = id;
1168 return this;
1169 }
1170
1171 @VisibleForTesting
1172 public Builder setEventBusExceptionHandler(
1173 SubscriberExceptionHandler eventBusExceptionHandler) {
1174 this.eventBusExceptionHandler = eventBusExceptionHandler;
1175 return this;
1176 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001177 }
1178}