| // 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 com.google.auto.value.AutoValue; |
| import com.google.common.collect.ImmutableList; |
| import com.google.devtools.build.lib.actions.Artifact; |
| import com.google.devtools.build.lib.analysis.FileProvider; |
| import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; |
| import com.google.devtools.build.lib.analysis.TransitiveInfoProvider; |
| 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.concurrent.ThreadSafety.Immutable; |
| import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; |
| import com.google.devtools.build.lib.util.FileType; |
| import java.util.Collection; |
| import java.util.Iterator; |
| |
| /** A collection of recursively collected Java build information. */ |
| @AutoValue |
| @Immutable |
| @AutoCodec |
| public abstract class JavaCompilationArgsProvider implements TransitiveInfoProvider { |
| |
| @AutoCodec |
| public static final JavaCompilationArgsProvider EMPTY = |
| create( |
| NestedSetBuilder.create(Order.NAIVE_LINK_ORDER), |
| NestedSetBuilder.create(Order.NAIVE_LINK_ORDER), |
| NestedSetBuilder.create(Order.NAIVE_LINK_ORDER), |
| NestedSetBuilder.create(Order.NAIVE_LINK_ORDER), |
| NestedSetBuilder.create(Order.NAIVE_LINK_ORDER), |
| NestedSetBuilder.create(Order.NAIVE_LINK_ORDER)); |
| |
| @AutoCodec.Instantiator |
| public static JavaCompilationArgsProvider create( |
| NestedSet<Artifact> runtimeJars, |
| NestedSet<Artifact> directCompileTimeJars, |
| NestedSet<Artifact> transitiveCompileTimeJars, |
| NestedSet<Artifact> directFullCompileTimeJars, |
| NestedSet<Artifact> transitiveFullCompileTimeJars, |
| NestedSet<Artifact> compileTimeJavaDependencyArtifacts) { |
| return new AutoValue_JavaCompilationArgsProvider( |
| runtimeJars, |
| directCompileTimeJars, |
| transitiveCompileTimeJars, |
| directFullCompileTimeJars, |
| transitiveFullCompileTimeJars, |
| compileTimeJavaDependencyArtifacts); |
| } |
| |
| /** Returns recursively collected runtime jars. */ |
| public abstract NestedSet<Artifact> getRuntimeJars(); |
| |
| /** |
| * Returns non-recursively collected compile-time jars. This is the set of jars that compilations |
| * are permitted to reference with Strict Java Deps enabled. |
| * |
| * <p>If you're reading this, you probably want {@link #getTransitiveCompileTimeJars}. |
| */ |
| public abstract NestedSet<Artifact> getDirectCompileTimeJars(); |
| |
| /** |
| * Returns recursively collected compile-time jars. This is the compile-time classpath passed to |
| * the compiler. |
| */ |
| public abstract NestedSet<Artifact> getTransitiveCompileTimeJars(); |
| |
| /** |
| * Returns non-recursively collected, non-interface compile-time jars. |
| * |
| * <p>If you're reading this, you probably want {@link #getTransitiveCompileTimeJars}. |
| */ |
| public abstract NestedSet<Artifact> getDirectFullCompileTimeJars(); |
| |
| /** |
| * Returns recursively collected, non-interface compile-time jars. |
| * |
| * <p>If you're reading this, you probably want {@link #getTransitiveCompileTimeJars}. |
| */ |
| public abstract NestedSet<Artifact> getTransitiveFullCompileTimeJars(); |
| |
| /** |
| * Returns non-recursively collected Java dependency artifacts for computing a restricted |
| * classpath when building this target (called when strict_java_deps = 1). |
| * |
| * <p>Note that dependency artifacts are needed only when non-recursive compilation args do not |
| * provide a safe super-set of dependencies. Non-strict targets such as proto_library, always |
| * collecting their transitive closure of deps, do not need to provide dependency artifacts. |
| */ |
| public abstract NestedSet<Artifact> getCompileTimeJavaDependencyArtifacts(); |
| |
| /** |
| * Returns a {@link JavaCompilationArgsProvider} for the given {@link TransitiveInfoCollection}s. |
| * |
| * <p>If the given targets have a {@link JavaCompilationArgsProvider}, the information from that |
| * provider will be returned. Otherwise, any jar files provided by the targets will be wrapped in |
| * the returned provider. |
| * |
| * @deprecated The handling of raw jar files is present for legacy compatibility. All new |
| * Java-based rules should require their dependencies to provide {@link |
| * JavaCompilationArgsProvider}, and that precompiled jar files be wrapped in {@code |
| * java_import}. New rules should not use this method, and existing rules should be cleaned up |
| * to disallow jar files in their deps. |
| */ |
| // TODO(b/11285003): disallow jar files in deps, require java_import instead |
| @Deprecated |
| public static JavaCompilationArgsProvider legacyFromTargets( |
| Iterable<? extends TransitiveInfoCollection> infos) { |
| Builder argsBuilder = builder(); |
| for (TransitiveInfoCollection info : infos) { |
| JavaCompilationArgsProvider provider = null; |
| |
| JavaStrictCompilationArgsProvider strictCompilationArgsProvider = |
| JavaInfo.getProvider(JavaStrictCompilationArgsProvider.class, info); |
| if (strictCompilationArgsProvider != null) { |
| provider = strictCompilationArgsProvider.getJavaCompilationArgsProvider(); |
| } |
| if (provider == null) { |
| provider = JavaInfo.getProvider(JavaCompilationArgsProvider.class, info); |
| } |
| if (provider != null) { |
| argsBuilder.addExports(provider); |
| } else { |
| NestedSet<Artifact> filesToBuild = info.getProvider(FileProvider.class).getFilesToBuild(); |
| for (Artifact jar : FileType.filter(filesToBuild.toList(), JavaSemantics.JAR)) { |
| argsBuilder |
| .addRuntimeJar(jar) |
| .addDirectCompileTimeJar(/* interfaceJar= */ jar, /* fullJar= */ jar); |
| } |
| } |
| } |
| return argsBuilder.build(); |
| } |
| |
| /** Enum to specify transitive compilation args traversal */ |
| public enum ClasspathType { |
| /* treat the same for compile time and runtime */ |
| BOTH, |
| |
| /* Only include on compile classpath */ |
| COMPILE_ONLY, |
| |
| /* Only include on runtime classpath */ |
| RUNTIME_ONLY |
| } |
| |
| /** |
| * Disable strict deps enforcement for the given {@link JavaCompilationArgsProvider}; the direct |
| * jars in the result include the full transitive compile-time classpath from the input. |
| */ |
| public static JavaCompilationArgsProvider makeNonStrict(JavaCompilationArgsProvider args) { |
| // Omit jdeps, which aren't available transitively and aren't useful for reduced classpath |
| // pruning for non-strict targets: the direct classpath and transitive classpath are the same, |
| // so there's nothing to prune, and reading jdeps at compile-time isn't free. |
| return builder() |
| .addDirectCompileTimeJars( |
| /* interfaceJars= */ args.getTransitiveCompileTimeJars(), |
| /* fullJars= */ args.getTransitiveFullCompileTimeJars()) |
| .addRuntimeJars(args.getRuntimeJars()) |
| .build(); |
| } |
| |
| /** |
| * Returns a {@link JavaCompilationArgsProvider} that forwards the union of information from the |
| * inputs. Direct deps of the inputs are merged into the direct deps of the outputs. |
| * |
| * <p>This is morally equivalent to an exports-only {@code java_import} rule that forwards some |
| * dependencies. |
| */ |
| public static JavaCompilationArgsProvider merge(Iterable<JavaCompilationArgsProvider> providers) { |
| Iterator<JavaCompilationArgsProvider> it = providers.iterator(); |
| if (!it.hasNext()) { |
| return EMPTY; |
| } |
| JavaCompilationArgsProvider first = it.next(); |
| if (!it.hasNext()) { |
| return first; |
| } |
| Builder javaCompilationArgs = builder(); |
| javaCompilationArgs.addExports(first); |
| do { |
| javaCompilationArgs.addExports(it.next()); |
| } while (it.hasNext()); |
| return javaCompilationArgs.build(); |
| } |
| |
| /** |
| * Returns a {@link JavaCompilationArgsProvider} that forwards the union of information from the |
| * inputs, see {@link #merge(Collection<JavaCompilationArgsProvider>)}. |
| */ |
| public static JavaCompilationArgsProvider merge(JavaCompilationArgsProvider... providers) { |
| return merge(ImmutableList.copyOf(providers)); |
| } |
| |
| /** Returns a new builder instance. */ |
| public static final Builder builder() { |
| return new Builder(); |
| } |
| |
| /** A {@link JavaCompilationArgsProvider}Builder. */ |
| public static final class Builder { |
| private final NestedSetBuilder<Artifact> runtimeJarsBuilder = NestedSetBuilder.naiveLinkOrder(); |
| private final NestedSetBuilder<Artifact> directCompileTimeJarsBuilder = |
| NestedSetBuilder.naiveLinkOrder(); |
| private final NestedSetBuilder<Artifact> transitiveCompileTimeJarsBuilder = |
| NestedSetBuilder.naiveLinkOrder(); |
| private final NestedSetBuilder<Artifact> directFullCompileTimeJarsBuilder = |
| NestedSetBuilder.naiveLinkOrder(); |
| private final NestedSetBuilder<Artifact> transitiveFullCompileTimeJarsBuilder = |
| NestedSetBuilder.naiveLinkOrder(); |
| private final NestedSetBuilder<Artifact> compileTimeJavaDependencyArtifactsBuilder = |
| NestedSetBuilder.naiveLinkOrder(); |
| |
| /** Use {@code TransitiveJavaCompilationArgs#builder()} to instantiate the builder. */ |
| private Builder() {} |
| |
| /** |
| * Legacy method for dealing with objects which construct {@link JavaCompilationArtifacts} |
| * objects. |
| */ |
| // TODO(bazel-team): Remove when we get rid of JavaCompilationArtifacts. |
| public Builder merge(JavaCompilationArtifacts other, boolean isNeverLink) { |
| if (!isNeverLink) { |
| addRuntimeJars(NestedSetBuilder.wrap(Order.NAIVE_LINK_ORDER, other.getRuntimeJars())); |
| } |
| addDirectCompileTimeJars( |
| /* interfaceJars= */ NestedSetBuilder.wrap( |
| Order.NAIVE_LINK_ORDER, other.getCompileTimeJars()), |
| /* fullJars= */ NestedSetBuilder.wrap( |
| Order.NAIVE_LINK_ORDER, other.getFullCompileTimeJars())); |
| return this; |
| } |
| |
| /** |
| * Legacy method for dealing with objects which construct {@link JavaCompilationArtifacts} |
| * objects. |
| */ |
| public Builder merge(JavaCompilationArtifacts other) { |
| return merge(other, /* isNeverLink= */ false); |
| } |
| |
| public Builder addRuntimeJar(Artifact runtimeJar) { |
| this.runtimeJarsBuilder.add(runtimeJar); |
| return this; |
| } |
| |
| public Builder addRuntimeJars(NestedSet<Artifact> runtimeJars) { |
| this.runtimeJarsBuilder.addTransitive(runtimeJars); |
| return this; |
| } |
| |
| /** Adds a pair of direct interface and implementation jars. */ |
| public Builder addDirectCompileTimeJar(Artifact interfaceJar, Artifact fullJar) { |
| this.directCompileTimeJarsBuilder.add(interfaceJar); |
| this.transitiveCompileTimeJarsBuilder.add(interfaceJar); |
| this.directFullCompileTimeJarsBuilder.add(fullJar); |
| this.transitiveFullCompileTimeJarsBuilder.add(fullJar); |
| return this; |
| } |
| |
| /** Adds paired sets of direct interface and implementation jars. */ |
| public Builder addDirectCompileTimeJars( |
| NestedSet<Artifact> interfaceJars, NestedSet<Artifact> fullJars) { |
| this.directCompileTimeJarsBuilder.addTransitive(interfaceJars); |
| this.transitiveCompileTimeJarsBuilder.addTransitive(interfaceJars); |
| this.directFullCompileTimeJarsBuilder.addTransitive(fullJars); |
| this.transitiveFullCompileTimeJarsBuilder.addTransitive(fullJars); |
| return this; |
| } |
| |
| /** |
| * Adds transitive interface compile-time jars. |
| * |
| * @deprecated this is necessary to support java_common.create_provider, which is also |
| * deprecated. It allows creating providers where the direct compile-time jars aren't a |
| * subset of the transitive jars, and it doesn't provide a way to associate the 'full' jars. |
| */ |
| @Deprecated |
| public Builder addTransitiveCompileTimeJars(NestedSet<Artifact> transitiveCompileTimeJars) { |
| this.transitiveCompileTimeJarsBuilder.addTransitive(transitiveCompileTimeJars); |
| return this; |
| } |
| |
| public Builder addCompileTimeJavaDependencyArtifacts( |
| NestedSet<Artifact> compileTimeJavaDependencyArtifacts) { |
| this.compileTimeJavaDependencyArtifactsBuilder.addTransitive( |
| compileTimeJavaDependencyArtifacts); |
| return this; |
| } |
| |
| /** |
| * Add the {@link JavaCompilationArgsProvider} for a dependency with export-like semantics; see |
| * also {@link #addExports(JavaCompilationArgsProvider, ClasspathType)}. |
| */ |
| public Builder addExports(JavaCompilationArgsProvider args) { |
| return addExports(args, ClasspathType.BOTH); |
| } |
| |
| /** |
| * Add the {@link JavaCompilationArgsProvider} for a dependency with export-like semantics: |
| * direct jars of the input are direct jars of the output. |
| * |
| * @param type of jars to collect; use {@link ClasspathType#RUNTIME_ONLY} for neverlink |
| */ |
| public Builder addExports(JavaCompilationArgsProvider args, ClasspathType type) { |
| return addArgs(args, type, true); |
| } |
| |
| /** |
| * Add the {@link JavaCompilationArgsProvider} for a dependency with dep-like semantics; see |
| * also {@link #addDeps(JavaCompilationArgsProvider, ClasspathType)}. |
| */ |
| public Builder addDeps(JavaCompilationArgsProvider args) { |
| return addDeps(args, ClasspathType.BOTH); |
| } |
| |
| /* |
| * Add the {@link JavaCompilationArgsProvider} for a dependency with dep-like semantics: |
| * direct jars of the input are <em>not</em> direct jars of the output. |
| |
| * @param type of jars to collect; use {@link ClasspathType#RUNTIME} for neverlink |
| */ |
| public Builder addDeps(JavaCompilationArgsProvider args, ClasspathType type) { |
| return addArgs(args, type, false); |
| } |
| |
| /** |
| * Includes the contents of another instance of {@link JavaCompilationArgsProvider}. |
| * |
| * @param args the {@link JavaCompilationArgsProvider} instance |
| * @param type the classpath(s) to consider |
| */ |
| private Builder addArgs( |
| JavaCompilationArgsProvider args, ClasspathType type, boolean recursive) { |
| if (!ClasspathType.RUNTIME_ONLY.equals(type)) { |
| if (recursive) { |
| directCompileTimeJarsBuilder.addTransitive(args.getDirectCompileTimeJars()); |
| directFullCompileTimeJarsBuilder.addTransitive(args.getDirectFullCompileTimeJars()); |
| compileTimeJavaDependencyArtifactsBuilder.addTransitive( |
| args.getCompileTimeJavaDependencyArtifacts()); |
| } |
| transitiveCompileTimeJarsBuilder.addTransitive(args.getTransitiveCompileTimeJars()); |
| transitiveFullCompileTimeJarsBuilder.addTransitive(args.getTransitiveFullCompileTimeJars()); |
| } |
| if (!ClasspathType.COMPILE_ONLY.equals(type)) { |
| runtimeJarsBuilder.addTransitive(args.getRuntimeJars()); |
| } |
| return this; |
| } |
| |
| /** Builds a {@link JavaCompilationArgsProvider}. */ |
| public JavaCompilationArgsProvider build() { |
| if (runtimeJarsBuilder.isEmpty() |
| && directCompileTimeJarsBuilder.isEmpty() |
| && transitiveCompileTimeJarsBuilder.isEmpty() |
| && directFullCompileTimeJarsBuilder.isEmpty() |
| && transitiveFullCompileTimeJarsBuilder.isEmpty() |
| && compileTimeJavaDependencyArtifactsBuilder.isEmpty()) { |
| return EMPTY; |
| } |
| return create( |
| runtimeJarsBuilder.build(), |
| directCompileTimeJarsBuilder.build(), |
| transitiveCompileTimeJarsBuilder.build(), |
| directFullCompileTimeJarsBuilder.build(), |
| transitiveFullCompileTimeJarsBuilder.build(), |
| compileTimeJavaDependencyArtifactsBuilder.build()); |
| } |
| } |
| } |