in src/main/java/com/google/devtools/build/lib/rules/java/JavaBinary.java [90:578]
public ConfiguredTarget create(RuleContext ruleContext)
throws InterruptedException, RuleErrorException, ActionConflictException {
final JavaCommon common = new JavaCommon(ruleContext, semantics);
DeployArchiveBuilder deployArchiveBuilder = new DeployArchiveBuilder(semantics, ruleContext);
Runfiles.Builder runfilesBuilder =
new Runfiles.Builder(
ruleContext.getWorkspaceName(),
ruleContext.getConfiguration().legacyExternalRunfiles());
List<String> jvmFlags = new ArrayList<>();
JavaTargetAttributes.Builder attributesBuilder = common.initCommon();
attributesBuilder.addClassPathResources(
ruleContext.getPrerequisiteArtifacts("classpath_resources").list());
// Add Java8 timezone resource data
addTimezoneResourceForJavaBinaries(ruleContext, attributesBuilder);
List<String> userJvmFlags = JavaCommon.getJvmFlags(ruleContext);
ruleContext.checkSrcsSamePackage(true);
boolean createExecutable = ruleContext.attributes().get("create_executable", Type.BOOLEAN);
if (!createExecutable
&& ruleContext.attributes().isAttributeValueExplicitlySpecified("launcher")) {
ruleContext.ruleError("launcher specified but create_executable is false");
}
if (!ruleContext.attributes().get("use_launcher", Type.BOOLEAN)
&& ruleContext.attributes().isAttributeValueExplicitlySpecified("launcher")) {
ruleContext.ruleError("launcher specified but use_launcher is false");
}
semantics.checkRule(ruleContext, common);
semantics.checkForProtoLibraryAndJavaProtoLibraryOnSameProto(ruleContext, common);
String mainClass = semantics.getMainClass(ruleContext, common.getSrcsArtifacts());
String originalMainClass = mainClass;
if (ruleContext.hasErrors()) {
return null;
}
// Collect the transitive dependencies.
JavaCompilationHelper helper =
new JavaCompilationHelper(ruleContext, semantics, common.getJavacOpts(), attributesBuilder);
List<TransitiveInfoCollection> deps =
Lists.newArrayList(common.targetsTreatedAsDeps(ClasspathType.COMPILE_ONLY));
helper.addLibrariesToAttributes(deps);
attributesBuilder.addNativeLibraries(
collectNativeLibraries(common.targetsTreatedAsDeps(ClasspathType.BOTH)));
// deploy_env is valid for java_binary, but not for java_test.
if (ruleContext.getRule().isAttrDefined("deploy_env", BuildType.LABEL_LIST)) {
for (JavaRuntimeClasspathProvider envTarget :
ruleContext.getPrerequisites("deploy_env", JavaRuntimeClasspathProvider.class)) {
attributesBuilder.addExcludedArtifacts(envTarget.getRuntimeClasspathNestedSet());
}
}
Artifact srcJar = ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_BINARY_SOURCE_JAR);
JavaSourceJarsProvider.Builder javaSourceJarsProviderBuilder =
JavaSourceJarsProvider.builder()
.addSourceJar(srcJar)
.addAllTransitiveSourceJars(common.collectTransitiveSourceJars(srcJar));
Artifact classJar = ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_BINARY_CLASS_JAR);
CppConfiguration cppConfiguration =
ruleContext.getConfiguration().getFragment(CppConfiguration.class);
CcToolchainProvider ccToolchain =
CppHelper.getToolchainUsingDefaultCcToolchainAttribute(ruleContext);
FeatureConfiguration featureConfiguration = null;
try {
featureConfiguration =
CcCommon.configureFeaturesOrThrowEvalException(
/* requestedFeatures= */ ImmutableSet.<String>builder()
.addAll(ruleContext.getFeatures())
.add(STATIC_LINKING_MODE)
.add(JAVA_LAUNCHER_LINK)
.build(),
/* unsupportedFeatures= */ ruleContext.getDisabledFeatures(),
ccToolchain,
cppConfiguration);
} catch (EvalException e) {
ruleContext.ruleError(e.getMessage());
}
boolean stripAsDefault =
ccToolchain.shouldCreatePerObjectDebugInfo(featureConfiguration, cppConfiguration)
&& cppConfiguration.getCompilationMode() == CompilationMode.OPT;
DeployArchiveBuilder unstrippedDeployArchiveBuilder = null;
if (stripAsDefault) {
unstrippedDeployArchiveBuilder = new DeployArchiveBuilder(semantics, ruleContext);
}
Pair<Artifact, Artifact> launcherAndUnstrippedLauncher =
semantics.getLauncher(
ruleContext,
common,
deployArchiveBuilder,
unstrippedDeployArchiveBuilder,
runfilesBuilder,
jvmFlags,
attributesBuilder,
stripAsDefault,
ccToolchain,
featureConfiguration);
Artifact launcher = launcherAndUnstrippedLauncher.first;
Artifact unstrippedLauncher = launcherAndUnstrippedLauncher.second;
JavaCompilationArtifacts.Builder javaArtifactsBuilder = new JavaCompilationArtifacts.Builder();
NestedSetBuilder<Artifact> filesBuilder = NestedSetBuilder.stableOrder();
Artifact executableForRunfiles = null;
if (createExecutable) {
// This artifact is named as the rule itself, e.g. //foo:bar_bin -> bazel-bin/foo/bar_bin
// On Windows, it's going to be bazel-bin/foo/bar_bin.exe
if (OS.getCurrent() == OS.WINDOWS) {
executableForRunfiles =
ruleContext.getImplicitOutputArtifact(ruleContext.getTarget().getName() + ".exe");
} else {
executableForRunfiles = ruleContext.createOutputArtifact();
}
filesBuilder.add(classJar).add(executableForRunfiles);
if (ruleContext.getConfiguration().isCodeCoverageEnabled()) {
mainClass = semantics.addCoverageSupport(helper, executableForRunfiles);
}
} else {
filesBuilder.add(classJar);
}
JavaCompileOutputs<Artifact> outputs = helper.createOutputs(classJar);
JavaRuleOutputJarsProvider.Builder ruleOutputJarsProviderBuilder =
JavaRuleOutputJarsProvider.builder()
.addJavaOutput(
JavaOutput.builder().fromJavaCompileOutputs(outputs).addSourceJar(srcJar).build());
JavaTargetAttributes attributes = attributesBuilder.build();
List<Artifact> nativeLibraries = attributes.getNativeLibraries();
if (!nativeLibraries.isEmpty()) {
jvmFlags.add(
"-Djava.library.path="
+ JavaCommon.javaLibraryPath(
nativeLibraries, ruleContext.getRule().getPackage().getWorkspaceName()));
}
JavaConfiguration javaConfig = ruleContext.getFragment(JavaConfiguration.class);
if (attributes.hasMessages()) {
helper.setTranslations(semantics.translate(ruleContext, attributes.getMessages()));
}
if (attributes.hasSources() || attributes.hasResources()) {
// We only want to add a jar to the classpath of a dependent rule if it has content.
javaArtifactsBuilder.addRuntimeJar(classJar);
}
GeneratedExtensionRegistryProvider generatedExtensionRegistryProvider =
semantics.createGeneratedExtensionRegistry(
ruleContext,
common,
filesBuilder,
javaArtifactsBuilder,
ruleOutputJarsProviderBuilder,
javaSourceJarsProviderBuilder);
javaArtifactsBuilder.setCompileTimeDependencies(outputs.depsProto());
JavaCompilationArtifacts javaArtifacts = javaArtifactsBuilder.build();
common.setJavaCompilationArtifacts(javaArtifacts);
helper.createCompileAction(outputs);
helper.createSourceJarAction(srcJar, outputs.genSource());
common.setClassPathFragment(
new ClasspathConfiguredFragment(
javaArtifacts, attributes, false, helper.getBootclasspathOrDefault()));
Iterables.addAll(
jvmFlags, semantics.getJvmFlags(ruleContext, common.getSrcsArtifacts(), userJvmFlags));
if (ruleContext.hasErrors()) {
return null;
}
Artifact executableToRun = executableForRunfiles;
if (createExecutable) {
String javaExecutable;
if (semantics.isJavaExecutableSubstitution()) {
javaExecutable = JavaCommon.getJavaBinSubstitution(ruleContext, launcher);
} else {
javaExecutable = JavaCommon.getJavaExecutableForStub(ruleContext, launcher);
}
// Create a shell stub for a Java application
executableToRun =
semantics.createStubAction(
ruleContext,
common,
jvmFlags,
executableForRunfiles,
mainClass,
originalMainClass,
filesBuilder,
javaExecutable,
/* createCoverageMetadataJar= */ false);
if (!executableToRun.equals(executableForRunfiles)) {
filesBuilder.add(executableToRun);
runfilesBuilder.addArtifact(executableToRun);
}
}
JavaSourceJarsProvider sourceJarsProvider = javaSourceJarsProviderBuilder.build();
NestedSet<Artifact> transitiveSourceJars = sourceJarsProvider.getTransitiveSourceJars();
// TODO(bazel-team): if (getOptions().sourceJars) then make this a dummy prerequisite for the
// DeployArchiveAction ? Needs a few changes there as we can't pass inputs
SingleJarActionBuilder.createSourceJarAction(
ruleContext,
semantics,
NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER),
transitiveSourceJars,
ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_BINARY_DEPLOY_SOURCE_JAR));
RuleConfiguredTargetBuilder builder = new RuleConfiguredTargetBuilder(ruleContext);
builder.add(
JavaPrimaryClassProvider.class,
new JavaPrimaryClassProvider(
semantics.getPrimaryClass(ruleContext, common.getSrcsArtifacts())));
if (generatedExtensionRegistryProvider != null) {
builder.addNativeDeclaredProvider(generatedExtensionRegistryProvider);
}
Artifact deployJar =
ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_BINARY_DEPLOY_JAR);
if (javaConfig.oneVersionEnforcementLevel() != OneVersionEnforcementLevel.OFF) {
// This JavaBinary class is also the implementation for java_test targets (via the
// {Google,Bazel}JavaTest subclass). java_test targets can have their one version enforcement
// disabled with a second flag (to avoid the incremental build performance cost at the expense
// of safety.)
if (javaConfig.enforceOneVersionOnJavaTests() || !isJavaTestRule(ruleContext)) {
builder.addOutputGroup(
OutputGroupInfo.HIDDEN_TOP_LEVEL,
OneVersionCheckActionBuilder.newBuilder()
.withEnforcementLevel(javaConfig.oneVersionEnforcementLevel())
.outputArtifact(
ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_ONE_VERSION_ARTIFACT))
.useToolchain(JavaToolchainProvider.from(ruleContext))
.checkJars(
NestedSetBuilder.fromNestedSet(attributes.getRuntimeClassPath())
.add(classJar)
.build())
.build(ruleContext));
}
}
NestedSet<Artifact> filesToBuild = filesBuilder.build();
NestedSet<Artifact> dynamicRuntimeActionInputs;
try {
dynamicRuntimeActionInputs = ccToolchain.getDynamicRuntimeLinkInputs(featureConfiguration);
} catch (EvalException e) {
throw ruleContext.throwWithRuleError(e);
}
collectDefaultRunfiles(
runfilesBuilder,
ruleContext,
common,
javaArtifacts,
filesToBuild,
launcher,
dynamicRuntimeActionInputs);
Runfiles defaultRunfiles = runfilesBuilder.build();
RunfilesSupport runfilesSupport = null;
Runfiles persistentTestRunnerRunfiles = null;
NestedSetBuilder<Artifact> extraFilesToRunBuilder = NestedSetBuilder.stableOrder();
if (createExecutable) {
List<String> extraArgs =
new ArrayList<>(semantics.getExtraArguments(ruleContext, common.getSrcsArtifacts()));
// The executable we pass here will be used when creating the runfiles directory. E.g. for the
// stub script called bazel-bin/foo/bar_bin, the runfiles directory will be created under
// bazel-bin/foo/bar_bin.runfiles . On platforms where there's an extra stub script (Windows)
// which dispatches to this one, we still create the runfiles directory for the shell script,
// but use the dispatcher script (a batch file) as the RunfilesProvider's executable.
runfilesSupport =
RunfilesSupport.withExecutable(
ruleContext, defaultRunfiles, executableForRunfiles, extraArgs);
extraFilesToRunBuilder.add(runfilesSupport.getRunfilesMiddleman());
if (JavaSemantics.isTestTargetAndPersistentTestRunner(ruleContext)) {
persistentTestRunnerRunfiles = JavaSemantics.getTestSupportRunfiles(ruleContext);
}
}
RunfilesProvider runfilesProvider =
RunfilesProvider.withData(
defaultRunfiles,
new Runfiles.Builder(
ruleContext.getWorkspaceName(),
ruleContext.getConfiguration().legacyExternalRunfiles())
.merge(runfilesSupport)
.build());
ImmutableList<String> deployManifestLines =
getDeployManifestLines(ruleContext, originalMainClass);
Artifact jsa = createSharedArchive(ruleContext, javaArtifacts, attributes);
deployArchiveBuilder
.setOutputJar(deployJar)
.setJavaStartClass(mainClass)
.setDeployManifestLines(deployManifestLines)
.setAttributes(attributes)
.addRuntimeJars(javaArtifacts.getRuntimeJars())
.setIncludeBuildData(true)
.setRunfilesMiddleman(
runfilesSupport == null ? null : runfilesSupport.getRunfilesMiddleman())
.setCompression(COMPRESSED)
.setLauncher(launcher)
.setOneVersionEnforcementLevel(
javaConfig.oneVersionEnforcementLevel(),
JavaToolchainProvider.from(ruleContext).getOneVersionAllowlist())
.setSharedArchive(jsa)
.build();
Artifact unstrippedDeployJar =
ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_UNSTRIPPED_BINARY_DEPLOY_JAR);
if (stripAsDefault) {
requireNonNull(unstrippedDeployArchiveBuilder); // guarded by stripAsDefault
unstrippedDeployArchiveBuilder
.setOutputJar(unstrippedDeployJar)
.setJavaStartClass(mainClass)
.setDeployManifestLines(deployManifestLines)
.setAttributes(attributes)
.addRuntimeJars(javaArtifacts.getRuntimeJars())
.setIncludeBuildData(true)
.setRunfilesMiddleman(
runfilesSupport == null ? null : runfilesSupport.getRunfilesMiddleman())
.setCompression(COMPRESSED)
.setLauncher(unstrippedLauncher);
unstrippedDeployArchiveBuilder.build();
} else {
// Write an empty file as the name_deploy.jar.unstripped when the default output jar is not
// stripped.
ruleContext.registerAction(
FileWriteAction.create(ruleContext, unstrippedDeployJar, "", false));
}
JavaRuleOutputJarsProvider ruleOutputJarsProvider = ruleOutputJarsProviderBuilder.build();
JavaInfo.Builder javaInfoBuilder = JavaInfo.Builder.create();
NestedSetBuilder<Pair<String, String>> coverageEnvironment = NestedSetBuilder.stableOrder();
NestedSetBuilder<Artifact> coverageSupportFiles = NestedSetBuilder.stableOrder();
if (ruleContext.getConfiguration().isCodeCoverageEnabled()) {
// Create an artifact that contains the runfiles relative paths of the jars on the runtime
// classpath. Using SourceManifestAction is the only reliable way to match the runfiles
// creation code.
Artifact runtimeClasspathArtifact =
ruleContext.getUniqueDirectoryArtifact(
"runtime_classpath_for_coverage",
"runtime_classpath.txt",
ruleContext.getBinOrGenfilesDirectory());
ruleContext.registerAction(
new SourceManifestAction(
ManifestType.SOURCES_ONLY,
ruleContext.getActionOwner(),
runtimeClasspathArtifact,
new Runfiles.Builder(
ruleContext.getWorkspaceName(),
ruleContext.getConfiguration().legacyExternalRunfiles())
// This matches the code below in collectDefaultRunfiles.
.addTransitiveArtifactsWrappedInStableOrder(common.getRuntimeClasspath())
.build(),
true));
filesBuilder.add(runtimeClasspathArtifact);
// Pass the artifact through an environment variable in the coverage environment so it
// can be read by the coverage collection script.
coverageEnvironment.add(
new Pair<>(
"JAVA_RUNTIME_CLASSPATH_FOR_COVERAGE", runtimeClasspathArtifact.getExecPathString()));
// Add the file to coverageSupportFiles so it ends up as an input for the test action
// when coverage is enabled.
coverageSupportFiles.add(runtimeClasspathArtifact);
// Make single jar reachable from the coverage environment because it needs to be executed
// by the coverage collection script.
Artifact singleJar = JavaToolchainProvider.from(ruleContext).getSingleJar();
coverageEnvironment.add(new Pair<>("SINGLE_JAR_TOOL", singleJar.getExecPathString()));
coverageSupportFiles.add(singleJar);
}
common.addTransitiveInfoProviders(
builder,
javaInfoBuilder,
filesToBuild,
classJar,
coverageEnvironment.build(),
coverageSupportFiles.build());
common.addGenJarsProvider(builder, javaInfoBuilder, outputs.genClass(), outputs.genSource());
// This rule specifically _won't_ build deploy_env, so we selectively propagate validations to
// filter out deploy_env's if there are any (and otherwise rely on automatic validation
// propagation). Note that any validations not propagated here will still be built if and when
// deploy_env is built.
if (ruleContext.getRule().isAttrDefined("deploy_env", BuildType.LABEL_LIST)) {
NestedSetBuilder<Artifact> excluded = NestedSetBuilder.stableOrder();
for (OutputGroupInfo outputGroup :
ruleContext.getPrerequisites("deploy_env", OutputGroupInfo.STARLARK_CONSTRUCTOR)) {
NestedSet<Artifact> toExclude = outputGroup.getOutputGroup(OutputGroupInfo.VALIDATION);
if (!toExclude.isEmpty()) {
excluded.addTransitive(toExclude);
}
}
if (!excluded.isEmpty()) {
NestedSetBuilder<Artifact> validations = NestedSetBuilder.stableOrder();
RuleConfiguredTargetBuilder.collectTransitiveValidationOutputGroups(
ruleContext,
attributeName -> !"deploy_env".equals(attributeName),
validations::addTransitive);
// Likely, deploy_env will overlap with deps/runtime_deps. Unless we're building an
// executable (which is rare and somewhat questionable when deploy_env is specified), we can
// exclude validations from deploy_env entirely from this rule, since this rule specifically
// never builds the referenced code.
if (createExecutable) {
// Executable classpath isn't affected by deploy_env, so build all collected validations.
builder.addOutputGroup(OutputGroupInfo.VALIDATION_TRANSITIVE, validations.build());
} else {
// Filter validations similar to JavaTargetAttributes.getRuntimeClassPathForArchive().
builder.addOutputGroup(
OutputGroupInfo.VALIDATION_TRANSITIVE,
NestedSetBuilder.wrap(
Order.STABLE_ORDER,
Iterables.filter(
validations.build().toList(),
Predicates.not(Predicates.in(excluded.build().toSet())))));
}
}
}
Artifact validation =
AndroidLintActionBuilder.create(
ruleContext,
javaConfig,
attributes,
helper.getBootclasspathOrDefault(),
common,
semantics,
outputs);
if (validation != null) {
builder.addOutputGroup(
OutputGroupInfo.VALIDATION, NestedSetBuilder.create(STABLE_ORDER, validation));
}
// Support test execution on darwin.
if (ApplePlatform.isApplePlatform(ruleContext.getConfiguration().getCpu())
&& TargetUtils.isTestRule(ruleContext.getRule())) {
builder.addNativeDeclaredProvider(
new ExecutionInfo(ImmutableMap.of(ExecutionRequirements.REQUIRES_DARWIN, "")));
}
JavaInfo javaInfo =
javaInfoBuilder
.addProvider(JavaSourceJarsProvider.class, sourceJarsProvider)
.addProvider(JavaRuleOutputJarsProvider.class, ruleOutputJarsProvider)
.addTransitiveOnlyRuntimeJars(common.getDependencies())
.build();
return builder
.setFilesToBuild(filesToBuild)
.addNativeDeclaredProvider(javaInfo)
.add(RunfilesProvider.class, runfilesProvider)
// The executable to run (below) may be different from the executable for runfiles (the one
// we create the runfiles support object with). On Linux they are the same (it's the same
// shell script), on Windows they are different (the executable to run is a batch file, the
// executable for runfiles is the shell script).
.setRunfilesSupport(runfilesSupport, executableToRun)
.setPersistentTestRunnerRunfiles(persistentTestRunnerRunfiles)
// Add the native libraries as test action tools. Useful for the persistent test runner
// to include them in the worker's key and re-build a worker if the native dependencies
// have changed.
.addTestActionTools(nativeLibraries)
.addFilesToRun(extraFilesToRunBuilder.build())
.add(
JavaRuntimeClasspathProvider.class,
new JavaRuntimeClasspathProvider(common.getRuntimeClasspath()))
.addOutputGroup(JavaSemantics.SOURCE_JARS_OUTPUT_GROUP, transitiveSourceJars)
.addOutputGroup(
JavaSemantics.DIRECT_SOURCE_JARS_OUTPUT_GROUP,
NestedSetBuilder.wrap(Order.STABLE_ORDER, sourceJarsProvider.getSourceJars()))
.build();
}