// 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.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.actions.ActionAnalysisMetadata; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.AnalysisEnvironment; import com.google.devtools.build.lib.analysis.AnalysisUtils; import com.google.devtools.build.lib.analysis.OutputGroupProvider; import com.google.devtools.build.lib.analysis.PrerequisiteArtifacts; import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.Runfiles; import com.google.devtools.build.lib.analysis.RunfilesProvider; import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; import com.google.devtools.build.lib.analysis.TransitiveInfoProvider; import com.google.devtools.build.lib.analysis.Util; 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.collect.nestedset.Order; import com.google.devtools.build.lib.packages.BuildType; import com.google.devtools.build.lib.packages.ClassObjectConstructor; import com.google.devtools.build.lib.packages.SkylarkClassObject; import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.packages.TargetUtils; import com.google.devtools.build.lib.rules.cpp.CppCompilationContext; import com.google.devtools.build.lib.rules.cpp.LinkerInput; import com.google.devtools.build.lib.rules.java.JavaCompilationArgs.ClasspathType; import com.google.devtools.build.lib.rules.test.InstrumentedFilesCollector; import com.google.devtools.build.lib.rules.test.InstrumentedFilesCollector.InstrumentationSpec; import com.google.devtools.build.lib.rules.test.InstrumentedFilesCollector.LocalMetadataCollector; import com.google.devtools.build.lib.rules.test.InstrumentedFilesProvider; import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.util.FileTypeSet; import com.google.devtools.build.lib.util.Pair; import com.google.devtools.build.lib.util.Preconditions; import com.google.devtools.build.lib.vfs.FileSystemUtils; import com.google.devtools.build.lib.vfs.PathFragment; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import javax.annotation.Nullable; /** * A helper class to create configured targets for Java rules. */ public class JavaCommon { private static final Function<TransitiveInfoCollection, Label> GET_COLLECTION_LABEL = new Function<TransitiveInfoCollection, Label>() { @Override public Label apply(TransitiveInfoCollection collection) { return collection.getLabel(); } }; public static final InstrumentationSpec JAVA_COLLECTION_SPEC = new InstrumentationSpec( FileTypeSet.of(JavaSemantics.JAVA_SOURCE)) .withSourceAttributes("srcs") .withDependencyAttributes("deps", "data", "exports", "runtime_deps"); /** Collects all metadata files generated by Java compilation actions. */ private static final LocalMetadataCollector JAVA_METADATA_COLLECTOR = new LocalMetadataCollector() { @Override public void collectMetadataArtifacts( Iterable<Artifact> objectFiles, AnalysisEnvironment analysisEnvironment, NestedSetBuilder<Artifact> metadataFilesBuilder) { for (Artifact artifact : objectFiles) { ActionAnalysisMetadata action = analysisEnvironment.getLocalGeneratingAction(artifact); if (action instanceof JavaCompileAction) { addOutputs(metadataFilesBuilder, action, JavaSemantics.COVERAGE_METADATA); } else if (action != null && action.getMnemonic().equals("JavaResourceJar")) { // recurse on resource jar actions collectMetadataArtifacts( action.getInputs(), analysisEnvironment, metadataFilesBuilder); } } } }; private ClasspathConfiguredFragment classpathFragment = new ClasspathConfiguredFragment(); private JavaCompilationArtifacts javaArtifacts = JavaCompilationArtifacts.EMPTY; private ImmutableList<String> javacOpts; // Targets treated as deps in compilation time, runtime time and both private final ImmutableMap<ClasspathType, ImmutableList<TransitiveInfoCollection>> targetsTreatedAsDeps; private final ImmutableList<Artifact> sources; private ImmutableList<JavaPluginInfoProvider> activePlugins = ImmutableList.of(); private final RuleContext ruleContext; private final JavaSemantics semantics; private JavaCompilationHelper javaCompilationHelper; public JavaCommon(RuleContext ruleContext, JavaSemantics semantics) { this(ruleContext, semantics, ruleContext.getPrerequisiteArtifacts("srcs", Mode.TARGET).list(), collectTargetsTreatedAsDeps(ruleContext, semantics, ClasspathType.COMPILE_ONLY), collectTargetsTreatedAsDeps(ruleContext, semantics, ClasspathType.RUNTIME_ONLY), collectTargetsTreatedAsDeps(ruleContext, semantics, ClasspathType.BOTH)); } public JavaCommon(RuleContext ruleContext, JavaSemantics semantics, ImmutableList<Artifact> sources) { this(ruleContext, semantics, sources, collectTargetsTreatedAsDeps(ruleContext, semantics, ClasspathType.COMPILE_ONLY), collectTargetsTreatedAsDeps(ruleContext, semantics, ClasspathType.RUNTIME_ONLY), collectTargetsTreatedAsDeps(ruleContext, semantics, ClasspathType.BOTH)); } public JavaCommon(RuleContext ruleContext, JavaSemantics semantics, ImmutableList<TransitiveInfoCollection> compileDeps, ImmutableList<TransitiveInfoCollection> runtimeDeps, ImmutableList<TransitiveInfoCollection> bothDeps) { this(ruleContext, semantics, ruleContext.getPrerequisiteArtifacts("srcs", Mode.TARGET).list(), compileDeps, runtimeDeps, bothDeps); } public JavaCommon(RuleContext ruleContext, JavaSemantics semantics, ImmutableList<Artifact> sources, ImmutableList<TransitiveInfoCollection> compileDeps, ImmutableList<TransitiveInfoCollection> runtimeDeps, ImmutableList<TransitiveInfoCollection> bothDeps) { this.ruleContext = ruleContext; this.semantics = semantics; this.sources = sources; this.targetsTreatedAsDeps = ImmutableMap.of( ClasspathType.COMPILE_ONLY, compileDeps, ClasspathType.RUNTIME_ONLY, runtimeDeps, ClasspathType.BOTH, bothDeps); } public JavaSemantics getJavaSemantics() { return semantics; } /** * Validates that the packages listed under "deps" all have the given constraint. If a package * does not have this attribute, an error is generated. */ public static final void validateConstraint(RuleContext ruleContext, String constraint, Iterable<? extends TransitiveInfoCollection> targets) { for (JavaConstraintProvider constraintProvider : AnalysisUtils.getProviders(targets, JavaConstraintProvider.class)) { if (!constraintProvider.getJavaConstraints().contains(constraint)) { ruleContext.attributeError("deps", String.format("%s: does not have constraint '%s'", constraintProvider.getLabel(), constraint)); } } } /** * Creates an action to aggregate all metadata artifacts into a single * <target_name>_instrumented.jar file. */ public static void createInstrumentedJarAction( RuleContext ruleContext, JavaSemantics semantics, List<Artifact> metadataArtifacts, Artifact instrumentedJar, String mainClass) throws InterruptedException { // In Jacoco's setup, metadata artifacts are real jars. new DeployArchiveBuilder(semantics, ruleContext) .setOutputJar(instrumentedJar) // We need to save the original mainClass because we're going to run inside CoverageRunner .setJavaStartClass(mainClass) .setAttributes(new JavaTargetAttributes.Builder(semantics).build()) .addRuntimeJars(ImmutableList.copyOf(metadataArtifacts)) .setCompression(DeployArchiveBuilder.Compression.UNCOMPRESSED) .build(); } public static ImmutableList<String> getConstraints(RuleContext ruleContext) { return ruleContext.getRule().isAttrDefined("constraints", Type.STRING_LIST) ? ImmutableList.copyOf(ruleContext.attributes().get("constraints", Type.STRING_LIST)) : ImmutableList.<String>of(); } public void setClassPathFragment(ClasspathConfiguredFragment classpathFragment) { this.classpathFragment = classpathFragment; } public void setJavaCompilationArtifacts(JavaCompilationArtifacts javaArtifacts) { this.javaArtifacts = javaArtifacts; } public JavaCompilationArtifacts getJavaCompilationArtifacts() { return javaArtifacts; } public NestedSet<Artifact> getProcessorClasspathJars() { NestedSetBuilder<Artifact> builder = NestedSetBuilder.naiveLinkOrder(); for (JavaPluginInfoProvider plugin : activePlugins) { builder.addTransitive(plugin.getProcessorClasspath()); } return builder.build(); } public ImmutableList<String> getProcessorClassNames() { Set<String> processorNames = new LinkedHashSet<>(); for (JavaPluginInfoProvider plugin : activePlugins) { processorNames.addAll(plugin.getProcessorClasses()); } return ImmutableList.copyOf(processorNames); } /** * Creates the java.library.path from a list of the native libraries. * Concatenates the parent directories of the shared libraries into a Java * search path. Each relative path entry is prepended with "${JAVA_RUNFILES}/" * so it can be resolved at runtime. * * @param sharedLibraries a collection of native libraries to create the java * library path from * @return a String containing the ":" separated java library path */ public static String javaLibraryPath( Collection<Artifact> sharedLibraries, String runfilePrefix) { StringBuilder buffer = new StringBuilder(); Set<PathFragment> entries = new HashSet<>(); for (Artifact sharedLibrary : sharedLibraries) { PathFragment entry = sharedLibrary.getRootRelativePath().getParentDirectory(); if (entries.add(entry)) { if (buffer.length() > 0) { buffer.append(':'); } buffer.append("${JAVA_RUNFILES}/" + runfilePrefix + "/"); buffer.append(entry.getPathString()); } } return buffer.toString(); } /** * Collects Java compilation arguments for this target. * * @param recursive Whether to scan dependencies recursively. * @param isNeverLink Whether the target has the 'neverlink' attr. * @param srcLessDepsExport If srcs is omitted, deps are exported * (deprecated behaviour for android_library only) */ public JavaCompilationArgs collectJavaCompilationArgs(boolean recursive, boolean isNeverLink, boolean srcLessDepsExport) { ClasspathType type = isNeverLink ? ClasspathType.COMPILE_ONLY : ClasspathType.BOTH; JavaCompilationArgs.Builder builder = JavaCompilationArgs.builder() .merge(getJavaCompilationArtifacts(), isNeverLink) .addTransitiveTargets(getExports(ruleContext), recursive, type); // TODO(bazel-team): remove srcs-less behaviour after android_library users are refactored if (recursive || srcLessDepsExport) { builder .addTransitiveTargets(targetsTreatedAsDeps(ClasspathType.COMPILE_ONLY), recursive, type) .addTransitiveTargets(getRuntimeDeps(ruleContext), recursive, ClasspathType.RUNTIME_ONLY); } return builder.build(); } /** * Collects Java dependency artifacts for this target. * * @param outDeps output (compile-time) dependency artifact of this target */ public NestedSet<Artifact> collectCompileTimeDependencyArtifacts(@Nullable Artifact outDeps) { NestedSetBuilder<Artifact> builder = NestedSetBuilder.stableOrder(); if (outDeps != null) { builder.add(outDeps); } for (JavaCompilationArgsProvider provider : JavaProvider.getProvidersFromListOfTargets( JavaCompilationArgsProvider.class, getExports(ruleContext))) { builder.addTransitive(provider.getCompileTimeJavaDependencyArtifacts()); } return builder.build(); } public static List<TransitiveInfoCollection> getExports(RuleContext ruleContext) { // We need to check here because there are classes inheriting from this class that implement // rules that don't have this attribute. if (ruleContext.attributes().has("exports", BuildType.LABEL_LIST)) { // Do not remove <SplitTransition<?>, BuildConfiguration>: // workaround for Java 7 type inference. return ImmutableList.<TransitiveInfoCollection>copyOf( ruleContext.getPrerequisites("exports", Mode.TARGET)); } else { return ImmutableList.of(); } } /** * Sanity checks the given runtime dependencies, and emits errors if there is a problem. * Also called by {@link #initCommon()} for the current target's runtime dependencies. */ public static void checkRuntimeDeps( RuleContext ruleContext, List<TransitiveInfoCollection> runtimeDepInfo) { for (TransitiveInfoCollection c : runtimeDepInfo) { JavaNeverlinkInfoProvider neverLinkedness = c.getProvider(JavaNeverlinkInfoProvider.class); if (neverLinkedness == null) { continue; } boolean reportError = !ruleContext.getConfiguration().getAllowRuntimeDepsOnNeverLink(); if (neverLinkedness.isNeverlink()) { String msg = String.format("neverlink dep %s not allowed in runtime deps", c.getLabel()); if (reportError) { ruleContext.attributeError("runtime_deps", msg); } else { ruleContext.attributeWarning("runtime_deps", msg); } } } } /** * Returns transitive Java native libraries. * * @see JavaNativeLibraryProvider */ protected NestedSet<LinkerInput> collectTransitiveJavaNativeLibraries() { NativeLibraryNestedSetBuilder builder = new NativeLibraryNestedSetBuilder(); builder.addJavaTargets(targetsTreatedAsDeps(ClasspathType.BOTH)); if (ruleContext.getRule().isAttrDefined("data", BuildType.LABEL_LIST)) { builder.addJavaTargets(ruleContext.getPrerequisites("data", Mode.DATA)); } return builder.build(); } /** * Collects transitive source jars for the current rule. * * @param targetSrcJars The source jar artifacts corresponding to the output of the current rule. * @return A nested set containing all of the source jar artifacts on which the current rule * transitively depends. */ public NestedSet<Artifact> collectTransitiveSourceJars(Artifact... targetSrcJars) { return collectTransitiveSourceJars(ImmutableList.copyOf(targetSrcJars)); } /** * Collects transitive source jars for the current rule. * * @param targetSrcJars The source jar artifacts corresponding to the output of the current rule. * @return A nested set containing all of the source jar artifacts on which the current rule * transitively depends. */ public NestedSet<Artifact> collectTransitiveSourceJars(Iterable<Artifact> targetSrcJars) { NestedSetBuilder<Artifact> builder = NestedSetBuilder.<Artifact>stableOrder() .addAll(targetSrcJars); for (JavaSourceJarsProvider sourceJarsProvider : JavaProvider.getProvidersFromListOfTargets( JavaSourceJarsProvider.class, getDependencies())) { builder.addTransitive(sourceJarsProvider.getTransitiveSourceJars()); } return builder.build(); } /** * Collects transitive gen jars for the current rule. */ private JavaGenJarsProvider collectTransitiveGenJars( boolean usesAnnotationProcessing, @Nullable Artifact genClassJar, @Nullable Artifact genSourceJar) { NestedSetBuilder<Artifact> classJarsBuilder = NestedSetBuilder.stableOrder(); NestedSetBuilder<Artifact> sourceJarsBuilder = NestedSetBuilder.stableOrder(); if (genClassJar != null) { classJarsBuilder.add(genClassJar); } if (genSourceJar != null) { sourceJarsBuilder.add(genSourceJar); } for (JavaGenJarsProvider dep : getDependencies(JavaGenJarsProvider.class)) { classJarsBuilder.addTransitive(dep.getTransitiveGenClassJars()); sourceJarsBuilder.addTransitive(dep.getTransitiveGenSourceJars()); } return new JavaGenJarsProvider( usesAnnotationProcessing, genClassJar, genSourceJar, getProcessorClasspathJars(), getProcessorClassNames(), classJarsBuilder.build(), sourceJarsBuilder.build() ); } /** * Collects transitive C++ dependencies. */ protected CppCompilationContext collectTransitiveCppDeps() { CppCompilationContext.Builder builder = new CppCompilationContext.Builder(ruleContext); for (TransitiveInfoCollection dep : targetsTreatedAsDeps(ClasspathType.BOTH)) { CppCompilationContext context = dep.getProvider(CppCompilationContext.class); if (context != null) { builder.mergeDependentContext(context); } } return builder.build(); } /** * Collects labels of targets and artifacts reached transitively via the "exports" attribute. */ protected JavaExportsProvider collectTransitiveExports() { NestedSetBuilder<Label> builder = NestedSetBuilder.stableOrder(); List<TransitiveInfoCollection> currentRuleExports = getExports(ruleContext); builder.addAll(Iterables.transform(currentRuleExports, GET_COLLECTION_LABEL)); for (TransitiveInfoCollection dep : currentRuleExports) { JavaExportsProvider exportsProvider = dep.getProvider(JavaExportsProvider.class); if (exportsProvider != null) { builder.addTransitive(exportsProvider.getTransitiveExports()); } } return new JavaExportsProvider(builder.build()); } public final void initializeJavacOpts() { Preconditions.checkState(javacOpts == null); javacOpts = computeJavacOpts(semantics.getExtraJavacOpts(ruleContext)); } /** * For backwards compatibility, this method allows multiple calls to set the Javac opts. Do not * use this. */ @Deprecated public final void initializeJavacOpts(Iterable<String> extraJavacOpts) { javacOpts = computeJavacOpts(extraJavacOpts); } private ImmutableList<String> computeJavacOpts(Iterable<String> extraJavacOpts) { return ImmutableList.copyOf(Iterables.concat( JavaToolchainProvider.fromRuleContext(ruleContext).getJavacOptions(), extraJavacOpts, ruleContext.getTokenizedStringListAttr("javacopts"))); } /** * Returns the string that the stub should use to determine the JVM * @param launcher if non-null, the cc_binary used to launch the Java Virtual Machine */ public static String getJavaBinSubstitution( RuleContext ruleContext, @Nullable Artifact launcher) { Preconditions.checkState(ruleContext.getConfiguration().hasFragment(Jvm.class)); PathFragment javaExecutable; if (launcher != null) { javaExecutable = launcher.getRootRelativePath(); } else { javaExecutable = ruleContext.getFragment(Jvm.class).getRunfilesJavaExecutable(); } if (!javaExecutable.isAbsolute()) { javaExecutable = PathFragment.create(PathFragment.create(ruleContext.getWorkspaceName()), javaExecutable); } javaExecutable = javaExecutable.normalize(); if (ruleContext.getConfiguration().runfilesEnabled()) { String prefix = ""; if (!javaExecutable.isAbsolute()) { prefix = "${JAVA_RUNFILES}/"; } return "JAVABIN=${JAVABIN:-" + prefix + javaExecutable.getPathString() + "}"; } else { return "JAVABIN=${JAVABIN:-$(rlocation " + javaExecutable.getPathString() + ")}"; } } /** * Heuristically determines the name of the primary Java class for this * executable, based on the rule name and the "srcs" list. * * <p>(This is expected to be the class containing the "main" method for a * java_binary, or a JUnit Test class for a java_test.) * * @param sourceFiles the source files for this rule * @return a fully qualified Java class name, or null if none could be * determined. */ public static String determinePrimaryClass( RuleContext ruleContext, ImmutableList<Artifact> sourceFiles) { return determinePrimaryClass(ruleContext.getTarget(), sourceFiles); } private static String determinePrimaryClass(Target target, ImmutableList<Artifact> sourceFiles) { if (!sourceFiles.isEmpty()) { String mainSource = target.getName() + ".java"; for (Artifact sourceFile : sourceFiles) { PathFragment path = sourceFile.getRootRelativePath(); if (path.getBaseName().equals(mainSource)) { return JavaUtil.getJavaFullClassname(FileSystemUtils.removeExtension(path)); } } } // Last resort: Use the name and package name of the target. // TODO(bazel-team): this should be fixed to use a source file from the dependencies to // determine the package of the Java class. return JavaUtil.getJavaFullClassname(Util.getWorkspaceRelativePath(target)); } /** * Gets the value of the "jvm_flags" attribute combining it with the default * options and expanding any make variables and $(location) tags. */ public static List<String> getJvmFlags(RuleContext ruleContext) { List<String> jvmFlags = new ArrayList<>(); jvmFlags.addAll(ruleContext.getFragment(JavaConfiguration.class).getDefaultJvmFlags()); jvmFlags.addAll(ruleContext.getExpandedStringListAttr("jvm_flags", RuleContext.Tokenize.NO)); return jvmFlags; } private static List<TransitiveInfoCollection> getRuntimeDeps(RuleContext ruleContext) { // We need to check here because there are classes inheriting from this class that implement // rules that don't have this attribute. if (ruleContext.attributes().has("runtime_deps", BuildType.LABEL_LIST)) { // Do not remove <TransitiveInfoCollection>: workaround for Java 7 type inference. return ImmutableList.<TransitiveInfoCollection>copyOf( ruleContext.getPrerequisites("runtime_deps", Mode.TARGET)); } else { return ImmutableList.of(); } } public JavaTargetAttributes.Builder initCommon() { return initCommon(ImmutableList.<Artifact>of(), semantics.getExtraJavacOpts(ruleContext)); } /** * Initialize the common actions and build various collections of artifacts * for the initializationHook() methods of the subclasses. * * <p>Note that not all subclasses call this method. * * @return the processed attributes */ public JavaTargetAttributes.Builder initCommon( Collection<Artifact> extraSrcs, Iterable<String> extraJavacOpts) { Preconditions.checkState(javacOpts == null); javacOpts = computeJavacOpts(extraJavacOpts); activePlugins = collectPlugins(); JavaTargetAttributes.Builder javaTargetAttributes = new JavaTargetAttributes.Builder(semantics); javaCompilationHelper = new JavaCompilationHelper( ruleContext, semantics, javacOpts, javaTargetAttributes); processSrcs(javaTargetAttributes); javaTargetAttributes.addSourceArtifacts(sources); javaTargetAttributes.addSourceArtifacts(extraSrcs); processRuntimeDeps(javaTargetAttributes); semantics.commonDependencyProcessing(ruleContext, javaTargetAttributes, targetsTreatedAsDeps(ClasspathType.COMPILE_ONLY)); semantics.checkProtoDeps(ruleContext, targetsTreatedAsDeps(ClasspathType.BOTH)); if (disallowDepsWithoutSrcs(ruleContext.getRule().getRuleClass()) && ruleContext.attributes().get("srcs", BuildType.LABEL_LIST).isEmpty() && ruleContext.getRule().isAttributeValueExplicitlySpecified("deps")) { ruleContext.attributeError("deps", "deps not allowed without srcs; move to runtime_deps?"); } for (Artifact resource : semantics.collectResources(ruleContext)) { javaTargetAttributes.addResource( JavaHelper.getJavaResourcePath(semantics, ruleContext, resource), resource); } if (ruleContext.attributes().has("resource_jars", BuildType.LABEL_LIST)) { javaTargetAttributes.addResourceJars(PrerequisiteArtifacts.nestedSet( ruleContext, "resource_jars", Mode.TARGET)); } addPlugins(javaTargetAttributes); javaTargetAttributes.setRuleKind(ruleContext.getRule().getRuleClass()); javaTargetAttributes.setTargetLabel(ruleContext.getLabel()); return javaTargetAttributes; } private boolean disallowDepsWithoutSrcs(String ruleClass) { return ruleClass.equals("java_library") || ruleClass.equals("java_binary") || ruleClass.equals("java_test"); } public ImmutableList<? extends TransitiveInfoCollection> targetsTreatedAsDeps( ClasspathType type) { return targetsTreatedAsDeps.get(type); } /** * Returns the default dependencies for the given classpath context. */ public static ImmutableList<TransitiveInfoCollection> defaultDeps(RuleContext ruleContext, JavaSemantics semantics, ClasspathType type) { return collectTargetsTreatedAsDeps(ruleContext, semantics, type); } private static ImmutableList<TransitiveInfoCollection> collectTargetsTreatedAsDeps( RuleContext ruleContext, JavaSemantics semantics, ClasspathType type) { ImmutableList.Builder<TransitiveInfoCollection> builder = new Builder<>(); if (!type.equals(ClasspathType.COMPILE_ONLY)) { builder.addAll(getRuntimeDeps(ruleContext)); builder.addAll(getExports(ruleContext)); } builder.addAll(ruleContext.getPrerequisites("deps", Mode.TARGET)); semantics.collectTargetsTreatedAsDeps(ruleContext, builder, type); // Implicitly add dependency on java launcher cc_binary when --java_launcher= is enabled, // or when launcher attribute is specified in a build rule. TransitiveInfoCollection launcher = JavaHelper.launcherForTarget(semantics, ruleContext); if (launcher != null) { builder.add(launcher); } return builder.build(); } public void addTransitiveInfoProviders( RuleConfiguredTargetBuilder builder, JavaSkylarkApiProvider.Builder skylarkApiProvider, NestedSet<Artifact> filesToBuild, @Nullable Artifact classJar) { addTransitiveInfoProviders( builder, skylarkApiProvider, filesToBuild, classJar, JAVA_COLLECTION_SPEC); } public void addTransitiveInfoProviders( RuleConfiguredTargetBuilder builder, JavaSkylarkApiProvider.Builder skylarkApiProvider, NestedSet<Artifact> filesToBuild, @Nullable Artifact classJar, InstrumentationSpec instrumentationSpec) { JavaCompilationInfoProvider compilationInfoProvider = createCompilationInfoProvider(); JavaExportsProvider exportsProvider = collectTransitiveExports(); skylarkApiProvider .setCompilationInfoProvider(compilationInfoProvider) .setExportsProvider(exportsProvider); builder .add( InstrumentedFilesProvider.class, getInstrumentationFilesProvider(ruleContext, filesToBuild, instrumentationSpec)) .add(JavaExportsProvider.class, exportsProvider) .addOutputGroup(OutputGroupProvider.FILES_TO_COMPILE, getFilesToCompile(classJar)) .add(JavaCompilationInfoProvider.class, compilationInfoProvider); } private static InstrumentedFilesProvider getInstrumentationFilesProvider(RuleContext ruleContext, NestedSet<Artifact> filesToBuild, InstrumentationSpec instrumentationSpec) { return InstrumentedFilesCollector.collect( ruleContext, instrumentationSpec, JAVA_METADATA_COLLECTOR, filesToBuild, NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), NestedSetBuilder.<Pair<String, String>>emptySet(Order.STABLE_ORDER), /*withBaselineCoverage*/!TargetUtils.isTestRule(ruleContext.getTarget())); } public void addGenJarsProvider( RuleConfiguredTargetBuilder builder, JavaSkylarkApiProvider.Builder javaSkylarkApiProvider, @Nullable Artifact genClassJar, @Nullable Artifact genSourceJar) { JavaGenJarsProvider genJarsProvider = collectTransitiveGenJars( javaCompilationHelper.usesAnnotationProcessing(), genClassJar, genSourceJar); NestedSetBuilder<Artifact> genJarsBuilder = NestedSetBuilder.stableOrder(); genJarsBuilder.addTransitive(genJarsProvider.getTransitiveGenClassJars()); genJarsBuilder.addTransitive(genJarsProvider.getTransitiveGenSourceJars()); javaSkylarkApiProvider.setGenJarsProvider1(genJarsProvider); builder .add(JavaGenJarsProvider.class, genJarsProvider) .addOutputGroup(JavaSemantics.GENERATED_JARS_OUTPUT_GROUP, genJarsBuilder.build()); } /** * Processes the sources of this target, adding them as messages or proper * sources. */ private void processSrcs(JavaTargetAttributes.Builder attributes) { for (MessageBundleProvider srcItem : ruleContext.getPrerequisites( "srcs", Mode.TARGET, MessageBundleProvider.class)) { attributes.addMessages(srcItem.getMessages()); } } /** * Processes the transitive runtime_deps of this target. */ private void processRuntimeDeps(JavaTargetAttributes.Builder attributes) { List<TransitiveInfoCollection> runtimeDepInfo = getRuntimeDeps(ruleContext); checkRuntimeDeps(ruleContext, runtimeDepInfo); JavaCompilationArgs args = JavaCompilationArgs.builder() .addTransitiveTargets(runtimeDepInfo, true, ClasspathType.RUNTIME_ONLY) .build(); attributes.addRuntimeClassPathEntries(args.getRuntimeJars()); attributes.addInstrumentationMetadataEntries(args.getInstrumentationMetadata()); } /** * Adds information about the annotation processors that should be run for this java target to * the target attributes. */ private void addPlugins(JavaTargetAttributes.Builder attributes) { for (JavaPluginInfoProvider plugin : activePlugins) { for (String name : plugin.getProcessorClasses()) { attributes.addProcessorName(name); } // Now get the plugin-libraries runtime classpath. attributes.addProcessorPath(plugin.getProcessorClasspath()); // Add api-generating plugins for (String name : plugin.getApiGeneratingProcessorClasses()) { attributes.addApiGeneratingProcessorName(name); } attributes.addApiGeneratingProcessorPath(plugin.getApiGeneratingProcessorClasspath()); } } private ImmutableList<JavaPluginInfoProvider> collectPlugins() { List<JavaPluginInfoProvider> result = new ArrayList<>(); Iterables.addAll(result, getPluginInfoProvidersForAttribute(ruleContext, ":java_plugins", Mode.HOST)); Iterables.addAll(result, getPluginInfoProvidersForAttribute(ruleContext, "plugins", Mode.HOST)); Iterables.addAll(result, getPluginInfoProvidersForAttribute(ruleContext, "deps", Mode.TARGET)); return ImmutableList.copyOf(result); } private static Iterable<JavaPluginInfoProvider> getPluginInfoProvidersForAttribute( RuleContext ruleContext, String attribute, Mode mode) { if (ruleContext.attributes().has(attribute, BuildType.LABEL_LIST)) { return ruleContext.getPrerequisites(attribute, mode, JavaPluginInfoProvider.class); } return ImmutableList.of(); } public static JavaPluginInfoProvider getTransitivePlugins(RuleContext ruleContext) { return JavaPluginInfoProvider.merge(Iterables.concat( getPluginInfoProvidersForAttribute(ruleContext, "exported_plugins", Mode.HOST), getPluginInfoProvidersForAttribute(ruleContext, "exports", Mode.TARGET))); } public static Runfiles getRunfiles( RuleContext ruleContext, JavaSemantics semantics, JavaCompilationArtifacts javaArtifacts, boolean neverLink) { // The "neverlink" attribute is transitive, so we don't add any // runfiles from this target or its dependencies. if (neverLink) { return Runfiles.EMPTY; } Runfiles.Builder runfilesBuilder = new Runfiles.Builder( ruleContext.getWorkspaceName(), ruleContext.getConfiguration().legacyExternalRunfiles()) .addArtifacts(javaArtifacts.getRuntimeJars()); runfilesBuilder.addRunfiles(ruleContext, RunfilesProvider.DEFAULT_RUNFILES); runfilesBuilder.add(ruleContext, JavaRunfilesProvider.TO_RUNFILES); List<TransitiveInfoCollection> depsForRunfiles = new ArrayList<>(); if (ruleContext.getRule().isAttrDefined("runtime_deps", BuildType.LABEL_LIST)) { depsForRunfiles.addAll(ruleContext.getPrerequisites("runtime_deps", Mode.TARGET)); } if (ruleContext.getRule().isAttrDefined("exports", BuildType.LABEL_LIST)) { depsForRunfiles.addAll(ruleContext.getPrerequisites("exports", Mode.TARGET)); } runfilesBuilder.addTargets(depsForRunfiles, RunfilesProvider.DEFAULT_RUNFILES); runfilesBuilder.addTargets(depsForRunfiles, JavaRunfilesProvider.TO_RUNFILES); TransitiveInfoCollection launcher = JavaHelper.launcherForTarget(semantics, ruleContext); if (launcher != null) { runfilesBuilder.addTarget(launcher, RunfilesProvider.DATA_RUNFILES); } semantics.addRunfilesForLibrary(ruleContext, runfilesBuilder); return runfilesBuilder.build(); } /** * Gets all the deps. */ public final Iterable<? extends TransitiveInfoCollection> getDependencies() { return targetsTreatedAsDeps(ClasspathType.BOTH); } /** * Gets all the deps that implement a particular provider. */ public final <P extends TransitiveInfoProvider> Iterable<P> getDependencies( Class<P> provider) { return AnalysisUtils.getProviders(getDependencies(), provider); } /** * Gets all the deps that implement a particular provider. */ public final <P extends SkylarkClassObject> Iterable<P> getDependencies( ClassObjectConstructor.Key provider, Class<P> resultClass) { return AnalysisUtils.getProviders(getDependencies(), provider, resultClass); } /** * Returns true if and only if this target has the neverlink attribute set to * 1, or false if the neverlink attribute does not exist (for example, on * *_binary targets) * * @return the value of the neverlink attribute. */ public static final boolean isNeverLink(RuleContext ruleContext) { return ruleContext.getRule().isAttrDefined("neverlink", Type.BOOLEAN) && ruleContext.attributes().get("neverlink", Type.BOOLEAN); } private static NestedSet<Artifact> getFilesToCompile(Artifact classJar) { if (classJar == null) { // Some subclasses don't produce jars return NestedSetBuilder.emptySet(Order.STABLE_ORDER); } return NestedSetBuilder.create(Order.STABLE_ORDER, classJar); } public ImmutableList<Artifact> getSrcsArtifacts() { return sources; } public ImmutableList<String> getJavacOpts() { return javacOpts; } public ImmutableList<Artifact> getBootClasspath() { return classpathFragment.getBootClasspath(); } public NestedSet<Artifact> getRuntimeClasspath() { return classpathFragment.getRuntimeClasspath(); } public NestedSet<Artifact> getCompileTimeClasspath() { return classpathFragment.getCompileTimeClasspath(); } public RuleContext getRuleContext() { return ruleContext; } private JavaCompilationInfoProvider createCompilationInfoProvider() { return new JavaCompilationInfoProvider.Builder() .setJavacOpts(javacOpts) .setBootClasspath(getBootClasspath()) .setCompilationClasspath(getCompileTimeClasspath()) .setRuntimeClasspath(getRuntimeClasspath()) .build(); } }