blob: 47986d1bffdc80c63dfaf2bd350116856d4621fa [file] [log] [blame]
// Copyright 2016 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.ImmutableList.toImmutableList;
import static com.google.devtools.build.lib.rules.java.JavaInfoBuildHelper.getStrictDepsMode;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Ascii;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.configuredtargets.AbstractConfiguredTarget;
import com.google.devtools.build.lib.analysis.configuredtargets.MergedConfiguredTarget;
import com.google.devtools.build.lib.analysis.platform.ConstraintValueInfo;
import com.google.devtools.build.lib.analysis.platform.ToolchainInfo;
import com.google.devtools.build.lib.analysis.starlark.StarlarkActionFactory;
import com.google.devtools.build.lib.analysis.starlark.StarlarkRuleContext;
import com.google.devtools.build.lib.cmdline.BazelModuleContext;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
import com.google.devtools.build.lib.collect.nestedset.Depset;
import com.google.devtools.build.lib.collect.nestedset.Depset.TypeException;
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.Info;
import com.google.devtools.build.lib.packages.NativeInfo;
import com.google.devtools.build.lib.packages.Provider;
import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
import com.google.devtools.build.lib.packages.StarlarkInfoNoSchema;
import com.google.devtools.build.lib.packages.StarlarkInfoWithSchema;
import com.google.devtools.build.lib.packages.semantics.BuildLanguageOptions;
import com.google.devtools.build.lib.rules.cpp.CcInfo;
import com.google.devtools.build.lib.rules.cpp.CppFileTypes;
import com.google.devtools.build.lib.rules.cpp.LibraryToLink;
import com.google.devtools.build.lib.starlarkbuildapi.core.ProviderApi;
import com.google.devtools.build.lib.starlarkbuildapi.java.JavaCommonApi;
import net.starlark.java.eval.EvalException;
import net.starlark.java.eval.Module;
import net.starlark.java.eval.Sequence;
import net.starlark.java.eval.Starlark;
import net.starlark.java.eval.StarlarkList;
import net.starlark.java.eval.StarlarkSemantics;
import net.starlark.java.eval.StarlarkThread;
/** A module that contains Starlark utilities for Java support. */
public class JavaStarlarkCommon
implements JavaCommonApi<
Artifact,
JavaInfo,
JavaToolchainProvider,
BootClassPathInfo.Provider,
ConstraintValueInfo,
StarlarkRuleContext,
StarlarkActionFactory> {
private static final ImmutableSet<String> PRIVATE_STARLARKIFACTION_ALLOWLIST =
ImmutableSet.of("bazel_internal/test");
private final JavaSemantics javaSemantics;
private void checkJavaToolchainIsDeclaredOnRule(RuleContext ruleContext)
throws EvalException, LabelSyntaxException {
ToolchainInfo toolchainInfo =
ruleContext.getToolchainInfo(Label.parseCanonical(javaSemantics.getJavaToolchainType()));
if (toolchainInfo == null) {
String ruleLocation = ruleContext.getRule().getLocation().toString();
String ruleClass = ruleContext.getRule().getRuleClassObject().getName();
throw Starlark.errorf(
"Rule '%s' in '%s' must declare '%s' toolchain in order to use java_common. See"
+ " https://github.com/bazelbuild/bazel/issues/18970.",
ruleClass, ruleLocation, javaSemantics.getJavaToolchainType());
}
}
@Override
public void checkJavaToolchainIsDeclaredOnRuleForStarlark(
StarlarkActionFactory actions, StarlarkThread thread)
throws EvalException, LabelSyntaxException {
checkPrivateAccess(thread);
checkJavaToolchainIsDeclaredOnRule(actions.getRuleContext());
}
public JavaStarlarkCommon(JavaSemantics javaSemantics) {
this.javaSemantics = javaSemantics;
}
@Override
public JavaInfo createJavaCompileAction(
StarlarkRuleContext starlarkRuleContext,
Sequence<?> sourceJars, // <Artifact> expected
Sequence<?> sourceFiles, // <Artifact> expected
Artifact outputJar,
Object outputSourceJar,
Sequence<?> javacOpts, // <String> expected
Sequence<?> deps, // <JavaInfo> expected
Sequence<?> runtimeDeps, // <JavaInfo> expected
Sequence<?> exports, // <JavaInfo> expected
Sequence<?> plugins, // <JavaPluginInfo> expected
Sequence<?> exportedPlugins, // <JavaPluginInfo> expected
Sequence<?> nativeLibraries, // <CcInfo> expected.
Sequence<?> annotationProcessorAdditionalInputs, // <Artifact> expected
Sequence<?> annotationProcessorAdditionalOutputs, // <Artifact> expected
String strictDepsMode,
JavaToolchainProvider javaToolchain,
Object bootClassPath,
Object hostJavabase,
Sequence<?> sourcepathEntries, // <Artifact> expected
Sequence<?> resources, // <Artifact> expected
Sequence<?> resourceJars, // <Artifact> expected
Sequence<?> classpathResources, // <Artifact> expected
Boolean neverlink,
Boolean enableAnnotationProcessing,
Boolean enableCompileJarAction,
Boolean enableJSpecify,
boolean includeCompilationInfo,
Object injectingRuleKind,
Sequence<?> addExports, // <String> expected
Sequence<?> addOpens, // <String> expected
StarlarkThread thread)
throws EvalException, InterruptedException, RuleErrorException, LabelSyntaxException {
checkJavaToolchainIsDeclaredOnRule(starlarkRuleContext.getRuleContext());
final ImmutableList<JavaPluginInfo> pluginsParam =
JavaPluginInfo.wrapSequence(plugins, "plugins");
final ImmutableList<JavaPluginInfo> exportedPluginsParam =
JavaPluginInfo.wrapSequence(exportedPlugins, "exported_plugins");
// checks for private API access
if (!enableCompileJarAction
|| !enableJSpecify
|| !includeCompilationInfo
|| !classpathResources.isEmpty()
|| !resourceJars.isEmpty()
|| injectingRuleKind != Starlark.NONE) {
checkPrivateAccess(thread);
}
if (starlarkRuleContext.getRuleContext().useAutoExecGroups()) {
String javaToolchainType = javaSemantics.getJavaToolchainType();
if (!starlarkRuleContext.getRuleContext().hasToolchainContext(javaToolchainType)) {
throw Starlark.errorf(
"Action declared for non-existent toolchain '%s'.", javaToolchainType);
}
}
return JavaInfoBuildHelper.getInstance()
.createJavaCompileAction(
starlarkRuleContext,
Sequence.cast(sourceJars, Artifact.class, "source_jars"),
Sequence.cast(sourceFiles, Artifact.class, "source_files"),
outputJar,
outputSourceJar == Starlark.NONE ? null : (Artifact) outputSourceJar,
Sequence.cast(javacOpts, String.class, "javac_opts"),
JavaInfo.wrapSequence(deps, "deps"),
JavaInfo.wrapSequence(runtimeDeps, "runtime_deps"),
JavaInfo.wrapSequence(exports, "exports"),
pluginsParam,
exportedPluginsParam,
Sequence.cast(nativeLibraries, CcInfo.class, "native_libraries"),
Sequence.cast(
annotationProcessorAdditionalInputs,
Artifact.class,
"annotation_processor_additional_inputs"),
Sequence.cast(
annotationProcessorAdditionalOutputs,
Artifact.class,
"annotation_processor_additional_outputs"),
strictDepsMode,
javaToolchain,
bootClassPath == Starlark.NONE ? null : (BootClassPathInfo) bootClassPath,
ImmutableList.copyOf(Sequence.cast(sourcepathEntries, Artifact.class, "sourcepath")),
Sequence.cast(resources, Artifact.class, "resources"),
Sequence.cast(resourceJars, Artifact.class, "resource_jars"),
Sequence.cast(classpathResources, Artifact.class, "classpath_resources"),
neverlink,
enableAnnotationProcessing,
enableCompileJarAction,
enableJSpecify,
includeCompilationInfo,
javaSemantics,
injectingRuleKind,
Sequence.cast(addExports, String.class, "add_exports"),
Sequence.cast(addOpens, String.class, "add_opens"),
thread);
}
@Override
public void createHeaderCompilationAction(
StarlarkRuleContext ctx,
JavaToolchainProvider toolchain,
Artifact headerJar,
Artifact headerDepsProto,
Info pluginInfo,
Depset sourceFiles,
Sequence<?> sourceJars,
Depset compileTimeClasspath,
Depset directJars,
Object bootClassPath,
Depset compileTimeJavaDeps,
Sequence<?> javacOpts,
String strictDepsMode,
Label targetLabel,
Object injectingRuleKind,
boolean enableDirectClasspath,
Sequence<?> additionalInputs)
throws EvalException, TypeException, RuleErrorException, LabelSyntaxException {
checkJavaToolchainIsDeclaredOnRule(ctx.getRuleContext());
JavaTargetAttributes.Builder attributesBuilder =
new JavaTargetAttributes.Builder(javaSemantics)
.addSourceJars(Sequence.cast(sourceJars, Artifact.class, "source_jars"))
.addSourceFiles(sourceFiles.toList(Artifact.class))
.addDirectJars(directJars.getSet(Artifact.class))
.addCompileTimeClassPathEntries(compileTimeClasspath.getSet(Artifact.class))
.setStrictJavaDeps(getStrictDepsMode(Ascii.toUpperCase(strictDepsMode)))
.setTargetLabel(targetLabel)
.setInjectingRuleKind(
injectingRuleKind == Starlark.NONE ? null : (String) injectingRuleKind)
.addPlugin(JavaPluginInfo.PROVIDER.wrap(pluginInfo))
.addCompileTimeDependencyArtifacts(compileTimeJavaDeps.getSet(Artifact.class));
if (bootClassPath instanceof BootClassPathInfo
&& !((BootClassPathInfo) bootClassPath).isEmpty()) {
attributesBuilder.setBootClassPath((BootClassPathInfo) bootClassPath);
}
JavaCompilationHelper compilationHelper =
new JavaCompilationHelper(
ctx.getRuleContext(),
javaSemantics,
Sequence.cast(javacOpts, String.class, "javac_opts").getImmutableList(),
attributesBuilder,
toolchain,
Sequence.cast(additionalInputs, Artifact.class, "additional_inputs")
.getImmutableList());
compilationHelper.enableDirectClasspath(enableDirectClasspath);
compilationHelper.createHeaderCompilationAction(headerJar, headerDepsProto);
}
@Override
public void createCompilationAction(
StarlarkRuleContext ctx,
JavaToolchainProvider javaToolchain,
Artifact output,
Object depsProto,
Object genClass,
Object genSource,
Artifact manifestProto,
Artifact nativeHeader,
Info pluginInfo,
Depset sourceFiles,
Sequence<?> sourceJars,
Sequence<?> resources,
Depset resourceJars,
Depset compileTimeClasspath,
Sequence<?> classpathResources,
Sequence<?> sourcepath,
Depset directJars,
Object bootClassPath,
Depset compileTimeJavaDeps,
Sequence<?> javacOpts,
String strictDepsMode,
Label targetLabel,
Object injectingRuleKind,
boolean enableJSpecify,
boolean enableDirectClasspath,
Sequence<?> additionalInputs,
Sequence<?> additionalOutputs)
throws EvalException, TypeException, RuleErrorException, LabelSyntaxException {
checkJavaToolchainIsDeclaredOnRule(ctx.getRuleContext());
JavaCompileOutputs<Artifact> outputs =
JavaCompileOutputs.builder()
.output(output)
.depsProto(depsProto == Starlark.NONE ? null : (Artifact) depsProto)
.genClass(genClass == Starlark.NONE ? null : (Artifact) genClass)
.genSource(genSource == Starlark.NONE ? null : (Artifact) genSource)
.manifestProto(manifestProto)
.nativeHeader(nativeHeader)
.build();
JavaTargetAttributes.Builder attributesBuilder =
new JavaTargetAttributes.Builder(javaSemantics)
.addSourceJars(Sequence.cast(sourceJars, Artifact.class, "source_jars"))
.addSourceFiles(sourceFiles.toList(Artifact.class))
.addDirectJars(directJars.getSet(Artifact.class))
.addCompileTimeClassPathEntries(compileTimeClasspath.getSet(Artifact.class))
.addClassPathResources(
Sequence.cast(classpathResources, Artifact.class, "classpath_resources"))
.setStrictJavaDeps(getStrictDepsMode(Ascii.toUpperCase(strictDepsMode)))
.setTargetLabel(targetLabel)
.setInjectingRuleKind(
injectingRuleKind == Starlark.NONE ? null : (String) injectingRuleKind)
.setSourcePath(
Sequence.cast(sourcepath, Artifact.class, "source_path").getImmutableList())
.addPlugin(JavaPluginInfo.PROVIDER.wrap(pluginInfo))
.addAdditionalOutputs(
Sequence.cast(additionalOutputs, Artifact.class, "additional_outputs"));
if (bootClassPath instanceof BootClassPathInfo
&& !((BootClassPathInfo) bootClassPath).isEmpty()) {
attributesBuilder.setBootClassPath((BootClassPathInfo) bootClassPath);
}
for (Artifact resource : Sequence.cast(resources, Artifact.class, "resources")) {
attributesBuilder.addResource(
JavaHelper.getJavaResourcePath(javaSemantics, ctx.getRuleContext(), resource), resource);
}
attributesBuilder.addResourceJars(resourceJars.getSet(Artifact.class));
attributesBuilder.addCompileTimeDependencyArtifacts(compileTimeJavaDeps.getSet(Artifact.class));
JavaCompilationHelper compilationHelper =
new JavaCompilationHelper(
ctx.getRuleContext(),
javaSemantics,
Sequence.cast(javacOpts, String.class, "javac_opts").getImmutableList(),
attributesBuilder,
javaToolchain,
Sequence.cast(additionalInputs, Artifact.class, "additional_inputs")
.getImmutableList());
compilationHelper.enableJspecify(enableJSpecify);
compilationHelper.enableDirectClasspath(enableDirectClasspath);
compilationHelper.createCompileAction(outputs);
}
@Override
// TODO(b/78512644): migrate callers to passing explicit javacopts or using custom toolchains, and
// delete
public ImmutableList<String> getDefaultJavacOpts(JavaToolchainProvider javaToolchain)
throws EvalException {
// We don't have a rule context if the default_javac_opts.java_toolchain parameter is set
return ((JavaToolchainProvider) javaToolchain).getJavacOptions(/* ruleContext= */ null);
}
@Override
public Provider getJavaToolchainProvider() {
return JavaToolchainProvider.PROVIDER;
}
@Override
public Provider getJavaRuntimeProvider() {
return JavaRuntimeInfo.PROVIDER;
}
@Override
public ProviderApi getBootClassPathInfo() {
return BootClassPathInfo.PROVIDER;
}
@Override
public String getTargetKind(Object target, boolean dereferenceAliases, StarlarkThread thread)
throws EvalException {
checkPrivateAccess(thread);
if (target instanceof MergedConfiguredTarget) {
target = ((MergedConfiguredTarget) target).getBaseConfiguredTarget();
}
if (dereferenceAliases && target instanceof ConfiguredTarget) {
target = ((ConfiguredTarget) target).getActual();
}
if (target instanceof AbstractConfiguredTarget) {
return ((AbstractConfiguredTarget) target).getRuleClassString();
}
return "";
}
protected static void checkPrivateAccess(StarlarkThread thread) throws EvalException {
Label label =
((BazelModuleContext) Module.ofInnermostEnclosingStarlarkFunction(thread).getClientData())
.label();
if (!PRIVATE_STARLARKIFACTION_ALLOWLIST.contains(label.getPackageName())
&& !label.getPackageIdentifier().getRepository().getName().equals("_builtins")) {
throw Starlark.errorf("Rule in '%s' cannot use private API", label.getPackageName());
}
}
@Override
public Sequence<Artifact> getBuildInfo(
StarlarkRuleContext starlarkRuleContext, boolean isStampingEnabled, StarlarkThread thread)
throws EvalException, InterruptedException {
checkPrivateAccess(thread);
RuleContext ruleContext = starlarkRuleContext.getRuleContext();
return StarlarkList.immutableCopyOf(
ruleContext
.getAnalysisEnvironment()
.getBuildInfo(
isStampingEnabled, JavaBuildInfoFactory.KEY, ruleContext.getConfiguration()));
}
@Override
public boolean getExperimentalJavaProtoLibraryDefaultHasServices(
StarlarkSemantics starlarkSemantics) throws EvalException {
return starlarkSemantics.getBool(
BuildLanguageOptions.EXPERIMENTAL_JAVA_PROTO_LIBRARY_DEFAULT_HAS_SERVICES);
}
@Override
public Sequence<String> collectNativeLibsDirs(Depset libraries, StarlarkThread thread)
throws EvalException, TypeException {
checkPrivateAccess(thread);
ImmutableList<Artifact> nativeLibraries =
LibraryToLink.getDynamicLibrariesForLinking(libraries.getSet(LibraryToLink.class));
ImmutableList<String> uniqueDirs =
nativeLibraries.stream()
.filter(
nativeLibrary -> {
String name = nativeLibrary.getFilename();
if (CppFileTypes.INTERFACE_SHARED_LIBRARY.matches(name)) {
return false;
}
if (!(CppFileTypes.SHARED_LIBRARY.matches(name)
|| CppFileTypes.VERSIONED_SHARED_LIBRARY.matches(name))) {
throw new IllegalArgumentException(
"not a shared library :" + nativeLibrary.prettyPrint());
}
return true;
})
.map(artifact -> artifact.getRootRelativePath().getParentDirectory().getPathString())
.distinct()
.collect(toImmutableList());
return StarlarkList.immutableCopyOf(uniqueDirs);
}
@Override
public Depset getRuntimeClasspathForArchive(
Depset runtimeClasspath, Depset excludedArtifacts, StarlarkThread thread)
throws EvalException, TypeException {
checkPrivateAccess(thread);
if (excludedArtifacts.isEmpty()) {
return runtimeClasspath;
} else {
return Depset.of(
Artifact.class,
NestedSetBuilder.wrap(
Order.STABLE_ORDER,
Iterables.filter(
runtimeClasspath.toList(Artifact.class),
Predicates.not(Predicates.in(excludedArtifacts.getSet().toSet())))));
}
}
@Override
public void checkProviderInstances(
Sequence<?> providers, String what, ProviderApi providerType, StarlarkThread thread)
throws EvalException {
checkPrivateAccess(thread);
if (providerType instanceof Provider) {
for (int i = 0; i < providers.size(); i++) {
Object elem = providers.get(i);
if (!isInstanceOfProvider(elem, (Provider) providerType)) {
throw Starlark.errorf(
"at index %d of %s, got element of type %s, want %s",
i, what, printableType(elem), ((Provider) providerType).getPrintableName());
}
}
} else {
throw Starlark.errorf("wanted Provider, got %s", Starlark.type(providerType));
}
}
@VisibleForTesting
static String printableType(Object elem) {
if (elem instanceof StarlarkInfoWithSchema) {
return ((StarlarkInfoWithSchema) elem).getProvider().getPrintableName();
} else if (elem instanceof NativeInfo) {
return ((NativeInfo) elem).getProvider().getPrintableName();
}
return Starlark.type(elem);
}
@Override
public boolean isLegacyGoogleApiEnabled(StarlarkThread thread) throws EvalException {
checkPrivateAccess(thread);
return thread.getSemantics().getBool(BuildLanguageOptions.EXPERIMENTAL_GOOGLE_LEGACY_API);
}
@Override
public boolean isDepsetForJavaOutputSourceJarsEnabled(StarlarkThread thread)
throws EvalException {
checkPrivateAccess(thread);
return thread
.getSemantics()
.getBool(BuildLanguageOptions.INCOMPATIBLE_DEPSET_FOR_JAVA_OUTPUT_SOURCE_JARS);
}
@Override
public JavaInfo wrapJavaInfo(Info javaInfo, StarlarkThread thread)
throws EvalException, RuleErrorException {
checkPrivateAccess(thread);
return JavaInfo.PROVIDER.wrap(javaInfo);
}
static boolean isInstanceOfProvider(Object obj, Provider provider) {
if (obj instanceof NativeInfo) {
return ((NativeInfo) obj).getProvider().getKey().equals(provider.getKey());
} else if (obj instanceof StarlarkInfoWithSchema) {
return ((StarlarkInfoWithSchema) obj).getProvider().getKey().equals(provider.getKey());
} else if (obj instanceof StarlarkInfoNoSchema) {
return ((StarlarkInfoNoSchema) obj).getProvider().getKey().equals(provider.getKey());
}
return false;
}
}