// Copyright 2016 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 static com.google.common.truth.Truth.assertThat;
import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.prettyJarNames;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.packages.SkylarkClassObject;
import com.google.devtools.build.lib.packages.SkylarkClassObjectConstructor.SkylarkKey;
import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
import java.util.Iterator;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Tests Skylark API for Java rules.
*/
@RunWith(JUnit4.class)
public class JavaSkylarkApiTest extends BuildViewTestCase {
@Test
public void testJavaPlugin() throws Exception {
scratch.file(
"java/test/extension.bzl",
"result = provider()",
"def impl(ctx):",
" depj = ctx.attr.dep.java",
" return [result(",
" processor_classpath = depj.annotation_processing.processor_classpath,",
" processor_classnames = depj.annotation_processing.processor_classnames,",
" )]",
"my_rule = rule(impl, attrs = { 'dep' : attr.label() })"
);
scratch.file(
"java/test/BUILD",
"load(':extension.bzl', 'my_rule')",
"java_library(name = 'plugin_dep',",
" srcs = [ 'ProcessorDep.java'])",
"java_plugin(name = 'plugin',",
" srcs = ['AnnotationProcessor.java'],",
" processor_class = 'com.google.process.stuff',",
" deps = [ ':plugin_dep' ])",
"java_library(name = 'to_be_processed',",
" plugins = [':plugin'],",
" srcs = ['ToBeProcessed.java'])",
"my_rule(name = 'my', dep = ':to_be_processed')");
ConfiguredTarget configuredTarget = getConfiguredTarget("//java/test:my");
SkylarkClassObject skylarkClassObject = configuredTarget.get(
new SkylarkKey(Label.parseAbsolute("//java/test:extension.bzl"), "result"));
assertThat(
Iterables.transform(
((SkylarkNestedSet) skylarkClassObject.getValue("processor_classpath"))
.toCollection(),
new Function<Object, String>() {
@Override
public String apply(Object o) {
return ((Artifact) o).getFilename();
}
}))
.containsExactly("libplugin.jar", "libplugin_dep.jar");
assertThat((List<?>) skylarkClassObject.getValue("processor_classnames"))
.containsExactly("com.google.process.stuff");
}
@Test
public void testJavaProviderFieldsAreSkylarkAccessible() throws Exception {
// The Skylark evaluation itself will test that compile_jars and
// transitive_runtime_jars are returning a list readable by Skylark with
// the expected number of entries.
scratch.file(
"java/test/extension.bzl",
"result = provider()",
"def impl(ctx):",
" java_provider = ctx.attr.dep[java_common.provider]",
" jp_cjar_cnt = len(java_provider.compile_jars)",
" jp_rjar_cnt = len(java_provider.transitive_runtime_jars)",
" if(jp_cjar_cnt != ctx.attr.cnt_cjar):",
" fail('#compile_jars is %d, not %d' % (jp_cjar_cnt, ctx.attr.cnt_cjar))",
" if(jp_rjar_cnt != ctx.attr.cnt_rjar):",
" fail('#transitive_runtime_jars is %d, not %d' % (jp_rjar_cnt, ctx.attr.cnt_rjar))",
" return [result(",
" compile_jars = java_provider.compile_jars,",
" transitive_runtime_jars = java_provider.transitive_runtime_jars,",
" )]",
"my_rule = rule(impl, attrs = { ",
" 'dep' : attr.label(), ",
" 'cnt_cjar' : attr.int(), ",
" 'cnt_rjar' : attr.int(), ",
"})");
scratch.file(
"java/test/BUILD",
"load(':extension.bzl', 'my_rule')",
"java_library(name = 'parent',",
" srcs = [ 'Parent.java'])",
"java_library(name = 'jl',",
" srcs = ['Jl.java'],",
" deps = [ ':parent' ])",
"my_rule(name = 'my', dep = ':jl', cnt_cjar = 1, cnt_rjar = 2)");
// Now, get that information and ensure it is equal to what the jl java_library
// was presenting
ConfiguredTarget myConfiguredTarget = getConfiguredTarget("//java/test:my");
ConfiguredTarget javaLibraryTarget = getConfiguredTarget("//java/test:jl");
// Extract out the information from skylark rule
SkylarkClassObject skylarkClassObject =
myConfiguredTarget.get(
new SkylarkKey(Label.parseAbsolute("//java/test:extension.bzl"), "result"));
SkylarkNestedSet rawMyCompileJars =
(SkylarkNestedSet) (skylarkClassObject.getValue("compile_jars"));
SkylarkNestedSet rawMyTransitiveRuntimeJars =
(SkylarkNestedSet) (skylarkClassObject.getValue("transitive_runtime_jars"));
NestedSet<Artifact> myCompileJars = rawMyCompileJars.getSet(Artifact.class);
NestedSet<Artifact> myTransitiveRuntimeJars = rawMyTransitiveRuntimeJars.getSet(Artifact.class);
// Extract out information from native rule
JavaCompilationArgsProvider jlJavaCompilationArgsProvider =
JavaProvider.getProvider(JavaCompilationArgsProvider.class, javaLibraryTarget);
NestedSet<Artifact> jlCompileJars =
jlJavaCompilationArgsProvider.getJavaCompilationArgs().getCompileTimeJars();
NestedSet<Artifact> jlTransitiveRuntimeJars =
jlJavaCompilationArgsProvider.getRecursiveJavaCompilationArgs().getRuntimeJars();
// Using reference equality since should be precisely identical
assertThat(myCompileJars == jlCompileJars).isTrue();
assertThat(myTransitiveRuntimeJars == jlTransitiveRuntimeJars).isTrue();
}
@Test
public void constructJavaProvider() throws Exception {
scratch.file(
"foo/extension.bzl",
"def _impl(ctx):",
" my_provider = java_common.create_provider(",
" compile_time_jars = depset(ctx.files.compile_time_jars),",
" runtime_jars = depset(ctx.files.runtime_jars))",
" return [my_provider]",
"my_rule = rule(_impl, ",
" attrs = { ",
" 'compile_time_jars' : attr.label_list(allow_files=['.jar']),",
" 'runtime_jars': attr.label_list(allow_files=['.jar'])",
"})");
scratch.file("foo/liba.jar");
scratch.file("foo/libb.jar");
scratch.file("foo/BUILD",
"load(':extension.bzl', 'my_rule')",
"my_rule(name = 'myrule',",
" compile_time_jars = ['liba.jar'],",
" runtime_jars = ['libb.jar']",
")"
);
ConfiguredTarget target = getConfiguredTarget("//foo:myrule");
JavaCompilationArgsProvider provider =
JavaProvider.getProvider(JavaCompilationArgsProvider.class, target);
assertThat(provider).isNotNull();
List<String> compileTimeJars =
prettyJarNames(provider.getJavaCompilationArgs().getCompileTimeJars());
assertThat(compileTimeJars).containsExactly("foo/liba.jar");
List<String> runtimeJars = prettyJarNames(
provider.getRecursiveJavaCompilationArgs().getRuntimeJars());
assertThat(runtimeJars).containsExactly("foo/libb.jar");
}
@Test
public void constructJavaProviderWithAnotherJavaProvider() throws Exception {
scratch.file(
"foo/extension.bzl",
"def _impl(ctx):",
" transitive_provider = java_common.merge(",
" [dep[java_common.provider] for dep in ctx.attr.deps])",
" my_provider = java_common.create_provider(",
" compile_time_jars = depset(ctx.files.compile_time_jars),",
" runtime_jars = depset(ctx.files.runtime_jars))",
" return [java_common.merge([my_provider, transitive_provider])]",
"my_rule = rule(_impl, ",
" attrs = { ",
" 'compile_time_jars' : attr.label_list(allow_files=['.jar']),",
" 'runtime_jars': attr.label_list(allow_files=['.jar']),",
" 'deps': attr.label_list()",
"})");
scratch.file("foo/liba.jar");
scratch.file("foo/libb.jar");
scratch.file("foo/BUILD",
"load(':extension.bzl', 'my_rule')",
"java_library(name = 'java_dep',",
" srcs = ['A.java'])",
"my_rule(name = 'myrule',",
" compile_time_jars = ['liba.jar'],",
" runtime_jars = ['libb.jar'],",
" deps = [':java_dep']",
")"
);
ConfiguredTarget target = getConfiguredTarget("//foo:myrule");
JavaCompilationArgsProvider provider =
JavaProvider.getProvider(JavaCompilationArgsProvider.class, target);
assertThat(provider).isNotNull();
List<String> compileTimeJars =
prettyJarNames(provider.getJavaCompilationArgs().getCompileTimeJars());
assertThat(compileTimeJars).containsExactly("foo/liba.jar", "foo/libjava_dep-hjar.jar");
List<String> runtimeJars = prettyJarNames(
provider.getRecursiveJavaCompilationArgs().getRuntimeJars());
assertThat(runtimeJars).containsExactly("foo/libb.jar", "foo/libjava_dep.jar");
}
@Test
public void constructJavaProviderJavaLibrary() throws Exception {
scratch.file(
"foo/extension.bzl",
"def _impl(ctx):",
" my_provider = java_common.create_provider(",
" compile_time_jars = depset(ctx.files.compile_time_jars),",
" runtime_jars = depset(ctx.files.runtime_jars))",
" return [my_provider]",
"my_rule = rule(_impl, ",
" attrs = { ",
" 'compile_time_jars' : attr.label_list(allow_files=['.jar']),",
" 'runtime_jars': attr.label_list(allow_files=['.jar'])",
"})");
scratch.file("foo/liba.jar");
scratch.file("foo/libb.jar");
scratch.file("foo/BUILD",
"load(':extension.bzl', 'my_rule')",
"my_rule(name = 'myrule',",
" compile_time_jars = ['liba.jar'],",
" runtime_jars = ['libb.jar']",
")",
"java_library(name = 'java_lib',",
" srcs = ['C.java'],",
" deps = [':myrule']",
")"
);
ConfiguredTarget target = getConfiguredTarget("//foo:java_lib");
JavaCompilationArgsProvider provider =
JavaProvider.getProvider(JavaCompilationArgsProvider.class, target);
List<String> compileTimeJars = prettyJarNames(
provider.getRecursiveJavaCompilationArgs().getCompileTimeJars());
assertThat(compileTimeJars).containsExactly("foo/libjava_lib-hjar.jar", "foo/liba.jar");
List<String> runtimeJars = prettyJarNames(
provider.getRecursiveJavaCompilationArgs().getRuntimeJars());
assertThat(runtimeJars).containsExactly("foo/libjava_lib.jar", "foo/libb.jar");
}
@Test
public void javaProviderExposedOnJavaLibrary() throws Exception {
scratch.file(
"foo/extension.bzl",
"my_provider = provider()",
"def _impl(ctx):",
" dep_params = ctx.attr.dep[java_common.provider]",
" return [my_provider(p = dep_params)]",
"my_rule = rule(_impl, attrs = { 'dep' : attr.label() })");
scratch.file(
"foo/BUILD",
"load(':extension.bzl', 'my_rule')",
"java_library(name = 'jl', srcs = ['java/A.java'])",
"my_rule(name = 'r', dep = ':jl')");
ConfiguredTarget myRuleTarget = getConfiguredTarget("//foo:r");
ConfiguredTarget javaLibraryTarget = getConfiguredTarget("//foo:jl");
SkylarkKey myProviderKey =
new SkylarkKey(Label.parseAbsolute("//foo:extension.bzl"), "my_provider");
SkylarkClassObject declaredProvider =
myRuleTarget.get(myProviderKey);
Object javaProvider = declaredProvider.getValue("p");
assertThat(javaProvider).isInstanceOf(JavaProvider.class);
assertThat(javaLibraryTarget.getProvider(JavaProvider.class)).isEqualTo(javaProvider);
}
@Test
public void javaProviderPropagation() throws Exception {
scratch.file(
"foo/extension.bzl",
"def _impl(ctx):",
" dep_params = ctx.attr.dep[java_common.provider]",
" return [dep_params]",
"my_rule = rule(_impl, attrs = { 'dep' : attr.label() })");
scratch.file(
"foo/BUILD",
"load(':extension.bzl', 'my_rule')",
"java_library(name = 'jl', srcs = ['java/A.java'])",
"my_rule(name = 'r', dep = ':jl')",
"java_library(name = 'jl_top', srcs = ['java/C.java'], deps = [':r'])");
ConfiguredTarget myRuleTarget = getConfiguredTarget("//foo:r");
ConfiguredTarget javaLibraryTarget = getConfiguredTarget("//foo:jl");
ConfiguredTarget topJavaLibraryTarget = getConfiguredTarget("//foo:jl_top");
Object javaProvider = myRuleTarget.get(JavaProvider.JAVA_PROVIDER.getKey());
assertThat(javaProvider).isInstanceOf(JavaProvider.class);
JavaProvider jlJavaProvider = javaLibraryTarget.getProvider(JavaProvider.class);
assertThat(jlJavaProvider == javaProvider).isTrue();
JavaProvider jlTopJavaProvider = topJavaLibraryTarget.getProvider(JavaProvider.class);
javaCompilationArgsHaveTheSameParent(
jlJavaProvider.getProvider(JavaCompilationArgsProvider.class).getJavaCompilationArgs(),
jlTopJavaProvider.getProvider(JavaCompilationArgsProvider.class).getJavaCompilationArgs());
}
@Test
public void strictDepsEnabled() throws Exception {
scratch.file(
"foo/custom_library.bzl",
"def _impl(ctx):",
" java_provider = java_common.merge([dep[java_common.provider] for dep in ctx.attr.deps])",
" if not ctx.attr.strict_deps:",
" java_provider = java_common.make_non_strict(java_provider)",
" return [java_provider]",
"custom_library = rule(",
" attrs = {",
" 'deps': attr.label_list(),",
" 'strict_deps': attr.bool()",
" },",
" implementation = _impl",
")"
);
scratch.file(
"foo/BUILD",
"load(':custom_library.bzl', 'custom_library')",
"custom_library(name = 'custom', deps = [':a'], strict_deps = True)",
"java_library(name = 'a', srcs = ['java/A.java'], deps = [':b'])",
"java_library(name = 'b', srcs = ['java/B.java'])"
);
ConfiguredTarget myRuleTarget = getConfiguredTarget("//foo:custom");
JavaCompilationArgsProvider javaCompilationArgsProvider =
JavaProvider.getProvider(JavaCompilationArgsProvider.class, myRuleTarget);
List<String> directJars = prettyJarNames(
javaCompilationArgsProvider.getJavaCompilationArgs().getRuntimeJars());
assertThat(directJars).containsExactly("foo/liba.jar");
}
@Test
public void strictDepsDisabled() throws Exception {
scratch.file(
"foo/custom_library.bzl",
"def _impl(ctx):",
" java_provider = java_common.merge([dep[java_common.provider] for dep in ctx.attr.deps])",
" if not ctx.attr.strict_deps:",
" java_provider = java_common.make_non_strict(java_provider)",
" return [java_provider]",
"custom_library = rule(",
" attrs = {",
" 'deps': attr.label_list(),",
" 'strict_deps': attr.bool()",
" },",
" implementation = _impl",
")"
);
scratch.file(
"foo/BUILD",
"load(':custom_library.bzl', 'custom_library')",
"custom_library(name = 'custom', deps = [':a'], strict_deps = False)",
"java_library(name = 'a', srcs = ['java/A.java'], deps = [':b'])",
"java_library(name = 'b', srcs = ['java/B.java'])"
);
ConfiguredTarget myRuleTarget = getConfiguredTarget("//foo:custom");
JavaCompilationArgsProvider javaCompilationArgsProvider =
JavaProvider.getProvider(JavaCompilationArgsProvider.class, myRuleTarget);
List<String> directJars = prettyJarNames(
javaCompilationArgsProvider.getJavaCompilationArgs().getRuntimeJars());
assertThat(directJars).containsExactly("foo/liba.jar", "foo/libb.jar");
}
private static boolean javaCompilationArgsHaveTheSameParent(
JavaCompilationArgs args, JavaCompilationArgs otherArgs) {
if (!nestedSetsOfArtifactHaveTheSameParent(
args.getCompileTimeJars(), otherArgs.getCompileTimeJars())) {
return false;
}
if (!nestedSetsOfArtifactHaveTheSameParent(
args.getInstrumentationMetadata(), otherArgs.getInstrumentationMetadata())) {
return false;
}
if (!nestedSetsOfArtifactHaveTheSameParent(args.getRuntimeJars(), otherArgs.getRuntimeJars())) {
return false;
}
return true;
}
private static boolean nestedSetsOfArtifactHaveTheSameParent(
NestedSet<Artifact> artifacts, NestedSet<Artifact> otherArtifacts) {
Iterator<Artifact> iterator = artifacts.iterator();
Iterator<Artifact> otherIterator = otherArtifacts.iterator();
while (iterator.hasNext() && otherIterator.hasNext()) {
Artifact artifact = (Artifact) iterator.next();
Artifact otherArtifact = (Artifact) otherIterator.next();
if (!artifact
.getPath()
.getParentDirectory()
.equals(otherArtifact.getPath().getParentDirectory())) {
return false;
}
}
if (iterator.hasNext() || otherIterator.hasNext()) {
return false;
}
return true;
}
}