// Copyright 2014 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.lib.exec; import com.google.common.base.Preconditions; import com.google.common.eventbus.EventBus; import com.google.devtools.build.lib.actions.ActionContext; import com.google.devtools.build.lib.actions.ActionExecutionContext.ShowSubcommands; import com.google.devtools.build.lib.actions.Executor; import com.google.devtools.build.lib.actions.ExecutorInitException; import com.google.devtools.build.lib.clock.Clock; import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.EventHandler; import com.google.devtools.build.lib.events.ExtendedEventHandler; import com.google.devtools.build.lib.events.Reporter; import com.google.devtools.build.lib.vfs.FileSystem; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.common.options.OptionsClassProvider; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; /** * The Executor class provides a dynamic abstraction of the various actual primitive system * operations that might be performed during a build step. * *

Constructions of this class might perform distributed execution, "virtual" execution for * testing purposes, or just print out the sequence of commands that would be executed, like Make's * "-n" option. */ @ThreadSafe public final class BlazeExecutor implements Executor { private final boolean verboseFailures; private final ShowSubcommands showSubcommands; private final FileSystem fileSystem; private final Path execRoot; private final Reporter reporter; private final EventBus eventBus; private final Clock clock; private final OptionsClassProvider options; private AtomicBoolean inExecutionPhase; private final Map, ActionContext> contextMap; /** * Constructs an Executor, bound to a specified output base path, and which will use the specified * reporter to announce SUBCOMMAND events, the given event bus to delegate events and the given * output streams for streaming output. The list of strategy implementation classes is used to * construct instances of the strategies mapped by their declared abstract type. This list is * uniquified before using. Each strategy instance is created with a reference to this Executor as * well as the given options object. * *

Don't forget to call startBuildRequest() and stopBuildRequest() for each request, and * shutdown() when you're done with this executor. */ public BlazeExecutor( FileSystem fileSystem, Path execRoot, Reporter reporter, EventBus eventBus, Clock clock, OptionsClassProvider options, SpawnActionContextMaps spawnActionContextMaps, Iterable contextProviders) throws ExecutorInitException { ExecutionOptions executionOptions = options.getOptions(ExecutionOptions.class); this.verboseFailures = executionOptions.verboseFailures; this.showSubcommands = executionOptions.showSubcommands; this.fileSystem = fileSystem; this.execRoot = execRoot; this.reporter = reporter; this.eventBus = eventBus; this.clock = clock; this.options = options; this.inExecutionPhase = new AtomicBoolean(false); this.contextMap = spawnActionContextMaps.contextMap(); if (executionOptions.debugPrintActionContexts) { spawnActionContextMaps.debugPrintSpawnActionContextMaps(reporter); } for (ActionContextProvider factory : contextProviders) { factory.executorCreated(spawnActionContextMaps.allContexts()); } } @Override public FileSystem getFileSystem() { return fileSystem; } @Override public Path getExecRoot() { return execRoot; } @Override public ExtendedEventHandler getEventHandler() { return reporter; } @Override public EventBus getEventBus() { return eventBus; } @Override public Clock getClock() { return clock; } @Override public ShowSubcommands reportsSubcommands() { return showSubcommands; } /** * This method is called before the start of the execution phase of each * build request. */ public void executionPhaseStarting() { Preconditions.checkState(!inExecutionPhase.getAndSet(true)); } /** * This method is called after the end of the execution phase of each build * request (even if there was an interrupt). */ public void executionPhaseEnding() { if (!inExecutionPhase.get()) { return; } inExecutionPhase.set(false); } public static void shutdownHelperPool(EventHandler reporter, ExecutorService pool, String name) { pool.shutdownNow(); boolean interrupted = false; while (true) { try { if (!pool.awaitTermination(10, TimeUnit.SECONDS)) { reporter.handle(Event.warn(name + " threadpool shutdown took greater than ten seconds")); } break; } catch (InterruptedException e) { interrupted = true; } } if (interrupted) { Thread.currentThread().interrupt(); } } @Override public T getContext(Class type) { return type.cast(contextMap.get(type)); } /** Returns true iff the --verbose_failures option was enabled. */ @Override public boolean getVerboseFailures() { return verboseFailures; } /** Returns the options associated with the execution. */ @Override public OptionsClassProvider getOptions() { return options; } }