// 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.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.FileProvider;
import com.google.devtools.build.lib.analysis.OutputGroupProvider;
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.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory;
import com.google.devtools.build.lib.rules.cpp.CcLinkParams;
import com.google.devtools.build.lib.rules.cpp.CcLinkParamsProvider;
import com.google.devtools.build.lib.rules.cpp.CcLinkParamsStore;
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 java.util.LinkedHashSet;
import java.util.Set;
/**
* An implementation for the "java_import" rule.
*/
public class JavaImport implements RuleConfiguredTargetFactory {
private final JavaSemantics semantics;
protected JavaImport(JavaSemantics semantics) {
this.semantics = semantics;
}
@Override
public ConfiguredTarget create(RuleContext ruleContext)
throws InterruptedException, RuleErrorException {
ImmutableList<Artifact> srcJars = ImmutableList.of();
ImmutableList<Artifact> jars = collectJars(ruleContext);
Artifact srcJar = ruleContext.getPrerequisiteArtifact("srcjar", Mode.TARGET);
if (ruleContext.hasErrors()) {
return null;
}
ImmutableList<TransitiveInfoCollection> targets =
ImmutableList.<TransitiveInfoCollection>builder()
.addAll(ruleContext.getPrerequisites("deps", Mode.TARGET))
.addAll(ruleContext.getPrerequisites("exports", Mode.TARGET))
.build();
final JavaCommon common = new JavaCommon(
ruleContext, semantics, /*srcs=*/ImmutableList.<Artifact>of(), targets, targets, targets);
semantics.checkRule(ruleContext, common);
// No need for javac options - no compilation happening here.
ImmutableBiMap.Builder<Artifact, Artifact> compilationToRuntimeJarMapBuilder =
ImmutableBiMap.builder();
ImmutableList<Artifact> interfaceJars =
processWithIjar(jars, ruleContext, compilationToRuntimeJarMapBuilder);
JavaCompilationArtifacts javaArtifacts = collectJavaArtifacts(jars, interfaceJars);
common.setJavaCompilationArtifacts(javaArtifacts);
CppCompilationContext transitiveCppDeps = common.collectTransitiveCppDeps();
NestedSet<LinkerInput> transitiveJavaNativeLibraries =
common.collectTransitiveJavaNativeLibraries();
boolean neverLink = JavaCommon.isNeverLink(ruleContext);
JavaCompilationArgs javaCompilationArgs =
common.collectJavaCompilationArgs(false, neverLink, false);
JavaCompilationArgs recursiveJavaCompilationArgs =
common.collectJavaCompilationArgs(true, neverLink, false);
NestedSet<Artifact> transitiveJavaSourceJars =
collectTransitiveJavaSourceJars(ruleContext, srcJar);
if (srcJar != null) {
srcJars = ImmutableList.of(srcJar);
}
// The "neverlink" attribute is transitive, so if it is enabled, we don't add any
// runfiles from this target or its dependencies.
Runfiles runfiles = neverLink ?
Runfiles.EMPTY :
new Runfiles.Builder(
ruleContext.getWorkspaceName(),
ruleContext.getConfiguration().legacyExternalRunfiles())
// add the jars to the runfiles
.addArtifacts(javaArtifacts.getRuntimeJars())
.addTargets(targets, RunfilesProvider.DEFAULT_RUNFILES)
.addRunfiles(ruleContext, RunfilesProvider.DEFAULT_RUNFILES)
.addTargets(targets, JavaRunfilesProvider.TO_RUNFILES)
.add(ruleContext, JavaRunfilesProvider.TO_RUNFILES)
.build();
CcLinkParamsStore ccLinkParamsStore = new CcLinkParamsStore() {
@Override
protected void collect(CcLinkParams.Builder builder, boolean linkingStatically,
boolean linkShared) {
builder.addTransitiveTargets(common.targetsTreatedAsDeps(ClasspathType.BOTH),
JavaCcLinkParamsProvider.TO_LINK_PARAMS, CcLinkParamsProvider.TO_LINK_PARAMS);
}
};
RuleConfiguredTargetBuilder ruleBuilder =
new RuleConfiguredTargetBuilder(ruleContext);
NestedSetBuilder<Artifact> filesBuilder = NestedSetBuilder.stableOrder();
filesBuilder.addAll(jars);
ImmutableBiMap<Artifact, Artifact> compilationToRuntimeJarMap =
compilationToRuntimeJarMapBuilder.build();
semantics.addProviders(
ruleContext,
common,
ImmutableList.<String>of(),
null /* classJar */,
srcJar /* srcJar */,
null /* genJar */,
null /* gensrcJar */,
compilationToRuntimeJarMap,
filesBuilder,
ruleBuilder);
NestedSet<Artifact> filesToBuild = filesBuilder.build();
JavaSourceInfoProvider javaSourceInfoProvider = new JavaSourceInfoProvider.Builder()
.setJarFiles(jars)
.setSourceJarsForJarFiles(srcJars)
.build();
JavaRuleOutputJarsProvider.Builder ruleOutputJarsProviderBuilder =
JavaRuleOutputJarsProvider.builder();
for (Artifact jar : jars) {
ruleOutputJarsProviderBuilder.addOutputJar(
jar, compilationToRuntimeJarMap.inverse().get(jar), srcJars);
}
NestedSet<Artifact> proguardSpecs = new ProguardLibrary(ruleContext).collectProguardSpecs();
JavaRuleOutputJarsProvider ruleOutputJarsProvider = ruleOutputJarsProviderBuilder.build();
JavaSourceJarsProvider sourceJarsProvider =
JavaSourceJarsProvider.create(transitiveJavaSourceJars, srcJars);
JavaCompilationArgsProvider compilationArgsProvider =
JavaCompilationArgsProvider.create(javaCompilationArgs, recursiveJavaCompilationArgs);
JavaSkylarkApiProvider.Builder skylarkApiProvider =
JavaSkylarkApiProvider.builder()
.setRuleOutputJarsProvider(ruleOutputJarsProvider)
.setSourceJarsProvider(sourceJarsProvider)
.setCompilationArgsProvider(compilationArgsProvider);
common.addTransitiveInfoProviders(ruleBuilder, skylarkApiProvider, filesToBuild, null);
JavaProvider javaProvider = JavaProvider.Builder.create()
.addProvider(JavaCompilationArgsProvider.class, compilationArgsProvider)
.addProvider(JavaRuleOutputJarsProvider.class, ruleOutputJarsProvider)
.addProvider(JavaSourceJarsProvider.class, sourceJarsProvider)
.build();
return ruleBuilder
.setFilesToBuild(filesToBuild)
.addSkylarkTransitiveInfo(JavaSkylarkApiProvider.NAME, skylarkApiProvider.build())
.addNativeDeclaredProvider(javaProvider)
.addProvider(JavaProvider.class, javaProvider)
.add(JavaRuleOutputJarsProvider.class, ruleOutputJarsProvider)
.add(
JavaRuntimeJarProvider.class,
new JavaRuntimeJarProvider(javaArtifacts.getRuntimeJars()))
.add(JavaNeverlinkInfoProvider.class, new JavaNeverlinkInfoProvider(neverLink))
.add(RunfilesProvider.class, RunfilesProvider.simple(runfiles))
.add(CcLinkParamsProvider.class, new CcLinkParamsProvider(ccLinkParamsStore))
.add(JavaCompilationArgsProvider.class, compilationArgsProvider)
.add(
JavaNativeLibraryProvider.class,
new JavaNativeLibraryProvider(transitiveJavaNativeLibraries))
.add(CppCompilationContext.class, transitiveCppDeps)
.add(JavaSourceInfoProvider.class, javaSourceInfoProvider)
.add(JavaSourceJarsProvider.class, sourceJarsProvider)
.add(ProguardSpecProvider.class, new ProguardSpecProvider(proguardSpecs))
.addOutputGroup(JavaSemantics.SOURCE_JARS_OUTPUT_GROUP, transitiveJavaSourceJars)
.addOutputGroup(OutputGroupProvider.HIDDEN_TOP_LEVEL, proguardSpecs)
.build();
}
private NestedSet<Artifact> collectTransitiveJavaSourceJars(RuleContext ruleContext,
Artifact srcJar) {
NestedSetBuilder<Artifact> transitiveJavaSourceJarBuilder =
NestedSetBuilder.stableOrder();
if (srcJar != null) {
transitiveJavaSourceJarBuilder.add(srcJar);
}
for (JavaSourceJarsProvider other : JavaProvider.getProvidersFromListOfTargets(
JavaSourceJarsProvider.class, ruleContext.getPrerequisites("exports", Mode.TARGET))) {
transitiveJavaSourceJarBuilder.addTransitive(other.getTransitiveSourceJars());
}
return transitiveJavaSourceJarBuilder.build();
}
private JavaCompilationArtifacts collectJavaArtifacts(
ImmutableList<Artifact> jars,
ImmutableList<Artifact> interfaceJars) {
JavaCompilationArtifacts.Builder javaArtifactsBuilder = new JavaCompilationArtifacts.Builder();
javaArtifactsBuilder.addRuntimeJars(jars);
// interfaceJars Artifacts have proper owner labels
javaArtifactsBuilder.addCompileTimeJars(interfaceJars);
return javaArtifactsBuilder.build();
}
private ImmutableList<Artifact> collectJars(RuleContext ruleContext) {
Set<Artifact> jars = new LinkedHashSet<>();
for (TransitiveInfoCollection info : ruleContext.getPrerequisites("jars", Mode.TARGET)) {
if (info.getProvider(JavaCompilationArgsProvider.class) != null) {
ruleContext.attributeError("jars", "should not refer to Java rules");
}
for (Artifact jar : info.getProvider(FileProvider.class).getFilesToBuild()) {
if (!JavaSemantics.JAR.matches(jar.getFilename())) {
ruleContext.attributeError("jars", jar.getFilename() + " is not a .jar file");
} else {
if (!jars.add(jar)) {
ruleContext.attributeError("jars", jar.getFilename() + " is a duplicate");
}
}
}
}
return ImmutableList.copyOf(jars);
}
private ImmutableList<Artifact> processWithIjar(ImmutableList<Artifact> jars,
RuleContext ruleContext,
ImmutableMap.Builder<Artifact, Artifact> compilationToRuntimeJarMap) {
ImmutableList.Builder<Artifact> interfaceJarsBuilder = ImmutableList.builder();
for (Artifact jar : jars) {
Artifact ijar = JavaCompilationHelper.createIjarAction(
ruleContext,
JavaCompilationHelper.getJavaToolchainProvider(ruleContext),
jar, true);
interfaceJarsBuilder.add(ijar);
compilationToRuntimeJarMap.put(ijar, jar);
}
return interfaceJarsBuilder.build();
}
}