blob: 4f0a9d9e8328a119a00fffaa60cfa354d340949b [file] [log] [blame]
// Copyright 2018 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.common.collect.Iterables.concat;
import static com.google.common.collect.Streams.stream;
import static com.google.devtools.build.lib.rules.java.JavaInfo.streamProviders;
import static java.util.stream.Stream.concat;
import com.google.common.base.Ascii;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Streams;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.actions.ActionConstructionContext;
import com.google.devtools.build.lib.analysis.config.CoreOptionConverters.StrictDepsMode;
import com.google.devtools.build.lib.analysis.starlark.StarlarkRuleContext;
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.RuleClass.ConfiguredTargetFactory.RuleErrorException;
import com.google.devtools.build.lib.rules.cpp.CcInfo;
import com.google.devtools.build.lib.shell.ShellUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
import net.starlark.java.eval.EvalException;
import net.starlark.java.eval.Starlark;
import net.starlark.java.eval.StarlarkThread;
/** Implements logic for creating JavaInfo from different set of input parameters. */
final class JavaInfoBuildHelper {
private static final JavaInfoBuildHelper INSTANCE = new JavaInfoBuildHelper();
private JavaInfoBuildHelper() {}
public static JavaInfoBuildHelper getInstance() {
return INSTANCE;
}
private static ImmutableList<JavaGenJarsProvider> collectJavaGenJarsProviders(
Iterable<JavaInfo> javaInfos) throws RuleErrorException, EvalException {
ImmutableList.Builder<JavaGenJarsProvider> builder = ImmutableList.builder();
for (JavaInfo javaInfo : javaInfos) {
JavaGenJarsProvider provider = javaInfo.getGenJarsProvider();
if (provider != null && !provider.isEmpty()) {
builder.add(provider);
}
}
return builder.build();
}
private JavaSourceJarsProvider createJavaSourceJarsProvider(
Iterable<Artifact> sourceJars, Iterable<JavaInfo> transitiveDeps) {
NestedSetBuilder<Artifact> transitiveSourceJars = NestedSetBuilder.stableOrder();
transitiveSourceJars.addAll(sourceJars);
fetchSourceJars(transitiveDeps).forEach(transitiveSourceJars::addTransitive);
return JavaSourceJarsProvider.create(transitiveSourceJars.build(), sourceJars);
}
private Stream<NestedSet<Artifact>> fetchSourceJars(Iterable<JavaInfo> javaInfos) {
// TODO(b/123265803): This step should be only necessary if transitive source jar doesn't
// include sourcejar at this level but they should.
Stream<NestedSet<Artifact>> sourceJars =
streamProviders(javaInfos, JavaSourceJarsProvider.class)
.map(JavaSourceJarsProvider::getSourceJars)
.map(sourceJarsList -> NestedSetBuilder.wrap(Order.STABLE_ORDER, sourceJarsList));
Stream<NestedSet<Artifact>> transitiveSourceJars =
streamProviders(javaInfos, JavaSourceJarsProvider.class)
.map(JavaSourceJarsProvider::getTransitiveSourceJars);
return concat(transitiveSourceJars, sourceJars);
}
private static JavaModuleFlagsProvider createJavaModuleFlagsProvider(
List<String> addExports, List<String> addOpens, Iterable<JavaInfo> transitiveDeps) {
return JavaModuleFlagsProvider.create(
addExports, addOpens, streamProviders(transitiveDeps, JavaModuleFlagsProvider.class));
}
private JavaPluginInfo mergeExportedJavaPluginInfo(
Iterable<JavaPluginInfo> plugins, Iterable<JavaInfo> javaInfos) {
return JavaPluginInfo.mergeWithoutJavaOutputs(
concat(
plugins,
stream(javaInfos)
.map(JavaInfo::getJavaPluginInfo)
.filter(Objects::nonNull)
.collect(toImmutableList())));
}
public JavaInfo createJavaCompileAction(
StarlarkRuleContext starlarkRuleContext,
List<Artifact> sourceJars,
List<Artifact> sourceFiles,
Artifact outputJar,
Artifact outputSourceJar,
List<String> javacOpts,
List<JavaInfo> deps,
List<JavaInfo> runtimeDeps,
List<JavaInfo> exports,
List<JavaPluginInfo> plugins,
List<JavaPluginInfo> exportedPlugins,
List<CcInfo> nativeLibraries,
List<Artifact> annotationProcessorAdditionalInputs,
List<Artifact> annotationProcessorAdditionalOutputs,
String strictDepsMode,
JavaToolchainProvider javaToolchain,
BootClassPathInfo bootClassPath,
ImmutableList<Artifact> sourcepathEntries,
List<Artifact> resources,
List<Artifact> resourceJars,
List<Artifact> classpathResources,
Boolean neverlink,
Boolean enableAnnotationProcessing,
Boolean enableCompileJarAction,
boolean enableJSpecify,
boolean includeCompilationInfo,
JavaSemantics javaSemantics,
Object injectingRuleKind,
List<String> addExports,
List<String> addOpens,
StarlarkThread thread)
throws EvalException, InterruptedException, RuleErrorException {
JavaToolchainProvider toolchainProvider = javaToolchain;
JavaPluginInfo pluginInfo = mergeExportedJavaPluginInfo(plugins, deps);
ImmutableList.Builder<String> allJavacOptsBuilder =
ImmutableList.<String>builder()
.addAll(toolchainProvider.getJavacOptions(starlarkRuleContext.getRuleContext()))
.addAll(
javaSemantics.getCompatibleJavacOptions(
starlarkRuleContext.getRuleContext(), toolchainProvider));
if (pluginInfo
.plugins()
.processorClasses()
.toSet()
.contains("com.google.devtools.build.runfiles.AutoBazelRepositoryProcessor")) {
allJavacOptsBuilder.add(
"-Abazel.repository=" + starlarkRuleContext.getRuleContext().getRepository().getName());
}
allJavacOptsBuilder
.addAll(
JavaCommon.computePerPackageJavacOpts(
starlarkRuleContext.getRuleContext(), toolchainProvider))
.addAll(JavaModuleFlagsProvider.toFlags(addExports, addOpens))
.addAll(tokenize(javacOpts));
JavaLibraryHelper helper =
new JavaLibraryHelper(starlarkRuleContext.getRuleContext())
.setOutput(outputJar)
.addSourceJars(sourceJars)
.addSourceFiles(sourceFiles)
.addResources(resources)
.addResourceJars(resourceJars)
.addClasspathResources(classpathResources)
.setSourcePathEntries(sourcepathEntries)
.addAdditionalOutputs(annotationProcessorAdditionalOutputs)
.enableJspecify(enableJSpecify)
.setJavacOpts(allJavacOptsBuilder.build());
if (injectingRuleKind != Starlark.NONE) {
helper.setInjectingRuleKind((String) injectingRuleKind);
}
streamProviders(runtimeDeps, JavaCompilationArgsProvider.class).forEach(helper::addRuntimeDep);
streamProviders(deps, JavaCompilationArgsProvider.class).forEach(helper::addDep);
streamProviders(exports, JavaCompilationArgsProvider.class).forEach(helper::addExport);
helper.setCompilationStrictDepsMode(getStrictDepsMode(Ascii.toUpperCase(strictDepsMode)));
// Optimization: skip this if there are no annotation processors, to avoid unnecessarily
// disabling the direct classpath optimization if `enable_annotation_processor = False`
// but there aren't any annotation processors.
if (!enableAnnotationProcessing && !pluginInfo.plugins().processorClasses().isEmpty()) {
pluginInfo = pluginInfo.disableAnnotationProcessing();
helper.enableDirectClasspath(false);
}
helper.setPlugins(pluginInfo);
helper.setNeverlink(neverlink);
JavaRuleOutputJarsProvider.Builder outputJarsBuilder = JavaRuleOutputJarsProvider.builder();
boolean createOutputSourceJar =
!(sourceJars.size() == 1
&& sourceFiles.isEmpty()
&& sourceJars.get(0).equals(outputSourceJar));
if (outputSourceJar == null) {
outputSourceJar = getDerivedSourceJar(starlarkRuleContext.getRuleContext(), outputJar);
}
JavaInfo.Builder javaInfoBuilder = JavaInfo.Builder.create();
JavaCompilationArtifacts artifacts =
helper.build(
javaSemantics,
toolchainProvider,
bootClassPath,
outputJarsBuilder,
createOutputSourceJar,
includeCompilationInfo,
outputSourceJar,
enableCompileJarAction,
javaInfoBuilder,
// Include JavaGenJarsProviders from both deps and exports in the JavaGenJarsProvider
// added to javaInfoBuilder for this target.
collectJavaGenJarsProviders(concat(deps, exports)),
ImmutableList.copyOf(annotationProcessorAdditionalInputs));
JavaCompilationArgsProvider javaCompilationArgsProvider =
helper.buildCompilationArgsProvider(artifacts, true, neverlink);
ImmutableList<Artifact> outputSourceJars = ImmutableList.of(outputSourceJar);
// When sources are not provided, the subsequent output Jar will be empty. As such, the output
// Jar is omitted from the set of Runtime Jars.
if (!sourceJars.isEmpty() || !sourceFiles.isEmpty() || !resources.isEmpty()) {
javaInfoBuilder.setRuntimeJars(ImmutableList.of(outputJar));
}
ImmutableList<JavaCcInfoProvider> transitiveNativeLibraries =
Streams.concat(
streamProviders(runtimeDeps, JavaCcInfoProvider.class),
streamProviders(exports, JavaCcInfoProvider.class),
streamProviders(deps, JavaCcInfoProvider.class),
Stream.of(JavaCcInfoProvider.create(CcInfo.merge(nativeLibraries))))
.collect(toImmutableList());
return javaInfoBuilder
.javaCompilationArgs(javaCompilationArgsProvider)
.javaSourceJars(
createJavaSourceJarsProvider(outputSourceJars, concat(runtimeDeps, exports, deps)))
.javaRuleOutputs(outputJarsBuilder.build())
.javaPluginInfo(mergeExportedJavaPluginInfo(exportedPlugins, exports))
.javaCcInfo(JavaCcInfoProvider.merge(transitiveNativeLibraries))
.javaModuleFlags(
createJavaModuleFlagsProvider(addExports, addOpens, concat(runtimeDeps, exports, deps)))
.setNeverlink(neverlink)
.build();
}
private static List<String> tokenize(List<String> input) throws EvalException {
List<String> output = new ArrayList<>();
for (String token : input) {
try {
ShellUtils.tokenize(output, token);
} catch (ShellUtils.TokenizationException e) {
throw Starlark.errorf("%s", e.getMessage());
}
}
return output;
}
/**
* Converts the supplied string to the appropriate {@link StrictDepsMode} enum instance.
*
* @param strictDepsMode the value to convert
*/
static StrictDepsMode getStrictDepsMode(String strictDepsMode) {
switch (strictDepsMode) {
case "OFF":
return StrictDepsMode.OFF;
case "ERROR":
case "DEFAULT":
return StrictDepsMode.ERROR;
case "WARN":
return StrictDepsMode.WARN;
default:
throw new IllegalArgumentException(
"StrictDepsMode "
+ strictDepsMode
+ " not allowed."
+ " Only OFF and ERROR values are accepted.");
}
}
private static Artifact getDerivedSourceJar(
ActionConstructionContext context, Artifact outputJar) {
return JavaCompilationHelper.derivedArtifact(context, outputJar, "", "-src.jar");
}
}