blob: f1775189a357a269b455407caf5716dc337ea1db [file] [log] [blame]
// 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.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.rules.cpp.CppFileTypes;
import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* An object that captures the temporary state we need to pass around while
* the initialization hook for a java rule is running.
*/
public class JavaTargetAttributes {
private static void checkJar(Artifact classPathEntry) {
if (!JavaSemantics.JAR.matches(classPathEntry.getFilename())) {
throw new IllegalArgumentException(
"not a jar file: " + classPathEntry.prettyPrint());
}
}
/**
* A builder class for JavaTargetAttributes.
*/
public static class Builder {
// The order of source files is important, and there must not be duplicates.
// Unfortunately, there is no interface in Java that represents a collection
// without duplicates that has a stable and deterministic iteration order,
// but is not sorted according to a property of the elements. Thus we are
// stuck with Set.
private final Set<Artifact> sourceFiles = new LinkedHashSet<>();
private final Set<Artifact> compileTimeJarFiles = new LinkedHashSet<>();
private final NestedSetBuilder<Artifact> runtimeClassPath =
NestedSetBuilder.naiveLinkOrder();
private final NestedSetBuilder<Artifact> compileTimeClassPath =
NestedSetBuilder.naiveLinkOrder();
private final List<Artifact> bootClassPath = new ArrayList<>();
private final List<Artifact> nativeLibraries = new ArrayList<>();
private final Set<Artifact> processorPath = new LinkedHashSet<>();
// Classpath directories can't be represented as artifacts (TreeArtifact isn't appropriate
// here since all we need is a path string to apply to the command line).
private final Set<PathFragment> processorPathDirs = new LinkedHashSet<>();
private final Set<String> processorNames = new LinkedHashSet<>();
private final Set<Artifact> apiGeneratingProcessorPath = new LinkedHashSet<>();
private final Set<String> apiGeneratingProcessorNames = new LinkedHashSet<>();
private final Map<PathFragment, Artifact> resources = new LinkedHashMap<>();
private final NestedSetBuilder<Artifact> resourceJars = NestedSetBuilder.stableOrder();
private final List<Artifact> messages = new ArrayList<>();
private final List<Artifact> instrumentationMetadata = new ArrayList<>();
private final List<Artifact> sourceJars = new ArrayList<>();
private final List<Artifact> classPathResources = new ArrayList<>();
private final Set<Artifact> additionalOutputs = new LinkedHashSet<>();
private BuildConfiguration.StrictDepsMode strictJavaDeps =
BuildConfiguration.StrictDepsMode.OFF;
private final NestedSetBuilder<Artifact> directJars = NestedSetBuilder.naiveLinkOrder();
private final List<Artifact> compileTimeDependencyArtifacts = new ArrayList<>();
private String ruleKind;
private Label targetLabel;
private final NestedSetBuilder<Artifact> excludedArtifacts =
NestedSetBuilder.naiveLinkOrder();
private boolean built = false;
private final JavaSemantics semantics;
public Builder(JavaSemantics semantics) {
this.semantics = semantics;
}
public Builder addSourceArtifacts(Iterable<Artifact> sourceArtifacts) {
Preconditions.checkArgument(!built);
for (Artifact srcArtifact : sourceArtifacts) {
String srcFilename = srcArtifact.getExecPathString();
if (JavaSemantics.SOURCE_JAR.matches(srcFilename)) {
sourceJars.add(srcArtifact);
} else if (JavaSemantics.PROPERTIES.matches(srcFilename)) {
// output files of the message compiler
resources.put(
semantics.getDefaultJavaResourcePath(srcArtifact.getRootRelativePath()), srcArtifact);
} else if (JavaSemantics.JAVA_SOURCE.matches(srcFilename)) {
sourceFiles.add(srcArtifact);
} else {
// try specific cases from the semantics.
semantics.addArtifactToJavaTargetAttribute(this, srcArtifact);
}
}
return this;
}
public Builder addSourceFiles(Iterable<Artifact> sourceFiles) {
Preconditions.checkArgument(!built);
for (Artifact artifact : sourceFiles) {
if (JavaSemantics.JAVA_SOURCE.matches(artifact.getFilename())) {
this.sourceFiles.add(artifact);
}
}
return this;
}
public Builder merge(JavaCompilationArgs context) {
Preconditions.checkArgument(!built);
addCompileTimeClassPathEntries(context.getCompileTimeJars());
addRuntimeClassPathEntries(context.getRuntimeJars());
addInstrumentationMetadataEntries(context.getInstrumentationMetadata());
return this;
}
public Builder addSourceJars(Collection<Artifact> sourceJars) {
Preconditions.checkArgument(!built);
this.sourceJars.addAll(sourceJars);
return this;
}
public Builder addSourceJar(Artifact sourceJar) {
Preconditions.checkArgument(!built);
this.sourceJars.add(sourceJar);
return this;
}
public Builder addCompileTimeJarFiles(Iterable<Artifact> jars) {
Preconditions.checkArgument(!built);
Iterables.addAll(compileTimeJarFiles, jars);
return this;
}
public Builder addRuntimeClassPathEntry(Artifact classPathEntry) {
Preconditions.checkArgument(!built);
checkJar(classPathEntry);
runtimeClassPath.add(classPathEntry);
return this;
}
public Builder addRuntimeClassPathEntries(NestedSet<Artifact> classPathEntries) {
Preconditions.checkArgument(!built);
runtimeClassPath.addTransitive(classPathEntries);
return this;
}
public Builder addCompileTimeClassPathEntries(NestedSet<Artifact> entries) {
Preconditions.checkArgument(!built);
compileTimeClassPath.addTransitive(entries);
return this;
}
public Builder setRuleKind(String ruleKind) {
Preconditions.checkArgument(!built);
this.ruleKind = ruleKind;
return this;
}
public Builder setTargetLabel(Label targetLabel) {
Preconditions.checkArgument(!built);
this.targetLabel = targetLabel;
return this;
}
/**
* Sets the bootclasspath to be passed to the Java compiler.
*
* <p>If this method is called, then the bootclasspath specified in this JavaTargetAttributes
* instance overrides the default bootclasspath.
*/
public Builder setBootClassPath(List<Artifact> jars) {
Preconditions.checkArgument(!built);
Preconditions.checkArgument(!jars.isEmpty());
Preconditions.checkState(bootClassPath.isEmpty());
bootClassPath.addAll(jars);
return this;
}
public Builder addExcludedArtifacts(NestedSet<Artifact> toExclude) {
Preconditions.checkArgument(!built);
excludedArtifacts.addTransitive(toExclude);
return this;
}
/**
* Controls how strict the javac compiler will be in checking correct use of
* direct dependencies.
*
* @param strictDeps one of WARN, ERROR or OFF
*/
public Builder setStrictJavaDeps(BuildConfiguration.StrictDepsMode strictDeps) {
Preconditions.checkArgument(!built);
strictJavaDeps = strictDeps;
return this;
}
/**
* In tandem with strictJavaDeps, directJars represents a subset of the compile-time, classpath
* jars that were provided by direct dependencies. When strictJavaDeps is OFF, there is no need
* to provide directJars, and no extra information is passed to javac. When strictJavaDeps is
* set to WARN or ERROR, the compiler command line will include extra flags to indicate the
* warning/error policy and to map the classpath jars to direct or transitive dependencies,
* using the information in directJars. The extra flags are formatted like this (same for
* --indirect_dependency): <pre>
* --direct_dependency
* foo/bar/lib.jar
* //java/com/google/foo:bar
* </pre>
*/
public Builder addDirectJars(NestedSet<Artifact> directJars) {
Preconditions.checkArgument(!built);
this.directJars.addTransitive(directJars);
return this;
}
public Builder addCompileTimeDependencyArtifacts(Iterable<Artifact> dependencyArtifacts) {
Preconditions.checkArgument(!built);
Iterables.addAll(this.compileTimeDependencyArtifacts, dependencyArtifacts);
return this;
}
public Builder addInstrumentationMetadataEntries(Iterable<Artifact> metadataEntries) {
Preconditions.checkArgument(!built);
Iterables.addAll(instrumentationMetadata, metadataEntries);
return this;
}
public Builder addNativeLibrary(Artifact nativeLibrary) {
Preconditions.checkArgument(!built);
String name = nativeLibrary.getFilename();
if (CppFileTypes.INTERFACE_SHARED_LIBRARY.matches(name)) {
return this;
}
if (!(CppFileTypes.SHARED_LIBRARY.matches(name)
|| CppFileTypes.VERSIONED_SHARED_LIBRARY.matches(name))) {
throw new IllegalArgumentException("not a shared library :" + nativeLibrary.prettyPrint());
}
nativeLibraries.add(nativeLibrary);
return this;
}
public Builder addNativeLibraries(Iterable<Artifact> nativeLibraries) {
Preconditions.checkArgument(!built);
for (Artifact nativeLibrary : nativeLibraries) {
addNativeLibrary(nativeLibrary);
}
return this;
}
public Builder addMessages(Collection<Artifact> messages) {
Preconditions.checkArgument(!built);
this.messages.addAll(messages);
return this;
}
public Builder addMessage(Artifact messagesArtifact) {
Preconditions.checkArgument(!built);
this.messages.add(messagesArtifact);
return this;
}
public Builder addResource(PathFragment execPath, Artifact resource) {
Preconditions.checkArgument(!built);
this.resources.put(execPath, resource);
return this;
}
public Builder addResourceJars(NestedSet<Artifact> resourceJars) {
Preconditions.checkArgument(!built);
this.resourceJars.addTransitive(resourceJars);
return this;
}
public Builder addProcessorName(String processor) {
Preconditions.checkArgument(!built);
processorNames.add(processor);
return this;
}
public Builder addProcessorPath(Iterable<Artifact> jars) {
Preconditions.checkArgument(!built);
Iterables.addAll(processorPath, jars);
return this;
}
public Builder addProcessorPathDir(PathFragment dir) {
Preconditions.checkArgument(!built);
processorPathDirs.add(dir);
return this;
}
public Builder addApiGeneratingProcessorName(String processor) {
Preconditions.checkArgument(!built);
apiGeneratingProcessorNames.add(processor);
return this;
}
public Builder addApiGeneratingProcessorPath(Iterable<Artifact> jars) {
Preconditions.checkArgument(!built);
Iterables.addAll(apiGeneratingProcessorPath, jars);
return this;
}
public Builder addClassPathResources(List<Artifact> classPathResources) {
Preconditions.checkArgument(!built);
this.classPathResources.addAll(classPathResources);
return this;
}
public Builder addClassPathResource(Artifact classPathResource) {
Preconditions.checkArgument(!built);
this.classPathResources.add(classPathResource);
return this;
}
/**
* Adds additional outputs to this target's compile action.
*/
public Builder addAdditionalOutputs(Iterable<Artifact> outputs) {
Preconditions.checkArgument(!built);
Iterables.addAll(additionalOutputs, outputs);
return this;
}
public JavaTargetAttributes build() {
built = true;
return new JavaTargetAttributes(
sourceFiles,
compileTimeJarFiles,
runtimeClassPath,
compileTimeClassPath,
bootClassPath,
nativeLibraries,
processorPath,
processorPathDirs,
processorNames,
apiGeneratingProcessorPath,
apiGeneratingProcessorNames,
resources,
resourceJars.build(),
messages,
sourceJars,
classPathResources,
additionalOutputs,
directJars.build(),
compileTimeDependencyArtifacts,
ruleKind,
targetLabel,
excludedArtifacts,
strictJavaDeps);
}
// TODO(bazel-team): Remove these 5 methods.
@Deprecated
public Set<Artifact> getSourceFiles() {
return sourceFiles;
}
@Deprecated
public boolean hasSourceFiles() {
return !sourceFiles.isEmpty();
}
@Deprecated
public List<Artifact> getInstrumentationMetadata() {
return instrumentationMetadata;
}
@Deprecated
public boolean hasSourceJars() {
return !sourceJars.isEmpty();
}
}
//
// -------------------------- END OF BUILDER CLASS -------------------------
//
private final ImmutableSet<Artifact> sourceFiles;
private final ImmutableSet<Artifact> compileTimeJarFiles;
private final NestedSet<Artifact> runtimeClassPath;
private final NestedSet<Artifact> compileTimeClassPath;
private final ImmutableList<Artifact> bootClassPath;
private final ImmutableList<Artifact> nativeLibraries;
private final ImmutableSet<Artifact> processorPath;
private final ImmutableSet<PathFragment> processorPathDirs;
private final ImmutableSet<String> processorNames;
private final ImmutableSet<Artifact> apiGeneratingProcessorPath;
private final ImmutableSet<String> apiGeneratingProcessorNames;
private final ImmutableMap<PathFragment, Artifact> resources;
private final NestedSet<Artifact> resourceJars;
private final ImmutableList<Artifact> messages;
private final ImmutableList<Artifact> sourceJars;
private final ImmutableList<Artifact> classPathResources;
private final ImmutableSet<Artifact> additionalOutputs;
private final NestedSet<Artifact> directJars;
private final ImmutableList<Artifact> compileTimeDependencyArtifacts;
private final String ruleKind;
private final Label targetLabel;
private final NestedSet<Artifact> excludedArtifacts;
private final BuildConfiguration.StrictDepsMode strictJavaDeps;
/** Constructor of JavaTargetAttributes. */
private JavaTargetAttributes(
Set<Artifact> sourceFiles,
Set<Artifact> compileTimeJarFiles,
NestedSetBuilder<Artifact> runtimeClassPath,
NestedSetBuilder<Artifact> compileTimeClassPath,
List<Artifact> bootClassPath,
List<Artifact> nativeLibraries,
Set<Artifact> processorPath,
Set<PathFragment> processorPathDirs,
Set<String> processorNames,
Set<Artifact> apiGeneratingProcessorPath,
Set<String> apiGeneratingProcessorNames,
Map<PathFragment, Artifact> resources,
NestedSet<Artifact> resourceJars,
List<Artifact> messages,
List<Artifact> sourceJars,
List<Artifact> classPathResources,
Set<Artifact> additionalOutputs,
NestedSet<Artifact> directJars,
List<Artifact> compileTimeDependencyArtifacts,
String ruleKind,
Label targetLabel,
NestedSetBuilder<Artifact> excludedArtifacts,
BuildConfiguration.StrictDepsMode strictJavaDeps) {
this.sourceFiles = ImmutableSet.copyOf(sourceFiles);
this.compileTimeJarFiles = ImmutableSet.copyOf(compileTimeJarFiles);
this.runtimeClassPath = runtimeClassPath.build();
this.directJars = directJars;
this.compileTimeClassPath =
NestedSetBuilder.<Artifact>naiveLinkOrder()
.addTransitive(directJars)
.addTransitive(compileTimeClassPath.build())
.build();
this.bootClassPath = ImmutableList.copyOf(bootClassPath);
this.nativeLibraries = ImmutableList.copyOf(nativeLibraries);
this.processorPath = ImmutableSet.copyOf(processorPath);
this.processorPathDirs = ImmutableSet.copyOf(processorPathDirs);
this.processorNames = ImmutableSet.copyOf(processorNames);
this.apiGeneratingProcessorPath = ImmutableSet.copyOf(apiGeneratingProcessorPath);
this.apiGeneratingProcessorNames = ImmutableSet.copyOf(apiGeneratingProcessorNames);
this.resources = ImmutableMap.copyOf(resources);
this.resourceJars = resourceJars;
this.messages = ImmutableList.copyOf(messages);
this.sourceJars = ImmutableList.copyOf(sourceJars);
this.classPathResources = ImmutableList.copyOf(classPathResources);
this.additionalOutputs = ImmutableSet.copyOf(additionalOutputs);
this.compileTimeDependencyArtifacts = ImmutableList.copyOf(compileTimeDependencyArtifacts);
this.ruleKind = ruleKind;
this.targetLabel = targetLabel;
this.excludedArtifacts = excludedArtifacts.build();
this.strictJavaDeps = strictJavaDeps;
}
public NestedSet<Artifact> getDirectJars() {
return directJars;
}
public List<Artifact> getCompileTimeDependencyArtifacts() {
return compileTimeDependencyArtifacts;
}
public List<Artifact> getSourceJars() {
return sourceJars;
}
public Map<PathFragment, Artifact> getResources() {
return resources;
}
public NestedSet<Artifact> getResourceJars() {
return resourceJars;
}
public List<Artifact> getMessages() {
return messages;
}
public ImmutableList<Artifact> getClassPathResources() {
return classPathResources;
}
public ImmutableSet<Artifact> getAdditionalOutputs() {
return additionalOutputs;
}
private NestedSet<Artifact> getExcludedArtifacts() {
return excludedArtifacts;
}
/**
* Returns the artifacts needed on the runtime classpath of this target.
*
* See also {@link #getRuntimeClassPathForArchive()}.
*/
public NestedSet<Artifact> getRuntimeClassPath() {
return runtimeClassPath;
}
/**
* Returns the classpath artifacts needed in a deploy jar for this target.
*
* This excludes the artifacts made available by jars in the deployment
* environment.
*/
public Iterable<Artifact> getRuntimeClassPathForArchive() {
Iterable<Artifact> runtimeClasspath = getRuntimeClassPath();
if (getExcludedArtifacts().isEmpty()) {
return runtimeClasspath;
} else {
return Iterables.filter(runtimeClasspath,
Predicates.not(Predicates.in(getExcludedArtifacts().toSet())));
}
}
public NestedSet<Artifact> getCompileTimeClassPath() {
return compileTimeClassPath;
}
public ImmutableList<Artifact> getBootClassPath() {
return bootClassPath;
}
public ImmutableSet<Artifact> getProcessorPath() {
return processorPath;
}
public ImmutableSet<PathFragment> getProcessorPathDirs() {
return processorPathDirs;
}
public Collection<Artifact> getApiGeneratingProcessorPath() {
return apiGeneratingProcessorPath;
}
public ImmutableSet<String> getApiGeneratingProcessorNames() {
return apiGeneratingProcessorNames;
}
public Set<Artifact> getSourceFiles() {
return sourceFiles;
}
public Set<Artifact> getCompileTimeJarFiles() {
return compileTimeJarFiles;
}
public List<Artifact> getNativeLibraries() {
return nativeLibraries;
}
public Collection<String> getProcessorNames() {
return processorNames;
}
public boolean hasSourceFiles() {
return !sourceFiles.isEmpty();
}
public boolean hasSourceJars() {
return !sourceJars.isEmpty();
}
public boolean hasResources() {
return !resources.isEmpty();
}
public boolean hasMessages() {
return !messages.isEmpty();
}
public boolean hasClassPathResources() {
return !classPathResources.isEmpty();
}
public String getRuleKind() {
return ruleKind;
}
public Label getTargetLabel() {
return targetLabel;
}
public BuildConfiguration.StrictDepsMode getStrictJavaDeps() {
return strictJavaDeps;
}
}