blob: 321bbcbe0133f35b01eb7680c1501b491bcb4345 [file] [log] [blame]
// Copyright 2014 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.devtools.build.lib.rules.java;
import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST;
import static com.google.devtools.build.lib.packages.ImplicitOutputsFunction.fromTemplates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.OutputGroupInfo;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
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.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.packages.Attribute.LabelLateBoundDefault;
import com.google.devtools.build.lib.packages.Attribute.LabelListLateBoundDefault;
import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SafeImplicitOutputsFunction;
import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
import com.google.devtools.build.lib.packages.Type;
import com.google.devtools.build.lib.rules.java.DeployArchiveBuilder.Compression;
import com.google.devtools.build.lib.rules.java.JavaCompilationArgsProvider.ClasspathType;
import com.google.devtools.build.lib.rules.java.JavaConfiguration.OneVersionEnforcementLevel;
import com.google.devtools.build.lib.rules.java.proto.GeneratedExtensionRegistryProvider;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.SerializationConstant;
import com.google.devtools.build.lib.util.FileType;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.List;
import javax.annotation.Nullable;
/** Pluggable Java compilation semantics. */
public interface JavaSemantics {
SafeImplicitOutputsFunction JAVA_LIBRARY_CLASS_JAR = fromTemplates("lib%{name}.jar");
SafeImplicitOutputsFunction JAVA_LIBRARY_SOURCE_JAR = fromTemplates("lib%{name}-src.jar");
SafeImplicitOutputsFunction JAVA_BINARY_CLASS_JAR = fromTemplates("%{name}.jar");
SafeImplicitOutputsFunction JAVA_BINARY_SOURCE_JAR = fromTemplates("%{name}-src.jar");
SafeImplicitOutputsFunction JAVA_BINARY_DEPLOY_JAR = fromTemplates("%{name}_deploy.jar");
SafeImplicitOutputsFunction JAVA_UNSTRIPPED_BINARY_DEPLOY_JAR =
fromTemplates("%{name}_deploy.jar.unstripped");
SafeImplicitOutputsFunction JAVA_BINARY_PROGUARD_MAP = fromTemplates("%{name}_proguard.map");
SafeImplicitOutputsFunction JAVA_BINARY_PROGUARD_PROTO_MAP =
fromTemplates("%{name}_proguard.pbmap");
SafeImplicitOutputsFunction JAVA_BINARY_PROGUARD_SEEDS = fromTemplates("%{name}_proguard.seeds");
SafeImplicitOutputsFunction JAVA_BINARY_PROGUARD_USAGE = fromTemplates("%{name}_proguard.usage");
SafeImplicitOutputsFunction JAVA_BINARY_PROGUARD_CONFIG =
fromTemplates("%{name}_proguard.config");
SafeImplicitOutputsFunction JAVA_ONE_VERSION_ARTIFACT = fromTemplates("%{name}-one-version.txt");
SafeImplicitOutputsFunction JAVA_BINARY_DEPLOY_SOURCE_JAR =
fromTemplates("%{name}_deploy-src.jar");
FileType JAVA_SOURCE = FileType.of(".java");
FileType JAR = FileType.of(".jar");
FileType PROPERTIES = FileType.of(".properties");
FileType SOURCE_JAR = FileType.of(".srcjar");
/** The java_toolchain.compatible_javacopts key for Android javacopts */
public static final String ANDROID_JAVACOPTS_KEY = "android";
/** The java_toolchain.compatible_javacopts key for testonly compilations. */
public static final String TESTONLY_JAVACOPTS_KEY = "testonly";
/** The java_toolchain.compatible_javacopts key for public visibility. */
public static final String PUBLIC_VISIBILITY_JAVACOPTS_KEY = "public_visibility";
static Label javaToolchainAttribute(RuleDefinitionEnvironment environment) {
return environment.getToolsLabel("//tools/jdk:current_java_toolchain");
}
/** Name of the output group used for transitive source jars. */
String SOURCE_JARS_OUTPUT_GROUP = OutputGroupInfo.HIDDEN_OUTPUT_GROUP_PREFIX + "source_jars";
/** Name of the output group used for direct source jars. */
String DIRECT_SOURCE_JARS_OUTPUT_GROUP =
OutputGroupInfo.HIDDEN_OUTPUT_GROUP_PREFIX + "direct_source_jars";
public String getJavaToolchainType();
public Label getJavaRuntimeToolchainType();
@SerializationConstant
LabelListLateBoundDefault<JavaConfiguration> JAVA_PLUGINS =
LabelListLateBoundDefault.fromTargetConfiguration(
JavaConfiguration.class,
(rule, attributes, javaConfig) -> ImmutableList.copyOf(javaConfig.getPlugins()));
/** Implementation for the :proguard attribute. */
@SerializationConstant
LabelLateBoundDefault<JavaConfiguration> PROGUARD =
LabelLateBoundDefault.fromTargetConfiguration(
JavaConfiguration.class,
null,
(rule, attributes, javaConfig) -> javaConfig.getProguardBinary());
@SerializationConstant
LabelLateBoundDefault<JavaConfiguration> BYTECODE_OPTIMIZER =
LabelLateBoundDefault.fromTargetConfiguration(
JavaConfiguration.class,
null,
(rule, attributes, javaConfig) -> {
// Use a modicum of smarts to avoid implicit dependencies where we don't need them.
boolean hasProguardSpecs =
attributes.has("proguard_specs")
&& !attributes.get("proguard_specs", LABEL_LIST).isEmpty();
JavaConfiguration.NamedLabel optimizer = javaConfig.getBytecodeOptimizer();
if ((!hasProguardSpecs && !javaConfig.runLocalJavaOptimizations())
|| !optimizer.label().isPresent()) {
return null;
}
return optimizer.label().get();
});
@SerializationConstant
LabelLateBoundDefault<JavaConfiguration> LOCAL_JAVA_OPTIMIZATION_CONFIGURATION =
LabelLateBoundDefault.fromTargetConfiguration(
JavaConfiguration.class,
null,
(rule, attributes, javaConfig) -> {
// Don't bother adding the configuration dep if we're not going to use it.
if (!javaConfig.runLocalJavaOptimizations()) {
return null;
}
return javaConfig.getLocalJavaOptimizationConfiguration();
});
String JACOCO_METADATA_PLACEHOLDER = "%set_jacoco_metadata%";
String JACOCO_MAIN_CLASS_PLACEHOLDER = "%set_jacoco_main_class%";
String JACOCO_JAVA_RUNFILES_ROOT_PLACEHOLDER = "%set_jacoco_java_runfiles_root%";
/**
* Verifies if the rule contains any errors.
*
* <p>Errors should be signaled through {@link RuleContext}.
*/
void checkRule(RuleContext ruleContext, JavaCommon javaCommon) throws RuleErrorException;
/**
* Verifies there are no conflicts in protos.
*
* <p>Errors should be signaled through {@link RuleContext}.
*/
void checkForProtoLibraryAndJavaProtoLibraryOnSameProto(
RuleContext ruleContext, JavaCommon javaCommon);
/**
* Returns the resources contributed by a Java rule (usually the contents of the {@code resources}
* attribute)
*/
ImmutableList<Artifact> collectResources(RuleContext ruleContext) throws RuleErrorException;
String getTestRunnerMainClass();
/**
* Constructs the command line to call SingleJar to join all artifacts from {@code classpath}
* (java code) and {@code resources} into {@code output}.
*/
CustomCommandLine buildSingleJarCommandLine(
String toolchainIdentifier,
Artifact output,
Label label,
String mainClass,
ImmutableList<String> manifestLines,
Iterable<Artifact> buildInfoFiles,
ImmutableList<Artifact> resources,
NestedSet<Artifact> classpath,
boolean includeBuildData,
Compression compression,
Artifact launcher,
OneVersionEnforcementLevel oneVersionEnforcementLevel,
Artifact oneVersionAllowlistArtifact,
Artifact sharedArchive,
boolean multiReleaseDeployJars,
PathFragment javaHome,
Artifact libModules,
NestedSet<Artifact> hermeticInputs,
NestedSet<String> addExports,
NestedSet<String> addOpens);
ImmutableList<Artifact> getBuildInfo(RuleContext ruleContext, int stamp)
throws RuleErrorException, InterruptedException;
/**
* Creates the action that writes the Java executable stub script.
*
* <p>Returns the launcher script artifact. This may or may not be the same as {@code executable},
* depending on the implementation of this method. If they are the same, then this Artifact should
* be used when creating both the {@code RunfilesProvider} and the {@code RunfilesSupport}. If
* they are different, the new value should be used when creating the {@code RunfilesProvider} (so
* it will be the stub script executed by "bazel run" for example), and the old value should be
* used when creating the {@code RunfilesSupport} (so the runfiles directory will be named after
* it).
*
* <p>For example on Windows we use a double dispatch approach: the launcher is a batch file (and
* is created and returned by this method) which shells out to a shell script (the {@code
* executable} argument).
*
* <p>In Blaze, this method considers {@code javaExecutable} as a substitution that can be
* directly used to replace %javabin% in stub script, but in Bazel this method considers {@code
* javaExecutable} as a file path for the JVM binary (java).
*
* <p>In *experimental* coverage mode creates a txt file containing the runtime jars names. {@code
* JacocoCoverageRunner} will use it to retrieve the name of the jars considered for collecting
* coverage. {@code JacocoCoverageRunner} will *not* collect coverage implicitly for all the
* runtime jars, only for those that pack a file ending in "-paths-for-coverage.txt".
*
* @param createCoverageMetadataJar is false for Java rules and true otherwise (e.g. android)
*/
public Artifact createStubAction(
RuleContext ruleContext,
JavaCommon javaCommon,
List<String> jvmFlags,
Artifact executable,
String javaStartClass,
String coverageStartClass,
NestedSetBuilder<Artifact> filesBuilder,
String javaExecutable,
boolean createCoverageMetadataJar)
throws InterruptedException, RuleErrorException;
/**
* Returns true if {@code createStubAction} considers {@code javaExecutable} as a substitution.
* Returns false if {@code createStubAction} considers {@code javaExecutable} as a file path.
*/
boolean isJavaExecutableSubstitution();
@Nullable
static 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)) {
return Iterables.getOnlyElement(ruleContext.getPrerequisites("$testsupport"));
} else {
return null;
}
}
static boolean useLegacyJavaTest(RuleContext ruleContext) {
return !ruleContext.attributes().isAttributeValueExplicitlySpecified("test_class")
&& ruleContext.getFragment(JavaConfiguration.class).useLegacyBazelJavaTest();
}
static boolean isJavaBinaryOrJavaTest(RuleContext ruleContext) {
return ruleContext.getRule().getRuleClass().equals("java_binary")
|| ruleContext.getRule().getRuleClass().equals("java_test");
}
/** Adds extra runfiles for a {@code java_library} rule. */
void addRunfilesForLibrary(RuleContext ruleContext, Runfiles.Builder runfilesBuilder);
/**
* Returns the command line options to be used when compiling Java code for {@code java_*} rules.
*
* <p>These will come after the default options specified by the toolchain, and before the ones in
* the {@code javacopts} attribute.
*/
ImmutableList<String> getCompatibleJavacOptions(
RuleContext ruleContext, JavaToolchainProvider toolchain) throws RuleErrorException;
/** Add additional targets to be treated as direct dependencies. */
void collectTargetsTreatedAsDeps(
RuleContext ruleContext,
ImmutableList.Builder<TransitiveInfoCollection> builder,
ClasspathType type);
/**
* Enables coverage support for the java target - adds instrumented jar to the classpath and
* modifies main class.
*
* @return new main class
*/
String addCoverageSupport(JavaCompilationHelper helper, Artifact executable)
throws InterruptedException, RuleErrorException;
/**
* Add a source artifact to a {@link JavaTargetAttributes.Builder}. It is called when a source
* artifact is processed but is not matched by default patterns in the {@link
* JavaTargetAttributes.Builder#addSourceArtifacts(Iterable)} method. The semantics can then
* detect its custom artifact types and add it to the builder.
*/
void addArtifactToJavaTargetAttribute(JavaTargetAttributes.Builder builder, Artifact srcArtifact);
/**
* Takes the path of a Java resource and tries to determine the Java root relative path of the
* resource.
*
* <p>This is only used if the Java rule doesn't have a {@code resource_strip_prefix} attribute.
*
* @param path the root relative path of the resource.
* @return the Java root relative path of the resource of the root relative path of the resource
* if no Java root relative path can be determined.
*/
PathFragment getDefaultJavaResourcePath(PathFragment path);
/**
* @return An artifact representing the protobuf-format version of the proguard mapping, or null
* if the proguard version doesn't support this.
*/
Artifact getProtoMapping(RuleContext ruleContext) throws InterruptedException;
/**
* Produces the proto generated extension registry artifacts, or <tt>null</tt> if no registry
* needs to be generated for the provided <tt>ruleContext</tt>.
*/
@Nullable
GeneratedExtensionRegistryProvider createGeneratedExtensionRegistry(
RuleContext ruleContext,
JavaCommon common,
NestedSetBuilder<Artifact> filesBuilder,
JavaCompilationArtifacts.Builder javaCompilationArtifactsBuilder,
JavaRuleOutputJarsProvider.Builder javaRuleOutputJarsProviderBuilder,
JavaSourceJarsProvider.Builder javaSourceJarsProviderBuilder)
throws InterruptedException, RuleErrorException;
Artifact getObfuscatedConstantStringMap(RuleContext ruleContext) throws InterruptedException;
}