| // 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.buildeventstream.BuildEventProtocolOptions; | |
| 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.KeepGoingOption; | |
| import com.google.devtools.build.lib.runtime.LoadingPhaseThreadsOption; | |
| import com.google.devtools.build.lib.runtime.UiOptions; | |
| 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. */ | |
| public 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"), | |
| getOptions(BuildEventProtocolOptions.class).expandFilesets, | |
| 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(UiOptions.class).runningInEmacs) { | |
| request.setRunningInEmacs(); | |
| } | |
| return request; | |
| } | |
| } |