// 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.base.Preconditions.checkNotNull;
import static com.google.devtools.build.lib.analysis.config.BuildConfiguration.StrictDepsMode.OFF;
import static com.google.devtools.build.lib.rules.java.JavaHelper.getHostJavabaseInputs;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.AnalysisEnvironment;
import com.google.devtools.build.lib.analysis.FileProvider;
import com.google.devtools.build.lib.analysis.FilesToRunProvider;
import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
import com.google.devtools.build.lib.analysis.actions.CustomCommandLine;
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.collect.ImmutableIterable;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.packages.AttributeMap;
import com.google.devtools.build.lib.rules.java.JavaConfiguration.JavaClasspathMode;
import com.google.devtools.build.lib.rules.test.InstrumentedFilesCollector;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.util.FileType;
import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
/**
* A helper class for compiling Java targets. It contains method to create the
* various intermediate Artifacts for using ijars and source ijars.
* <p>
* Also supports the creation of resource and source only Jars.
*/
public final class JavaCompilationHelper {
private final RuleContext ruleContext;
private final JavaToolchainProvider javaToolchain;
private final NestedSet<Artifact> hostJavabase;
private final Iterable<Artifact> jacocoInstrumentation;
private JavaTargetAttributes.Builder attributes;
private JavaTargetAttributes builtAttributes;
private final ImmutableList<String> customJavacOpts;
private final ImmutableList<String> customJavacJvmOpts;
private final List<Artifact> translations = new ArrayList<>();
private boolean translationsFrozen;
private final JavaSemantics semantics;
private final ImmutableList<Artifact> additionalJavaBaseInputs;
private static final String DEFAULT_ATTRIBUTES_SUFFIX = "";
private static final PathFragment JAVAC = PathFragment.create("_javac");
public JavaCompilationHelper(RuleContext ruleContext, JavaSemantics semantics,
ImmutableList<String> javacOpts, JavaTargetAttributes.Builder attributes,
JavaToolchainProvider javaToolchainProvider,
NestedSet<Artifact> hostJavabase,
Iterable<Artifact> jacocoInstrumentation,
ImmutableList<Artifact> additionalJavaBaseInputs) {
this.ruleContext = ruleContext;
this.javaToolchain = javaToolchainProvider;
this.hostJavabase = hostJavabase;
this.jacocoInstrumentation = jacocoInstrumentation;
this.attributes = attributes;
this.customJavacOpts = javacOpts;
this.customJavacJvmOpts = javaToolchain.getJvmOptions();
this.semantics = semantics;
this.additionalJavaBaseInputs = additionalJavaBaseInputs;
}
public JavaCompilationHelper(RuleContext ruleContext, JavaSemantics semantics,
ImmutableList<String> javacOpts, JavaTargetAttributes.Builder attributes,
JavaToolchainProvider javaToolchainProvider,
NestedSet<Artifact> hostJavabase,
Iterable<Artifact> jacocoInstrumentation) {
this(ruleContext, semantics, javacOpts, attributes, javaToolchainProvider, hostJavabase,
jacocoInstrumentation, ImmutableList.<Artifact>of());
}
public JavaCompilationHelper(RuleContext ruleContext, JavaSemantics semantics,
ImmutableList<String> javacOpts, JavaTargetAttributes.Builder attributes) {
this(
ruleContext,
semantics,
javacOpts,
attributes,
getJavaToolchainProvider(ruleContext),
getHostJavabaseInputs(ruleContext),
getInstrumentationJars(ruleContext));
}
public JavaCompilationHelper(RuleContext ruleContext, JavaSemantics semantics,
ImmutableList<String> javacOpts, JavaTargetAttributes.Builder attributes,
ImmutableList<Artifact> additionalJavaBaseInputs) {
this(
ruleContext,
semantics,
javacOpts,
attributes,
getJavaToolchainProvider(ruleContext),
getHostJavabaseInputs(ruleContext),
getInstrumentationJars(ruleContext),
additionalJavaBaseInputs);
}
public JavaCompilationHelper(RuleContext ruleContext, JavaSemantics semantics,
JavaTargetAttributes.Builder attributes) {
this(ruleContext, semantics, getDefaultJavacOptsFromRule(ruleContext), attributes);
}
public JavaTargetAttributes getAttributes() {
if (builtAttributes == null) {
builtAttributes = attributes.build();
}
return builtAttributes;
}
public RuleContext getRuleContext() {
return ruleContext;
}
private AnalysisEnvironment getAnalysisEnvironment() {
return ruleContext.getAnalysisEnvironment();
}
private BuildConfiguration getConfiguration() {
return ruleContext.getConfiguration();
}
private JavaConfiguration getJavaConfiguration() {
return ruleContext.getFragment(JavaConfiguration.class);
}
/**
* Creates the Action that compiles Java source files.
*
* @param outputJar the class jar Artifact to create with the Action
* @param manifestProtoOutput the output artifact for the manifest proto emitted from JavaBuilder
* @param gensrcOutputJar the generated sources jar Artifact to create with the Action
* (null if no sources will be generated).
* @param outputDepsProto the compiler-generated jdeps file to create with the Action
* (null if not requested)
* @param outputMetadata metadata file (null if no instrumentation is needed).
*/
public void createCompileAction(
Artifact outputJar,
Artifact manifestProtoOutput,
@Nullable Artifact gensrcOutputJar,
@Nullable Artifact outputDepsProto,
@Nullable Artifact outputMetadata) {
JavaTargetAttributes attributes = getAttributes();
Artifact classJar;
if (attributes.getResources().isEmpty()
&& attributes.getResourceJars().isEmpty()
&& attributes.getClassPathResources().isEmpty()
&& getTranslations().isEmpty()) {
// if there are sources and no resource, the only output is from the javac action
classJar = outputJar;
} else {
// otherwise create a separate jar for the compilation and add resources with singlejar
classJar =
ruleContext.getDerivedArtifact(
FileSystemUtils.appendWithoutExtension(outputJar.getRootRelativePath(), "-class"),
outputJar.getRoot());
createResourceJarAction(outputJar, ImmutableList.of(classJar));
}
JavaCompileAction.Builder builder = createJavaCompileActionBuilder(semantics);
builder.setClasspathEntries(attributes.getCompileTimeClassPath());
builder.setBootclasspathEntries(getBootclasspathOrDefault());
builder.setSourcePathEntries(attributes.getSourcePath());
builder.setExtdirInputs(getExtdirInputs());
builder.setLangtoolsJar(javaToolchain.getJavac());
builder.setJavaBuilderJar(javaToolchain.getJavaBuilder());
builder.setOutputJar(classJar);
builder.setManifestProtoOutput(manifestProtoOutput);
builder.setGensrcOutputJar(gensrcOutputJar);
builder.setOutputDepsProto(outputDepsProto);
builder.setAdditionalOutputs(attributes.getAdditionalOutputs());
builder.setMetadata(outputMetadata);
builder.setInstrumentationJars(jacocoInstrumentation);
builder.setSourceFiles(attributes.getSourceFiles());
builder.addSourceJars(attributes.getSourceJars());
builder.setJavacOpts(customJavacOpts);
builder.setJavacJvmOpts(customJavacJvmOpts);
builder.setJavacExecutionInfo(getExecutionInfo());
builder.setCompressJar(true);
builder.setSourceGenDirectory(sourceGenDir(classJar));
builder.setTempDirectory(tempDir(classJar));
builder.setClassDirectory(classDir(classJar));
builder.setProcessorPaths(attributes.getProcessorPath());
builder.addProcessorNames(attributes.getProcessorNames());
builder.addProcessorFlags(attributes.getProcessorFlags());
builder.setStrictJavaDeps(attributes.getStrictJavaDeps());
builder.setDirectJars(attributes.getDirectJars());
builder.setCompileTimeDependencyArtifacts(attributes.getCompileTimeDependencyArtifacts());
builder.setRuleKind(attributes.getRuleKind());
builder.setTargetLabel(
attributes.getTargetLabel() == null
? ruleContext.getLabel() : attributes.getTargetLabel());
AttributeMap attributeMap = ruleContext.attributes();
if (attributeMap.has("testonly", Type.BOOLEAN)) {
builder.setTestOnly(attributeMap.get("testonly", Type.BOOLEAN));
}
getAnalysisEnvironment().registerAction(builder.build());
}
private ImmutableMap<String, String> getExecutionInfo() {
if (javaToolchain.getJavacSupportsWorkers()) {
return ImmutableMap.of("supports-workers", "1");
}
return ImmutableMap.of();
}
/** Returns the bootclasspath explicit set in attributes if present, or else the default. */
public ImmutableList<Artifact> getBootclasspathOrDefault() {
JavaTargetAttributes attributes = getAttributes();
if (!attributes.getBootClassPath().isEmpty()) {
return attributes.getBootClassPath();
} else {
return getBootClasspath();
}
}
/**
* Returns the instrumentation metadata files to be generated for a given output jar.
*
* <p>Only called if the output jar actually needs to be instrumented.
*/
@Nullable
private static Artifact createInstrumentationMetadataArtifact(
RuleContext ruleContext, Artifact outputJar) {
PathFragment packageRelativePath = outputJar.getRootRelativePath().relativeTo(
ruleContext.getPackageDirectory());
return ruleContext.getPackageRelativeArtifact(
FileSystemUtils.replaceExtension(packageRelativePath, ".em"), outputJar.getRoot());
}
/**
* Creates the Action that compiles Java source files and optionally instruments them for
* coverage.
*
* @param outputJar the class jar Artifact to create with the Action
* @param manifestProtoOutput the output artifact for the manifest proto emitted from JavaBuilder
* @param gensrcJar the generated sources jar Artifact to create with the Action
* @param outputDepsProto the compiler-generated jdeps file to create with the Action
* @param javaArtifactsBuilder the build to store the instrumentation metadata in
*/
public void createCompileActionWithInstrumentation(
Artifact outputJar,
Artifact manifestProtoOutput,
@Nullable Artifact gensrcJar,
@Nullable Artifact outputDepsProto,
JavaCompilationArtifacts.Builder javaArtifactsBuilder) {
createCompileAction(
outputJar,
manifestProtoOutput,
gensrcJar,
outputDepsProto,
createInstrumentationMetadata(outputJar, javaArtifactsBuilder));
}
/**
* Creates the instrumentation metadata artifact if needed.
*
* @return the instrumentation metadata artifact or null if instrumentation is
* disabled
*/
@Nullable
public Artifact createInstrumentationMetadata(Artifact outputJar,
JavaCompilationArtifacts.Builder javaArtifactsBuilder) {
// If we need to instrument the jar, add additional output (the coverage metadata file) to the
// JavaCompileAction.
Artifact instrumentationMetadata = null;
if (shouldInstrumentJar()) {
instrumentationMetadata = createInstrumentationMetadataArtifact(
getRuleContext(), outputJar);
if (instrumentationMetadata != null) {
javaArtifactsBuilder.addInstrumentationMetadata(instrumentationMetadata);
}
}
return instrumentationMetadata;
}
private boolean shouldInstrumentJar() {
// TODO(bazel-team): What about source jars?
return getConfiguration().isCodeCoverageEnabled() && attributes.hasSourceFiles()
&& InstrumentedFilesCollector.shouldIncludeLocalSources(getRuleContext());
}
private boolean shouldUseHeaderCompilation() {
if (!getJavaConfiguration().useHeaderCompilation()) {
return false;
}
if (!attributes.hasSourceFiles() && !attributes.hasSourceJars()) {
return false;
}
if (javaToolchain.getForciblyDisableHeaderCompilation()) {
return false;
}
if (javaToolchain.getHeaderCompiler() == null) {
getRuleContext()
.ruleError(
String.format(
"header compilation was requested but it is not supported by the current Java"
+ " toolchain '%s'; see the java_toolchain.header_compiler attribute",
javaToolchain.getToolchainLabel()));
return false;
}
return true;
}
/**
* Creates the Action that compiles ijars from source.
*
* @param runtimeJar the jar output of this java compilation, used to create output-relative
* paths for new artifacts.
*/
private Artifact createHeaderCompilationAction(
Artifact runtimeJar, JavaCompilationArtifacts.Builder artifactBuilder) {
Artifact headerJar =
getAnalysisEnvironment()
.getDerivedArtifact(
FileSystemUtils.replaceExtension(runtimeJar.getRootRelativePath(), "-hjar.jar"),
runtimeJar.getRoot());
Artifact headerDeps =
getAnalysisEnvironment()
.getDerivedArtifact(
FileSystemUtils.replaceExtension(runtimeJar.getRootRelativePath(), "-hjar.jdeps"),
runtimeJar.getRoot());
JavaTargetAttributes attributes = getAttributes();
JavaHeaderCompileAction.Builder builder =
new JavaHeaderCompileAction.Builder(getRuleContext());
builder.setSourceFiles(attributes.getSourceFiles());
builder.addSourceJars(attributes.getSourceJars());
builder.setClasspathEntries(attributes.getCompileTimeClassPath());
builder.setBootclasspathEntries(
ImmutableIterable.from(Iterables.concat(getBootclasspathOrDefault(), getExtdirInputs())));
// only run API-generating annotation processors during header compilation
builder.setProcessorPaths(attributes.getApiGeneratingProcessorPath());
builder.addProcessorNames(attributes.getApiGeneratingProcessorNames());
builder.addProcessorFlags(attributes.getProcessorFlags());
builder.setJavacOpts(getJavacOpts());
builder.setTempDirectory(tempDir(headerJar));
builder.setOutputJar(headerJar);
builder.setOutputDepsProto(headerDeps);
builder.setStrictJavaDeps(attributes.getStrictJavaDeps());
builder.setCompileTimeDependencyArtifacts(attributes.getCompileTimeDependencyArtifacts());
builder.setDirectJars(attributes.getDirectJars());
builder.setRuleKind(attributes.getRuleKind());
builder.setTargetLabel(attributes.getTargetLabel());
builder.setJavaBaseInputs(
NestedSetBuilder
.fromNestedSet(hostJavabase)
.addAll(additionalJavaBaseInputs)
.build());
builder.setJavacJar(javaToolchain.getJavac());
builder.build(javaToolchain);
artifactBuilder.setCompileTimeDependencies(headerDeps);
return headerJar;
}
/**
* Returns the artifact for a jar file containing class files that were generated by
* annotation processors.
*/
public Artifact createGenJar(Artifact outputJar) {
return getRuleContext().getDerivedArtifact(
FileSystemUtils.appendWithoutExtension(outputJar.getRootRelativePath(), "-gen"),
outputJar.getRoot());
}
/**
* Returns the artifact for a jar file containing source files that were generated by
* annotation processors.
*/
public Artifact createGensrcJar(Artifact outputJar) {
return getRuleContext().getDerivedArtifact(
FileSystemUtils.appendWithoutExtension(outputJar.getRootRelativePath(), "-gensrc"),
outputJar.getRoot());
}
/**
* Returns whether this target uses annotation processing.
*/
public boolean usesAnnotationProcessing() {
JavaTargetAttributes attributes = getAttributes();
return getJavacOpts().contains("-processor") || !attributes.getProcessorNames().isEmpty();
}
/**
* Returns the artifact for the manifest proto emitted from JavaBuilder. For example, for a
* class jar foo.jar, returns "foo.jar_manifest_proto".
*
* @param outputJar The artifact for the class jar emitted form JavaBuilder
* @return The output artifact for the manifest proto emitted from JavaBuilder
*/
public Artifact createManifestProtoOutput(Artifact outputJar) {
return getRuleContext().getDerivedArtifact(
FileSystemUtils.appendExtension(outputJar.getRootRelativePath(), "_manifest_proto"),
outputJar.getRoot());
}
/**
* Creates the action for creating the gen jar.
*
* @param classJar The artifact for the class jar emitted from JavaBuilder
* @param manifestProto The artifact for the manifest proto emitted from JavaBuilder
* @param genClassJar The artifact for the gen jar to output
*/
public void createGenJarAction(Artifact classJar, Artifact manifestProto,
Artifact genClassJar) {
getRuleContext()
.registerAction(
new SpawnAction.Builder()
.addInput(manifestProto)
.addInput(classJar)
.addOutput(genClassJar)
.addTransitiveInputs(getHostJavabaseInputs(getRuleContext()))
.setJarExecutable(
getRuleContext()
.getHostConfiguration()
.getFragment(Jvm.class)
.getJavaExecutable(),
getGenClassJar(ruleContext),
javaToolchain.getJvmOptions())
.setCommandLine(
CustomCommandLine.builder()
.addExecPath("--manifest_proto", manifestProto)
.addExecPath("--class_jar", classJar)
.addExecPath("--output_jar", genClassJar)
.add("--temp_dir")
.addPath(tempDir(genClassJar))
.build())
.setProgressMessage("Building genclass jar " + genClassJar.prettyPrint())
.setMnemonic("JavaSourceJar")
.build(getRuleContext()));
}
/** Returns the GenClass deploy jar Artifact. */
private Artifact getGenClassJar(RuleContext ruleContext) {
Artifact genClass = javaToolchain.getGenClass();
if (genClass != null) {
return genClass;
}
return ruleContext.getPrerequisiteArtifact("$genclass", Mode.HOST);
}
/**
* Creates the jdeps file artifact if needed. Returns null if the target can't emit dependency
* information (i.e there is no compilation step, the target acts as an alias).
*
* @param outputJar output jar artifact used to derive the name
* @return the jdeps file artifact or null if the target can't generate such a file
*/
public Artifact createOutputDepsProtoArtifact(Artifact outputJar,
JavaCompilationArtifacts.Builder builder) {
if (!generatesOutputDeps()) {
return null;
}
Artifact outputDepsProtoArtifact =
getRuleContext()
.getDerivedArtifact(
FileSystemUtils.replaceExtension(outputJar.getRootRelativePath(), ".jdeps"),
outputJar.getRoot());
builder.setCompileTimeDependencies(outputDepsProtoArtifact);
return outputDepsProtoArtifact;
}
/**
* Returns whether this target emits dependency information. Compilation must occur, so certain
* targets acting as aliases have to be filtered out.
*/
private boolean generatesOutputDeps() {
return getJavaConfiguration().getGenerateJavaDeps() && attributes.hasSources();
}
/**
* Creates and registers an Action that packages all of the resources into a Jar. This includes
* the declared resources, the classpath resources and the translated messages.
*/
public void createResourceJarAction(Artifact resourceJar) {
createResourceJarAction(resourceJar, ImmutableList.<Artifact>of());
}
private void createResourceJarAction(Artifact resourceJar, ImmutableList<Artifact> extraJars) {
checkNotNull(resourceJar, "resource jar output must not be null");
JavaTargetAttributes attributes = getAttributes();
new ResourceJarActionBuilder()
.setJavabase(
NestedSetBuilder.fromNestedSet(hostJavabase).addAll(additionalJavaBaseInputs).build())
.setJavaToolchain(javaToolchain)
.setOutputJar(resourceJar)
.setResources(attributes.getResources())
.setClasspathResources(attributes.getClassPathResources())
.setTranslations(getTranslations())
.setResourceJars(
NestedSetBuilder.fromNestedSet(attributes.getResourceJars()).addAll(extraJars).build())
.build(semantics, ruleContext);
}
private JavaCompileAction.Builder createJavaCompileActionBuilder(
JavaSemantics semantics) {
JavaCompileAction.Builder builder = new JavaCompileAction.Builder(ruleContext, semantics);
builder.setJavaExecutable(
ruleContext.getHostConfiguration().getFragment(Jvm.class).getJavaExecutable());
builder.setJavaBaseInputs(
NestedSetBuilder
.fromNestedSet(hostJavabase)
.addAll(additionalJavaBaseInputs)
.build());
builder.setTargetLabel(ruleContext.getLabel());
return builder;
}
/**
* Produces a derived directory where source files generated by annotation processors should be
* stored.
*/
private PathFragment sourceGenDir(Artifact outputJar) {
return workDir(outputJar, "_sourcegenfiles");
}
private PathFragment tempDir(Artifact outputJar) {
return workDir(outputJar, "_temp");
}
private PathFragment classDir(Artifact outputJar) {
return workDir(outputJar, "_classes");
}
/**
* For an output jar and a suffix, produces a derived directory under
* {@code bin} directory with a given suffix.
*
* <p>Note that this won't work if a rule produces two jars with the same basename.
*/
private PathFragment workDir(Artifact outputJar, String suffix) {
String basename = FileSystemUtils.removeExtension(outputJar.getExecPath().getBaseName());
return getConfiguration().getBinDirectory(ruleContext.getRule().getRepository()).getExecPath()
.getRelative(ruleContext.getUniqueDirectory(JAVAC))
.getRelative(basename + suffix);
}
/**
* Creates an Action that packages the Java source files into a Jar. If {@code gensrcJar} is
* non-null, includes the contents of the {@code gensrcJar} with the output source jar.
*
* @param outputJar the Artifact to create with the Action
* @param gensrcJar the generated sources jar Artifact that should be included with the
* sources in the output Artifact. May be null.
*/
public void createSourceJarAction(Artifact outputJar, @Nullable Artifact gensrcJar) {
JavaTargetAttributes attributes = getAttributes();
Collection<Artifact> resourceJars = new ArrayList<>(attributes.getSourceJars());
if (gensrcJar != null) {
resourceJars.add(gensrcJar);
}
Map<PathFragment, Artifact> resources = new LinkedHashMap<>();
for (Artifact sourceFile : attributes.getSourceFiles()) {
resources.put(semantics.getDefaultJavaResourcePath(sourceFile.getRootRelativePath()), sourceFile);
}
SingleJarActionBuilder.createSourceJarAction(ruleContext, resources, resourceJars, outputJar);
}
/**
* Creates the actions that produce the interface jar. Adds the jar artifacts to the given
* JavaCompilationArtifacts builder.
*
* @return the header jar (if requested), or ijar (if requested), or else the class jar
*/
public Artifact createCompileTimeJarAction(
Artifact runtimeJar, JavaCompilationArtifacts.Builder builder) {
Artifact jar;
if (shouldUseHeaderCompilation()) {
jar = createHeaderCompilationAction(runtimeJar, builder);
} else if (getJavaConfiguration().getUseIjars()) {
jar = createIjarAction(ruleContext, javaToolchain, runtimeJar, false);
} else {
jar = runtimeJar;
}
builder.addCompileTimeJar(jar);
return jar;
}
private void addArgsAndJarsToAttributes(
JavaCompilationArgs args, NestedSet<Artifact> directJars) {
// Can only be non-null when isStrict() returns true.
if (directJars != null) {
attributes.addDirectJars(directJars);
}
attributes.merge(args);
}
private void addLibrariesToAttributesInternal(Iterable<? extends TransitiveInfoCollection> deps) {
JavaCompilationArgs args = JavaCompilationArgs.builder()
.addTransitiveTargets(deps).build();
NestedSet<Artifact> directJars = isStrict()
? getNonRecursiveCompileTimeJarsFromCollection(deps)
: null;
addArgsAndJarsToAttributes(args, directJars);
}
private boolean isStrict() {
return getStrictJavaDeps() != OFF;
}
private NestedSet<Artifact> getNonRecursiveCompileTimeJarsFromCollection(
Iterable<? extends TransitiveInfoCollection> deps) {
JavaCompilationArgs.Builder builder = JavaCompilationArgs.builder();
builder.addTransitiveTargets(deps, /*recursive=*/false);
return builder.build().getCompileTimeJars();
}
static void addDependencyArtifactsToAttributes(
JavaTargetAttributes.Builder attributes,
Iterable<? extends JavaCompilationArgsProvider> deps) {
NestedSetBuilder<Artifact> result = NestedSetBuilder.stableOrder();
for (JavaCompilationArgsProvider provider : deps) {
result.addTransitive(provider.getCompileTimeJavaDependencyArtifacts());
}
attributes.addCompileTimeDependencyArtifacts(result.build());
}
/**
* Adds the compile time and runtime Java libraries in the transitive closure
* of the deps to the attributes.
*
* @param deps the dependencies to be included as roots of the transitive
* closure
*/
public void addLibrariesToAttributes(Iterable<? extends TransitiveInfoCollection> deps) {
// Enforcing strict Java dependencies: when the --strict_java_deps flag is
// WARN or ERROR, or is DEFAULT and strict_java_deps attribute is unset,
// we use a stricter javac compiler to perform direct deps checks.
attributes.setStrictJavaDeps(getStrictJavaDeps());
addLibrariesToAttributesInternal(deps);
JavaClasspathMode classpathMode = getJavaConfiguration().getReduceJavaClasspath();
if (isStrict() && classpathMode != JavaClasspathMode.OFF) {
List<JavaCompilationArgsProvider> compilationArgsProviders = new LinkedList<>();
for (TransitiveInfoCollection dep : deps) {
JavaCompilationArgsProvider provider =
JavaProvider.getProvider(JavaCompilationArgsProvider.class, dep);
if (provider != null) {
compilationArgsProviders.add(provider);
}
}
addDependencyArtifactsToAttributes(attributes, compilationArgsProviders);
}
}
/**
* Determines whether to enable strict_java_deps.
*
* @return filtered command line flag value, defaulting to ERROR
*/
public StrictDepsMode getStrictJavaDeps() {
return getJavaConfiguration().getFilteredStrictJavaDeps();
}
/**
* Gets the value of the "javacopts" attribute combining them with the
* default options. If the current rule has no javacopts attribute, this
* method only returns the default options.
*/
@VisibleForTesting
public ImmutableList<String> getJavacOpts() {
return customJavacOpts;
}
/**
* Obtains the standard list of javac opts needed to build {@code rule}.
*
* This method must only be called during initialization.
*
* @param ruleContext a rule context
* @return a list of options to provide to javac
*/
private static ImmutableList<String> getDefaultJavacOptsFromRule(RuleContext ruleContext) {
return ImmutableList.copyOf(
Iterables.concat(
JavaToolchainProvider.fromRuleContext(ruleContext).getJavacOptions(),
ruleContext.getTokenizedStringListAttr("javacopts")));
}
public void setTranslations(Collection<Artifact> translations) {
Preconditions.checkArgument(!translationsFrozen);
this.translations.addAll(translations);
}
private ImmutableList<Artifact> getTranslations() {
translationsFrozen = true;
return ImmutableList.copyOf(translations);
}
public static JavaToolchainProvider getJavaToolchainProvider(
RuleContext ruleContext, String implicitAttributesSuffix) {
return ruleContext.getPrerequisite(
":java_toolchain" + implicitAttributesSuffix, Mode.TARGET, JavaToolchainProvider.class);
}
public static JavaToolchainProvider getJavaToolchainProvider(RuleContext ruleContext) {
return getJavaToolchainProvider(ruleContext, DEFAULT_ATTRIBUTES_SUFFIX);
}
/**
* Returns the instrumentation jar in the given semantics.
*/
public static Iterable<Artifact> getInstrumentationJars(
RuleContext ruleContext, String implicitAttributesSuffix) {
TransitiveInfoCollection instrumentationTarget = ruleContext.getPrerequisite(
"$jacoco_instrumentation" + implicitAttributesSuffix, Mode.HOST);
if (instrumentationTarget == null) {
return ImmutableList.<Artifact>of();
}
return FileType.filter(
instrumentationTarget.getProvider(FileProvider.class).getFilesToBuild(),
JavaSemantics.JAR);
}
public static Iterable<Artifact> getInstrumentationJars(RuleContext ruleContext) {
return getInstrumentationJars(ruleContext, DEFAULT_ATTRIBUTES_SUFFIX);
}
/**
* Returns the javac bootclasspath artifacts from the given toolchain (if it has any) or the rule.
*/
public static ImmutableList<Artifact> getBootClasspath(JavaToolchainProvider javaToolchain) {
return ImmutableList.copyOf(javaToolchain.getBootclasspath());
}
private ImmutableList<Artifact> getBootClasspath() {
return ImmutableList.copyOf(javaToolchain.getBootclasspath());
}
/**
* Returns the extdir artifacts.
*/
private final ImmutableList<Artifact> getExtdirInputs() {
return ImmutableList.copyOf(javaToolchain.getExtclasspath());
}
/**
* Creates the Action that creates ijars from Jar files.
*
* @param inputJar the Jar to create the ijar for
* @param addPrefix whether to prefix the path of the generated ijar with the package and
* name of the current rule
* @return the Artifact to create with the Action
*/
protected static Artifact createIjarAction(
RuleContext ruleContext,
JavaToolchainProvider javaToolchain,
Artifact inputJar, boolean addPrefix) {
Artifact interfaceJar = getIjarArtifact(ruleContext, inputJar, addPrefix);
FilesToRunProvider ijarTarget = javaToolchain.getIjar();
if (!ruleContext.hasErrors()) {
ruleContext.registerAction(new SpawnAction.Builder()
.addInput(inputJar)
.addOutput(interfaceJar)
.setExecutable(ijarTarget)
// On Windows, ijar.exe needs msys-2.0.dll and zlib1.dll in PATH.
// Use default shell environment so that those can be found.
// TODO(dslomov): revisit this. If ijar is not msys-dependent, this is not needed.
.useDefaultShellEnvironment()
.addArgument(inputJar.getExecPathString())
.addArgument(interfaceJar.getExecPathString())
.setProgressMessage("Extracting interface " + ruleContext.getLabel())
.setMnemonic("JavaIjar")
.build(ruleContext));
}
return interfaceJar;
}
private static Artifact getIjarArtifact(
RuleContext ruleContext, Artifact jar, boolean addPrefix) {
if (addPrefix) {
PathFragment ruleBase = ruleContext.getUniqueDirectory("_ijar");
PathFragment artifactDirFragment = jar.getRootRelativePath().getParentDirectory();
String ijarBasename = FileSystemUtils.removeExtension(jar.getFilename()) + "-ijar.jar";
return ruleContext.getDerivedArtifact(
ruleBase.getRelative(artifactDirFragment).getRelative(ijarBasename),
ruleContext.getConfiguration().getGenfilesDirectory(
ruleContext.getRule().getRepository()));
} else {
return derivedArtifact(ruleContext, jar, "", "-ijar.jar");
}
}
/**
* Creates a derived artifact from the given artifact by adding the given
* prefix and removing the extension and replacing it by the given suffix.
* The new artifact will have the same root as the given one.
*/
private static Artifact derivedArtifact(
RuleContext ruleContext, Artifact artifact, String prefix, String suffix) {
PathFragment path = artifact.getRootRelativePath();
String basename = FileSystemUtils.removeExtension(path.getBaseName()) + suffix;
path = path.replaceName(prefix + basename);
return ruleContext.getDerivedArtifact(path, artifact.getRoot());
}
}