// 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.InvalidConfigurationException; import com.google.devtools.build.lib.exec.ExecutionOptions; import com.google.devtools.build.lib.packages.SkylarkSemanticsOptions; 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.OptionsClassProvider; import com.google.devtools.common.options.OptionsProvider; import java.util.ArrayList; import java.util.List; 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 OptionsClassProvider { private final UUID id; private final LoadingCache, Optional> optionsCache; /** 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 targets; private long startTimeMillis = 0; // milliseconds since UNIX epoch. private boolean needsInstrumentationFilter; private boolean runningInEmacs; private boolean runTests; private static final ImmutableList> MANDATORY_OPTIONS = ImmutableList.of( BuildRequestOptions.class, PackageCacheOptions.class, SkylarkSemanticsOptions.class, LoadingOptions.class, AnalysisOptions.class, ExecutionOptions.class, KeepGoingOption.class, LoadingPhaseThreadsOption.class); private BuildRequest(String commandName, final OptionsProvider options, final OptionsProvider startupOptions, List 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, Optional>() { @Override public Optional load(Class key) throws Exception { OptionsBase result = options.getOptions(key); if (result == null && startupOptions != null) { result = startupOptions.getOptions(key); } return Optional.fromNullable(result); } }); for (Class optionsClass : MANDATORY_OPTIONS) { Preconditions.checkNotNull(getOptions(optionsClass)); } } /** * 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 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 getOptions(Class 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. * *

Issues warnings or throws {@code InvalidConfigurationException} for option settings that * conflict. * * @return list of warnings */ public List validateOptions() throws InvalidConfigurationException { List warnings = new ArrayList<>(); int localTestJobs = getExecutionOptions().localTestJobs; if (localTestJobs < 0) { throw new InvalidConfigurationException(String.format( "Invalid parameter for --local_test_jobs: %d. Only values 0 or greater are " + "allowed.", 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= 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 getMultiCpus() { return ImmutableSortedSet.copyOf(getBuildOptions().multiCpus); } public ImmutableList getAspects() { return ImmutableList.copyOf(getBuildOptions().aspects); } public static BuildRequest create(String commandName, OptionsProvider options, OptionsProvider startupOptions, List 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; } }