blob: 87cf21ad3efbfb7ce37fbd0ab3b33c34c4beaad9 [file] [log] [blame]
// 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.buildtool;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSortedSet;
import com.google.devtools.build.lib.analysis.AnalysisOptions;
import com.google.devtools.build.lib.analysis.OutputGroupInfo;
import com.google.devtools.build.lib.analysis.TopLevelArtifactContext;
import com.google.devtools.build.lib.analysis.config.BuildOptions;
import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
import com.google.devtools.build.lib.exec.ExecutionOptions;
import com.google.devtools.build.lib.packages.StarlarkSemanticsOptions;
import com.google.devtools.build.lib.pkgcache.LoadingOptions;
import com.google.devtools.build.lib.pkgcache.PackageCacheOptions;
import com.google.devtools.build.lib.runtime.BlazeCommandEventHandler;
import com.google.devtools.build.lib.runtime.KeepGoingOption;
import com.google.devtools.build.lib.runtime.LoadingPhaseThreadsOption;
import com.google.devtools.build.lib.util.OptionsUtils;
import com.google.devtools.build.lib.util.io.OutErr;
import com.google.devtools.common.options.OptionsBase;
import com.google.devtools.common.options.OptionsParsingResult;
import com.google.devtools.common.options.OptionsProvider;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
/**
* A BuildRequest represents a single invocation of the build tool by a user.
* A request specifies a list of targets to be built for a single
* configuration, a pair of output/error streams, and additional options such
* as --keep_going, --jobs, etc.
*/
public class BuildRequest implements OptionsProvider {
private final UUID id;
private final LoadingCache<Class<? extends OptionsBase>, Optional<OptionsBase>> optionsCache;
private final Map<String, Object> starlarkOptions;
/** A human-readable description of all the non-default option settings. */
private final String optionsDescription;
/**
* The name of the Blaze command that the user invoked.
* Used for --announce.
*/
private final String commandName;
private final OutErr outErr;
private final List<String> targets;
private long startTimeMillis = 0; // milliseconds since UNIX epoch.
private boolean needsInstrumentationFilter;
private boolean runningInEmacs;
private boolean runTests;
private static final ImmutableList<Class<? extends OptionsBase>> MANDATORY_OPTIONS =
ImmutableList.of(
BuildRequestOptions.class,
PackageCacheOptions.class,
StarlarkSemanticsOptions.class,
LoadingOptions.class,
AnalysisOptions.class,
ExecutionOptions.class,
KeepGoingOption.class,
LoadingPhaseThreadsOption.class);
private BuildRequest(String commandName,
final OptionsParsingResult options,
final OptionsParsingResult startupOptions,
List<String> targets,
OutErr outErr,
UUID id,
long startTimeMillis) {
this.commandName = commandName;
this.optionsDescription = OptionsUtils.asShellEscapedString(options);
this.outErr = outErr;
this.targets = targets;
this.id = id;
this.startTimeMillis = startTimeMillis;
this.optionsCache = CacheBuilder.newBuilder()
.build(new CacheLoader<Class<? extends OptionsBase>, Optional<OptionsBase>>() {
@Override
public Optional<OptionsBase> load(Class<? extends OptionsBase> key) throws Exception {
OptionsBase result = options.getOptions(key);
if (result == null && startupOptions != null) {
result = startupOptions.getOptions(key);
}
return Optional.fromNullable(result);
}
});
this.starlarkOptions = options.getStarlarkOptions();
for (Class<? extends OptionsBase> optionsClass : MANDATORY_OPTIONS) {
Preconditions.checkNotNull(getOptions(optionsClass));
}
}
/**
* Since the OptionsProvider interface is used by many teams, this method is String-keyed even
* though it should always contain labels for our purposes. Consumers of this method should
* probably use the {@link BuildOptions#labelizeStarlarkOptions} method before doing meaningful
* work with the results.
*/
@Override
public Map<String, Object> getStarlarkOptions() {
return starlarkOptions;
}
/**
* Returns a unique identifier that universally identifies this build.
*/
public UUID getId() {
return id;
}
/**
* Returns the name of the Blaze command that the user invoked.
*/
public String getCommandName() {
return commandName;
}
/**
* Set to true if this build request was initiated by Emacs.
* (Certain output formatting may be necessary.)
*/
public void setRunningInEmacs() {
runningInEmacs = true;
}
boolean isRunningInEmacs() {
return runningInEmacs;
}
/**
* Enables test execution for this build request.
*/
public void setRunTests() {
runTests = true;
}
/**
* Returns true if tests should be run by the build tool.
*/
public boolean shouldRunTests() {
return runTests;
}
/**
* Returns the (immutable) list of targets to build in commandline
* form.
*/
public List<String> getTargets() {
return targets;
}
/**
* Returns the output/error streams to which errors and progress messages
* should be sent during the fulfillment of this request.
*/
public OutErr getOutErr() {
return outErr;
}
@Override
@SuppressWarnings("unchecked")
public <T extends OptionsBase> T getOptions(Class<T> clazz) {
try {
return (T) optionsCache.get(clazz).orNull();
} catch (ExecutionException e) {
throw new IllegalStateException(e);
}
}
/**
* Returns the set of command-line options specified for this request.
*/
public BuildRequestOptions getBuildOptions() {
return getOptions(BuildRequestOptions.class);
}
/**
* Returns the set of options related to the loading phase.
*/
public PackageCacheOptions getPackageCacheOptions() {
return getOptions(PackageCacheOptions.class);
}
/**
* Returns the set of options related to the loading phase.
*/
public LoadingOptions getLoadingOptions() {
return getOptions(LoadingOptions.class);
}
/**
* Returns the set of command-line options related to the view specified for
* this request.
*/
public AnalysisOptions getViewOptions() {
return getOptions(AnalysisOptions.class);
}
/** Returns the value of the --keep_going option. */
boolean getKeepGoing() {
return getOptions(KeepGoingOption.class).keepGoing;
}
/** Returns the value of the --loading_phase_threads option. */
int getLoadingPhaseThreadCount() {
return getOptions(LoadingPhaseThreadsOption.class).threads;
}
/**
* Returns the set of execution options specified for this request.
*/
public ExecutionOptions getExecutionOptions() {
return getOptions(ExecutionOptions.class);
}
/**
* Returns the human-readable description of the non-default options
* for this build request.
*/
public String getOptionsDescription() {
return optionsDescription;
}
/**
* Return the time (according to System.currentTimeMillis()) at which the
* service of this request was started.
*/
public long getStartTime() {
return startTimeMillis;
}
public void setNeedsInstrumentationFilter(boolean needInstrumentationFilter) {
this.needsInstrumentationFilter = needInstrumentationFilter;
}
public boolean needsInstrumentationFilter() {
return needsInstrumentationFilter;
}
/**
* Validates the options for this BuildRequest.
*
* <p>Issues warnings or throws {@code InvalidConfigurationException} for option settings that
* conflict.
*
* @return list of warnings
*/
public List<String> validateOptions() throws InvalidConfigurationException {
List<String> warnings = new ArrayList<>();
int localTestJobs = getExecutionOptions().localTestJobs;
int jobs = getBuildOptions().jobs;
if (localTestJobs > jobs) {
warnings.add(
String.format("High value for --local_test_jobs: %d. This exceeds the value for --jobs: "
+ "%d. Only up to %d local tests will run concurrently.", localTestJobs, jobs, jobs));
}
// Validate other BuildRequest options.
if (getBuildOptions().verboseExplanations && getBuildOptions().explanationPath == null) {
warnings.add("--verbose_explanations has no effect when --explain=<file> is not enabled");
}
return warnings;
}
/** Creates a new TopLevelArtifactContext from this build request. */
public TopLevelArtifactContext getTopLevelArtifactContext() {
return new TopLevelArtifactContext(
getOptions(ExecutionOptions.class).testStrategy.equals("exclusive"),
OutputGroupInfo.determineOutputGroups(getBuildOptions().outputGroups));
}
public ImmutableSortedSet<String> getMultiCpus() {
return ImmutableSortedSet.copyOf(getBuildOptions().multiCpus);
}
public ImmutableList<String> getAspects() {
return ImmutableList.copyOf(getBuildOptions().aspects);
}
public static BuildRequest create(String commandName, OptionsParsingResult options,
OptionsParsingResult startupOptions,
List<String> targets, OutErr outErr, UUID commandId, long commandStartTime) {
BuildRequest request = new BuildRequest(commandName, options, startupOptions, targets, outErr,
commandId, commandStartTime);
// All this, just to pass a global boolean from the client to the server. :(
if (options.getOptions(BlazeCommandEventHandler.Options.class).runningInEmacs) {
request.setRunningInEmacs();
}
return request;
}
}