blob: 2004ffe64a56592808e1ada50c140ac66b646dbb [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.base.Predicate;
20import com.google.common.base.Predicates;
21import com.google.common.collect.ImmutableList;
22import com.google.common.collect.ImmutableMap;
Ulf Adams47cb9162015-09-18 08:12:30 +000023import com.google.common.collect.Iterables;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010024import com.google.common.collect.Lists;
25import com.google.common.collect.Sets;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010026import com.google.common.eventbus.SubscriberExceptionContext;
27import com.google.common.eventbus.SubscriberExceptionHandler;
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +000028import com.google.common.util.concurrent.Futures;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010029import com.google.common.util.concurrent.Uninterruptibles;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010030import com.google.devtools.build.lib.analysis.BlazeDirectories;
31import com.google.devtools.build.lib.analysis.BlazeVersionInfo;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010032import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
33import com.google.devtools.build.lib.analysis.WorkspaceStatusAction;
34import com.google.devtools.build.lib.analysis.config.BinTools;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010035import com.google.devtools.build.lib.analysis.config.BuildOptions;
36import com.google.devtools.build.lib.analysis.config.ConfigurationFactory;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010037import com.google.devtools.build.lib.events.Event;
38import com.google.devtools.build.lib.events.OutputFilter;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010039import com.google.devtools.build.lib.packages.PackageFactory;
40import com.google.devtools.build.lib.packages.Preprocessor;
41import com.google.devtools.build.lib.packages.RuleClassProvider;
Nathan Harmatad4803012015-09-08 20:03:22 +000042import com.google.devtools.build.lib.profiler.AutoProfiler;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010043import com.google.devtools.build.lib.profiler.MemoryProfiler;
44import com.google.devtools.build.lib.profiler.ProfilePhase;
45import com.google.devtools.build.lib.profiler.Profiler;
46import com.google.devtools.build.lib.profiler.Profiler.ProfiledTaskKinds;
47import com.google.devtools.build.lib.profiler.ProfilerTask;
Nathan Harmataf0cc5b82016-03-18 14:56:28 +000048import com.google.devtools.build.lib.query2.AbstractBlazeQueryEnvironment;
49import com.google.devtools.build.lib.query2.QueryEnvironmentFactory;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010050import com.google.devtools.build.lib.query2.output.OutputFormatter;
51import com.google.devtools.build.lib.rules.test.CoverageReportActionFactory;
Lukacs Berkice1445f2016-04-19 15:52:55 +000052import com.google.devtools.build.lib.runtime.BlazeCommandDispatcher.LockingMode;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010053import com.google.devtools.build.lib.runtime.commands.BuildCommand;
54import com.google.devtools.build.lib.runtime.commands.CanonicalizeCommand;
55import com.google.devtools.build.lib.runtime.commands.CleanCommand;
Kristina Chodorow5cb95fc2015-06-12 15:35:49 +000056import com.google.devtools.build.lib.runtime.commands.DumpCommand;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010057import com.google.devtools.build.lib.runtime.commands.HelpCommand;
58import com.google.devtools.build.lib.runtime.commands.InfoCommand;
Lukacs Berki662166f2015-05-29 10:50:25 +000059import com.google.devtools.build.lib.runtime.commands.MobileInstallCommand;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010060import com.google.devtools.build.lib.runtime.commands.ProfileCommand;
61import com.google.devtools.build.lib.runtime.commands.QueryCommand;
62import com.google.devtools.build.lib.runtime.commands.RunCommand;
63import com.google.devtools.build.lib.runtime.commands.ShutdownCommand;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010064import com.google.devtools.build.lib.runtime.commands.TestCommand;
65import com.google.devtools.build.lib.runtime.commands.VersionCommand;
Luis Fernando Pino Duqueb1b28b62016-02-25 14:25:19 +000066import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy;
Lukacs Berkibb0dac72016-04-19 07:21:19 +000067import com.google.devtools.build.lib.server.AfUnixServer;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010068import com.google.devtools.build.lib.server.RPCServer;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010069import com.google.devtools.build.lib.server.signal.InterruptSignalHandler;
70import com.google.devtools.build.lib.skyframe.DiffAwareness;
71import com.google.devtools.build.lib.skyframe.PrecomputedValue;
72import com.google.devtools.build.lib.skyframe.SequencedSkyframeExecutorFactory;
Janak Ramakrishnan6ddbb6e2015-07-28 21:39:22 +000073import com.google.devtools.build.lib.skyframe.SkyValueDirtinessChecker;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010074import com.google.devtools.build.lib.skyframe.SkyframeExecutor;
75import com.google.devtools.build.lib.skyframe.SkyframeExecutorFactory;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010076import com.google.devtools.build.lib.util.AbruptExitException;
77import com.google.devtools.build.lib.util.BlazeClock;
78import com.google.devtools.build.lib.util.Clock;
79import com.google.devtools.build.lib.util.ExitCode;
80import com.google.devtools.build.lib.util.LoggingUtil;
81import com.google.devtools.build.lib.util.OS;
82import com.google.devtools.build.lib.util.OsUtils;
Mark Schaller6df81792015-12-10 18:47:47 +000083import com.google.devtools.build.lib.util.Preconditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010084import com.google.devtools.build.lib.util.ThreadUtils;
85import com.google.devtools.build.lib.util.io.OutErr;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010086import com.google.devtools.build.lib.vfs.FileSystem;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010087import com.google.devtools.build.lib.vfs.JavaIoFileSystem;
88import com.google.devtools.build.lib.vfs.Path;
89import com.google.devtools.build.lib.vfs.PathFragment;
90import com.google.devtools.build.lib.vfs.UnixFileSystem;
Lukacs Berki58dbb7f2016-01-29 11:56:29 +000091import com.google.devtools.build.lib.vfs.WindowsFileSystem;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010092import com.google.devtools.build.skyframe.SkyFunction;
93import com.google.devtools.build.skyframe.SkyFunctionName;
94import com.google.devtools.common.options.Option;
95import com.google.devtools.common.options.OptionPriority;
96import com.google.devtools.common.options.OptionsBase;
97import com.google.devtools.common.options.OptionsClassProvider;
98import com.google.devtools.common.options.OptionsParser;
99import com.google.devtools.common.options.OptionsParsingException;
100import com.google.devtools.common.options.OptionsProvider;
101import com.google.devtools.common.options.TriState;
102
103import java.io.BufferedOutputStream;
104import java.io.IOException;
105import java.io.OutputStream;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100106import java.lang.reflect.Field;
107import java.util.ArrayList;
108import java.util.Arrays;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100109import java.util.Date;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100110import java.util.Iterator;
Ulf Adams47cb9162015-09-18 08:12:30 +0000111import java.util.LinkedHashMap;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100112import java.util.List;
113import java.util.Map;
114import java.util.Set;
115import java.util.UUID;
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +0000116import java.util.concurrent.Future;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100117import java.util.concurrent.TimeUnit;
118import java.util.concurrent.atomic.AtomicInteger;
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +0000119import java.util.logging.Handler;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100120import java.util.logging.Level;
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +0000121import java.util.logging.LogRecord;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100122import java.util.logging.Logger;
123import java.util.regex.Pattern;
124
125import javax.annotation.Nullable;
126
127/**
Ulf Adams633f5392015-09-15 11:13:08 +0000128 * The BlazeRuntime class encapsulates the immutable configuration of the current instance. These
129 * runtime settings and services are available to most parts of any Blaze application for the
130 * duration of the batch run or server lifetime.
131 *
132 * <p>The parts specific to the current command are stored in {@link CommandEnvironment}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100133 */
134public final class BlazeRuntime {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100135 private static final Pattern suppressFromLog = Pattern.compile(".*(auth|pass|cookie).*",
136 Pattern.CASE_INSENSITIVE);
137
138 private static final Logger LOG = Logger.getLogger(BlazeRuntime.class.getName());
139
Ulf Adams50e7db62015-10-20 09:14:16 +0000140 private final Iterable<BlazeModule> blazeModules;
141 private final Map<String, BlazeCommand> commandMap = new LinkedHashMap<>();
142 private final Clock clock;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100143
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100144 private final PackageFactory packageFactory;
145 private final ConfigurationFactory configurationFactory;
146 private final ConfiguredRuleClassProvider ruleClassProvider;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100147
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100148 private final AtomicInteger storedExitCode = new AtomicInteger();
149
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100150 // We pass this through here to make it available to the MasterLogWriter.
151 private final OptionsProvider startupOptionsProvider;
152
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100153 private final ProjectFile.Provider projectFileProvider;
Luis Fernando Pino Duqueb1b28b62016-02-25 14:25:19 +0000154 @Nullable
155 private final InvocationPolicy invocationPolicy;
Ulf Adamsfe77c492016-03-21 15:22:19 +0000156 private final QueryEnvironmentFactory queryEnvironmentFactory;
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000157 private final SubscriberExceptionHandler eventBusExceptionHandler;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100158
Ulf Adams50e7db62015-10-20 09:14:16 +0000159 // Workspace state (currently exactly one workspace per server)
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000160 private BlazeWorkspace workspace;
Ulf Adams50e7db62015-10-20 09:14:16 +0000161
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000162 private BlazeRuntime(
Nathan Harmataf0cc5b82016-03-18 14:56:28 +0000163 QueryEnvironmentFactory queryEnvironmentFactory,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100164 PackageFactory pkgFactory, ConfiguredRuleClassProvider ruleClassProvider,
Kristina Chodorowb579b942015-03-02 15:51:58 +0000165 ConfigurationFactory configurationFactory, Clock clock,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100166 OptionsProvider startupOptionsProvider, Iterable<BlazeModule> blazeModules,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100167 SubscriberExceptionHandler eventBusExceptionHandler,
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000168 ProjectFile.Provider projectFileProvider,
Luis Fernando Pino Duqueb1b28b62016-02-25 14:25:19 +0000169 InvocationPolicy invocationPolicy, Iterable<BlazeCommand> commands) {
Ulf Adams50e7db62015-10-20 09:14:16 +0000170 // Server state
171 this.blazeModules = blazeModules;
172 overrideCommands(commands);
173
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100174 this.packageFactory = pkgFactory;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100175 this.projectFileProvider = projectFileProvider;
Luis Fernando Pino Duqueb1b28b62016-02-25 14:25:19 +0000176 this.invocationPolicy = invocationPolicy;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100177
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100178 this.ruleClassProvider = ruleClassProvider;
179 this.configurationFactory = configurationFactory;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100180 this.clock = clock;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100181 this.startupOptionsProvider = startupOptionsProvider;
Nathan Harmataf0cc5b82016-03-18 14:56:28 +0000182 this.queryEnvironmentFactory = queryEnvironmentFactory;
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000183 this.eventBusExceptionHandler = eventBusExceptionHandler;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100184 }
185
Luis Fernando Pino Duqueb1b28b62016-02-25 14:25:19 +0000186 private static InvocationPolicy createInvocationPolicyFromModules(
187 InvocationPolicy initialInvocationPolicy,
188 Iterable<BlazeModule> modules) {
189 InvocationPolicy.Builder builder = InvocationPolicy.newBuilder();
190 builder.mergeFrom(initialInvocationPolicy);
191 // Merge the policies from the modules
192 for (BlazeModule module : modules) {
193 InvocationPolicy modulePolicy = module.getInvocationPolicy();
194 if (modulePolicy != null) {
195 builder.mergeFrom(module.getInvocationPolicy());
196 }
197 }
198 return builder.build();
199 }
200
Ulf Adams8b56c8c2016-04-12 13:45:56 +0000201 public void initWorkspace(BlazeDirectories directories, BinTools binTools)
202 throws AbruptExitException {
203 SkyframeExecutorFactory skyframeExecutorFactory = null;
204 for (BlazeModule module : blazeModules) {
205 SkyframeExecutorFactory skyFactory = module.getSkyframeExecutorFactory(directories);
206 if (skyFactory != null) {
207 Preconditions.checkState(skyframeExecutorFactory == null,
208 "At most one Skyframe factory supported. But found two: %s and %s", skyFactory,
209 skyframeExecutorFactory);
210 skyframeExecutorFactory = skyFactory;
211 }
212 }
213 if (skyframeExecutorFactory == null) {
214 skyframeExecutorFactory = new SequencedSkyframeExecutorFactory();
215 }
216
217 WorkspaceStatusAction.Factory workspaceStatusActionFactory = null;
218 for (BlazeModule module : blazeModules) {
219 WorkspaceStatusAction.Factory candidate = module.getWorkspaceStatusActionFactory();
220 if (candidate != null) {
221 Preconditions.checkState(workspaceStatusActionFactory == null,
222 "more than one module defines a workspace status action factory");
223 workspaceStatusActionFactory = candidate;
224 }
225 }
226
227 Iterable<DiffAwareness.Factory> diffAwarenessFactories;
228 {
229 ImmutableList.Builder<DiffAwareness.Factory> builder = new ImmutableList.Builder<>();
230 boolean watchFS = startupOptionsProvider != null
231 && startupOptionsProvider.getOptions(BlazeServerStartupOptions.class).watchFS;
232 for (BlazeModule module : blazeModules) {
233 builder.addAll(module.getDiffAwarenessFactories(watchFS));
234 }
235 diffAwarenessFactories = builder.build();
236 }
237
238 // Merge filters from Blaze modules that allow some action inputs to be missing.
239 Predicate<PathFragment> allowedMissingInputs = null;
240 for (BlazeModule module : blazeModules) {
241 Predicate<PathFragment> modulePredicate = module.getAllowedMissingInputs();
242 if (modulePredicate != null) {
243 Preconditions.checkArgument(allowedMissingInputs == null,
244 "More than one Blaze module allows missing inputs.");
245 allowedMissingInputs = modulePredicate;
246 }
247 }
248 if (allowedMissingInputs == null) {
249 allowedMissingInputs = Predicates.alwaysFalse();
250 }
251
252 Preprocessor.Factory.Supplier preprocessorFactorySupplier = null;
253 for (BlazeModule module : blazeModules) {
254 Preprocessor.Factory.Supplier modulePreprocessorFactorySupplier =
255 module.getPreprocessorFactorySupplier();
256 if (modulePreprocessorFactorySupplier != null) {
257 Preconditions.checkState(preprocessorFactorySupplier == null,
258 "more than one module defines a preprocessor factory supplier");
259 preprocessorFactorySupplier = modulePreprocessorFactorySupplier;
260 }
261 }
262 if (preprocessorFactorySupplier == null) {
263 preprocessorFactorySupplier = Preprocessor.Factory.Supplier.NullSupplier.INSTANCE;
264 }
265
266 // We use an immutable map builder for the nice side effect that it throws if a duplicate key
267 // is inserted.
268 ImmutableMap.Builder<SkyFunctionName, SkyFunction> skyFunctions = ImmutableMap.builder();
269 for (BlazeModule module : blazeModules) {
270 skyFunctions.putAll(module.getSkyFunctions(directories));
271 }
272
273 ImmutableList.Builder<PrecomputedValue.Injected> precomputedValues = ImmutableList.builder();
274 for (BlazeModule module : blazeModules) {
275 precomputedValues.addAll(module.getPrecomputedSkyframeValues());
276 }
277
278 ImmutableList.Builder<SkyValueDirtinessChecker> customDirtinessCheckers =
279 ImmutableList.builder();
280 for (BlazeModule module : blazeModules) {
281 customDirtinessCheckers.addAll(module.getCustomDirtinessCheckers());
282 }
283
284 SkyframeExecutor skyframeExecutor = skyframeExecutorFactory.create(
285 packageFactory,
286 directories,
287 binTools,
288 workspaceStatusActionFactory,
289 ruleClassProvider.getBuildInfoFactories(),
290 diffAwarenessFactories,
291 allowedMissingInputs,
292 preprocessorFactorySupplier,
293 skyFunctions.build(),
294 precomputedValues.build(),
295 customDirtinessCheckers.build());
296 this.workspace = new BlazeWorkspace(
297 this, directories, skyframeExecutor, eventBusExceptionHandler, workspaceStatusActionFactory,
298 binTools);
299 }
300
Ulf Adams16aa2542016-03-30 07:26:46 +0000301 @Nullable public CoverageReportActionFactory getCoverageReportActionFactory() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100302 CoverageReportActionFactory firstFactory = null;
303 for (BlazeModule module : blazeModules) {
304 CoverageReportActionFactory factory = module.getCoverageReportFactory();
305 if (factory != null) {
306 Preconditions.checkState(firstFactory == null,
307 "only one Blaze Module can have a Coverage Report Factory");
308 firstFactory = factory;
309 }
310 }
311 return firstFactory;
312 }
313
314 /**
Ulf Adams47cb9162015-09-18 08:12:30 +0000315 * Adds the given command under the given name to the map of commands.
316 *
317 * @throws AssertionError if the name is already used by another command.
318 */
319 private void addCommand(BlazeCommand command) {
320 String name = command.getClass().getAnnotation(Command.class).name();
321 if (commandMap.containsKey(name)) {
322 throw new IllegalStateException("Command name or alias " + name + " is already used.");
323 }
324 commandMap.put(name, command);
325 }
326
327 final void overrideCommands(Iterable<BlazeCommand> commands) {
328 commandMap.clear();
329 for (BlazeCommand command : commands) {
330 addCommand(command);
331 }
332 for (BlazeModule module : blazeModules) {
333 for (BlazeCommand command : module.getCommands()) {
334 addCommand(command);
335 }
336 }
337 }
338
Ulf Adams633f5392015-09-15 11:13:08 +0000339 public CommandEnvironment initCommand() {
Ulf Adams69cc0032016-03-30 13:52:25 +0000340 return workspace.initCommand();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100341 }
342
Luis Fernando Pino Duqueb1b28b62016-02-25 14:25:19 +0000343 @Nullable
344 public InvocationPolicy getInvocationPolicy() {
345 return invocationPolicy;
346 }
347
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100348 /**
349 * Conditionally enable profiling.
350 */
Ulf Adams633f5392015-09-15 11:13:08 +0000351 private final boolean initProfiler(CommandEnvironment env, CommonCommandOptions options,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100352 UUID buildID, long execStartTimeNanos) {
353 OutputStream out = null;
354 boolean recordFullProfilerData = false;
355 ProfiledTaskKinds profiledTasks = ProfiledTaskKinds.NONE;
356
357 try {
358 if (options.profilePath != null) {
Ulf Adamsab43b972016-03-30 12:23:50 +0000359 Path profilePath = env.getWorkspace().getRelative(options.profilePath);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100360
361 recordFullProfilerData = options.recordFullProfilerData;
362 out = new BufferedOutputStream(profilePath.getOutputStream(), 1024 * 1024);
Ulf Adams633f5392015-09-15 11:13:08 +0000363 env.getReporter().handle(Event.info("Writing profile data to '" + profilePath + "'"));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100364 profiledTasks = ProfiledTaskKinds.ALL;
365 } else if (options.alwaysProfileSlowOperations) {
366 recordFullProfilerData = false;
367 out = null;
368 profiledTasks = ProfiledTaskKinds.SLOWEST;
369 }
370 if (profiledTasks != ProfiledTaskKinds.NONE) {
371 Profiler.instance().start(profiledTasks, out,
Ulf Adamsab43b972016-03-30 12:23:50 +0000372 "Blaze profile for " + env.getOutputBase() + " at " + new Date()
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100373 + ", build ID: " + buildID,
374 recordFullProfilerData, clock, execStartTimeNanos);
375 return true;
376 }
377 } catch (IOException e) {
Ulf Adams633f5392015-09-15 11:13:08 +0000378 env.getReporter().handle(Event.error("Error while creating profile file: " + e.getMessage()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100379 }
380 return false;
381 }
382
Ulf Adamsab43b972016-03-30 12:23:50 +0000383 public BlazeWorkspace getWorkspace() {
384 return workspace;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100385 }
386
387 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100388 * The directory in which blaze stores the server state - that is, the socket
389 * file and a log.
390 */
Ulf Adams94b72db2016-03-30 11:58:37 +0000391 private Path getServerDirectory() {
Ulf Adamsab43b972016-03-30 12:23:50 +0000392 return getWorkspace().getDirectories().getOutputBase().getChild("server");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100393 }
394
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100395 /**
Nathan Harmataf0cc5b82016-03-18 14:56:28 +0000396 * Returns the {@link QueryEnvironmentFactory} that should be used to create a
397 * {@link AbstractBlazeQueryEnvironment}, whenever one is needed.
398 */
399 public QueryEnvironmentFactory getQueryEnvironmentFactory() {
400 return queryEnvironmentFactory;
401 }
402
403 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100404 * Returns the package factory.
405 */
406 public PackageFactory getPackageFactory() {
407 return packageFactory;
408 }
409
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100410 public ImmutableList<OutputFormatter> getQueryOutputFormatters() {
411 ImmutableList.Builder<OutputFormatter> result = ImmutableList.builder();
412 result.addAll(OutputFormatter.getDefaultFormatters());
413 for (BlazeModule module : blazeModules) {
414 result.addAll(module.getQueryOutputFormatters());
415 }
416
417 return result.build();
418 }
419
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100420 /**
421 * Returns the rule class provider.
422 */
423 public ConfiguredRuleClassProvider getRuleClassProvider() {
424 return ruleClassProvider;
425 }
426
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100427 public Iterable<BlazeModule> getBlazeModules() {
428 return blazeModules;
429 }
430
431 @SuppressWarnings("unchecked")
432 public <T extends BlazeModule> T getBlazeModule(Class<T> moduleClass) {
433 for (BlazeModule module : blazeModules) {
434 if (module.getClass() == moduleClass) {
435 return (T) module;
436 }
437 }
438
439 return null;
440 }
441
442 public ConfigurationFactory getConfigurationFactory() {
443 return configurationFactory;
444 }
445
446 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100447 * Returns a provider for project file objects. Can be null if no such provider was set by any of
448 * the modules.
449 */
450 @Nullable
451 public ProjectFile.Provider getProjectFileProvider() {
452 return projectFileProvider;
453 }
454
455 /**
456 * Hook method called by the BlazeCommandDispatcher prior to the dispatch of
457 * each command.
458 *
459 * @param options The CommonCommandOptions used by every command.
460 * @throws AbruptExitException if this command is unsuitable to be run as specified
461 */
Ulf Adams50e7db62015-10-20 09:14:16 +0000462 void beforeCommand(CommandEnvironment env, CommonCommandOptions options, long execStartTimeNanos)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100463 throws AbruptExitException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100464 // Conditionally enable profiling
465 // We need to compensate for launchTimeNanos (measurements taken outside of the jvm).
466 long startupTimeNanos = options.startupTime * 1000000L;
Ulf Adams3815b4c2015-09-18 07:34:13 +0000467 if (initProfiler(env, options, env.getCommandId(), execStartTimeNanos - startupTimeNanos)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100468 Profiler profiler = Profiler.instance();
469
470 // Instead of logEvent() we're calling the low level function to pass the timings we took in
471 // the launcher. We're setting the INIT phase marker so that it follows immediately the LAUNCH
472 // phase.
473 profiler.logSimpleTaskDuration(execStartTimeNanos - startupTimeNanos, 0, ProfilerTask.PHASE,
474 ProfilePhase.LAUNCH.description);
475 profiler.logSimpleTaskDuration(execStartTimeNanos, 0, ProfilerTask.PHASE,
476 ProfilePhase.INIT.description);
477 }
478
479 if (options.memoryProfilePath != null) {
Ulf Adamsc5855302015-10-20 08:46:38 +0000480 Path memoryProfilePath = env.getWorkingDirectory().getRelative(options.memoryProfilePath);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100481 try {
482 MemoryProfiler.instance().start(memoryProfilePath.getOutputStream());
483 } catch (IOException e) {
Ulf Adams633f5392015-09-15 11:13:08 +0000484 env.getReporter().handle(
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100485 Event.error("Error while creating memory profile file: " + e.getMessage()));
486 }
487 }
488
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100489 // Initialize exit code to dummy value for afterCommand.
490 storedExitCode.set(ExitCode.RESERVED.getNumericExitCode());
491 }
492
493 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100494 * Posts the {@link CommandCompleteEvent}, so that listeners can tidy up. Called by {@link
495 * #afterCommand}, and by BugReport when crashing from an exception in an async thread.
496 */
497 public void notifyCommandComplete(int exitCode) {
498 if (!storedExitCode.compareAndSet(ExitCode.RESERVED.getNumericExitCode(), exitCode)) {
499 // This command has already been called, presumably because there is a race between the main
500 // thread and a worker thread that crashed. Don't try to arbitrate the dispute. If the main
501 // thread won the race (unlikely, but possible), this may be incorrectly logged as a success.
502 return;
503 }
Ulf Adamsab43b972016-03-30 12:23:50 +0000504 workspace.getSkyframeExecutor().getEventBus().post(new CommandCompleteEvent(exitCode));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100505 }
506
507 /**
508 * Hook method called by the BlazeCommandDispatcher after the dispatch of each
509 * command.
510 */
511 @VisibleForTesting
Ulf Adams633f5392015-09-15 11:13:08 +0000512 public void afterCommand(CommandEnvironment env, int exitCode) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100513 // Remove any filters that the command might have added to the reporter.
Ulf Adams633f5392015-09-15 11:13:08 +0000514 env.getReporter().setOutputFilter(OutputFilter.OUTPUT_EVERYTHING);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100515
516 notifyCommandComplete(exitCode);
517
518 for (BlazeModule module : blazeModules) {
519 module.afterCommand();
520 }
521
Ulf Adamsab43b972016-03-30 12:23:50 +0000522 env.getBlazeWorkspace().clearEventBus();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100523
524 try {
525 Profiler.instance().stop();
526 MemoryProfiler.instance().stop();
527 } catch (IOException e) {
Ulf Adams633f5392015-09-15 11:13:08 +0000528 env.getReporter().handle(Event.error("Error while writing profile file: " + e.getMessage()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100529 }
530 }
531
532 // Make sure we keep a strong reference to this logger, so that the
533 // configuration isn't lost when the gc kicks in.
534 private static Logger templateLogger = Logger.getLogger("com.google.devtools.build");
535
536 /**
537 * Configures "com.google.devtools.build.*" loggers to the given
538 * {@code level}. Note: This code relies on static state.
539 */
540 public static void setupLogging(Level level) {
541 templateLogger.setLevel(level);
542 templateLogger.info("Log level: " + templateLogger.getLevel());
543 }
544
545 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100546 * Returns the Clock-instance used for the entire build. Before,
547 * individual classes (such as Profiler) used to specify the type
548 * of clock (e.g. EpochClock) they wanted to use. This made it
549 * difficult to get Blaze working on Windows as some of the clocks
550 * available for Linux aren't (directly) available on Windows.
551 * Setting the Blaze-wide clock upon construction of BlazeRuntime
552 * allows injecting whatever Clock instance should be used from
553 * BlazeMain.
554 *
555 * @return The Blaze-wide clock
556 */
557 public Clock getClock() {
558 return clock;
559 }
560
561 public OptionsProvider getStartupOptionsProvider() {
562 return startupOptionsProvider;
563 }
564
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100565 public Map<String, BlazeCommand> getCommandMap() {
566 return commandMap;
567 }
568
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100569 public void shutdown() {
570 for (BlazeModule module : blazeModules) {
571 module.blazeShutdown();
572 }
573 }
574
575 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100576 * Returns the defaults package for the default settings. Should only be called by commands that
577 * do <i>not</i> process {@link BuildOptions}, since build options can alter the contents of the
578 * defaults package, which will not be reflected here.
579 */
580 public String getDefaultsPackageContent() {
Janak Ramakrishnanb92c0972016-03-23 16:47:13 +0000581 return ruleClassProvider.getDefaultsPackageContent(getInvocationPolicy());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100582 }
583
584 /**
585 * Returns the defaults package for the given options taken from an optionsProvider.
586 */
587 public String getDefaultsPackageContent(OptionsClassProvider optionsProvider) {
588 return ruleClassProvider.getDefaultsPackageContent(optionsProvider);
589 }
590
591 /**
592 * Creates a BuildOptions class for the given options taken from an optionsProvider.
593 */
594 public BuildOptions createBuildOptions(OptionsClassProvider optionsProvider) {
595 return ruleClassProvider.createBuildOptions(optionsProvider);
596 }
597
598 /**
599 * An EventBus exception handler that will report the exception to a remote server, if a
600 * handler is registered.
601 */
602 public static final class RemoteExceptionHandler implements SubscriberExceptionHandler {
603 @Override
604 public void handleException(Throwable exception, SubscriberExceptionContext context) {
Eric Fellheimere95a80a2015-02-07 00:40:26 +0000605 LOG.log(Level.SEVERE, "Failure in EventBus subscriber", exception);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100606 LoggingUtil.logToRemote(Level.SEVERE, "Failure in EventBus subscriber.", exception);
607 }
608 }
609
610 /**
611 * An EventBus exception handler that will call BugReport.handleCrash exiting
612 * the current thread.
613 */
614 public static final class BugReportingExceptionHandler implements SubscriberExceptionHandler {
615 @Override
616 public void handleException(Throwable exception, SubscriberExceptionContext context) {
617 BugReport.handleCrash(exception);
618 }
619 }
620
621 /**
622 * Main method for the Blaze server startup. Note: This method logs
623 * exceptions to remote servers. Do not add this to a unittest.
624 */
625 public static void main(Iterable<Class<? extends BlazeModule>> moduleClasses, String[] args) {
626 setupUncaughtHandler(args);
627 List<BlazeModule> modules = createModules(moduleClasses);
Googlerc8c64e72015-03-23 23:22:18 +0000628 // blaze.cc will put --batch first if the user set it.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100629 if (args.length >= 1 && args[0].equals("--batch")) {
630 // Run Blaze in batch mode.
631 System.exit(batchMain(modules, args));
632 }
633 LOG.info("Starting Blaze server with args " + Arrays.toString(args));
634 try {
635 // Run Blaze in server mode.
636 System.exit(serverMain(modules, OutErr.SYSTEM_OUT_ERR, args));
637 } catch (RuntimeException | Error e) { // A definite bug...
638 BugReport.printBug(OutErr.SYSTEM_OUT_ERR, e);
639 BugReport.sendBugReport(e, Arrays.asList(args));
640 System.exit(ExitCode.BLAZE_INTERNAL_ERROR.getNumericExitCode());
641 throw e; // Shouldn't get here.
642 }
643 }
644
645 @VisibleForTesting
646 public static List<BlazeModule> createModules(
647 Iterable<Class<? extends BlazeModule>> moduleClasses) {
648 ImmutableList.Builder<BlazeModule> result = ImmutableList.builder();
649 for (Class<? extends BlazeModule> moduleClass : moduleClasses) {
650 try {
651 BlazeModule module = moduleClass.newInstance();
652 result.add(module);
653 } catch (Throwable e) {
654 throw new IllegalStateException("Cannot instantiate module " + moduleClass.getName(), e);
655 }
656 }
657
658 return result.build();
659 }
660
661 /**
662 * Generates a string form of a request to be written to the logs,
663 * filtering the user environment to remove anything that looks private.
664 * The current filter criteria removes any variable whose name includes
665 * "auth", "pass", or "cookie".
666 *
667 * @param requestStrings
668 * @return the filtered request to write to the log.
669 */
670 @VisibleForTesting
671 public static String getRequestLogString(List<String> requestStrings) {
672 StringBuilder buf = new StringBuilder();
673 buf.append('[');
674 String sep = "";
675 for (String s : requestStrings) {
676 buf.append(sep);
677 if (s.startsWith("--client_env")) {
678 int varStart = "--client_env=".length();
679 int varEnd = s.indexOf('=', varStart);
680 String varName = s.substring(varStart, varEnd);
681 if (suppressFromLog.matcher(varName).matches()) {
682 buf.append("--client_env=");
683 buf.append(varName);
684 buf.append("=__private_value_removed__");
685 } else {
686 buf.append(s);
687 }
688 } else {
689 buf.append(s);
690 }
691 sep = ", ";
692 }
693 buf.append(']');
694 return buf.toString();
695 }
696
697 /**
698 * Command line options split in to two parts: startup options and everything else.
699 */
700 @VisibleForTesting
701 static class CommandLineOptions {
702 private final List<String> startupArgs;
703 private final List<String> otherArgs;
704
705 CommandLineOptions(List<String> startupArgs, List<String> otherArgs) {
706 this.startupArgs = ImmutableList.copyOf(startupArgs);
707 this.otherArgs = ImmutableList.copyOf(otherArgs);
708 }
709
710 public List<String> getStartupArgs() {
711 return startupArgs;
712 }
713
714 public List<String> getOtherArgs() {
715 return otherArgs;
716 }
717 }
718
719 /**
720 * Splits given arguments into two lists - arguments matching options defined in this class
721 * and everything else, while preserving order in each list.
722 */
723 static CommandLineOptions splitStartupOptions(
724 Iterable<BlazeModule> modules, String... args) {
725 List<String> prefixes = new ArrayList<>();
726 List<Field> startupFields = Lists.newArrayList();
727 for (Class<? extends OptionsBase> defaultOptions
728 : BlazeCommandUtils.getStartupOptions(modules)) {
729 startupFields.addAll(ImmutableList.copyOf(defaultOptions.getFields()));
730 }
731
732 for (Field field : startupFields) {
733 if (field.isAnnotationPresent(Option.class)) {
734 prefixes.add("--" + field.getAnnotation(Option.class).name());
735 if (field.getType() == boolean.class || field.getType() == TriState.class) {
736 prefixes.add("--no" + field.getAnnotation(Option.class).name());
737 }
738 }
739 }
740
741 List<String> startupArgs = new ArrayList<>();
742 List<String> otherArgs = Lists.newArrayList(args);
743
744 for (Iterator<String> argi = otherArgs.iterator(); argi.hasNext(); ) {
745 String arg = argi.next();
746 if (!arg.startsWith("--")) {
747 break; // stop at command - all startup options would be specified before it.
748 }
749 for (String prefix : prefixes) {
750 if (arg.startsWith(prefix)) {
751 startupArgs.add(arg);
752 argi.remove();
753 break;
754 }
755 }
756 }
757 return new CommandLineOptions(startupArgs, otherArgs);
758 }
759
760 private static void captureSigint() {
761 final Thread mainThread = Thread.currentThread();
762 final AtomicInteger numInterrupts = new AtomicInteger();
763
764 final Runnable interruptWatcher = new Runnable() {
765 @Override
766 public void run() {
767 int count = 0;
768 // Not an actual infinite loop because it's run in a daemon thread.
769 while (true) {
770 count++;
771 Uninterruptibles.sleepUninterruptibly(10, TimeUnit.SECONDS);
772 LOG.warning("Slow interrupt number " + count + " in batch mode");
773 ThreadUtils.warnAboutSlowInterrupt();
774 }
775 }
776 };
777
778 new InterruptSignalHandler() {
779 @Override
Janak Ramakrishnan70c57902016-03-10 00:58:59 +0000780 protected void onSignal() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100781 LOG.info("User interrupt");
782 OutErr.SYSTEM_OUT_ERR.printErrLn("Blaze received an interrupt");
783 mainThread.interrupt();
784
785 int curNumInterrupts = numInterrupts.incrementAndGet();
786 if (curNumInterrupts == 1) {
787 Thread interruptWatcherThread = new Thread(interruptWatcher, "interrupt-watcher");
788 interruptWatcherThread.setDaemon(true);
789 interruptWatcherThread.start();
790 } else if (curNumInterrupts == 2) {
791 LOG.warning("Second --batch interrupt: Reverting to JVM SIGINT handler");
792 uninstall();
793 }
794 }
795 };
796 }
797
798 /**
799 * A main method that runs blaze commands in batch mode. The return value indicates the desired
800 * exit status of the program.
801 */
802 private static int batchMain(Iterable<BlazeModule> modules, String[] args) {
803 captureSigint();
804 CommandLineOptions commandLineOptions = splitStartupOptions(modules, args);
805 LOG.info("Running Blaze in batch mode with startup args "
806 + commandLineOptions.getStartupArgs());
807
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100808 BlazeRuntime runtime;
809 try {
810 runtime = newRuntime(modules, parseOptions(modules, commandLineOptions.getStartupArgs()));
811 } catch (OptionsParsingException e) {
812 OutErr.SYSTEM_OUT_ERR.printErr(e.getMessage());
813 return ExitCode.COMMAND_LINE_ERROR.getNumericExitCode();
814 } catch (AbruptExitException e) {
815 OutErr.SYSTEM_OUT_ERR.printErr(e.getMessage());
816 return e.getExitCode().getNumericExitCode();
817 }
818
Ulf Adams47cb9162015-09-18 08:12:30 +0000819 BlazeCommandDispatcher dispatcher = new BlazeCommandDispatcher(runtime);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100820
821 try {
822 LOG.info(getRequestLogString(commandLineOptions.getOtherArgs()));
823 return dispatcher.exec(commandLineOptions.getOtherArgs(), OutErr.SYSTEM_OUT_ERR,
Lukacs Berkice1445f2016-04-19 15:52:55 +0000824 LockingMode.ERROR_OUT, "batch client", runtime.getClock().currentTimeMillis());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100825 } catch (BlazeCommandDispatcher.ShutdownBlazeServerException e) {
826 return e.getExitStatus();
Lukacs Berkice1445f2016-04-19 15:52:55 +0000827 } catch (InterruptedException e) {
828 // This is almost main(), so it's okay to just swallow it. We are exiting soon.
829 return ExitCode.INTERRUPTED.getNumericExitCode();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100830 } finally {
831 runtime.shutdown();
832 dispatcher.shutdown();
833 }
834 }
835
836 /**
837 * A main method that does not send email. The return value indicates the desired exit status of
838 * the program.
839 */
840 private static int serverMain(Iterable<BlazeModule> modules, OutErr outErr, String[] args) {
841 try {
Lukacs Berkibb0dac72016-04-19 07:21:19 +0000842 RPCServer blazeServer = createBlazeRPCServer(modules, Arrays.asList(args));
843 blazeServer.serve();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100844 return ExitCode.SUCCESS.getNumericExitCode();
845 } catch (OptionsParsingException e) {
846 outErr.printErr(e.getMessage());
847 return ExitCode.COMMAND_LINE_ERROR.getNumericExitCode();
848 } catch (IOException e) {
849 outErr.printErr("I/O Error: " + e.getMessage());
850 return ExitCode.BUILD_FAILURE.getNumericExitCode();
851 } catch (AbruptExitException e) {
852 outErr.printErr(e.getMessage());
853 return e.getExitCode().getNumericExitCode();
854 }
855 }
856
857 private static FileSystem fileSystemImplementation() {
Damien Martin-Guillerez8c04e9e2016-01-18 12:53:39 +0000858 if ("0".equals(System.getProperty("io.bazel.UnixFileSystem"))) {
859 // Ignore UnixFileSystem, to be used for bootstrapping.
Dmitry Lomovbe939512016-02-02 20:26:25 +0000860 return OS.getCurrent() == OS.WINDOWS ? new WindowsFileSystem() : new JavaIoFileSystem();
Damien Martin-Guillerez8c04e9e2016-01-18 12:53:39 +0000861 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100862 // The JNI-based UnixFileSystem is faster, but on Windows it is not available.
Lukacs Berki58dbb7f2016-01-29 11:56:29 +0000863 return OS.getCurrent() == OS.WINDOWS ? new WindowsFileSystem() : new UnixFileSystem();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100864 }
865
866 /**
867 * Creates and returns a new Blaze RPCServer. Call {@link RPCServer#serve()} to start the server.
868 */
Lukacs Berkibb0dac72016-04-19 07:21:19 +0000869 private static RPCServer createBlazeRPCServer(
870 Iterable<BlazeModule> modules, List<String> args)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100871 throws IOException, OptionsParsingException, AbruptExitException {
872 OptionsProvider options = parseOptions(modules, args);
873 BlazeServerStartupOptions startupOptions = options.getOptions(BlazeServerStartupOptions.class);
874
Lukacs Berkibb0dac72016-04-19 07:21:19 +0000875 BlazeRuntime runtime = newRuntime(modules, options);
876 BlazeCommandDispatcher dispatcher = new BlazeCommandDispatcher(runtime);
Lukacs Berki8b3b9182016-04-14 08:29:05 +0000877 CommandExecutor commandExecutor = new CommandExecutor(runtime, dispatcher);
Lukacs Berkibb0dac72016-04-19 07:21:19 +0000878
879
Lukacs Berki8b3b9182016-04-14 08:29:05 +0000880 if (startupOptions.grpcPort != -1) {
881 try {
Lukacs Berkibb0dac72016-04-19 07:21:19 +0000882 // This is necessary so that Bazel kind of works during bootstrapping, at which time the
883 // gRPC server is not compiled in so that we don't need gRPC for bootstrapping.
Lukacs Berki8b3b9182016-04-14 08:29:05 +0000884 Class<?> factoryClass = Class.forName(
885 "com.google.devtools.build.lib.server.GrpcServerImpl$Factory");
Lukacs Berkibb0dac72016-04-19 07:21:19 +0000886 RPCServer.Factory factory = (RPCServer.Factory) factoryClass.newInstance();
887 return factory.create(commandExecutor, runtime.getClock(),
888 startupOptions.grpcPort, runtime.getServerDirectory());
Lukacs Berki8b3b9182016-04-14 08:29:05 +0000889 } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
890 throw new AbruptExitException("gRPC server not compiled in", ExitCode.BLAZE_INTERNAL_ERROR);
891 }
Lukacs Berkibb0dac72016-04-19 07:21:19 +0000892 } else {
893 return AfUnixServer.newServerWith(runtime.getClock(), commandExecutor,
894 runtime.getServerDirectory(), runtime.workspace.getWorkspace(),
895 startupOptions.maxIdleSeconds);
Lukacs Berki8b3b9182016-04-14 08:29:05 +0000896 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100897 }
898
899 private static Function<String, String> sourceFunctionForMap(final Map<String, String> map) {
900 return new Function<String, String>() {
901 @Override
902 public String apply(String input) {
903 if (!map.containsKey(input)) {
904 return "default";
905 }
906
907 if (map.get(input).isEmpty()) {
908 return "command line";
909 }
910
911 return map.get(input);
912 }
913 };
914 }
915
916 /**
917 * Parses the command line arguments into a {@link OptionsParser} object.
918 *
919 * <p>This function needs to parse the --option_sources option manually so that the real option
920 * parser can set the source for every option correctly. If that cannot be parsed or is missing,
921 * we just report an unknown source for every startup option.
922 */
923 private static OptionsProvider parseOptions(
924 Iterable<BlazeModule> modules, List<String> args) throws OptionsParsingException {
925 Set<Class<? extends OptionsBase>> optionClasses = Sets.newHashSet();
926 optionClasses.addAll(BlazeCommandUtils.getStartupOptions(modules));
927 // First parse the command line so that we get the option_sources argument
928 OptionsParser parser = OptionsParser.newOptionsParser(optionClasses);
929 parser.setAllowResidue(false);
930 parser.parse(OptionPriority.COMMAND_LINE, null, args);
931 Function<? super String, String> sourceFunction =
932 sourceFunctionForMap(parser.getOptions(BlazeServerStartupOptions.class).optionSources);
933
934 // Then parse the command line again, this time with the correct option sources
935 parser = OptionsParser.newOptionsParser(optionClasses);
936 parser.setAllowResidue(false);
937 parser.parseWithSourceFunction(OptionPriority.COMMAND_LINE, sourceFunction, args);
938 return parser;
939 }
940
941 /**
942 * Creates a new blaze runtime, given the install and output base directories.
943 *
944 * <p>Note: This method can and should only be called once per startup, as it also creates the
945 * filesystem object that will be used for the runtime. So it should only ever be called from the
946 * main method of the Blaze program.
947 *
948 * @param options Blaze startup options.
949 *
950 * @return a new BlazeRuntime instance initialized with the given filesystem and directories, and
951 * an error string that, if not null, describes a fatal initialization failure that makes
952 * this runtime unsuitable for real commands
953 */
954 private static BlazeRuntime newRuntime(
955 Iterable<BlazeModule> blazeModules, OptionsProvider options) throws AbruptExitException {
956 for (BlazeModule module : blazeModules) {
957 module.globalInit(options);
958 }
959
960 BlazeServerStartupOptions startupOptions = options.getOptions(BlazeServerStartupOptions.class);
Janak Ramakrishnan70c57902016-03-10 00:58:59 +0000961 if (startupOptions.batch && startupOptions.oomMoreEagerly) {
962 new OomSignalHandler();
Janak Ramakrishnan8cc772e2016-03-23 17:26:12 +0000963 new RetainedHeapLimiter(startupOptions.oomMoreEagerlyThreshold).install();
Janak Ramakrishnan70c57902016-03-10 00:58:59 +0000964 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100965 PathFragment workspaceDirectory = startupOptions.workspaceDirectory;
966 PathFragment installBase = startupOptions.installBase;
967 PathFragment outputBase = startupOptions.outputBase;
968
969 OsUtils.maybeForceJNI(installBase); // Must be before first use of JNI.
970
971 // From the point of view of the Java program --install_base and --output_base
972 // are mandatory options, despite the comment in their declarations.
973 if (installBase == null || !installBase.isAbsolute()) { // (includes "" default case)
974 throw new IllegalArgumentException(
975 "Bad --install_base option specified: '" + installBase + "'");
976 }
977 if (outputBase != null && !outputBase.isAbsolute()) { // (includes "" default case)
978 throw new IllegalArgumentException(
979 "Bad --output_base option specified: '" + outputBase + "'");
980 }
981
982 PathFragment outputPathFragment = BlazeDirectories.outputPathFromOutputBase(
Lukacs Berki5fb98d12015-12-09 15:29:46 +0000983 outputBase, workspaceDirectory, startupOptions.deepExecRoot);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100984 FileSystem fs = null;
985 for (BlazeModule module : blazeModules) {
986 FileSystem moduleFs = module.getFileSystem(options, outputPathFragment);
987 if (moduleFs != null) {
988 Preconditions.checkState(fs == null, "more than one module returns a file system");
989 fs = moduleFs;
990 }
991 }
992
993 if (fs == null) {
994 fs = fileSystemImplementation();
995 }
996 Path.setFileSystemForSerialization(fs);
997
998 Path installBasePath = fs.getPath(installBase);
999 Path outputBasePath = fs.getPath(outputBase);
1000 Path workspaceDirectoryPath = null;
1001 if (!workspaceDirectory.equals(PathFragment.EMPTY_FRAGMENT)) {
1002 workspaceDirectoryPath = fs.getPath(workspaceDirectory);
1003 }
1004
Lukacs Berki42f67cb2016-01-20 14:04:18 +00001005 if (fs instanceof UnixFileSystem) {
1006 ((UnixFileSystem) fs).setRootsWithAllowedHardlinks(
1007 // Some tests pass nulls for these paths, so remove these from the list
1008 Iterables.filter(
1009 Arrays.asList(installBasePath, outputBasePath, workspaceDirectoryPath),
1010 Predicates.notNull()));
1011 }
1012
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001013 BlazeDirectories directories =
Eric Fellheimer4c5eb0f2015-08-12 15:02:24 +00001014 new BlazeDirectories(installBasePath, outputBasePath, workspaceDirectoryPath,
Lukacs Berki5fb98d12015-12-09 15:29:46 +00001015 startupOptions.deepExecRoot, startupOptions.installMD5);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001016
1017 Clock clock = BlazeClock.instance();
1018
1019 BinTools binTools;
1020 try {
1021 binTools = BinTools.forProduction(directories);
1022 } catch (IOException e) {
1023 throw new AbruptExitException(
1024 "Cannot enumerate embedded binaries: " + e.getMessage(),
1025 ExitCode.LOCAL_ENVIRONMENTAL_ERROR);
1026 }
1027
1028 BlazeRuntime.Builder runtimeBuilder = new BlazeRuntime.Builder().setDirectories(directories)
1029 .setStartupOptionsProvider(options)
1030 .setBinTools(binTools)
1031 .setClock(clock)
1032 // TODO(bazel-team): Make BugReportingExceptionHandler the default.
1033 // See bug "Make exceptions in EventBus subscribers fatal"
1034 .setEventBusExceptionHandler(
1035 startupOptions.fatalEventBusExceptions || !BlazeVersionInfo.instance().isReleasedBlaze()
1036 ? new BlazeRuntime.BugReportingExceptionHandler()
1037 : new BlazeRuntime.RemoteExceptionHandler());
1038
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +00001039 if (System.getenv("TEST_TMPDIR") != null
1040 && System.getenv("NO_CRASH_ON_LOGGING_IN_TEST") == null) {
1041 LoggingUtil.installRemoteLogger(getTestCrashLogger());
1042 }
1043
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001044 for (BlazeModule blazeModule : blazeModules) {
1045 runtimeBuilder.addBlazeModule(blazeModule);
1046 }
Ulf Adams47cb9162015-09-18 08:12:30 +00001047 runtimeBuilder.addCommands(getBuiltinCommandList());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001048
1049 BlazeRuntime runtime = runtimeBuilder.build();
Nathan Harmatad4803012015-09-08 20:03:22 +00001050 AutoProfiler.setClock(runtime.getClock());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001051 BugReport.setRuntime(runtime);
1052 return runtime;
1053 }
1054
1055 /**
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +00001056 * Returns a logger that crashes as soon as it's written to, since tests should not cause events
1057 * that would be logged.
1058 */
1059 @VisibleForTesting
1060 public static Future<Logger> getTestCrashLogger() {
1061 Logger crashLogger = Logger.getAnonymousLogger();
1062 crashLogger.addHandler(
1063 new Handler() {
1064 @Override
1065 public void publish(LogRecord record) {
Michajlo Matijkiwf5cc0b22016-01-20 17:44:44 +00001066 System.err.println("Remote logging disabled for testing, forcing abrupt shutdown.");
1067 System.err.printf("%s#%s: %s\n",
1068 record.getSourceClassName(),
1069 record.getSourceMethodName(),
1070 record.getMessage());
1071
Janak Ramakrishnana88e65b2015-09-17 15:07:26 +00001072 Throwable e = record.getThrown();
Michajlo Matijkiwf5cc0b22016-01-20 17:44:44 +00001073 if (e != null) {
1074 e.printStackTrace();
Janak Ramakrishnana88e65b2015-09-17 15:07:26 +00001075 }
Michajlo Matijkiwf5cc0b22016-01-20 17:44:44 +00001076
1077 Runtime.getRuntime().halt(ExitCode.BLAZE_INTERNAL_ERROR.getNumericExitCode());
Janak Ramakrishnanae9b95f2015-08-04 03:47:04 +00001078 }
1079
1080 @Override
1081 public void flush() {
1082 throw new IllegalStateException();
1083 }
1084
1085 @Override
1086 public void close() {
1087 throw new IllegalStateException();
1088 }
1089 });
1090 return Futures.immediateFuture(crashLogger);
1091 }
1092
1093 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001094 * Make sure async threads cannot be orphaned. This method makes sure bugs are reported to
1095 * telemetry and the proper exit code is reported.
1096 */
1097 private static void setupUncaughtHandler(final String[] args) {
Janak Ramakrishnanb6582fa2016-03-14 16:19:40 +00001098 Thread.setDefaultUncaughtExceptionHandler(
1099 new Thread.UncaughtExceptionHandler() {
1100 @Override
1101 public void uncaughtException(Thread thread, Throwable throwable) {
1102 BugReport.handleCrash(throwable, args);
1103 }
1104 });
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001105 }
1106
1107
1108 /**
1109 * Returns an immutable list containing new instances of each Blaze command.
1110 */
1111 @VisibleForTesting
1112 public static List<BlazeCommand> getBuiltinCommandList() {
1113 return ImmutableList.of(
1114 new BuildCommand(),
1115 new CanonicalizeCommand(),
1116 new CleanCommand(),
Kristina Chodorow5cb95fc2015-06-12 15:35:49 +00001117 new DumpCommand(),
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001118 new HelpCommand(),
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001119 new InfoCommand(),
Lukacs Berki662166f2015-05-29 10:50:25 +00001120 new MobileInstallCommand(),
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001121 new ProfileCommand(),
1122 new QueryCommand(),
1123 new RunCommand(),
1124 new ShutdownCommand(),
1125 new TestCommand(),
1126 new VersionCommand());
1127 }
1128
1129 /**
1130 * A builder for {@link BlazeRuntime} objects. The only required fields are the {@link
1131 * BlazeDirectories}, and the {@link RuleClassProvider} (except for testing). All other fields
1132 * have safe default values.
1133 *
1134 * <p>If a {@link ConfigurationFactory} is set, then the builder ignores the host system flag.
1135 * <p>The default behavior of the BlazeRuntime's EventBus is to exit when a subscriber throws
1136 * an exception. Please plan appropriately.
1137 */
1138 public static class Builder {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001139 private BlazeDirectories directories;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001140 private ConfigurationFactory configurationFactory;
1141 private Clock clock;
1142 private OptionsProvider startupOptionsProvider;
Ulf Adams47cb9162015-09-18 08:12:30 +00001143 private final List<BlazeModule> blazeModules = new ArrayList<>();
Ulf Adams8b56c8c2016-04-12 13:45:56 +00001144 private SubscriberExceptionHandler eventBusExceptionHandler = new RemoteExceptionHandler();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001145 private BinTools binTools;
1146 private UUID instanceId;
Ulf Adams47cb9162015-09-18 08:12:30 +00001147 private final List<BlazeCommand> commands = new ArrayList<>();
Luis Fernando Pino Duqueb1b28b62016-02-25 14:25:19 +00001148 private InvocationPolicy invocationPolicy = InvocationPolicy.getDefaultInstance();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001149
1150 public BlazeRuntime build() throws AbruptExitException {
1151 Preconditions.checkNotNull(directories);
1152 Preconditions.checkNotNull(startupOptionsProvider);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001153
1154 Clock clock = (this.clock == null) ? BlazeClock.instance() : this.clock;
1155 UUID instanceId = (this.instanceId == null) ? UUID.randomUUID() : this.instanceId;
1156
1157 Preconditions.checkNotNull(clock);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001158
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001159 for (BlazeModule module : blazeModules) {
1160 module.blazeStartup(startupOptionsProvider,
1161 BlazeVersionInfo.instance(), instanceId, directories, clock);
Ulf Adams8b56c8c2016-04-12 13:45:56 +00001162 }
1163
1164 QueryEnvironmentFactory queryEnvironmentFactory = null;
1165 for (BlazeModule module : blazeModules) {
Nathan Harmataf0cc5b82016-03-18 14:56:28 +00001166 QueryEnvironmentFactory queryEnvFactory = module.getQueryEnvironmentFactory();
1167 if (queryEnvFactory != null) {
1168 Preconditions.checkState(queryEnvironmentFactory == null,
1169 "At most one query environment factory supported. But found two: %s and %s",
1170 queryEnvFactory,
1171 queryEnvironmentFactory);
1172 queryEnvironmentFactory = queryEnvFactory;
1173 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001174 }
Nathan Harmataf0cc5b82016-03-18 14:56:28 +00001175 if (queryEnvironmentFactory == null) {
1176 queryEnvironmentFactory = new QueryEnvironmentFactory();
1177 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001178
1179 ConfiguredRuleClassProvider.Builder ruleClassBuilder =
1180 new ConfiguredRuleClassProvider.Builder();
1181 for (BlazeModule module : blazeModules) {
1182 module.initializeRuleClasses(ruleClassBuilder);
1183 }
1184
1185 Map<String, String> platformRegexps = null;
1186 {
1187 ImmutableMap.Builder<String, String> builder = new ImmutableMap.Builder<>();
1188 for (BlazeModule module : blazeModules) {
1189 builder.putAll(module.getPlatformSetRegexps());
1190 }
1191 platformRegexps = builder.build();
1192 if (platformRegexps.isEmpty()) {
1193 platformRegexps = null; // Use the default.
1194 }
1195 }
1196
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001197 // Merge filters from Blaze modules that allow some action inputs to be missing.
1198 Predicate<PathFragment> allowedMissingInputs = null;
1199 for (BlazeModule module : blazeModules) {
1200 Predicate<PathFragment> modulePredicate = module.getAllowedMissingInputs();
1201 if (modulePredicate != null) {
1202 Preconditions.checkArgument(allowedMissingInputs == null,
1203 "More than one Blaze module allows missing inputs.");
1204 allowedMissingInputs = modulePredicate;
1205 }
1206 }
1207 if (allowedMissingInputs == null) {
1208 allowedMissingInputs = Predicates.alwaysFalse();
1209 }
1210
1211 ConfiguredRuleClassProvider ruleClassProvider = ruleClassBuilder.build();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001212
1213 List<PackageFactory.EnvironmentExtension> extensions = new ArrayList<>();
1214 for (BlazeModule module : blazeModules) {
1215 extensions.add(module.getPackageEnvironmentExtension());
1216 }
1217
Ulf Adams8b56c8c2016-04-12 13:45:56 +00001218 PackageFactory packageFactory = new PackageFactory(
1219 ruleClassProvider, platformRegexps, extensions, BlazeVersionInfo.instance().getVersion());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001220
1221 if (configurationFactory == null) {
1222 configurationFactory = new ConfigurationFactory(
1223 ruleClassProvider.getConfigurationCollectionFactory(),
1224 ruleClassProvider.getConfigurationFragments());
1225 }
1226
1227 ProjectFile.Provider projectFileProvider = null;
1228 for (BlazeModule module : blazeModules) {
1229 ProjectFile.Provider candidate = module.createProjectFileProvider();
1230 if (candidate != null) {
1231 Preconditions.checkState(projectFileProvider == null,
1232 "more than one module defines a project file provider");
1233 projectFileProvider = candidate;
1234 }
1235 }
1236
Luis Fernando Pino Duqueb1b28b62016-02-25 14:25:19 +00001237 invocationPolicy = createInvocationPolicyFromModules(invocationPolicy, blazeModules);
1238
Ulf Adams8b56c8c2016-04-12 13:45:56 +00001239 BlazeRuntime runtime = new BlazeRuntime(queryEnvironmentFactory, packageFactory,
1240 ruleClassProvider, configurationFactory, clock, startupOptionsProvider,
1241 ImmutableList.copyOf(blazeModules), eventBusExceptionHandler, projectFileProvider,
1242 invocationPolicy, commands);
1243 runtime.initWorkspace(directories, binTools);
1244 return runtime;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001245 }
1246
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001247 public Builder setBinTools(BinTools binTools) {
1248 this.binTools = binTools;
1249 return this;
1250 }
1251
Luis Fernando Pino Duqueb1b28b62016-02-25 14:25:19 +00001252 public Builder setInvocationPolicy(InvocationPolicy invocationPolicy) {
1253 this.invocationPolicy = invocationPolicy;
1254 return this;
1255 }
1256
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001257 public Builder setDirectories(BlazeDirectories directories) {
1258 this.directories = directories;
1259 return this;
1260 }
1261
1262 /**
1263 * Creates and sets a new {@link BlazeDirectories} instance with the given
1264 * parameters.
1265 */
1266 public Builder setDirectories(Path installBase, Path outputBase,
1267 Path workspace) {
1268 this.directories = new BlazeDirectories(installBase, outputBase, workspace);
1269 return this;
1270 }
1271
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001272 public Builder setConfigurationFactory(ConfigurationFactory configurationFactory) {
1273 this.configurationFactory = configurationFactory;
1274 return this;
1275 }
1276
1277 public Builder setClock(Clock clock) {
1278 this.clock = clock;
1279 return this;
1280 }
1281
1282 public Builder setStartupOptionsProvider(OptionsProvider startupOptionsProvider) {
1283 this.startupOptionsProvider = startupOptionsProvider;
1284 return this;
1285 }
1286
1287 public Builder addBlazeModule(BlazeModule blazeModule) {
1288 blazeModules.add(blazeModule);
1289 return this;
1290 }
1291
1292 public Builder setInstanceId(UUID id) {
1293 instanceId = id;
1294 return this;
1295 }
1296
1297 @VisibleForTesting
1298 public Builder setEventBusExceptionHandler(
1299 SubscriberExceptionHandler eventBusExceptionHandler) {
1300 this.eventBusExceptionHandler = eventBusExceptionHandler;
1301 return this;
1302 }
Ulf Adams47cb9162015-09-18 08:12:30 +00001303
1304 public Builder addCommands(BlazeCommand... commands) {
1305 this.commands.addAll(Arrays.asList(commands));
1306 return this;
1307 }
1308
1309 public Builder addCommands(Iterable<BlazeCommand> commands) {
1310 Iterables.addAll(this.commands, commands);
1311 return this;
1312 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001313 }
1314}