blob: dd2d5de509b2d070cbd8f6fc7d3602e89af83249 [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.actions;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.extra.EnvironmentVariable;
import com.google.devtools.build.lib.actions.extra.SpawnInfo;
import com.google.devtools.build.lib.util.CommandDescriptionForm;
import com.google.devtools.build.lib.util.CommandFailureUtils;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.concurrent.Immutable;
/** Base implementation of a Spawn. */
@Immutable
public class BaseSpawn implements Spawn {
private final ImmutableList<String> arguments;
private final ImmutableMap<String, String> environment;
private final ImmutableMap<String, String> executionInfo;
private final ImmutableMap<PathFragment, Artifact> runfilesManifests;
private final ImmutableSet<PathFragment> optionalOutputFiles;
private final RunfilesSupplier runfilesSupplier;
private final ActionExecutionMetadata action;
private final ResourceSet localResources;
// TODO(bazel-team): When we migrate ActionSpawn to use this constructor decide on and enforce
// policy on runfilesManifests and runfilesSupplier being non-empty (ie: are overlapping mappings
// allowed?).
@VisibleForTesting
BaseSpawn(
List<String> arguments,
Map<String, String> environment,
Map<String, String> executionInfo,
Map<PathFragment, Artifact> runfilesManifests,
RunfilesSupplier runfilesSupplier,
ActionExecutionMetadata action,
ResourceSet localResources,
Collection<PathFragment> optionalOutputFiles) {
this.arguments = ImmutableList.copyOf(arguments);
this.environment = ImmutableMap.copyOf(environment);
this.executionInfo = ImmutableMap.copyOf(executionInfo);
this.runfilesManifests = ImmutableMap.copyOf(runfilesManifests);
this.runfilesSupplier = runfilesSupplier;
this.action = action;
this.localResources = localResources;
this.optionalOutputFiles = ImmutableSet.copyOf(optionalOutputFiles);
}
/**
* Returns a new Spawn. The caller must not modify the parameters after the call; neither will
* this method.
*/
public BaseSpawn(
List<String> arguments,
Map<String, String> environment,
Map<String, String> executionInfo,
RunfilesSupplier runfilesSupplier,
ActionExecutionMetadata action,
ResourceSet localResources) {
this(
arguments,
environment,
executionInfo,
ImmutableMap.<PathFragment, Artifact>of(),
runfilesSupplier,
action,
localResources,
ImmutableSet.<PathFragment>of());
}
/**
* Returns a new Spawn. The caller must not modify the parameters after the call; neither will
* this method.
*/
public BaseSpawn(
List<String> arguments,
Map<String, String> environment,
Map<String, String> executionInfo,
Map<PathFragment, Artifact> runfilesManifests,
ActionExecutionMetadata action,
ResourceSet localResources) {
this(
arguments,
environment,
executionInfo,
runfilesManifests,
EmptyRunfilesSupplier.INSTANCE,
action,
localResources,
ImmutableSet.<PathFragment>of());
}
/** Returns a new Spawn. */
public BaseSpawn(
List<String> arguments,
Map<String, String> environment,
Map<String, String> executionInfo,
ActionExecutionMetadata action,
ResourceSet localResources) {
this(
arguments,
environment,
executionInfo,
ImmutableMap.<PathFragment, Artifact>of(),
action,
localResources);
}
public BaseSpawn(
List<String> arguments,
Map<String, String> environment,
Map<String, String> executionInfo,
RunfilesSupplier runfilesSupplier,
ActionExecutionMetadata action,
ResourceSet localResources,
Collection<PathFragment> optionalOutputFiles) {
this(
arguments,
environment,
executionInfo,
ImmutableMap.<PathFragment, Artifact>of(),
runfilesSupplier,
action,
localResources,
optionalOutputFiles);
}
public static PathFragment runfilesForFragment(PathFragment pathFragment) {
return pathFragment.getParentDirectory().getChild(pathFragment.getBaseName() + ".runfiles");
}
@Override
public boolean hasNoSandbox() {
return executionInfo.containsKey("nosandbox");
}
@Override
public boolean isRemotable() {
return !executionInfo.containsKey("local");
}
@Override
public final ImmutableMap<String, String> getExecutionInfo() {
return executionInfo;
}
@Override
public String asShellCommand(Path workingDir) {
return asShellCommand(getArguments(), workingDir, getEnvironment());
}
@Override
public ImmutableMap<PathFragment, Artifact> getRunfilesManifests() {
return runfilesManifests;
}
@Override
public RunfilesSupplier getRunfilesSupplier() {
return runfilesSupplier;
}
@Override
public ImmutableList<Artifact> getFilesetManifests() {
return ImmutableList.<Artifact>of();
}
@Override
public SpawnInfo getExtraActionInfo() {
SpawnInfo.Builder info = SpawnInfo.newBuilder();
info.addAllArgument(getArguments());
for (Map.Entry<String, String> variable : getEnvironment().entrySet()) {
info.addVariable(
EnvironmentVariable.newBuilder()
.setName(variable.getKey())
.setValue(variable.getValue())
.build());
}
for (ActionInput input : getInputFiles()) {
// Explicitly ignore middleman artifacts here.
if (!(input instanceof Artifact) || !((Artifact) input).isMiddlemanArtifact()) {
info.addInputFile(input.getExecPathString());
}
}
info.addAllOutputFile(ActionInputHelper.toExecPaths(getOutputFiles()));
return info.build();
}
@Override
public ImmutableList<String> getArguments() {
// TODO(bazel-team): this method should be final, as the correct value of the args can be
// injected in the ctor.
return arguments;
}
@Override
public ImmutableMap<String, String> getEnvironment() {
PathFragment runfilesRoot = getRunfilesRoot();
if (runfilesRoot == null
|| (environment.containsKey("JAVA_RUNFILES")
&& environment.containsKey("PYTHON_RUNFILES"))) {
return environment;
} else {
ImmutableMap.Builder<String, String> env = ImmutableMap.builder();
env.putAll(environment);
// TODO(bazel-team): Unify these into a single env variable.
String runfilesRootString = runfilesRoot.getPathString();
env.put("JAVA_RUNFILES", runfilesRootString);
env.put("PYTHON_RUNFILES", runfilesRootString);
return env.build();
}
}
/** @return the runfiles directory if there is only one, otherwise null */
private PathFragment getRunfilesRoot() {
Set<PathFragment> runfilesSupplierRoots = runfilesSupplier.getRunfilesDirs();
if (runfilesSupplierRoots.size() == 1 && runfilesManifests.isEmpty()) {
return Iterables.getOnlyElement(runfilesSupplierRoots);
} else if (runfilesManifests.size() == 1 && runfilesSupplierRoots.isEmpty()) {
return Iterables.getOnlyElement(runfilesManifests.keySet());
} else {
return null;
}
}
@Override
public Iterable<? extends ActionInput> getToolFiles() {
return action.getTools();
}
@Override
public Iterable<? extends ActionInput> getInputFiles() {
return action.getInputs();
}
@Override
public Collection<? extends ActionInput> getOutputFiles() {
return action.getOutputs();
}
@Override
public Collection<PathFragment> getOptionalOutputFiles() {
return optionalOutputFiles;
}
@Override
public ActionExecutionMetadata getResourceOwner() {
return action;
}
@Override
public ResourceSet getLocalResources() {
return localResources;
}
@Override
public ActionOwner getOwner() {
return action.getOwner();
}
@Override
public String getMnemonic() {
return action.getMnemonic();
}
/** Convert a working dir + environment map + arg list into a Bourne shell command. */
public static String asShellCommand(
Collection<String> arguments, Path workingDirectory, Map<String, String> environment) {
// We print this command out in such a way that it can safely be
// copied+pasted as a Bourne shell command. This is extremely valuable for
// debugging.
return CommandFailureUtils.describeCommand(
CommandDescriptionForm.COMPLETE, arguments, environment, workingDirectory.getPathString());
}
/** A local spawn requiring zero resources. */
public static class Local extends BaseSpawn {
public Local(
List<String> arguments, Map<String, String> environment, ActionExecutionMetadata action) {
this(arguments, environment, ImmutableMap.<String, String>of(), action);
}
public Local(
List<String> arguments,
Map<String, String> environment,
Map<String, String> executionInfo,
ActionExecutionMetadata action) {
super(arguments, environment, buildExecutionInfo(executionInfo), action, ResourceSet.ZERO);
}
private static ImmutableMap<String, String> buildExecutionInfo(
Map<String, String> additionalExecutionInfo) {
ImmutableMap.Builder<String, String> executionInfo = ImmutableMap.builder();
executionInfo.putAll(additionalExecutionInfo);
executionInfo.put("local", "");
return executionInfo.build();
}
}
}