| // 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.common.base.Preconditions.checkNotNull; |
| import static com.google.common.base.Preconditions.checkState; |
| import static com.google.devtools.build.lib.rules.java.JavaCommon.collectJavaCompilationArgs; |
| |
| import com.google.common.base.Preconditions; |
| 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.RuleContext; |
| import com.google.devtools.build.lib.analysis.config.CoreOptionConverters.StrictDepsMode; |
| import com.google.devtools.build.lib.rules.java.JavaConfiguration.JavaClasspathMode; |
| import com.google.devtools.build.lib.rules.java.JavaRuleOutputJarsProvider.OutputJar; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import javax.annotation.Nullable; |
| |
| /** |
| * A class to create Java compile actions in a way that is consistent with java_library. Rules that |
| * generate source files and emulate java_library on top of that should use this class instead of |
| * the lower-level API in JavaCompilationHelper. |
| * |
| * <p>Rules that want to use this class are required to have an implicit dependency on the Java |
| * compiler. |
| */ |
| public final class JavaLibraryHelper { |
| private final RuleContext ruleContext; |
| |
| private Artifact output; |
| private final List<Artifact> sourceJars = new ArrayList<>(); |
| private final List<Artifact> sourceFiles = new ArrayList<>(); |
| private final List<Artifact> resources = new ArrayList<>(); |
| |
| /** |
| * Contains all the dependencies; these are treated as both compile-time and runtime dependencies. |
| */ |
| private final List<JavaCompilationArgsProvider> deps = new ArrayList<>(); |
| |
| private final List<JavaCompilationArgsProvider> exports = new ArrayList<>(); |
| private JavaPluginInfoProvider plugins = JavaPluginInfoProvider.empty(); |
| private ImmutableList<String> javacOpts = ImmutableList.of(); |
| private ImmutableList<Artifact> sourcePathEntries = ImmutableList.of(); |
| private final List<Artifact> additionalOutputs = new ArrayList<>(); |
| |
| /** @see {@link #setCompilationStrictDepsMode}. */ |
| private StrictDepsMode strictDepsMode = StrictDepsMode.ERROR; |
| |
| private final JavaClasspathMode classpathMode; |
| private String injectingRuleKind; |
| private boolean neverlink; |
| |
| public JavaLibraryHelper(RuleContext ruleContext) { |
| this.ruleContext = ruleContext; |
| ruleContext.getConfiguration(); |
| this.classpathMode = ruleContext.getFragment(JavaConfiguration.class).getReduceJavaClasspath(); |
| } |
| |
| public JavaLibraryHelper setNeverlink(boolean neverlink) { |
| this.neverlink = neverlink; |
| return this; |
| } |
| |
| /** |
| * Sets the final output jar; if this is not set, then the {@link #build} method throws an {@link |
| * IllegalStateException}. Note that this class may generate not just the output itself, but also |
| * a number of additional intermediate files and outputs. |
| */ |
| public JavaLibraryHelper setOutput(Artifact output) { |
| this.output = output; |
| return this; |
| } |
| |
| /** Adds the given source jars. Any .java files in these jars will be compiled. */ |
| public JavaLibraryHelper addSourceJars(Iterable<Artifact> sourceJars) { |
| Iterables.addAll(this.sourceJars, sourceJars); |
| return this; |
| } |
| |
| /** Adds the given source jars. Any .java files in these jars will be compiled. */ |
| public JavaLibraryHelper addSourceJars(Artifact... sourceJars) { |
| return this.addSourceJars(Arrays.asList(sourceJars)); |
| } |
| |
| public JavaLibraryHelper addResources(Iterable<Artifact> resources) { |
| Iterables.addAll(this.resources, resources); |
| return this; |
| } |
| |
| public JavaLibraryHelper addDep(JavaCompilationArgsProvider provider) { |
| checkNotNull(provider); |
| this.deps.add(provider); |
| return this; |
| } |
| |
| /** Adds the given source files to be compiled. */ |
| public JavaLibraryHelper addSourceFiles(Iterable<Artifact> sourceFiles) { |
| Iterables.addAll(this.sourceFiles, sourceFiles); |
| return this; |
| } |
| |
| public JavaLibraryHelper addExport(JavaCompilationArgsProvider provider) { |
| exports.add(provider); |
| return this; |
| } |
| |
| public JavaLibraryHelper addAdditionalOutputs(Iterable<Artifact> outputs) { |
| Iterables.addAll(additionalOutputs, outputs); |
| return this; |
| } |
| |
| public JavaLibraryHelper setPlugins(JavaPluginInfoProvider plugins) { |
| checkNotNull(plugins, "plugins must not be null"); |
| checkState(this.plugins.isEmpty()); |
| this.plugins = plugins; |
| return this; |
| } |
| |
| /** Sets the compiler options. */ |
| public JavaLibraryHelper setJavacOpts(Iterable<String> javacOpts) { |
| this.javacOpts = ImmutableList.copyOf(javacOpts); |
| return this; |
| } |
| |
| public JavaLibraryHelper setSourcePathEntries(List<Artifact> sourcepathEntries) { |
| this.sourcePathEntries = ImmutableList.copyOf(sourcepathEntries); |
| return this; |
| } |
| |
| public JavaLibraryHelper setInjectingRuleKind(String injectingRuleKind) { |
| this.injectingRuleKind = injectingRuleKind; |
| return this; |
| } |
| |
| /** |
| * When in strict mode, compiling the source-jars passed to this JavaLibraryHelper will break if |
| * they depend on classes not in any of the {@link |
| * JavaCompilationArgsProvider#getDirectCompileTimeJars()} passed in {@link #addDep}, even if they |
| * do appear in {@link JavaCompilationArgsProvider#getTransitiveCompileTimeJars()}. That is, |
| * depending on a class requires a direct dependency on it. |
| * |
| * <p>Contrast this with the strictness-parameter to {@link #buildCompilationArgsProvider}, which |
| * controls whether others depending on the result of this compilation, can perform strict-deps |
| * checks at all. |
| * |
| * <p>Defaults to {@link StrictDepsMode#ERROR}. |
| */ |
| public JavaLibraryHelper setCompilationStrictDepsMode(StrictDepsMode strictDepsMode) { |
| this.strictDepsMode = strictDepsMode; |
| return this; |
| } |
| |
| /** |
| * Creates the compile actions (including the ones for ijar and source jar). Also fills in the |
| * {@link JavaRuleOutputJarsProvider.Builder} with the corresponding compilation outputs. |
| * |
| * @param semantics implementation specific java rules semantics |
| * @param javaToolchainProvider used for retrieving misc java tools |
| * @param hostJavabase the target of the host javabase used to retrieve the java executable and |
| * its necessary inputs |
| * @param outputJarsBuilder populated with the outputs of the created actions |
| * @param outputSourceJar if not-null, the output of an source jar action that will be created |
| */ |
| public JavaCompilationArtifacts build( |
| JavaSemantics semantics, |
| JavaToolchainProvider javaToolchainProvider, |
| JavaRuntimeInfo hostJavabase, |
| JavaRuleOutputJarsProvider.Builder outputJarsBuilder, |
| boolean createOutputSourceJar, |
| @Nullable Artifact outputSourceJar) |
| throws InterruptedException { |
| return build( |
| semantics, |
| javaToolchainProvider, |
| hostJavabase, |
| outputJarsBuilder, |
| createOutputSourceJar, |
| outputSourceJar, |
| /* javaInfoBuilder= */ null, |
| ImmutableList.of(), // ignored when javaInfoBuilder is null |
| ImmutableList.of()); |
| } |
| |
| public JavaCompilationArtifacts build( |
| JavaSemantics semantics, |
| JavaToolchainProvider javaToolchainProvider, |
| JavaRuntimeInfo hostJavabase, |
| JavaRuleOutputJarsProvider.Builder outputJarsBuilder, |
| boolean createOutputSourceJar, |
| @Nullable Artifact outputSourceJar, |
| @Nullable JavaInfo.Builder javaInfoBuilder, |
| Iterable<JavaGenJarsProvider> transitiveJavaGenJars, |
| ImmutableList<Artifact> additionalJavaBaseInputs) |
| throws InterruptedException { |
| |
| Preconditions.checkState(output != null, "must have an output file; use setOutput()"); |
| Preconditions.checkState( |
| !createOutputSourceJar || outputSourceJar != null, |
| "outputSourceJar cannot be null when createOutputSourceJar is true"); |
| |
| JavaTargetAttributes.Builder attributes = new JavaTargetAttributes.Builder(semantics); |
| attributes.addSourceJars(sourceJars); |
| attributes.addSourceFiles(sourceFiles); |
| addDepsToAttributes(attributes); |
| attributes.setStrictJavaDeps(strictDepsMode); |
| attributes.setTargetLabel(ruleContext.getLabel()); |
| attributes.setInjectingRuleKind(injectingRuleKind); |
| attributes.setSourcePath(sourcePathEntries); |
| JavaCommon.addPlugins(attributes, plugins); |
| attributes.addAdditionalOutputs(additionalOutputs); |
| |
| for (Artifact resource : resources) { |
| attributes.addResource( |
| JavaHelper.getJavaResourcePath(semantics, ruleContext, resource), resource); |
| } |
| |
| if (isStrict() && classpathMode != JavaClasspathMode.OFF) { |
| JavaCompilationHelper.addDependencyArtifactsToAttributes(attributes, deps); |
| } |
| |
| JavaCompilationArtifacts.Builder artifactsBuilder = new JavaCompilationArtifacts.Builder(); |
| JavaCompilationHelper helper = |
| new JavaCompilationHelper( |
| ruleContext, |
| semantics, |
| javacOpts, |
| attributes, |
| javaToolchainProvider, |
| hostJavabase, |
| additionalJavaBaseInputs); |
| JavaCompileOutputs<Artifact> outputs = helper.createOutputs(output); |
| artifactsBuilder.setCompileTimeDependencies(outputs.depsProto()); |
| helper.createCompileAction(outputs); |
| |
| Artifact iJar = null; |
| if (!sourceJars.isEmpty() || !sourceFiles.isEmpty()) { |
| artifactsBuilder.addRuntimeJar(output); |
| iJar = helper.createCompileTimeJarAction(output, artifactsBuilder); |
| } |
| |
| if (createOutputSourceJar) { |
| helper.createSourceJarAction( |
| outputSourceJar, outputs.genSource(), javaToolchainProvider, hostJavabase); |
| } |
| ImmutableList<Artifact> outputSourceJars = |
| outputSourceJar == null ? ImmutableList.of() : ImmutableList.of(outputSourceJar); |
| outputJarsBuilder |
| .addOutputJar(new OutputJar(output, iJar, outputs.manifestProto(), outputSourceJars)) |
| .setJdeps(outputs.depsProto()) |
| .setNativeHeaders(outputs.nativeHeader()); |
| |
| JavaCompilationArtifacts javaArtifacts = artifactsBuilder.build(); |
| if (javaInfoBuilder != null) { |
| ClasspathConfiguredFragment classpathFragment = |
| new ClasspathConfiguredFragment( |
| javaArtifacts, |
| attributes.build(), |
| neverlink, |
| JavaCompilationHelper.getBootClasspath(javaToolchainProvider)); |
| |
| javaInfoBuilder.addProvider( |
| JavaCompilationInfoProvider.class, |
| new JavaCompilationInfoProvider.Builder() |
| .setJavacOpts(javacOpts) |
| .setBootClasspath(classpathFragment.getBootClasspath()) |
| .setCompilationClasspath(classpathFragment.getCompileTimeClasspath()) |
| .setRuntimeClasspath(classpathFragment.getRuntimeClasspath()) |
| .build()); |
| |
| javaInfoBuilder.addProvider( |
| JavaGenJarsProvider.class, |
| createJavaGenJarsProvider( |
| helper, outputs.genClass(), outputs.genSource(), transitiveJavaGenJars)); |
| } |
| |
| return javaArtifacts; |
| } |
| |
| private JavaGenJarsProvider createJavaGenJarsProvider( |
| JavaCompilationHelper helper, |
| @Nullable Artifact genClassJar, |
| @Nullable Artifact genSourceJar, |
| Iterable<JavaGenJarsProvider> transitiveJavaGenJars) { |
| return JavaGenJarsProvider.create( |
| helper.usesAnnotationProcessing(), |
| genClassJar, |
| genSourceJar, |
| plugins, |
| transitiveJavaGenJars); |
| } |
| |
| /** |
| * Returns a JavaCompilationArgsProvider that fully encapsulates this compilation, based on the |
| * result of a call to build(). (that is, it contains the compile-time and runtime jars, separated |
| * by direct vs transitive jars). |
| * |
| * @param isReportedAsStrict if true, the result's direct JavaCompilationArgs only contain classes |
| * resulting from compiling the source-jars. If false, the direct JavaCompilationArgs contain |
| * both these classes, as well as any classes from transitive dependencies. A value of 'false' |
| * means this compilation cannot be checked for strict-deps, by any consumer (depending) |
| * compilation. Contrast this with {@link #setCompilationStrictDepsMode}. |
| */ |
| public JavaCompilationArgsProvider buildCompilationArgsProvider( |
| JavaCompilationArtifacts artifacts, boolean isReportedAsStrict, boolean isNeverlink) { |
| |
| JavaCompilationArgsProvider directArgs = |
| collectJavaCompilationArgs( |
| /* isNeverLink= */ isNeverlink, |
| /* srcLessDepsExport= */ false, |
| artifacts, |
| deps, |
| /* runtimeDeps= */ ImmutableList.of(), |
| exports); |
| |
| if (!isReportedAsStrict) { |
| directArgs = JavaCompilationArgsProvider.makeNonStrict(directArgs); |
| } |
| return directArgs; |
| } |
| |
| private void addDepsToAttributes(JavaTargetAttributes.Builder attributes) { |
| JavaCompilationArgsProvider argsProvider = JavaCompilationArgsProvider.merge(deps); |
| |
| if (isStrict()) { |
| attributes.addDirectJars(argsProvider.getDirectCompileTimeJars()); |
| } |
| |
| attributes.addCompileTimeClassPathEntries(argsProvider.getTransitiveCompileTimeJars()); |
| attributes.addRuntimeClassPathEntries(argsProvider.getRuntimeJars()); |
| } |
| |
| private boolean isStrict() { |
| return strictDepsMode != StrictDepsMode.OFF; |
| } |
| } |