com.google.devtools.build.lib.rules.java.JavaCommon.java Source code

Java tutorial

Introduction

Here is the source code for com.google.devtools.build.lib.rules.java.JavaCommon.java

Source

// 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.SkylarkProviders;
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.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);
                }
            }
        }
    };

    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
     * &lt;target_name&gt;_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 ImmutableList<Artifact> getProcessorClasspathJars() {
        Set<Artifact> processorClasspath = new LinkedHashSet<>();
        for (JavaPluginInfoProvider plugin : activePlugins) {
            for (Artifact classpathJar : plugin.getProcessorClasspath()) {
                processorClasspath.add(classpathJar);
            }
        }
        return ImmutableList.copyOf(processorClasspath);
    }

    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();
        Set<JavaCompilationArgsProvider> addedProviders = new LinkedHashSet<>();
        if (outDeps != null) {
            builder.add(outDeps);
        }

        for (JavaCompilationArgsProvider provider : AnalysisUtils.getProviders(getExports(ruleContext),
                JavaCompilationArgsProvider.class)) {
            builder.addTransitive(provider.getCompileTimeJavaDependencyArtifacts());
            addedProviders.add(provider);
        }

        // We also check for artifacts in the JavaProvider of the dependencies (might exist when
        // information is passed from a custom Skylark Java rule).
        for (SkylarkProviders skylarkProviders : AnalysisUtils.getProviders(getExports(ruleContext),
                SkylarkProviders.class)) {
            JavaProvider javaProvider = (JavaProvider) skylarkProviders
                    .getDeclaredProvider(JavaProvider.JAVA_PROVIDER.getKey());
            if (javaProvider != null) {
                JavaCompilationArgsProvider compilationArgsProvider = javaProvider
                        .getProvider(JavaCompilationArgsProvider.class);
                if (!addedProviders.contains(compilationArgsProvider)) {
                    builder.addTransitive((javaProvider.getProvider(JavaCompilationArgsProvider.class))
                            .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 (TransitiveInfoCollection dep : getDependencies()) {
            JavaSourceJarsProvider sourceJarsProvider = dep.getProvider(JavaSourceJarsProvider.class);
            if (sourceJarsProvider == null) {
                // A target can either have both JavaSourceJarsProvider and JavaProvider that
                // encapsulates the same information, or just one of them.
                sourceJarsProvider = JavaProvider.getProvider(JavaSourceJarsProvider.class, dep);
            }
            if (sourceJarsProvider != null) {
                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 = new PathFragment(new PathFragment(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));

        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);

        // 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);
    }

    /**
     * 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();
    }
}