// 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.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.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.util.FileType;
import java.util.Collection;
/** A container of Java compilation artifacts. */
@AutoValue
public abstract class JavaCompilationArgs {
// TODO(bazel-team): It would be desirable to use LinkOrderNestedSet here so that
// parents-before-deps is preserved for graphs that are not trees. However, the legacy
// JavaLibraryCollector implemented naive link ordering and many targets in the
// depot depend on the consistency of left-to-right ordering that is not provided by
// LinkOrderNestedSet. They simply list their local dependencies before
// other targets that may use conflicting dependencies, and the local deps
// appear earlier on the classpath, as desired. Behavior of LinkOrderNestedSet
// can be very unintuitive in case of conflicting orders, because the order is
// decided by the rightmost branch in such cases. For example, if A depends on {junit4,
// B}, B depends on {C, D}, C depends on {junit3}, and D depends on {junit4},
// the classpath of A will have junit3 before junit4.
public static final JavaCompilationArgs EMPTY_ARGS =
JavaCompilationArgs.create(
NestedSetBuilder.<Artifact>create(Order.NAIVE_LINK_ORDER),
NestedSetBuilder.<Artifact>create(Order.NAIVE_LINK_ORDER),
NestedSetBuilder.<Artifact>create(Order.NAIVE_LINK_ORDER));
private static JavaCompilationArgs create(
NestedSet<Artifact> runtimeJars,
NestedSet<Artifact> compileTimeJars,
NestedSet<Artifact> instrumentationMetadata) {
return new AutoValue_JavaCompilationArgs(runtimeJars, compileTimeJars, instrumentationMetadata);
}
/** Returns transitive runtime jars. */
public abstract NestedSet<Artifact> getRuntimeJars();
/** Returns transitive compile-time jars. */
public abstract NestedSet<Artifact> getCompileTimeJars();
/** Returns transitive instrumentation metadata jars. */
public abstract NestedSet<Artifact> getInstrumentationMetadata();
/**
* Returns a new builder instance.
*/
public static final Builder builder() {
return new Builder();
}
/**
* Builder for {@link JavaCompilationArgs}.
*/
public static final class Builder {
private final NestedSetBuilder<Artifact> runtimeJarsBuilder =
NestedSetBuilder.naiveLinkOrder();
private final NestedSetBuilder<Artifact> compileTimeJarsBuilder =
NestedSetBuilder.naiveLinkOrder();
private final NestedSetBuilder<Artifact> instrumentationMetadataBuilder =
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(other.getRuntimeJars());
}
addCompileTimeJars(other.getCompileTimeJars());
addInstrumentationMetadata(other.getInstrumentationMetadata());
return this;
}
/**
* Legacy method for dealing with objects which construct
* {@link JavaCompilationArtifacts} objects.
*/
public Builder merge(JavaCompilationArtifacts other) {
return merge(other, false);
}
public Builder addRuntimeJar(Artifact runtimeJar) {
this.runtimeJarsBuilder.add(runtimeJar);
return this;
}
public Builder addRuntimeJars(Iterable<Artifact> runtimeJars) {
this.runtimeJarsBuilder.addAll(runtimeJars);
return this;
}
public Builder addTransitiveRuntimeJars(NestedSet<Artifact> runtimeJars) {
this.runtimeJarsBuilder.addTransitive(runtimeJars);
return this;
}
public Builder addCompileTimeJar(Artifact compileTimeJar) {
this.compileTimeJarsBuilder.add(compileTimeJar);
return this;
}
public Builder addCompileTimeJars(Iterable<Artifact> compileTimeJars) {
this.compileTimeJarsBuilder.addAll(compileTimeJars);
return this;
}
public Builder addTransitiveCompileTimeJars(NestedSet<Artifact> compileTimeJars) {
this.compileTimeJarsBuilder.addTransitive(compileTimeJars);
return this;
}
public Builder addInstrumentationMetadata(Artifact instrumentationMetadata) {
this.instrumentationMetadataBuilder.add(instrumentationMetadata);
return this;
}
public Builder addInstrumentationMetadata(Collection<Artifact> instrumentationMetadata) {
this.instrumentationMetadataBuilder.addAll(instrumentationMetadata);
return this;
}
public Builder addTransitiveCompilationArgs(
JavaCompilationArgsProvider dep, boolean recursive, ClasspathType type) {
JavaCompilationArgs args = recursive
? dep.getRecursiveJavaCompilationArgs()
: dep.getJavaCompilationArgs();
addTransitiveArgs(args, type);
return this;
}
/**
* Merges the artifacts of another target.
*/
public Builder addTransitiveTarget(TransitiveInfoCollection dep, boolean recursive,
ClasspathType type) {
JavaCompilationArgsProvider provider =
JavaProvider.getProvider(JavaCompilationArgsProvider.class, dep);
if (provider != null) {
addTransitiveCompilationArgs(provider, recursive, type);
return this;
} else {
NestedSet<Artifact> filesToBuild =
dep.getProvider(FileProvider.class).getFilesToBuild();
for (Artifact jar : FileType.filter(filesToBuild, JavaSemantics.JAR)) {
addCompileTimeJar(jar);
addRuntimeJar(jar);
}
}
return this;
}
/**
* Merges the artifacts of a collection of targets.
*/
public Builder addTransitiveTargets(Iterable<? extends TransitiveInfoCollection> deps,
boolean recursive, ClasspathType type) {
for (TransitiveInfoCollection dep : deps) {
addTransitiveTarget(dep, recursive, type);
}
return this;
}
/**
* Merges the artifacts of a collection of targets.
*/
public Builder addTransitiveTargets(Iterable<? extends TransitiveInfoCollection> deps,
boolean recursive) {
return addTransitiveTargets(deps, recursive, ClasspathType.BOTH);
}
/**
* Merges the artifacts of a collection of targets.
*/
public Builder addTransitiveDependencies(Iterable<JavaCompilationArgsProvider> deps,
boolean recursive) {
for (JavaCompilationArgsProvider dep : deps) {
addTransitiveDependency(dep, recursive, ClasspathType.BOTH);
}
return this;
}
/**
* Merges the artifacts of another target.
*/
private Builder addTransitiveDependency(JavaCompilationArgsProvider dep, boolean recursive,
ClasspathType type) {
JavaCompilationArgs args = recursive
? dep.getRecursiveJavaCompilationArgs()
: dep.getJavaCompilationArgs();
addTransitiveArgs(args, type);
return this;
}
/**
* Merges the artifacts of a collection of targets.
*/
public Builder addTransitiveTargets(Iterable<? extends TransitiveInfoCollection> deps) {
return addTransitiveTargets(deps, /*recursive=*/true, ClasspathType.BOTH);
}
/**
* Includes the contents of another instance of JavaCompilationArgs.
*
* @param args the JavaCompilationArgs instance
* @param type the classpath(s) to consider
*/
public Builder addTransitiveArgs(JavaCompilationArgs args, ClasspathType type) {
if (!ClasspathType.RUNTIME_ONLY.equals(type)) {
compileTimeJarsBuilder.addTransitive(args.getCompileTimeJars());
}
if (!ClasspathType.COMPILE_ONLY.equals(type)) {
runtimeJarsBuilder.addTransitive(args.getRuntimeJars());
}
instrumentationMetadataBuilder.addTransitive(
args.getInstrumentationMetadata());
return this;
}
/**
* Builds a {@link JavaCompilationArgs} object.
*/
public JavaCompilationArgs build() {
return JavaCompilationArgs.create(
runtimeJarsBuilder.build(),
compileTimeJarsBuilder.build(),
instrumentationMetadataBuilder.build());
}
}
/**
* Enum to specify transitive compilation args traversal
*/
public static 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;
}
JavaCompilationArgs() {}
}