// 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.rules.java;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.devtools.build.lib.packages.Aspect.INJECTING_RULE_KIND_PARAMETER_KEY;
import static com.google.devtools.build.lib.util.Preconditions.checkNotNull;
import static java.nio.charset.StandardCharsets.ISO_8859_1;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableCollection;
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.Action;
import com.google.devtools.build.lib.actions.ActionOwner;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.ArtifactOwner;
import com.google.devtools.build.lib.actions.EmptyRunfilesSupplier;
import com.google.devtools.build.lib.actions.ParameterFile;
import com.google.devtools.build.lib.actions.ResourceSet;
import com.google.devtools.build.lib.actions.Root;
import com.google.devtools.build.lib.actions.extra.ExtraActionInfo;
import com.google.devtools.build.lib.actions.extra.JavaCompileInfo;
import com.google.devtools.build.lib.analysis.AnalysisEnvironment;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.actions.CommandLine;
import com.google.devtools.build.lib.analysis.actions.CustomCommandLine;
import com.google.devtools.build.lib.analysis.actions.CustomCommandLine.CustomMultiArgv;
import com.google.devtools.build.lib.analysis.actions.ParameterFileWriteAction;
import com.google.devtools.build.lib.analysis.actions.SpawnAction;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration.StrictDepsMode;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.collect.nestedset.Order;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadCompatible;
import com.google.devtools.build.lib.rules.java.JavaConfiguration.JavaClasspathMode;
import com.google.devtools.build.lib.skyframe.AspectValue;
import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.lib.util.StringCanonicalizer;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
/** Action that represents a Java compilation. */
@ThreadCompatible
@Immutable
public final class JavaCompileAction extends SpawnAction {
private static final String JACOCO_INSTRUMENTATION_PROCESSOR = "jacoco";
private static final ResourceSet LOCAL_RESOURCES =
ResourceSet.createWithRamCpuIo(750 /*MB*/, 0.5 /*CPU*/, 0.0 /*IO*/);
/** Environment variable that sets the UTF-8 charset. */
static final ImmutableMap<String, String> UTF8_ENVIRONMENT =
ImmutableMap.of("LC_CTYPE", "en_US.UTF-8");
private final CommandLine javaCompileCommandLine;
private final CommandLine commandLine;
/**
* The directory in which generated classfiles are placed.
* May be erased/created by the JavaBuilder.
*/
private final PathFragment classDirectory;
private final Artifact outputJar;
/**
* The list of classpath entries to specify to javac.
*/
private final NestedSet<Artifact> classpathEntries;
/** The list of bootclasspath entries to specify to javac. */
private final ImmutableList<Artifact> bootclasspathEntries;
/** The list of sourcepath entries to specify to javac. */
private final ImmutableList<Artifact> sourcePathEntries;
/**
* The path to the extdir to specify to javac.
*/
private final ImmutableList<Artifact> extdirInputs;
/** The list of classpath entries to search for annotation processors. */
private final NestedSet<Artifact> processorPath;
/**
* The list of annotation processor classes to run.
*/
private final ImmutableList<String> processorNames;
/** Set of additional Java source files to compile. */
private final ImmutableList<Artifact> sourceJars;
/** The set of explicit Java source files to compile. */
private final ImmutableSet<Artifact> sourceFiles;
/**
* The compiler options to pass to javac.
*/
private final ImmutableList<String> javacOpts;
/** The subset of classpath jars provided by direct dependencies. */
private final NestedSet<Artifact> directJars;
/**
* The level of strict dependency checks (off, warnings, or errors).
*/
private final BuildConfiguration.StrictDepsMode strictJavaDeps;
/** The set of .jdeps artifacts provided by direct dependencies. */
private final NestedSet<Artifact> compileTimeDependencyArtifacts;
/**
* Constructs an action to compile a set of Java source files to class files.
*
* @param owner the action owner, typically a java_* RuleConfiguredTarget.
* @param tools the tools used by the action
* @param inputs the inputs of the action
* @param outputs the outputs of the action
* @param javaCompileCommandLine the command line for the java library builder - it's actually
* written to the parameter file, but other parts (for example, ide_build_info) need access to
* the data
* @param commandLine the actual invocation command line
* @param classDirectory the directory in which generated classfiles are placed
* @param outputJar the jar file the compilation outputs will be written to
* @param classpathEntries the compile-time classpath entries
* @param bootclasspathEntries the compile-time bootclasspath entries
* @param extdirInputs the compile-time extclasspath entries
* @param processorPath the classpath to search for annotation processors
* @param processorNames the annotation processors to run
* @param sourceJars jars of sources to compile
* @param sourceFiles source files to compile
* @param javacOpts the javac options for the compilation
* @param directJars the subset of classpath jars provided by direct dependencies
* @param executionInfo the execution info
* @param strictJavaDeps the Strict Java Deps mode
* @param compileTimeDependencyArtifacts the jdeps files for direct dependencies
* @param progressMessage the progress message
*/
private JavaCompileAction(
ActionOwner owner,
NestedSet<Artifact> tools,
NestedSet<Artifact> inputs,
Collection<Artifact> outputs,
CommandLine javaCompileCommandLine,
CommandLine commandLine,
PathFragment classDirectory,
Artifact outputJar,
NestedSet<Artifact> classpathEntries,
ImmutableList<Artifact> bootclasspathEntries,
ImmutableList<Artifact> sourcePathEntries,
ImmutableList<Artifact> extdirInputs,
NestedSet<Artifact> processorPath,
List<String> processorNames,
Collection<Artifact> sourceJars,
ImmutableSet<Artifact> sourceFiles,
List<String> javacOpts,
NestedSet<Artifact> directJars,
Map<String, String> executionInfo,
StrictDepsMode strictJavaDeps,
NestedSet<Artifact> compileTimeDependencyArtifacts,
String progressMessage) {
super(
owner,
tools,
inputs,
outputs,
LOCAL_RESOURCES,
commandLine,
false,
ImmutableMap.copyOf(UTF8_ENVIRONMENT),
ImmutableSet.copyOf(ImmutableSet.<String>of()),
ImmutableMap.copyOf(executionInfo),
progressMessage,
EmptyRunfilesSupplier.INSTANCE,
"Javac",
false /*executeUnconditionally*/,
null /*extraActionInfoSupplier*/);
this.javaCompileCommandLine = javaCompileCommandLine;
this.commandLine = commandLine;
this.classDirectory = checkNotNull(classDirectory);
this.outputJar = outputJar;
this.classpathEntries = classpathEntries;
this.bootclasspathEntries = ImmutableList.copyOf(bootclasspathEntries);
this.sourcePathEntries = ImmutableList.copyOf(sourcePathEntries);
this.extdirInputs = extdirInputs;
this.processorPath = processorPath;
this.processorNames = ImmutableList.copyOf(processorNames);
this.sourceJars = ImmutableList.copyOf(sourceJars);
this.sourceFiles = sourceFiles;
this.javacOpts = ImmutableList.copyOf(javacOpts);
this.directJars = checkNotNull(directJars, "directJars must not be null");
this.strictJavaDeps = strictJavaDeps;
this.compileTimeDependencyArtifacts = compileTimeDependencyArtifacts;
}
/** Returns the given (passed to constructor) source files. */
@VisibleForTesting
ImmutableSet<Artifact> getSourceFiles() {
return sourceFiles;
}
/**
* Returns the list of paths that represents the classpath.
*/
@VisibleForTesting
public Iterable<Artifact> getClasspath() {
return classpathEntries;
}
/** Returns the list of paths that represents the bootclasspath. */
@VisibleForTesting
Collection<Artifact> getBootclasspath() {
return bootclasspathEntries;
}
/** Returns the list of paths that represents the sourcepath. */
@VisibleForTesting
public Collection<Artifact> getSourcePathEntries() {
return sourcePathEntries;
}
/**
* Returns the path to the extdir.
*/
@VisibleForTesting
public Collection<Artifact> getExtdir() {
return extdirInputs;
}
/**
* Returns the list of paths that represents the source jars.
*/
@VisibleForTesting
public Collection<Artifact> getSourceJars() {
return sourceJars;
}
/** Returns the list of paths that represents the processor path. */
@VisibleForTesting
public NestedSet<Artifact> getProcessorpath() {
return processorPath;
}
@VisibleForTesting
public List<String> getJavacOpts() {
return javacOpts;
}
@VisibleForTesting
public NestedSet<Artifact> getDirectJars() {
return directJars;
}
@VisibleForTesting
public NestedSet<Artifact> getCompileTimeDependencyArtifacts() {
return compileTimeDependencyArtifacts;
}
@VisibleForTesting
public BuildConfiguration.StrictDepsMode getStrictJavaDepsMode() {
return strictJavaDeps;
}
public PathFragment getClassDirectory() {
return classDirectory;
}
/**
* Returns the list of class names of processors that should
* be run.
*/
@VisibleForTesting
public List<String> getProcessorNames() {
return processorNames;
}
/**
* Returns the output jar artifact that gets generated by archiving the results of the Java
* compilation.
*/
public Artifact getOutputJar() {
return outputJar;
}
@Override
public Artifact getPrimaryOutput() {
return getOutputJar();
}
/**
* Constructs a command line that can be used to invoke the
* JavaBuilder.
*
* <p>Do not use this method, except for testing (and for the in-process
* strategy).
*/
@VisibleForTesting
public Iterable<String> buildCommandLine() {
return javaCompileCommandLine.arguments();
}
/** Returns the command and arguments for a java compile action. */
public List<String> getCommand() {
return ImmutableList.copyOf(commandLine.arguments());
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append("JavaBuilder ");
Joiner.on(' ').appendTo(result, commandLine.arguments());
return result.toString();
}
@Override
public ExtraActionInfo.Builder getExtraActionInfo() {
JavaCompileInfo.Builder info = JavaCompileInfo.newBuilder();
info.addAllSourceFile(Artifact.toExecPaths(getSourceFiles()));
info.addAllClasspath(Artifact.toExecPaths(getClasspath()));
info.addAllBootclasspath(Artifact.toExecPaths(getBootclasspath()));
info.addAllSourcepath(Artifact.toExecPaths(getSourceJars()));
info.addAllJavacOpt(getJavacOpts());
info.addAllProcessor(getProcessorNames());
info.addAllProcessorpath(Artifact.toExecPaths(getProcessorpath()));
info.setOutputjar(getOutputJar().getExecPathString());
return super.getExtraActionInfo()
.setExtension(JavaCompileInfo.javaCompileInfo, info.build());
}
/**
* Builds the list of mappings between jars on the classpath and their originating targets names.
*/
@VisibleForTesting
static class JarsToTargetsArgv extends CustomMultiArgv {
private final Iterable<Artifact> classpath;
private final NestedSet<Artifact> directJars;
@VisibleForTesting
JarsToTargetsArgv(Iterable<Artifact> classpath, NestedSet<Artifact> directJars) {
this.classpath = classpath;
this.directJars = directJars;
}
@Override
public Iterable<String> argv() {
Set<Artifact> directJarSet = directJars.toSet();
ImmutableList.Builder<String> builder = ImmutableList.builder();
for (Artifact jar : classpath) {
builder.add(directJarSet.contains(jar) ? "--direct_dependency" : "--indirect_dependency");
builder.add(jar.getExecPathString());
builder.add(getArtifactOwnerGeneralizedLabel(jar));
}
return builder.build();
}
private String getArtifactOwnerGeneralizedLabel(Artifact artifact) {
ArtifactOwner owner = checkNotNull(artifact.getArtifactOwner(), artifact);
StringBuilder result = new StringBuilder();
Label label = owner.getLabel();
result.append(
label.getPackageIdentifier().getRepository().isDefault()
|| label.getPackageIdentifier().getRepository().isMain()
? label.toString()
// Escape '@' prefix for .params file.
: "@" + label);
if (owner instanceof AspectValue.AspectKey) {
AspectValue.AspectKey aspectOwner = (AspectValue.AspectKey) owner;
ImmutableCollection<String> injectingRuleKind =
aspectOwner.getParameters().getAttribute(INJECTING_RULE_KIND_PARAMETER_KEY);
if (injectingRuleKind.size() == 1) {
result.append(' ').append(getOnlyElement(injectingRuleKind));
}
}
return result.toString();
}
}
/** Creates an ArgvFragment containing the common initial command line arguments */
private static CustomMultiArgv spawnCommandLineBase(
final PathFragment javaExecutable,
final Artifact javaBuilderJar,
final Artifact langtoolsJar,
final ImmutableList<Artifact> instrumentationJars,
final ImmutableList<String> javaBuilderJvmFlags,
final String javaBuilderMainClass,
final String pathDelimiter) {
return new CustomMultiArgv() {
@Override
public Iterable<String> argv() {
checkNotNull(langtoolsJar);
checkNotNull(javaBuilderJar);
CustomCommandLine.Builder builder = CustomCommandLine.builder()
.addPath(javaExecutable)
// Langtools jar is placed on the boot classpath so that it can override classes
// in the JRE. Typically this has no effect since langtools.jar does not have
// classes in common with rt.jar. However, it is necessary when using a version
// of javac.jar generated via ant from the langtools build.xml that is of a
// different version than AND has an overlap in contents with the default
// run-time (eg while upgrading the Java version).
.addPaths("-Xbootclasspath/p:%s", langtoolsJar.getExecPath())
.add(javaBuilderJvmFlags);
if (!instrumentationJars.isEmpty()) {
builder
.addJoinExecPaths("-cp", pathDelimiter,
Iterables.concat(instrumentationJars, ImmutableList.of(javaBuilderJar)))
.add(javaBuilderMainClass);
} else {
// If there are no instrumentation jars, use simpler '-jar' option to launch JavaBuilder.
builder.addExecPath("-jar", javaBuilderJar);
}
return builder.build().arguments();
}
};
}
/**
* Tells {@link Builder} how to create new artifacts. Is there so that {@link Builder} can be
* exercised in tests without creating a full {@link RuleContext}.
*/
public interface ArtifactFactory {
/**
* Create an artifact with the specified root-relative path under the specified root.
*/
Artifact create(PathFragment rootRelativePath, Root root);
}
@VisibleForTesting
static ArtifactFactory createArtifactFactory(final AnalysisEnvironment env) {
return new ArtifactFactory() {
@Override
public Artifact create(PathFragment rootRelativePath, Root root) {
return env.getDerivedArtifact(rootRelativePath, root);
}
};
}
/**
* Builder class to construct Java compile actions.
*/
public static class Builder {
private final ActionOwner owner;
private final AnalysisEnvironment analysisEnvironment;
private final ArtifactFactory artifactFactory;
private final BuildConfiguration configuration;
private final JavaSemantics semantics;
private PathFragment javaExecutable;
private List<Artifact> javabaseInputs = ImmutableList.of();
private Artifact outputJar;
private Artifact gensrcOutputJar;
private Artifact manifestProtoOutput;
private Artifact outputDepsProto;
private Collection<Artifact> additionalOutputs;
private Artifact paramFile;
private Artifact metadata;
private ImmutableSet<Artifact> sourceFiles = ImmutableSet.of();
private final Collection<Artifact> sourceJars = new ArrayList<>();
private BuildConfiguration.StrictDepsMode strictJavaDeps =
BuildConfiguration.StrictDepsMode.OFF;
private NestedSet<Artifact> directJars = NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER);
private NestedSet<Artifact> compileTimeDependencyArtifacts =
NestedSetBuilder.emptySet(Order.STABLE_ORDER);
private List<String> javacOpts = new ArrayList<>();
private ImmutableList<String> javacJvmOpts = ImmutableList.of();
private ImmutableMap<String, String> executionInfo = ImmutableMap.of();
private boolean compressJar;
private NestedSet<Artifact> classpathEntries =
NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER);
private ImmutableList<Artifact> bootclasspathEntries = ImmutableList.of();
private ImmutableList<Artifact> sourcePathEntries = ImmutableList.of();
private ImmutableList<Artifact> extdirInputs = ImmutableList.of();
private Artifact javaBuilderJar;
private Artifact langtoolsJar;
private ImmutableList<Artifact> instrumentationJars = ImmutableList.of();
private PathFragment sourceGenDirectory;
private PathFragment tempDirectory;
private PathFragment classDirectory;
private NestedSet<Artifact> processorPath = NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER);
private final List<String> processorNames = new ArrayList<>();
/** The list of custom javac flags to pass to annotation processors. */
private final List<String> processorFlags = new ArrayList<>();
private String ruleKind;
private Label targetLabel;
private boolean testOnly = false;
/**
* Creates a Builder from an owner and a build configuration.
*/
public Builder(ActionOwner owner, AnalysisEnvironment analysisEnvironment,
ArtifactFactory artifactFactory, BuildConfiguration configuration,
JavaSemantics semantics) {
this.owner = owner;
this.analysisEnvironment = analysisEnvironment;
this.artifactFactory = artifactFactory;
this.configuration = configuration;
this.semantics = semantics;
}
/**
* Creates a Builder from an owner and a build configuration.
*/
public Builder(final RuleContext ruleContext, JavaSemantics semantics) {
this(ruleContext.getActionOwner(),
ruleContext.getAnalysisEnvironment(),
new ArtifactFactory() {
@Override
public Artifact create(PathFragment rootRelativePath, Root root) {
return ruleContext.getDerivedArtifact(rootRelativePath, root);
}
},
ruleContext.getConfiguration(), semantics);
}
public JavaCompileAction build() {
// TODO(bazel-team): all the params should be calculated before getting here, and the various
// aggregation code below should go away.
final String pathSeparator = configuration.getHostPathSeparator();
final List<String> internedJcopts = new ArrayList<>();
for (String jcopt : javacOpts) {
internedJcopts.add(StringCanonicalizer.intern(jcopt));
}
// Invariant: if strictJavaDeps is OFF, then directJars and
// dependencyArtifacts are ignored
if (strictJavaDeps == BuildConfiguration.StrictDepsMode.OFF) {
directJars = NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER);
compileTimeDependencyArtifacts = NestedSetBuilder.emptySet(Order.STABLE_ORDER);
}
// Invariant: if java_classpath is set to 'off', dependencyArtifacts are ignored
JavaConfiguration javaConfiguration = configuration.getFragment(JavaConfiguration.class);
if (javaConfiguration.getReduceJavaClasspath() == JavaClasspathMode.OFF) {
compileTimeDependencyArtifacts = NestedSetBuilder.emptySet(Order.STABLE_ORDER);
}
if (paramFile == null) {
paramFile = artifactFactory.create(
ParameterFile.derivePath(outputJar.getRootRelativePath()),
configuration.getBinDirectory(targetLabel.getPackageIdentifier().getRepository()));
}
Preconditions.checkState(javaExecutable != null, owner);
ImmutableList.Builder<Artifact> outputsBuilder = ImmutableList.<Artifact>builder()
.addAll(
new ArrayList<>(Collections2.filter(Arrays.asList(
outputJar,
metadata,
gensrcOutputJar,
manifestProtoOutput,
outputDepsProto), Predicates.notNull())));
if (additionalOutputs != null) {
outputsBuilder.addAll(additionalOutputs);
}
ImmutableList<Artifact> outputs = outputsBuilder.build();
CustomCommandLine paramFileContents = buildParamFileContents(internedJcopts);
Action parameterFileWriteAction = new ParameterFileWriteAction(owner, paramFile,
paramFileContents, ParameterFile.ParameterFileType.UNQUOTED, ISO_8859_1);
analysisEnvironment.registerAction(parameterFileWriteAction);
CustomMultiArgv spawnCommandLineBase = spawnCommandLineBase(
javaExecutable,
javaBuilderJar,
langtoolsJar,
instrumentationJars,
javacJvmOpts,
semantics.getJavaBuilderMainClass(),
pathSeparator);
// The actual params-file-based command line executed for a compile action.
CommandLine javaBuilderCommandLine = CustomCommandLine.builder()
.add(spawnCommandLineBase)
.addPaths("@%s", paramFile.getExecPath())
.build();
NestedSet<Artifact> tools =
NestedSetBuilder.<Artifact>stableOrder()
.add(langtoolsJar)
.add(javaBuilderJar)
.addAll(instrumentationJars)
.build();
NestedSet<Artifact> inputs =
NestedSetBuilder.<Artifact>stableOrder()
.addTransitive(classpathEntries)
.addTransitive(compileTimeDependencyArtifacts)
.addTransitive(processorPath)
.addAll(sourceJars)
.addAll(sourceFiles)
.addAll(javabaseInputs)
.addAll(bootclasspathEntries)
.addAll(sourcePathEntries)
.addAll(extdirInputs)
.add(paramFile)
.addTransitive(tools)
.build();
return new JavaCompileAction(
owner,
tools,
inputs,
outputs,
paramFileContents,
javaBuilderCommandLine,
classDirectory,
outputJar,
classpathEntries,
bootclasspathEntries,
sourcePathEntries,
extdirInputs,
processorPath,
processorNames,
sourceJars,
sourceFiles,
internedJcopts,
directJars,
executionInfo,
strictJavaDeps,
compileTimeDependencyArtifacts,
buildProgressMessage());
}
private CustomCommandLine buildParamFileContents(Collection<String> javacOpts) {
checkNotNull(classDirectory, "classDirectory should not be null");
checkNotNull(tempDirectory, "tempDirectory should not be null");
CustomCommandLine.Builder result = CustomCommandLine.builder();
result.add("--classdir").addPath(classDirectory);
result.add("--tempdir").addPath(tempDirectory);
if (outputJar != null) {
result.addExecPath("--output", outputJar);
}
if (sourceGenDirectory != null) {
result.add("--sourcegendir").addPath(sourceGenDirectory);
}
if (gensrcOutputJar != null) {
result.addExecPath("--generated_sources_output", gensrcOutputJar);
}
if (manifestProtoOutput != null) {
result.addExecPath("--output_manifest_proto", manifestProtoOutput);
}
if (compressJar) {
result.add("--compress_jar");
}
if (outputDepsProto != null) {
result.addExecPath("--output_deps_proto", outputDepsProto);
}
if (!extdirInputs.isEmpty()) {
result.addExecPaths("--extclasspath", extdirInputs);
}
if (!bootclasspathEntries.isEmpty()) {
result.addExecPaths("--bootclasspath", bootclasspathEntries);
}
if (!sourcePathEntries.isEmpty()) {
result.addExecPaths("--sourcepath", sourcePathEntries);
}
if (!processorPath.isEmpty()) {
result.addExecPaths("--processorpath", processorPath);
}
if (!processorNames.isEmpty()) {
result.add("--processors", processorNames);
}
if (!processorFlags.isEmpty()) {
result.add("--javacopts", processorFlags);
}
if (!sourceJars.isEmpty()) {
result.addExecPaths("--source_jars", sourceJars);
}
if (!sourceFiles.isEmpty()) {
result.addExecPaths("--sources", sourceFiles);
}
if (!javacOpts.isEmpty()) {
result.add("--javacopts", javacOpts);
}
if (ruleKind != null) {
result.add("--rule_kind");
result.add(ruleKind);
}
if (targetLabel != null) {
result.add("--target_label");
if (targetLabel.getPackageIdentifier().getRepository().isDefault()
|| targetLabel.getPackageIdentifier().getRepository().isMain()) {
result.add(targetLabel.toString());
} else {
// @-prefixed strings will be assumed to be filenames and expanded by
// {@link JavaLibraryBuildRequest}, so add an extra &at; to escape it.
result.add("@" + targetLabel);
}
}
if (testOnly) {
result.add("--testonly");
}
if (!classpathEntries.isEmpty()) {
result.addExecPaths("--classpath", classpathEntries);
}
// strict_java_deps controls whether the mapping from jars to targets is
// written out and whether we try to minimize the compile-time classpath.
if (strictJavaDeps != BuildConfiguration.StrictDepsMode.OFF) {
result.add("--strict_java_deps");
result.add(strictJavaDeps.toString());
result.add(new JarsToTargetsArgv(classpathEntries, directJars));
if (configuration.getFragment(JavaConfiguration.class).getReduceJavaClasspath()
== JavaClasspathMode.JAVABUILDER) {
result.add("--reduce_classpath");
if (!compileTimeDependencyArtifacts.isEmpty()) {
result.addExecPaths("--deps_artifacts", compileTimeDependencyArtifacts);
}
}
}
if (metadata != null) {
result.add("--post_processor");
result.addExecPath(JACOCO_INSTRUMENTATION_PROCESSOR, metadata);
result.addPath(
configuration
.getCoverageMetadataDirectory(targetLabel.getPackageIdentifier().getRepository())
.getExecPath());
result.add("-*Test");
result.add("-*TestCase");
}
return result.build();
}
private String buildProgressMessage() {
StringBuilder sb = new StringBuilder("Building ");
sb.append(outputJar.prettyPrint());
sb.append(" (");
boolean first = true;
first = appendCount(sb, first, sourceFiles.size(), "source file");
first = appendCount(sb, first, sourceJars.size(), "source jar");
sb.append(")");
return sb.toString();
}
/**
* Append an input count to the progress message, e.g. "2 source jars". If an input count has
* already been appended, prefix with ", ".
*/
private static boolean appendCount(StringBuilder sb, boolean first, int count, String name) {
if (count > 0) {
if (!first) {
sb.append(", ");
} else {
first = false;
}
sb.append(count).append(' ').append(name);
if (count > 1) {
sb.append('s');
}
}
return first;
}
public Builder setParameterFile(Artifact paramFile) {
this.paramFile = paramFile;
return this;
}
public Builder setJavaExecutable(PathFragment javaExecutable) {
this.javaExecutable = javaExecutable;
return this;
}
public Builder setJavaBaseInputs(Iterable<Artifact> javabaseInputs) {
this.javabaseInputs = ImmutableList.copyOf(javabaseInputs);
return this;
}
public Builder setOutputJar(Artifact outputJar) {
this.outputJar = outputJar;
return this;
}
public Builder setGensrcOutputJar(Artifact gensrcOutputJar) {
this.gensrcOutputJar = gensrcOutputJar;
return this;
}
public Builder setManifestProtoOutput(Artifact manifestProtoOutput) {
this.manifestProtoOutput = manifestProtoOutput;
return this;
}
public Builder setOutputDepsProto(Artifact outputDepsProto) {
this.outputDepsProto = outputDepsProto;
return this;
}
public Builder setAdditionalOutputs(Collection<Artifact> outputs) {
this.additionalOutputs = outputs;
return this;
}
public Builder setMetadata(Artifact metadata) {
this.metadata = metadata;
return this;
}
public Builder setSourceFiles(ImmutableSet<Artifact> sourceFiles) {
this.sourceFiles = sourceFiles;
return this;
}
public Builder addSourceJars(Collection<Artifact> sourceJars) {
this.sourceJars.addAll(sourceJars);
return this;
}
/**
* Sets the strictness of Java dependency checking, see {@link
* com.google.devtools.build.lib.analysis.config.BuildConfiguration.StrictDepsMode}.
*/
public Builder setStrictJavaDeps(BuildConfiguration.StrictDepsMode strictDeps) {
strictJavaDeps = strictDeps;
return this;
}
/** Accumulates the given jar artifacts as being provided by direct dependencies. */
public Builder setDirectJars(NestedSet<Artifact> directJars) {
this.directJars = checkNotNull(directJars, "directJars must not be null");
return this;
}
public Builder setCompileTimeDependencyArtifacts(NestedSet<Artifact> dependencyArtifacts) {
checkNotNull(compileTimeDependencyArtifacts, "dependencyArtifacts must not be null");
this.compileTimeDependencyArtifacts = dependencyArtifacts;
return this;
}
public Builder setJavacOpts(Iterable<String> copts) {
this.javacOpts = ImmutableList.copyOf(copts);
return this;
}
public Builder setJavacJvmOpts(ImmutableList<String> opts) {
this.javacJvmOpts = opts;
return this;
}
public Builder setJavacExecutionInfo(ImmutableMap<String, String> executionInfo) {
this.executionInfo = executionInfo;
return this;
}
public Builder setCompressJar(boolean compressJar) {
this.compressJar = compressJar;
return this;
}
public Builder setClasspathEntries(NestedSet<Artifact> classpathEntries) {
this.classpathEntries = classpathEntries;
return this;
}
public Builder setBootclasspathEntries(Iterable<Artifact> bootclasspathEntries) {
this.bootclasspathEntries = ImmutableList.copyOf(bootclasspathEntries);
return this;
}
public Builder setSourcePathEntries(Iterable<Artifact> sourcePathEntries) {
this.sourcePathEntries = ImmutableList.copyOf(sourcePathEntries);
return this;
}
public Builder setExtdirInputs(Iterable<Artifact> extdirEntries) {
this.extdirInputs = ImmutableList.copyOf(extdirEntries);
return this;
}
/**
* Sets the directory where source files generated by annotation processors should be stored.
*/
public Builder setSourceGenDirectory(PathFragment sourceGenDirectory) {
this.sourceGenDirectory = sourceGenDirectory;
return this;
}
public Builder setTempDirectory(PathFragment tempDirectory) {
this.tempDirectory = tempDirectory;
return this;
}
public Builder setClassDirectory(PathFragment classDirectory) {
this.classDirectory = classDirectory;
return this;
}
public Builder setProcessorPaths(NestedSet<Artifact> processorPaths) {
this.processorPath = processorPaths;
return this;
}
public Builder addProcessorNames(Collection<String> processorNames) {
this.processorNames.addAll(processorNames);
return this;
}
public Builder addProcessorFlags(Collection<String> processorFlags) {
this.processorFlags.addAll(processorFlags);
return this;
}
public Builder setLangtoolsJar(Artifact langtoolsJar) {
this.langtoolsJar = langtoolsJar;
return this;
}
public Builder setJavaBuilderJar(Artifact javaBuilderJar) {
this.javaBuilderJar = javaBuilderJar;
return this;
}
public Builder setInstrumentationJars(Iterable<Artifact> instrumentationJars) {
this.instrumentationJars = ImmutableList.copyOf(instrumentationJars);
return this;
}
public Builder setRuleKind(String ruleKind) {
this.ruleKind = ruleKind;
return this;
}
public Builder setTargetLabel(Label targetLabel) {
this.targetLabel = targetLabel;
return this;
}
public Builder setTestOnly(boolean testOnly) {
this.testOnly = testOnly;
return this;
}
}
}