| // 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.Preconditions; |
| 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.StrictDepsMode; |
| 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.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; |
| import javax.annotation.Nullable; |
| |
| /** |
| * 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 NestedSetBuilder<Artifact> runtimeClassPath = NestedSetBuilder.naiveLinkOrder(); |
| |
| private final NestedSetBuilder<Artifact> compileTimeClassPath = |
| NestedSetBuilder.naiveLinkOrder(); |
| |
| private final List<Artifact> bootClassPath = new ArrayList<>(); |
| private final List<Artifact> sourcePath = new ArrayList<>(); |
| private final List<Artifact> nativeLibraries = new ArrayList<>(); |
| |
| private JavaPluginInfoProvider plugins = JavaPluginInfoProvider.empty(); |
| |
| 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<>(); |
| |
| /** @see {@link #setStrictJavaDeps}. */ |
| private StrictDepsMode strictJavaDeps = StrictDepsMode.ERROR; |
| |
| private final NestedSetBuilder<Artifact> directJars = NestedSetBuilder.naiveLinkOrder(); |
| private final NestedSetBuilder<Artifact> compileTimeDependencyArtifacts = |
| NestedSetBuilder.stableOrder(); |
| private Label targetLabel; |
| @Nullable private String injectingRuleKind; |
| |
| 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(JavaCompilationArgsProvider context) { |
| Preconditions.checkArgument(!built); |
| addCompileTimeClassPathEntries(context.getTransitiveCompileTimeJars()); |
| 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 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 setTargetLabel(Label targetLabel) { |
| Preconditions.checkArgument(!built); |
| this.targetLabel = targetLabel; |
| return this; |
| } |
| |
| public Builder setInjectingRuleKind(@Nullable String injectingRuleKind) { |
| Preconditions.checkArgument(!built); |
| this.injectingRuleKind = injectingRuleKind; |
| 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; |
| } |
| |
| /** Sets the sourcepath to be passed to the Java compiler. */ |
| public Builder setSourcePath(List<Artifact> artifacts) { |
| Preconditions.checkArgument(!built); |
| Preconditions.checkArgument(sourcePath.isEmpty()); |
| sourcePath.addAll(artifacts); |
| 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. |
| * |
| * <p>Defaults to {@link StrictDepsMode#ERROR}. |
| * |
| * @param strictDeps one of WARN, ERROR or OFF |
| */ |
| public Builder setStrictJavaDeps(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 compiler command line will include an extra flag to |
| * indicate which classpath jars are direct dependencies. |
| */ |
| public Builder addDirectJars(NestedSet<Artifact> directJars) { |
| Preconditions.checkArgument(!built); |
| this.directJars.addTransitive(directJars); |
| return this; |
| } |
| |
| public Builder addCompileTimeDependencyArtifacts(NestedSet<Artifact> dependencyArtifacts) { |
| Preconditions.checkArgument(!built); |
| compileTimeDependencyArtifacts.addTransitive(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 addPlugin(JavaPluginInfoProvider plugins) { |
| Preconditions.checkArgument(!built); |
| this.plugins = JavaPluginInfoProvider.merge(this.plugins, plugins); |
| 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, |
| runtimeClassPath, |
| compileTimeClassPath, |
| bootClassPath, |
| sourcePath, |
| nativeLibraries, |
| plugins, |
| resources, |
| resourceJars.build(), |
| messages, |
| sourceJars, |
| classPathResources, |
| additionalOutputs, |
| directJars.build(), |
| compileTimeDependencyArtifacts.build(), |
| targetLabel, |
| injectingRuleKind, |
| excludedArtifacts, |
| strictJavaDeps); |
| } |
| |
| // TODO(bazel-team): delete the following methods - users should use the built |
| // JavaTargetAttributes instead of accessing mutable state in the Builder. |
| |
| /** @deprecated prefer {@link JavaTargetAttributes#getSourceFiles} */ |
| @Deprecated |
| public Set<Artifact> getSourceFiles() { |
| return sourceFiles; |
| } |
| |
| /** @deprecated prefer {@link JavaTargetAttributes#hasSources} */ |
| @Deprecated |
| public boolean hasSources() { |
| return !sourceFiles.isEmpty() || !sourceJars.isEmpty(); |
| } |
| |
| /** |
| * @deprecated prefer to use a built {@link JavaTargetAttributes} instead of accessing mutable |
| * state in the {@link Builder}. |
| */ |
| @Deprecated |
| public boolean hasSourceFiles() { |
| return !sourceFiles.isEmpty(); |
| } |
| |
| /** |
| * @deprecated prefer to use a built {@link JavaTargetAttributes} instead of accessing mutable |
| * state in the {@link Builder}. |
| */ |
| @Deprecated |
| public List<Artifact> getInstrumentationMetadata() { |
| return instrumentationMetadata; |
| } |
| |
| /** |
| * @deprecated prefer to use a built {@link JavaTargetAttributes} instead of accessing mutable |
| * state in the {@link Builder}. |
| */ |
| @Deprecated |
| public boolean hasSourceJars() { |
| return !sourceJars.isEmpty(); |
| } |
| } |
| |
| // |
| // -------------------------- END OF BUILDER CLASS ------------------------- |
| // |
| |
| private final ImmutableSet<Artifact> sourceFiles; |
| |
| private final NestedSet<Artifact> runtimeClassPath; |
| private final NestedSet<Artifact> compileTimeClassPath; |
| |
| private final ImmutableList<Artifact> bootClassPath; |
| private final ImmutableList<Artifact> sourcePath; |
| private final ImmutableList<Artifact> nativeLibraries; |
| |
| private final JavaPluginInfoProvider plugins; |
| |
| 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 NestedSet<Artifact> compileTimeDependencyArtifacts; |
| private final Label targetLabel; |
| @Nullable private final String injectingRuleKind; |
| |
| private final NestedSet<Artifact> excludedArtifacts; |
| private final StrictDepsMode strictJavaDeps; |
| |
| /** Constructor of JavaTargetAttributes. */ |
| private JavaTargetAttributes( |
| Set<Artifact> sourceFiles, |
| NestedSetBuilder<Artifact> runtimeClassPath, |
| NestedSetBuilder<Artifact> compileTimeClassPath, |
| List<Artifact> bootClassPath, |
| List<Artifact> sourcePath, |
| List<Artifact> nativeLibraries, |
| JavaPluginInfoProvider plugins, |
| Map<PathFragment, Artifact> resources, |
| NestedSet<Artifact> resourceJars, |
| List<Artifact> messages, |
| List<Artifact> sourceJars, |
| List<Artifact> classPathResources, |
| Set<Artifact> additionalOutputs, |
| NestedSet<Artifact> directJars, |
| NestedSet<Artifact> compileTimeDependencyArtifacts, |
| Label targetLabel, |
| @Nullable String injectingRuleKind, |
| NestedSetBuilder<Artifact> excludedArtifacts, |
| StrictDepsMode strictJavaDeps) { |
| this.sourceFiles = ImmutableSet.copyOf(sourceFiles); |
| this.runtimeClassPath = runtimeClassPath.build(); |
| this.directJars = directJars; |
| this.compileTimeClassPath = |
| NestedSetBuilder.<Artifact>naiveLinkOrder() |
| .addTransitive(directJars) |
| .addTransitive(compileTimeClassPath.build()) |
| .build(); |
| this.bootClassPath = ImmutableList.copyOf(bootClassPath); |
| this.sourcePath = ImmutableList.copyOf(sourcePath); |
| this.nativeLibraries = ImmutableList.copyOf(nativeLibraries); |
| this.plugins = plugins; |
| 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 = compileTimeDependencyArtifacts; |
| this.targetLabel = targetLabel; |
| this.injectingRuleKind = injectingRuleKind; |
| this.excludedArtifacts = excludedArtifacts.build(); |
| this.strictJavaDeps = strictJavaDeps; |
| } |
| |
| public NestedSet<Artifact> getDirectJars() { |
| return directJars; |
| } |
| |
| public NestedSet<Artifact> getCompileTimeDependencyArtifacts() { |
| return compileTimeDependencyArtifacts; |
| } |
| |
| public ImmutableList<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. |
| * |
| * <p>See also {@link #getRuntimeClassPathForArchive()}. |
| */ |
| public NestedSet<Artifact> getRuntimeClassPath() { |
| return runtimeClassPath; |
| } |
| |
| /** |
| * Returns the classpath artifacts needed in a deploy jar for this target. |
| * |
| * <p>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 ImmutableList<Artifact> getSourcePath() { |
| return sourcePath; |
| } |
| |
| public JavaPluginInfoProvider plugins() { |
| return plugins; |
| } |
| |
| public ImmutableSet<Artifact> getSourceFiles() { |
| return sourceFiles; |
| } |
| |
| public List<Artifact> getNativeLibraries() { |
| return nativeLibraries; |
| } |
| |
| public boolean hasSources() { |
| return !sourceFiles.isEmpty() || !sourceJars.isEmpty(); |
| } |
| |
| public boolean hasResources() { |
| return !resources.isEmpty() |
| || !messages.isEmpty() |
| || !classPathResources.isEmpty() |
| || !resourceJars.isEmpty(); |
| } |
| |
| public boolean hasMessages() { |
| return !messages.isEmpty(); |
| } |
| |
| public Label getTargetLabel() { |
| return targetLabel; |
| } |
| |
| @Nullable |
| public String getInjectingRuleKind() { |
| return injectingRuleKind; |
| } |
| |
| public StrictDepsMode getStrictJavaDeps() { |
| return strictJavaDeps; |
| } |
| } |