// 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.bazel.rules.java;
import static com.google.common.base.Strings.isNullOrEmpty;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
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.RuleConfiguredTarget.Mode;
import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.Runfiles;
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.TemplateExpansionAction;
import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction.ComputedSubstitution;
import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction.Substitution;
import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction.Template;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.bazel.rules.BazelConfiguration;
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.packages.BuildType;
import com.google.devtools.build.lib.packages.TargetUtils;
import com.google.devtools.build.lib.rules.java.DeployArchiveBuilder;
import com.google.devtools.build.lib.rules.java.DeployArchiveBuilder.Compression;
import com.google.devtools.build.lib.rules.java.JavaCommon;
import com.google.devtools.build.lib.rules.java.JavaCompilationArgs;
import com.google.devtools.build.lib.rules.java.JavaCompilationArgs.ClasspathType;
import com.google.devtools.build.lib.rules.java.JavaCompilationArgsProvider;
import com.google.devtools.build.lib.rules.java.JavaCompilationArtifacts;
import com.google.devtools.build.lib.rules.java.JavaCompilationHelper;
import com.google.devtools.build.lib.rules.java.JavaConfiguration;
import com.google.devtools.build.lib.rules.java.JavaHelper;
import com.google.devtools.build.lib.rules.java.JavaRuleOutputJarsProvider;
import com.google.devtools.build.lib.rules.java.JavaSemantics;
import com.google.devtools.build.lib.rules.java.JavaSourceJarsProvider;
import com.google.devtools.build.lib.rules.java.JavaTargetAttributes;
import com.google.devtools.build.lib.rules.java.JavaUtil;
import com.google.devtools.build.lib.rules.java.Jvm;
import com.google.devtools.build.lib.rules.java.proto.GeneratedExtensionRegistryProvider;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.util.OS;
import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.lib.util.ShellEscaper;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.annotation.Nullable;
/**
* Semantics for Bazel Java rules
*/
public class BazelJavaSemantics implements JavaSemantics {
public static final BazelJavaSemantics INSTANCE = new BazelJavaSemantics();
private static final Template STUB_SCRIPT =
Template.forResource(BazelJavaSemantics.class, "java_stub_template.txt");
private static final Template STUB_SCRIPT_WINDOWS =
Template.forResource(BazelJavaSemantics.class, "java_stub_template_windows.txt");
private static final String CLASSPATH_PLACEHOLDER = "%classpath%";
private static final String RELATIVE_CLASSPATHS_PLACEHOLDER = "%relative_classpath%";
private static final Template CLASSPATH_FILE_TEMPLATE =
Template.forString(RELATIVE_CLASSPATHS_PLACEHOLDER);
private static final String JAVABUILDER_CLASS_NAME =
"com.google.devtools.build.buildjar.BazelJavaBuilder";
private static final String JACOCO_COVERAGE_RUNNER_MAIN_CLASS =
"com.google.testing.coverage.JacocoCoverageRunner";
private static final String BAZEL_TEST_RUNNER_MAIN_CLASS =
"com.google.testing.junit.runner.BazelTestRunner";
private static final String EXPERIMENTAL_TEST_RUNNER_MAIN_CLASS =
"com.google.testing.junit.runner.ExperimentalTestRunner";
private BazelJavaSemantics() {
}
private boolean isJavaBinaryOrJavaTest(RuleContext ruleContext) {
String ruleClass = ruleContext.getRule().getRuleClass();
return ruleClass.equals("java_binary") || ruleClass.equals("java_test");
}
@Override
public void checkRule(RuleContext ruleContext, JavaCommon javaCommon) {
}
@Override
public void checkForProtoLibraryAndJavaProtoLibraryOnSameProto(
RuleContext ruleContext, JavaCommon javaCommon) {}
@Override
public void checkProtoDeps(
RuleContext ruleContext, Collection<? extends TransitiveInfoCollection> deps) {}
private static final String JUNIT4_RUNNER = "org.junit.runner.JUnitCore";
@Nullable
private String getMainClassInternal(RuleContext ruleContext, ImmutableList<Artifact> sources) {
if (!ruleContext.attributes().get("create_executable", Type.BOOLEAN)) {
return null;
}
String mainClass = getMainClassFromRule(ruleContext);
if (mainClass.isEmpty()) {
mainClass = JavaCommon.determinePrimaryClass(ruleContext, sources);
}
return mainClass;
}
private String getMainClassFromRule(RuleContext ruleContext) {
String mainClass = ruleContext.attributes().get("main_class", Type.STRING);
if (!mainClass.isEmpty()) {
return mainClass;
}
if (useLegacyJavaTest(ruleContext)) {
// Legacy behavior for java_test rules: main_class defaulted to JUnit4 runner.
// TODO(dmarting): remove once we drop the legacy bazel java_test behavior.
if ("java_test".equals(ruleContext.getRule().getRuleClass())) {
return JUNIT4_RUNNER;
}
} else {
if (ruleContext.attributes().get("use_testrunner", Type.BOOLEAN)) {
return ruleContext.getFragment(JavaConfiguration.class).useExperimentalTestRunner()
? EXPERIMENTAL_TEST_RUNNER_MAIN_CLASS
: BAZEL_TEST_RUNNER_MAIN_CLASS;
}
}
return mainClass;
}
private boolean isExperimentalJavaTest(RuleContext ruleContext) {
return TargetUtils.isTestRule(ruleContext.getRule())
&& getMainClassFromRule(ruleContext).equals(EXPERIMENTAL_TEST_RUNNER_MAIN_CLASS);
}
private void checkMainClass(RuleContext ruleContext, ImmutableList<Artifact> sources) {
boolean createExecutable = ruleContext.attributes().get("create_executable", Type.BOOLEAN);
String mainClass = getMainClassInternal(ruleContext, sources);
if (!createExecutable && !isNullOrEmpty(mainClass)) {
ruleContext.ruleError("main class must not be specified when executable is not created");
}
if (createExecutable && isNullOrEmpty(mainClass)) {
if (sources.isEmpty()) {
ruleContext.ruleError("need at least one of 'main_class' or Java source files");
}
mainClass = JavaCommon.determinePrimaryClass(ruleContext, sources);
if (mainClass == null) {
ruleContext.ruleError("cannot determine main class for launching "
+ "(found neither a source file '" + ruleContext.getTarget().getName()
+ ".java', nor a main_class attribute, and package name "
+ "doesn't include 'java' or 'javatests')");
}
}
}
@Override
public String getMainClass(RuleContext ruleContext, ImmutableList<Artifact> sources) {
checkMainClass(ruleContext, sources);
return getMainClassInternal(ruleContext, sources);
}
@Override
public ImmutableList<Artifact> collectResources(RuleContext ruleContext) {
if (!ruleContext.getRule().isAttrDefined("resources", BuildType.LABEL_LIST)) {
return ImmutableList.of();
}
return ruleContext.getPrerequisiteArtifacts("resources", Mode.TARGET).list();
}
/**
* Used to generate the Classpaths contained within the stub.
*/
private static class ComputedClasspathSubstitution extends ComputedSubstitution {
private final NestedSet<Artifact> jars;
private final String workspacePrefix;
private final boolean isRunfilesEnabled;
ComputedClasspathSubstitution(
NestedSet<Artifact> jars, String workspacePrefix, boolean isRunfilesEnabled) {
super(CLASSPATH_PLACEHOLDER);
this.jars = jars;
this.workspacePrefix = workspacePrefix;
this.isRunfilesEnabled = isRunfilesEnabled;
}
/**
* Builds a class path by concatenating the root relative paths of the artifacts. Each relative
* path entry is prepended with "${RUNPATH}" which will be expanded by the stub script at
* runtime, to either "${JAVA_RUNFILES}/" or if we are lucky, the empty string.
*/
@Override
public String getValue() {
StringBuilder buffer = new StringBuilder();
buffer.append("\"");
for (Artifact artifact : jars) {
if (buffer.length() > 1) {
buffer.append(File.pathSeparatorChar);
}
if (!isRunfilesEnabled) {
buffer.append("$(rlocation ");
PathFragment runfilePath =
PathFragment.create(PathFragment.create(workspacePrefix), artifact.getRunfilesPath());
buffer.append(runfilePath.normalize().getPathString());
buffer.append(")");
} else {
buffer.append("${RUNPATH}");
buffer.append(artifact.getRunfilesPath().getPathString());
}
}
buffer.append("\"");
return buffer.toString();
}
}
@Override
public Artifact createStubAction(
RuleContext ruleContext,
JavaCommon javaCommon,
List<String> jvmFlags,
Artifact executable,
String javaStartClass,
String javaExecutable) {
Preconditions.checkState(ruleContext.getConfiguration().hasFragment(Jvm.class));
Preconditions.checkNotNull(jvmFlags);
Preconditions.checkNotNull(executable);
Preconditions.checkNotNull(javaStartClass);
Preconditions.checkNotNull(javaExecutable);
List<Substitution> arguments = new ArrayList<>();
String workspaceName = ruleContext.getWorkspaceName();
final String workspacePrefix = workspaceName + (workspaceName.isEmpty() ? "" : "/");
final boolean isRunfilesEnabled = ruleContext.getConfiguration().runfilesEnabled();
arguments.add(Substitution.of("%runfiles_manifest_only%", isRunfilesEnabled ? "" : "1"));
arguments.add(Substitution.of("%workspace_prefix%", workspacePrefix));
arguments.add(Substitution.of("%javabin%", javaExecutable));
arguments.add(Substitution.of("%needs_runfiles%",
ruleContext.getFragment(Jvm.class).getJavaExecutable().isAbsolute() ? "0" : "1"));
TransitiveInfoCollection testSupport = getTestSupport(ruleContext);
NestedSet<Artifact> testSupportJars =
testSupport == null
? NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER)
: getRuntimeJarsForTargets(testSupport);
NestedSetBuilder<Artifact> classpathBuilder = NestedSetBuilder.naiveLinkOrder();
classpathBuilder.addTransitive(javaCommon.getRuntimeClasspath());
if (enforceExplicitJavaTestDeps(ruleContext)) {
// Currently, this is only needed when --explicit_java_test_deps=true, as otherwise the
// testSupport classpath is wrongly present in the javaCommon.getRuntimeClasspath().
classpathBuilder.addTransitive(testSupportJars);
}
NestedSet<Artifact> classpath = classpathBuilder.build();
if (isExperimentalJavaTest(ruleContext)) {
if (!isRunfilesEnabled) {
ruleContext.ruleError(
"ExperimentalTestRunner can't work on Windows since Windows doesn't support runfiles.");
}
if (testSupport == null) {
// This may happen when the user sets use_testrunner=0 and manually chooses
// main_class=ExperimentalTestRunner.
ruleContext.ruleError("Unexpected usage of ExperimentalTestRunner.");
}
// Keep only the locations containing the classes to start the test runner itself within,
// classpath variable, and place all the paths required for the test run in a classpaths file,
// so that the classes for the test target may be loaded by a separate ClassLoader.
classpath = testSupportJars;
}
arguments.add(new ComputedClasspathSubstitution(classpath, workspacePrefix, isRunfilesEnabled));
JavaCompilationArtifacts javaArtifacts = javaCommon.getJavaCompilationArtifacts();
String path =
javaArtifacts.getInstrumentedJar() != null
? "${JAVA_RUNFILES}/"
+ workspacePrefix
+ javaArtifacts.getInstrumentedJar().getRootRelativePath().getPathString()
: "";
arguments.add(
Substitution.of(
"%set_jacoco_metadata%",
ruleContext.getConfiguration().isCodeCoverageEnabled()
? "export JACOCO_METADATA_JAR=" + path
: ""));
arguments.add(Substitution.of("%java_start_class%",
ShellEscaper.escapeString(javaStartClass)));
arguments.add(Substitution.ofSpaceSeparatedList("%jvm_flags%", ImmutableList.copyOf(jvmFlags)));
ruleContext.registerAction(new TemplateExpansionAction(
ruleContext.getActionOwner(), executable, STUB_SCRIPT, arguments, true));
if (OS.getCurrent() == OS.WINDOWS) {
Artifact newExecutable =
ruleContext.getImplicitOutputArtifact(ruleContext.getTarget().getName() + ".cmd");
ruleContext.registerAction(
new TemplateExpansionAction(
ruleContext.getActionOwner(),
newExecutable,
STUB_SCRIPT_WINDOWS,
ImmutableList.of(
Substitution.of(
"%bash_exe_path%",
ruleContext
.getFragment(BazelConfiguration.class)
.getShellExecutable()
.getPathString()),
Substitution.of(
"%cygpath_exe_path%",
ruleContext
.getFragment(BazelConfiguration.class)
.getShellExecutable()
.replaceName("cygpath.exe")
.getPathString())),
true));
return newExecutable;
} else {
return executable;
}
}
private static boolean enforceExplicitJavaTestDeps(RuleContext ruleContext) {
return ruleContext.getFragment(JavaConfiguration.class).explicitJavaTestDeps();
}
/**
* Substitutes the placeholder with {@link File#pathSeparatorChar} separated relative classpaths.
*/
private static class ComputedRelativeClasspathsSubstitution extends ComputedSubstitution {
private final JavaCommon javaCommon;
private static final Function<Artifact, String> PATHS_FROM_ARTIFACTS =
new Function<Artifact, String>() {
@Nullable
@Override
public String apply(@Nullable Artifact artifact) {
return artifact == null ? null : artifact.getRunfilesPathString();
}
};
public ComputedRelativeClasspathsSubstitution(JavaCommon javaCommon) {
super(RELATIVE_CLASSPATHS_PLACEHOLDER);
this.javaCommon = javaCommon;
}
@Override
public String getValue() {
// TODO(kush): Get this to work when runfilesEnabled=false, like in windows.
Iterable<String> paths =
Iterables.transform(javaCommon.getRuntimeClasspath(), PATHS_FROM_ARTIFACTS);
return Joiner.on(File.pathSeparatorChar).skipNulls().join(paths);
}
}
@Override
public Optional<Artifact> createClasspathsFile(RuleContext ruleContext, JavaCommon javaCommon)
throws InterruptedException {
if (!isExperimentalJavaTest(ruleContext)) {
return Optional.absent();
}
Artifact classpathFile = ruleContext.getImplicitOutputArtifact(JAVA_TEST_CLASSPATHS_FILE);
List<Substitution> substitutions =
ImmutableList.<Substitution>of(new ComputedRelativeClasspathsSubstitution(javaCommon));
ruleContext.registerAction(
new TemplateExpansionAction(
ruleContext.getActionOwner(),
classpathFile,
CLASSPATH_FILE_TEMPLATE,
substitutions,
/*makeExecutable=*/ false));
return Optional.of(classpathFile);
}
@Nullable
private TransitiveInfoCollection getTestSupport(RuleContext ruleContext) {
if (!isJavaBinaryOrJavaTest(ruleContext)) {
return null;
}
if (useLegacyJavaTest(ruleContext)) {
return null;
}
boolean createExecutable = ruleContext.attributes().get("create_executable", Type.BOOLEAN);
if (createExecutable && ruleContext.attributes().get("use_testrunner", Type.BOOLEAN)) {
String testSupport =
isExperimentalJavaTest(ruleContext) ? "$experimental_testsupport" : "$testsupport";
return Iterables.getOnlyElement(ruleContext.getPrerequisites(testSupport, Mode.TARGET));
} else {
return null;
}
}
private static NestedSet<Artifact> getRuntimeJarsForTargets(TransitiveInfoCollection... deps) {
// The dep may be a simple JAR and not a java rule, hence we can't simply do
// dep.getProvider(JavaCompilationArgsProvider.class).getRecursiveJavaCompilationArgs(),
// so we reuse the logic within JavaCompilationArgs to handle both scenarios.
JavaCompilationArgs args =
JavaCompilationArgs.builder()
.addTransitiveTargets(
ImmutableList.copyOf(deps), /*recursive=*/ true, ClasspathType.RUNTIME_ONLY)
.build();
return args.getRuntimeJars();
}
@Override
public void addRunfilesForBinary(RuleContext ruleContext, Artifact launcher,
Runfiles.Builder runfilesBuilder) {
TransitiveInfoCollection testSupport = getTestSupport(ruleContext);
if (testSupport != null) {
// Not using addTransitiveArtifacts() due to the mismatch in NestedSet ordering.
runfilesBuilder.addArtifacts(getRuntimeJarsForTargets(testSupport));
}
}
@Override
public void addRunfilesForLibrary(RuleContext ruleContext, Runfiles.Builder runfilesBuilder) {
}
@Override
public void collectTargetsTreatedAsDeps(
RuleContext ruleContext,
ImmutableList.Builder<TransitiveInfoCollection> builder,
ClasspathType type) {
if (type == ClasspathType.COMPILE_ONLY && enforceExplicitJavaTestDeps(ruleContext)) {
// We add the test support below, but the test framework's deps are not relevant for
// COMPILE_ONLY, hence we return here.
// TODO(bazel-team): Ideally enforceExplicitJavaTestDeps should be the default behaviour,
// since the testSupport deps don't belong to the COMPILE_ONLY classpath, but since many
// targets may break, we are keeping it behind this flag.
return;
}
TransitiveInfoCollection testSupport = getTestSupport(ruleContext);
if (testSupport != null) {
builder.add(testSupport);
}
}
@Override
public Iterable<String> getExtraJavacOpts(RuleContext ruleContext) {
return ImmutableList.<String>of();
}
@Override
public void addProviders(RuleContext ruleContext,
JavaCommon javaCommon,
List<String> jvmFlags,
Artifact classJar,
Artifact srcJar,
Artifact genJar,
Artifact gensrcJar,
ImmutableMap<Artifact, Artifact> compilationToRuntimeJarMap,
NestedSetBuilder<Artifact> filesBuilder,
RuleConfiguredTargetBuilder ruleBuilder) {
}
// TODO(dmarting): simplify that logic when we remove the legacy Bazel java_test behavior.
private String getPrimaryClassLegacy(RuleContext ruleContext, ImmutableList<Artifact> sources) {
boolean createExecutable = ruleContext.attributes().get("create_executable", Type.BOOLEAN);
if (!createExecutable) {
return null;
}
return getMainClassInternal(ruleContext, sources);
}
private String getPrimaryClassNew(RuleContext ruleContext, ImmutableList<Artifact> sources) {
boolean createExecutable = ruleContext.attributes().get("create_executable", Type.BOOLEAN);
if (!createExecutable) {
return null;
}
boolean useTestrunner = ruleContext.attributes().get("use_testrunner", Type.BOOLEAN);
String testClass = ruleContext.getRule().isAttrDefined("test_class", Type.STRING)
? ruleContext.attributes().get("test_class", Type.STRING) : "";
if (useTestrunner) {
if (testClass.isEmpty()) {
testClass = JavaCommon.determinePrimaryClass(ruleContext, sources);
if (testClass == null) {
ruleContext.ruleError("cannot determine junit.framework.Test class "
+ "(Found no source file '" + ruleContext.getTarget().getName()
+ ".java' and package name doesn't include 'java' or 'javatests'. "
+ "You might want to rename the rule or add a 'test_class' "
+ "attribute.)");
}
}
return testClass;
} else {
if (!testClass.isEmpty()) {
ruleContext.attributeError("test_class", "this attribute is only meaningful to "
+ "BazelTestRunner, but you are not using it (use_testrunner = 0)");
}
return getMainClassInternal(ruleContext, sources);
}
}
@Override
public String getPrimaryClass(RuleContext ruleContext, ImmutableList<Artifact> sources) {
return useLegacyJavaTest(ruleContext)
? getPrimaryClassLegacy(ruleContext, sources)
: getPrimaryClassNew(ruleContext, sources);
}
@Override
public Iterable<String> getJvmFlags(
RuleContext ruleContext, ImmutableList<Artifact> sources, List<String> userJvmFlags) {
ImmutableList.Builder<String> jvmFlags = ImmutableList.builder();
jvmFlags.addAll(userJvmFlags);
if (!useLegacyJavaTest(ruleContext)) {
if (ruleContext.attributes().get("use_testrunner", Type.BOOLEAN)) {
String testClass = ruleContext.getRule().isAttrDefined("test_class", Type.STRING)
? ruleContext.attributes().get("test_class", Type.STRING) : "";
if (testClass.isEmpty()) {
testClass = JavaCommon.determinePrimaryClass(ruleContext, sources);
}
if (testClass == null) {
ruleContext.ruleError("cannot determine test class");
} else {
// Always run junit tests with -ea (enable assertion)
jvmFlags.add("-ea");
// "suite" is a misnomer.
jvmFlags.add("-Dbazel.test_suite=" + ShellEscaper.escapeString(testClass));
}
}
}
return jvmFlags.build();
}
/**
* Returns whether coverage has instrumented artifacts.
*/
public static boolean hasInstrumentationMetadata(JavaTargetAttributes.Builder attributes) {
return !attributes.getInstrumentationMetadata().isEmpty();
}
// TODO(yueg): refactor this (only mainClass different for now)
@Override
public String addCoverageSupport(
JavaCompilationHelper helper,
JavaTargetAttributes.Builder attributes,
Artifact executable,
Artifact instrumentationMetadata,
JavaCompilationArtifacts.Builder javaArtifactsBuilder,
String mainClass)
throws InterruptedException {
// This method can be called only for *_binary/*_test targets.
Preconditions.checkNotNull(executable);
// Add our own metadata artifact (if any).
if (instrumentationMetadata != null) {
attributes.addInstrumentationMetadataEntries(ImmutableList.of(instrumentationMetadata));
}
if (!hasInstrumentationMetadata(attributes)) {
return mainClass;
}
Artifact instrumentedJar =
helper
.getRuleContext()
.getBinArtifact(helper.getRuleContext().getLabel().getName() + "_instrumented.jar");
// Create an instrumented Jar. This will be referenced on the runtime classpath prior
// to all other Jars.
JavaCommon.createInstrumentedJarAction(
helper.getRuleContext(),
this,
attributes.getInstrumentationMetadata(),
instrumentedJar,
mainClass);
javaArtifactsBuilder.setInstrumentedJar(instrumentedJar);
// Add the coverage runner to the list of dependencies when compiling in coverage mode.
TransitiveInfoCollection runnerTarget =
helper.getRuleContext().getPrerequisite("$jacocorunner", Mode.TARGET);
if (runnerTarget.getProvider(JavaCompilationArgsProvider.class) != null) {
helper.addLibrariesToAttributes(ImmutableList.of(runnerTarget));
} else {
helper
.getRuleContext()
.ruleError(
"this rule depends on "
+ helper.getRuleContext().attributes().get("$jacocorunner", BuildType.LABEL)
+ " which is not a java_library rule, or contains errors");
}
// We do not add the instrumented jar to the runtime classpath, but provide it in the shell
// script via an environment variable.
return JACOCO_COVERAGE_RUNNER_MAIN_CLASS;
}
@Override
public CustomCommandLine buildSingleJarCommandLine(BuildConfiguration configuration,
Artifact output, String mainClass, ImmutableList<String> manifestLines,
Iterable<Artifact> buildInfoFiles, ImmutableList<Artifact> resources,
Iterable<Artifact> classpath, boolean includeBuildData,
Compression compression, Artifact launcher) {
return DeployArchiveBuilder.defaultSingleJarCommandLine(output, mainClass, manifestLines,
buildInfoFiles, resources, classpath, includeBuildData, compression, launcher).build();
}
@Override
public ImmutableList<Artifact> translate(RuleContext ruleContext, JavaConfiguration javaConfig,
List<Artifact> messages) {
return ImmutableList.<Artifact>of();
}
@Override
public Artifact getLauncher(RuleContext ruleContext, JavaCommon common,
DeployArchiveBuilder deployArchiveBuilder, Runfiles.Builder runfilesBuilder,
List<String> jvmFlags, JavaTargetAttributes.Builder attributesBuilder, boolean shouldStrip) {
return JavaHelper.launcherArtifactForTarget(this, ruleContext);
}
@Override
public void addDependenciesForRunfiles(RuleContext ruleContext, Runfiles.Builder builder) {
}
@Override
public void addArtifactToJavaTargetAttribute(JavaTargetAttributes.Builder builder,
Artifact srcArtifact) {
}
@Override
public void commonDependencyProcessing(RuleContext ruleContext,
JavaTargetAttributes.Builder attributes,
Collection<? extends TransitiveInfoCollection> deps) {
}
@Override
public PathFragment getDefaultJavaResourcePath(PathFragment path) {
// Look for src/.../resources to match Maven repository structure.
for (int i = 0; i < path.segmentCount() - 2; ++i) {
if (path.getSegment(i).equals("src") && path.getSegment(i + 2).equals("resources")) {
return path.subFragment(i + 3, path.segmentCount());
}
}
PathFragment javaPath = JavaUtil.getJavaPath(path);
return javaPath == null ? path : javaPath;
}
@Override
public List<String> getExtraArguments(RuleContext ruleContext, ImmutableList<Artifact> sources) {
if (ruleContext.getRule().getRuleClass().equals("java_test")) {
if (useLegacyJavaTest(ruleContext)) {
if (ruleContext.getConfiguration().getTestArguments().isEmpty()
&& !ruleContext.attributes().isAttributeValueExplicitlySpecified("args")) {
ImmutableList.Builder<String> builder = ImmutableList.builder();
for (Artifact artifact : sources) {
PathFragment path = artifact.getRootRelativePath();
String className = JavaUtil.getJavaFullClassname(FileSystemUtils.removeExtension(path));
if (className != null) {
builder.add(className);
}
}
return builder.build();
}
}
}
return ImmutableList.<String>of();
}
private boolean useLegacyJavaTest(RuleContext ruleContext) {
return !ruleContext.attributes().isAttributeValueExplicitlySpecified("test_class")
&& ruleContext.getFragment(JavaConfiguration.class).useLegacyBazelJavaTest();
}
@Override
public String getJavaBuilderMainClass() {
return JAVABUILDER_CLASS_NAME;
}
@Override
public Artifact getProtoMapping(RuleContext ruleContext) throws InterruptedException {
return null;
}
@Nullable
@Override
public GeneratedExtensionRegistryProvider createGeneratedExtensionRegistry(
RuleContext ruleContext,
JavaCommon common,
NestedSetBuilder<Artifact> filesBuilder,
JavaCompilationArtifacts.Builder javaCompilationArtifactsBuilder,
JavaRuleOutputJarsProvider.Builder javaRuleOutputJarsProviderBuilder,
JavaSourceJarsProvider.Builder javaSourceJarsProviderBuilder)
throws InterruptedException {
return null;
}
@Override
public Artifact getObfuscatedConstantStringMap(RuleContext ruleContext)
throws InterruptedException {
return null;
}
}