// 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.IterablesChain; 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 sourceFiles = new LinkedHashSet<>(); private final Set compileTimeJarFiles = new LinkedHashSet<>(); private final NestedSetBuilder runtimeClassPath = NestedSetBuilder.naiveLinkOrder(); private final NestedSetBuilder compileTimeClassPath = NestedSetBuilder.naiveLinkOrder(); private final List bootClassPath = new ArrayList<>(); private final List nativeLibraries = new ArrayList<>(); private final Set processorPath = new LinkedHashSet<>(); private final Set processorNames = new LinkedHashSet<>(); private final Set apiGeneratingProcessorPath = new LinkedHashSet<>(); private final Set apiGeneratingProcessorNames = new LinkedHashSet<>(); private final Map resources = new LinkedHashMap<>(); 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 BuildConfiguration.StrictDepsMode strictJavaDeps = BuildConfiguration.StrictDepsMode.OFF; private final NestedSetBuilder directJars = NestedSetBuilder.naiveLinkOrder(); private final List compileTimeDependencyArtifacts = new ArrayList<>(); private String ruleKind; private Label targetLabel; 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(JavaCompilationArgs context) { Preconditions.checkArgument(!built); addCompileTimeClassPathEntries(context.getCompileTimeJars()); 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 addCompileTimeJarFiles(Iterable 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 classPathEntries) { Preconditions.checkArgument(!built); runtimeClassPath.addTransitive(classPathEntries); return this; } public Builder addCompileTimeClassPathEntries(NestedSet 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. * *

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; } 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 extra flags are formatted like this (same for * --indirect_dependency):

     * --direct_dependency
     * foo/bar/lib.jar
     * //java/com/google/foo:bar
     * 
*/ public Builder addDirectJars(NestedSet directJars) { Preconditions.checkArgument(!built); this.directJars.addTransitive(directJars); return this; } public Builder addCompileTimeDependencyArtifacts(Iterable dependencyArtifacts) { Preconditions.checkArgument(!built); Iterables.addAll(this.compileTimeDependencyArtifacts, 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 addProcessorName(String processor) { Preconditions.checkArgument(!built); processorNames.add(processor); return this; } public Builder addProcessorPath(Iterable jars) { Preconditions.checkArgument(!built); Iterables.addAll(processorPath, jars); return this; } public Builder addApiGeneratingProcessorName(String processor) { Preconditions.checkArgument(!built); apiGeneratingProcessorNames.add(processor); return this; } public Builder addApiGeneratingProcessorPath(Iterable jars) { Preconditions.checkArgument(!built); Iterables.addAll(apiGeneratingProcessorPath, jars); 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; } public JavaTargetAttributes build() { built = true; return new JavaTargetAttributes( sourceFiles, compileTimeJarFiles, runtimeClassPath, compileTimeClassPath, bootClassPath, nativeLibraries, processorPath, processorNames, apiGeneratingProcessorPath, apiGeneratingProcessorNames, resources, messages, sourceJars, classPathResources, directJars.build(), compileTimeDependencyArtifacts, ruleKind, targetLabel, excludedArtifacts, strictJavaDeps); } // TODO(bazel-team): Remove these 5 methods. @Deprecated public Set getSourceFiles() { return sourceFiles; } @Deprecated public boolean hasSourceFiles() { return !sourceFiles.isEmpty(); } @Deprecated public List getInstrumentationMetadata() { return instrumentationMetadata; } @Deprecated public boolean hasSourceJars() { return !sourceJars.isEmpty(); } } // // -------------------------- END OF BUILDER CLASS ------------------------- // private final ImmutableSet sourceFiles; private final ImmutableSet compileTimeJarFiles; private final NestedSet runtimeClassPath; private final NestedSet compileTimeClassPath; private final ImmutableList bootClassPath; private final ImmutableList nativeLibraries; private final ImmutableSet processorPath; private final ImmutableSet processorNames; private final ImmutableSet apiGeneratingProcessorPath; private final ImmutableSet apiGeneratingProcessorNames; private final ImmutableMap resources; private final ImmutableList messages; private final ImmutableList sourceJars; private final ImmutableList classPathResources; private final NestedSet directJars; private final ImmutableList compileTimeDependencyArtifacts; private final String ruleKind; private final Label targetLabel; private final NestedSet excludedArtifacts; private final BuildConfiguration.StrictDepsMode strictJavaDeps; /** Constructor of JavaTargetAttributes. */ private JavaTargetAttributes( Set sourceFiles, Set compileTimeJarFiles, NestedSetBuilder runtimeClassPath, NestedSetBuilder compileTimeClassPath, List bootClassPath, List nativeLibraries, Set processorPath, Set processorNames, Set apiGeneratingProcessorPath, Set apiGeneratingProcessorNames, Map resources, List messages, List sourceJars, List classPathResources, NestedSet directJars, List compileTimeDependencyArtifacts, String ruleKind, Label targetLabel, NestedSetBuilder excludedArtifacts, BuildConfiguration.StrictDepsMode strictJavaDeps) { this.sourceFiles = ImmutableSet.copyOf(sourceFiles); this.compileTimeJarFiles = ImmutableSet.copyOf(compileTimeJarFiles); this.runtimeClassPath = runtimeClassPath.build(); this.directJars = directJars; this.compileTimeClassPath = NestedSetBuilder.naiveLinkOrder() .addTransitive(directJars) .addTransitive(compileTimeClassPath.build()) .build(); this.bootClassPath = ImmutableList.copyOf(bootClassPath); this.nativeLibraries = ImmutableList.copyOf(nativeLibraries); this.processorPath = ImmutableSet.copyOf(processorPath); this.processorNames = ImmutableSet.copyOf(processorNames); this.apiGeneratingProcessorPath = ImmutableSet.copyOf(apiGeneratingProcessorPath); this.apiGeneratingProcessorNames = ImmutableSet.copyOf(apiGeneratingProcessorNames); this.resources = ImmutableMap.copyOf(resources); this.messages = ImmutableList.copyOf(messages); this.sourceJars = ImmutableList.copyOf(sourceJars); this.classPathResources = ImmutableList.copyOf(classPathResources); this.compileTimeDependencyArtifacts = ImmutableList.copyOf(compileTimeDependencyArtifacts); this.ruleKind = ruleKind; this.targetLabel = targetLabel; this.excludedArtifacts = excludedArtifacts.build(); this.strictJavaDeps = strictJavaDeps; } public NestedSet getDirectJars() { return directJars; } public List getCompileTimeDependencyArtifacts() { return compileTimeDependencyArtifacts; } public List getSourceJars() { return sourceJars; } public Map getResources() { return resources; } public List getMessages() { return messages; } public ImmutableList getClassPathResources() { return classPathResources; } 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()))); } } public NestedSet getCompileTimeClassPath() { return compileTimeClassPath; } public ImmutableList getBootClassPath() { return bootClassPath; } public ImmutableSet getProcessorPath() { return processorPath; } public Collection getApiGeneratingProcessorPath() { return apiGeneratingProcessorPath; } public ImmutableSet getApiGeneratingProcessorNames() { return apiGeneratingProcessorNames; } public Set getSourceFiles() { return sourceFiles; } public Set getCompileTimeJarFiles() { return compileTimeJarFiles; } public List getNativeLibraries() { return nativeLibraries; } public Collection 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 Iterable getArchiveInputs(boolean includeClasspath) { IterablesChain.Builder inputs = IterablesChain.builder(); if (includeClasspath) { inputs.add(ImmutableList.copyOf(getRuntimeClassPathForArchive())); } inputs.add(ImmutableList.copyOf(getResources().values())); inputs.add(getClassPathResources()); return inputs.build(); } public String getRuleKind() { return ruleKind; } public Label getTargetLabel() { return targetLabel; } public BuildConfiguration.StrictDepsMode getStrictJavaDeps() { return strictJavaDeps; } }