// 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; 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 sourceFiles = new LinkedHashSet<>(); private final NestedSetBuilder runtimeClassPath = NestedSetBuilder.naiveLinkOrder(); private final NestedSetBuilder compileTimeClassPath = NestedSetBuilder.naiveLinkOrder(); private final List bootClassPath = new ArrayList<>(); private final List sourcePath = new ArrayList<>(); private final List nativeLibraries = new ArrayList<>(); private JavaPluginInfoProvider plugins = JavaPluginInfoProvider.empty(); private final Map resources = new LinkedHashMap<>(); private final NestedSetBuilder resourceJars = NestedSetBuilder.stableOrder(); private final List messages = new ArrayList<>(); private final List instrumentationMetadata = new ArrayList<>(); private final List sourceJars = new ArrayList<>(); private final List classPathResources = new ArrayList<>(); private final Set additionalOutputs = new LinkedHashSet<>(); private BuildConfiguration.StrictDepsMode strictJavaDeps = BuildConfiguration.StrictDepsMode.OFF; private final NestedSetBuilder directJars = NestedSetBuilder.naiveLinkOrder(); private final NestedSetBuilder compileTimeDependencyArtifacts = NestedSetBuilder.stableOrder(); private Label targetLabel; @Nullable private String injectingRuleKind; private final NestedSetBuilder excludedArtifacts = NestedSetBuilder.naiveLinkOrder(); private boolean built = false; private final JavaSemantics semantics; public Builder(JavaSemantics semantics) { this.semantics = semantics; } public Builder addSourceArtifacts(Iterable 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 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 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 classPathEntries) { Preconditions.checkArgument(!built); runtimeClassPath.addTransitive(classPathEntries); return this; } public Builder addCompileTimeClassPathEntries(NestedSet 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. * *

If this method is called, then the bootclasspath specified in this JavaTargetAttributes * instance overrides the default bootclasspath. */ public Builder setBootClassPath(List 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 artifacts) { Preconditions.checkArgument(!built); Preconditions.checkArgument(sourcePath.isEmpty()); sourcePath.addAll(artifacts); return this; } public Builder addExcludedArtifacts(NestedSet 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 compiler command line will include an extra flag to * indicate which classpath jars are direct dependencies. */ public Builder addDirectJars(NestedSet directJars) { Preconditions.checkArgument(!built); this.directJars.addTransitive(directJars); return this; } public Builder addCompileTimeDependencyArtifacts(NestedSet dependencyArtifacts) { Preconditions.checkArgument(!built); compileTimeDependencyArtifacts.addTransitive(dependencyArtifacts); return this; } public Builder addInstrumentationMetadataEntries(Iterable 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 nativeLibraries) { Preconditions.checkArgument(!built); for (Artifact nativeLibrary : nativeLibraries) { addNativeLibrary(nativeLibrary); } return this; } public Builder addMessages(Collection 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 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 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 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 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 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 sourceFiles; private final NestedSet runtimeClassPath; private final NestedSet compileTimeClassPath; private final ImmutableList bootClassPath; private final ImmutableList sourcePath; private final ImmutableList nativeLibraries; private final JavaPluginInfoProvider plugins; private final ImmutableMap resources; private final NestedSet resourceJars; private final ImmutableList messages; private final ImmutableList sourceJars; private final ImmutableList classPathResources; private final ImmutableSet additionalOutputs; private final NestedSet directJars; private final NestedSet compileTimeDependencyArtifacts; private final Label targetLabel; @Nullable private String injectingRuleKind; private final NestedSet excludedArtifacts; private final BuildConfiguration.StrictDepsMode strictJavaDeps; /** Constructor of JavaTargetAttributes. */ private JavaTargetAttributes( Set sourceFiles, NestedSetBuilder runtimeClassPath, NestedSetBuilder compileTimeClassPath, List bootClassPath, List sourcePath, List nativeLibraries, JavaPluginInfoProvider plugins, Map resources, NestedSet resourceJars, List messages, List sourceJars, List classPathResources, Set additionalOutputs, NestedSet directJars, NestedSet compileTimeDependencyArtifacts, Label targetLabel, @Nullable String injectingRuleKind, NestedSetBuilder excludedArtifacts, BuildConfiguration.StrictDepsMode strictJavaDeps) { this.sourceFiles = ImmutableSet.copyOf(sourceFiles); this.runtimeClassPath = runtimeClassPath.build(); this.directJars = directJars; this.compileTimeClassPath = NestedSetBuilder.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 getDirectJars() { return directJars; } public NestedSet getCompileTimeDependencyArtifacts() { return compileTimeDependencyArtifacts; } public List getSourceJars() { return sourceJars; } public Map getResources() { return resources; } public NestedSet getResourceJars() { return resourceJars; } public List getMessages() { return messages; } public ImmutableList getClassPathResources() { return classPathResources; } public ImmutableSet getAdditionalOutputs() { return additionalOutputs; } private NestedSet getExcludedArtifacts() { return excludedArtifacts; } /** * Returns the artifacts needed on the runtime classpath of this target. * * See also {@link #getRuntimeClassPathForArchive()}. */ public NestedSet 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 getRuntimeClassPathForArchive() { Iterable runtimeClasspath = getRuntimeClassPath(); if (getExcludedArtifacts().isEmpty()) { return runtimeClasspath; } else { return Iterables.filter(runtimeClasspath, Predicates.not(Predicates.in(getExcludedArtifacts().toSet()))); } } /** * Adds the classpath artifacts needed in a deploy jar for this target to the passed nested set. * *

This excludes the artifacts made available by jars in the deployment environment. */ public void addRuntimeClassPathForArchiveToNestedSet(NestedSetBuilder builder) { NestedSet runtimeClasspath = getRuntimeClassPath(); if (getExcludedArtifacts().isEmpty()) { builder.addTransitive(runtimeClasspath); } else { builder.addAll( Iterables.filter( runtimeClasspath, Predicates.not(Predicates.in(getExcludedArtifacts().toSet())))); } } public NestedSet getCompileTimeClassPath() { return compileTimeClassPath; } public ImmutableList getBootClassPath() { return bootClassPath; } public ImmutableList getSourcePath() { return sourcePath; } public JavaPluginInfoProvider plugins() { return plugins; } public ImmutableSet getSourceFiles() { return sourceFiles; } public List 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 BuildConfiguration.StrictDepsMode getStrictJavaDeps() { return strictJavaDeps; } }