// 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.collect.Iterables.getOnlyElement; 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; /** 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), NestedSetBuilder.create(Order.NAIVE_LINK_ORDER)); @AutoCodec.Instantiator public static JavaCompilationArgsProvider create( NestedSet runtimeJars, NestedSet directCompileTimeJars, NestedSet transitiveCompileTimeJars, NestedSet directFullCompileTimeJars, NestedSet transitiveFullCompileTimeJars, NestedSet instrumentationMetadata, NestedSet compileTimeJavaDependencyArtifacts) { return new AutoValue_JavaCompilationArgsProvider( runtimeJars, directCompileTimeJars, transitiveCompileTimeJars, directFullCompileTimeJars, transitiveFullCompileTimeJars, instrumentationMetadata, compileTimeJavaDependencyArtifacts); } /** Returns recursively collected runtime jars. */ public abstract NestedSet 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. * *

If you're reading this, you probably want {@link #getTransitiveCompileTimeJars}. */ public abstract NestedSet getDirectCompileTimeJars(); /** * Returns recursively collected compile-time jars. This is the compile-time classpath passed to * the compiler. */ public abstract NestedSet getTransitiveCompileTimeJars(); /** * Returns non-recursively collected, non-interface compile-time jars. * *

If you're reading this, you probably want {@link #getTransitiveCompileTimeJars}. */ public abstract NestedSet getDirectFullCompileTimeJars(); /** * Returns recursively collected, non-interface compile-time jars. * *

If you're reading this, you probably want {@link #getTransitiveCompileTimeJars}. */ public abstract NestedSet getTransitiveFullCompileTimeJars(); /** Returns recursively collected instrumentation metadata. */ public abstract NestedSet getInstrumentationMetadata(); /** * Returns non-recursively collected Java dependency artifacts for * computing a restricted classpath when building this target (called when * strict_java_deps = 1). * *

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 getCompileTimeJavaDependencyArtifacts(); /** * Returns a {@link JavaCompilationArgsProvider} for the given {@link TransitiveInfoCollection}s. * *

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 infos) { return legacyFromTargets(infos, /* javaProtoLibraryStrictDeps= */ false); } @Deprecated public static JavaCompilationArgsProvider legacyFromTargets( Iterable infos, boolean javaProtoLibraryStrictDeps) { Builder argsBuilder = builder(); for (TransitiveInfoCollection info : infos) { JavaCompilationArgsProvider provider = null; if (javaProtoLibraryStrictDeps) { 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 filesToBuild = info.getProvider(FileProvider.class).getFilesToBuild(); for (Artifact jar : FileType.filter(filesToBuild, 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()) .addInstrumentationMetadata(args.getInstrumentationMetadata()) .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. * *

This is moralley equivalent to an exports-only {@code java_import} rule that forwards some * dependencies. */ public static JavaCompilationArgsProvider merge( Collection providers) { if (providers.size() == 1) { return getOnlyElement(providers); } Builder javaCompilationArgs = builder(); for (JavaCompilationArgsProvider provider : providers) { javaCompilationArgs.addExports(provider); } return javaCompilationArgs.build(); } /** * Returns a {@link JavaCompilationArgsProvider} that forwards the union of information from the * inputs, see {@link #merge(Collection)}. */ 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 runtimeJarsBuilder = NestedSetBuilder.naiveLinkOrder(); private final NestedSetBuilder directCompileTimeJarsBuilder = NestedSetBuilder.naiveLinkOrder(); private final NestedSetBuilder transitiveCompileTimeJarsBuilder = NestedSetBuilder.naiveLinkOrder(); private final NestedSetBuilder directFullCompileTimeJarsBuilder = NestedSetBuilder.naiveLinkOrder(); private final NestedSetBuilder transitiveFullCompileTimeJarsBuilder = NestedSetBuilder.naiveLinkOrder(); private final NestedSetBuilder instrumentationMetadataBuilder = NestedSetBuilder.naiveLinkOrder(); private final NestedSetBuilder 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())); addInstrumentationMetadata( NestedSetBuilder.wrap(Order.NAIVE_LINK_ORDER, other.getInstrumentationMetadata())); 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 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 interfaceJars, NestedSet 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 transitiveCompileTimeJars) { this.transitiveCompileTimeJarsBuilder.addTransitive(transitiveCompileTimeJars); return this; } public Builder addInstrumentationMetadata(Artifact instrumentationMetadata) { this.instrumentationMetadataBuilder.add(instrumentationMetadata); return this; } public Builder addInstrumentationMetadata(NestedSet instrumentationMetadata) { this.instrumentationMetadataBuilder.addTransitive(instrumentationMetadata); return this; } public Builder addCompileTimeJavaDependencyArtifacts( NestedSet 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 not 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()); } instrumentationMetadataBuilder.addTransitive(args.getInstrumentationMetadata()); return this; } /** Builds a {@link JavaCompilationArgsProvider}. */ public JavaCompilationArgsProvider build() { if (runtimeJarsBuilder.isEmpty() && directCompileTimeJarsBuilder.isEmpty() && transitiveCompileTimeJarsBuilder.isEmpty() && directFullCompileTimeJarsBuilder.isEmpty() && transitiveFullCompileTimeJarsBuilder.isEmpty() && instrumentationMetadataBuilder.isEmpty() && compileTimeJavaDependencyArtifactsBuilder.isEmpty()) { return EMPTY; } return create( runtimeJarsBuilder.build(), directCompileTimeJarsBuilder.build(), transitiveCompileTimeJarsBuilder.build(), directFullCompileTimeJarsBuilder.build(), transitiveFullCompileTimeJarsBuilder.build(), instrumentationMetadataBuilder.build(), compileTimeJavaDependencyArtifactsBuilder.build()); } } }