blob: 9d96f2c7ba2602ad1df562a977234883d66b93db [file] [log] [blame]
// 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.proto;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.truth.Truth.assertThat;
import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.prettyArtifactNames;
import static com.google.devtools.build.lib.rules.java.JavaCompileActionTestHelper.getDirectJars;
import static com.google.devtools.build.lib.rules.java.JavaCompileActionTestHelper.getJavacArguments;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.eventbus.EventBus;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.ExtraActionArtifactsProvider;
import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
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.Provider;
import com.google.devtools.build.lib.packages.SkylarkProvider;
import com.google.devtools.build.lib.packages.StructImpl;
import com.google.devtools.build.lib.rules.java.JavaCompilationArgsProvider;
import com.google.devtools.build.lib.rules.java.JavaCompileAction;
import com.google.devtools.build.lib.rules.java.JavaInfo;
import com.google.devtools.build.lib.rules.java.JavaSourceJarsProvider;
import com.google.devtools.build.lib.rules.java.ProguardSpecProvider;
import com.google.devtools.build.lib.testutil.MoreAsserts;
import com.google.devtools.build.runtime.Runfiles;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.List;
import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests for the Skylark version of java_lite_proto_library rule. */
@RunWith(JUnit4.class)
public class SkylarkJavaLiteProtoLibraryTest extends BuildViewTestCase {
private static final String RULE_DIRECTORY = "tools/build_rules/java_lite_proto_library";
private ActionsTestUtil actionsTestUtil;
@Before
public final void setUpMocks() throws Exception {
scratch.file(
"java/com/google/io/protocol/BUILD",
"package(default_visibility=['//visibility:public'])",
"java_import(name = 'protocol',",
" jars = [ 'protocol.jar' ])");
scratch.file(
"java/com/google/io/protocol2/BUILD",
"package(default_visibility=['//visibility:public'])",
"java_import(name = 'protocol2',",
" jars = [ 'protocol2.jar' ])");
scratch.file("net/proto2/compiler/public/BUILD", "exports_files(['protocol_compiler'])");
// TODO(b/77901188): remove once j_p_l migration is complete
scratch.file(
"third_party/java/jsr250_annotations/BUILD",
"package(default_visibility=['//visibility:public'])",
"licenses(['notice'])",
"java_import(name = 'jsr250_source_annotations',",
" jars = [ 'jsr250_source_annotations.jar' ])");
mockToolchains();
actionsTestUtil = actionsTestUtil();
}
@Before
public final void setupSkylarkRule() throws Exception {
File[] files = Runfiles.location(RULE_DIRECTORY).listFiles();
for (File file : files) {
scratch.file(RULE_DIRECTORY + "/" + file.getName(), Files.readAllBytes(file.toPath()));
}
scratch.file(RULE_DIRECTORY + "/BUILD", "exports_files(['java_lite_proto_library.bzl'])");
invalidatePackages();
}
private void mockToolchains() throws IOException {
mockRuntimes();
scratch.file(
"tools/proto/toolchains/BUILD",
"package(default_visibility=['//visibility:public'])",
"proto_lang_toolchain(",
" name = 'javalite',",
" command_line = '--java_out=lite,immutable,no_enforce_api_compatibility:$(OUT)',",
" runtime = '//protobuf:javalite_runtime',",
")");
}
private void mockRuntimes() throws IOException {
mockToolsConfig.overwrite(
"protobuf/BUILD",
"package(default_visibility=['//visibility:public'])",
"java_library(name = 'javalite_runtime', srcs = ['javalite_runtime.java'])");
}
/** Tests that java_binaries which depend on proto_libraries depend on the right set of files. */
@Test
public void testBinaryDeps() throws Exception {
scratch.file(
"x/BUILD",
"load('//" + RULE_DIRECTORY + ":java_lite_proto_library.bzl', ",
" 'java_lite_proto_library')",
"java_lite_proto_library(name = 'lite_pb2', deps = [':foo'])",
"proto_library(name = 'foo', srcs = ['foo.proto', 'bar.proto'], deps = [':baz'])",
"proto_library(name = 'baz', srcs = ['baz.proto'])");
ConfiguredTarget target = getConfiguredTarget("//x:lite_pb2");
NestedSet<Artifact> filesToBuild = getFilesToBuild(target);
Iterable<String> deps = prettyArtifactNames(actionsTestUtil.artifactClosureOf(filesToBuild));
// Should depend on compiler and Java proto1 API.
assertThat(deps).contains("net/proto2/compiler/public/protocol_compiler");
// Also should not depend on RPC APIs.
assertThat(deps).doesNotContain("apps/xplat/rpc/codegen/protoc-gen-rpc");
// Should depend on Java outputs.
assertThat(deps).contains("x/foo-lite-src.jar");
assertThat(deps).contains("x/baz-lite-src.jar");
// Should depend on Java libraries.
assertThat(deps).contains("x/libfoo-lite.jar");
assertThat(deps).contains("x/libbaz-lite.jar");
assertThat(deps).contains("protobuf/libjavalite_runtime-hjar.jar");
}
/** Tests that we pass the correct arguments to the protocol compiler. */
@Test
public void testJavaProto2CompilerArgs() throws Exception {
scratch.file(
"x/BUILD",
"load('//" + RULE_DIRECTORY + ":java_lite_proto_library.bzl',",
" 'java_lite_proto_library')",
"java_lite_proto_library(name = 'lite_pb2', deps = [':protolib'])",
"proto_library(name = 'protolib', srcs = ['file.proto'])");
String genfilesDir = targetConfig.getGenfilesFragment().getPathString();
List<String> args =
getGeneratingSpawnAction(getConfiguredTarget("//x:lite_pb2"), "x/protolib-lite-src.jar")
.getRemainingArguments();
assertThat(args)
.containsAllOf(
"--java_out=lite,immutable,no_enforce_api_compatibility:"
+ genfilesDir
+ "/x/protolib-lite-src.jar",
"-Ix/file.proto=x/file.proto",
"x/file.proto")
.inOrder();
}
@Test
public void testProtoLibraryBuildsCompiledJar() throws Exception {
ConfiguredTarget target =
scratchConfiguredTarget(
"java",
"lite_pb2",
"load('//tools/build_rules/java_lite_proto_library:java_lite_proto_library.bzl',",
" 'java_lite_proto_library')",
"java_lite_proto_library(name = 'lite_pb2', deps = [':compiled'])",
"proto_library(name = 'compiled',",
" srcs = [ 'ok.proto' ])");
Artifact compiledJar =
ActionsTestUtil.getFirstArtifactEndingWith(
getFilesToBuild(target), "/libcompiled-lite.jar");
assertThat(compiledJar).isNotNull();
}
@Test
public void testCommandLineContainsTargetLabel() throws Exception {
scratch.file(
"java/lib/BUILD",
"load('//tools/build_rules/java_lite_proto_library:java_lite_proto_library.bzl',",
" 'java_lite_proto_library')",
"java_lite_proto_library(name = 'lite_pb2', deps = [':proto'])",
"proto_library(name = 'proto', srcs = ['dummy.proto'])");
JavaCompileAction javacAction =
(JavaCompileAction)
getGeneratingAction(
getConfiguredTarget("//java/lib:lite_pb2"), "java/lib/libproto-lite.jar");
List<String> commandLine =
ImmutableList.copyOf((Iterable<String>) getJavacArguments(javacAction));
MoreAsserts.assertContainsSublist(commandLine, "--target_label", "//java/lib:proto");
}
@Test
public void testEmptySrcsForJavaApi() throws Exception {
ConfiguredTarget target =
scratchConfiguredTarget(
"notbad",
"lite_pb2",
"load('//tools/build_rules/java_lite_proto_library:java_lite_proto_library.bzl',",
" 'java_lite_proto_library')",
"java_lite_proto_library(name = 'lite_pb2', deps = [':null_lib'])",
"proto_library(name = 'null_lib')");
JavaCompilationArgsProvider compilationArgsProvider =
getProvider(JavaCompilationArgsProvider.class, target);
assertThat(compilationArgsProvider).isNotNull();
assertThat(compilationArgsProvider.getDirectCompileTimeJars()).isNotNull();
JavaSourceJarsProvider sourceJarsProvider = getProvider(JavaSourceJarsProvider.class, target);
assertThat(sourceJarsProvider).isNotNull();
assertThat(sourceJarsProvider.getSourceJars()).isNotNull();
}
@Test
public void testSameVersionCompilerArguments() throws Exception {
scratch.file(
"cross/BUILD",
"load('//tools/build_rules/java_lite_proto_library:java_lite_proto_library.bzl',",
" 'java_lite_proto_library')",
"java_lite_proto_library(name = 'lite_pb2', deps = ['bravo'], strict_deps = 0)",
"proto_library(name = 'bravo', srcs = ['bravo.proto'], deps = [':alpha'])",
"proto_library(name = 'alpha')");
String genfilesDir = targetConfig.getGenfilesFragment().getPathString();
ConfiguredTarget litepb2 = getConfiguredTarget("//cross:lite_pb2");
List<String> args =
getGeneratingSpawnAction(litepb2, "cross/bravo-lite-src.jar").getRemainingArguments();
assertThat(args)
.containsAllOf(
"--java_out=lite,immutable,no_enforce_api_compatibility:"
+ genfilesDir
+ "/cross/bravo-lite-src.jar",
"-Icross/bravo.proto=cross/bravo.proto",
"cross/bravo.proto")
.inOrder();
List<String> directJars =
prettyArtifactNames(
getProvider(JavaCompilationArgsProvider.class, litepb2).getRuntimeJars());
assertThat(directJars)
.containsExactly("cross/libbravo-lite.jar", "protobuf/libjavalite_runtime.jar");
}
@Test
@Ignore
// TODO(elenairina): Enable this test when proguard specs are supported in the Skylark version of
// java_lite_proto_library OR delete this if Proguard support will be removed from Java rules.
public void testExportsProguardSpecsForSupportLibraries() throws Exception {
scratch.overwriteFile(
"protobuf/BUILD",
"package(default_visibility=['//visibility:public'])",
"java_library(name = 'javalite_runtime', srcs = ['javalite_runtime.java'], "
+ "proguard_specs = ['javalite_runtime.pro'])");
scratch.file(
"x/BUILD",
"load('//tools/build_rules/java_lite_proto_library:java_lite_proto_library.bzl',",
" 'java_lite_proto_library')",
"java_lite_proto_library(name = 'lite_pb2', deps = [':foo'])",
"proto_library(name = 'foo', deps = [':bar'])",
"proto_library(name = 'bar')");
NestedSet<Artifact> providedSpecs =
getConfiguredTarget("//x:lite_pb2")
.get(ProguardSpecProvider.PROVIDER)
.getTransitiveProguardSpecs();
assertThat(ActionsTestUtil.baseArtifactNames(providedSpecs))
.containsExactly("javalite_runtime.pro_valid");
}
@Test
public void testExperimentalProtoExtraActions() throws Exception {
scratch.file(
"x/BUILD",
"load('//tools/build_rules/java_lite_proto_library:java_lite_proto_library.bzl',",
" 'java_lite_proto_library')",
"java_lite_proto_library(name = 'lite_pb2', deps = [':foo'])",
"proto_library(name = 'foo', srcs = ['foo.proto'])");
scratch.file(
"xa/BUILD",
"extra_action(",
" name = 'xa',",
" cmd = 'echo $(EXTRA_ACTION_FILE)')",
"action_listener(",
" name = 'al',",
" mnemonics = ['Javac'],",
" extra_actions = [':xa'])");
useConfiguration("--experimental_action_listener=//xa:al");
ConfiguredTarget ct = getConfiguredTarget("//x:lite_pb2");
Iterable<Artifact> artifacts =
ct.getProvider(ExtraActionArtifactsProvider.class).getTransitiveExtraActionArtifacts();
Iterable<String> extraActionOwnerLabels =
transform(
artifacts,
/** A long way to say (Artifact s) -> s.getOwnerLabel().toString() */
new Function<Artifact, String>() {
@Nullable
@Override
public String apply(@Nullable Artifact artifact) {
return artifact == null ? null : artifact.getOwnerLabel().toString();
}
});
assertThat(extraActionOwnerLabels).contains("//x:foo");
}
/**
* Verify that a java_lite_proto_library exposes Skylark providers for the Java code it generates.
*/
@Test
public void testJavaProtosExposeSkylarkProviders() throws Exception {
scratch.file(
"proto/extensions.bzl",
"def _impl(ctx):",
" print (ctx.attr.dep[JavaInfo])",
"custom_rule = rule(",
" implementation=_impl,",
" attrs={",
" 'dep': attr.label()",
" },",
")");
scratch.file(
"proto/BUILD",
"load('//proto:extensions.bzl', 'custom_rule')",
"load('//tools/build_rules/java_lite_proto_library:java_lite_proto_library.bzl',",
" 'java_lite_proto_library')",
"proto_library(",
" name = 'proto',",
" srcs = [ 'file.proto' ],",
")",
"java_lite_proto_library(name = 'lite_pb2', deps = [':proto'])",
"custom_rule(name = 'custom', dep = ':lite_pb2')");
update(
ImmutableList.of("//proto:custom"),
/* keepGoing= */ false,
/* loadingPhaseThreads= */ 1,
/* doAnalysis= */ true,
new EventBus());
// Implicitly check that `update()` above didn't throw an exception. This implicitly checks that
// ctx.attr.dep.java.{transitive_deps, outputs}, above, is defined.
}
@Test
public void testProtoLibraryInterop() throws Exception {
scratch.file(
"proto/BUILD",
"load('//tools/build_rules/java_lite_proto_library:java_lite_proto_library.bzl',",
" 'java_lite_proto_library')",
"proto_library(",
" name = 'proto',",
" srcs = [ 'file.proto' ],",
")",
"java_lite_proto_library(name = 'lite_pb2', deps = [':proto'])");
update(
ImmutableList.of("//proto:lite_pb2"),
/* keepGoing= */ false,
/* loadingPhaseThreads= */ 1,
/* doAnalysis= */ true,
new EventBus());
}
/**
* Tests that a java_lite_proto_library only provides direct jars corresponding on the
* proto_library rules it directly depends on, excluding anything that the proto_library rules
* depends on themselves. This does not concern strict-deps in the compilation of the generated
* Java code itself, only compilation of regular code in java_library/java_binary and similar
* rules.
*/
@Test
public void jplCorrectlyDefinesDirectJars_strictDepsEnabled() throws Exception {
scratch.file(
"x/BUILD",
"load('//tools/build_rules/java_lite_proto_library:java_lite_proto_library.bzl',",
" 'java_lite_proto_library')",
"java_lite_proto_library(name = 'foo_lite_pb2', deps = [':foo'], strict_deps = 1)",
"proto_library(",
" name = 'foo',",
" srcs = [ 'foo.proto' ],",
" deps = [ ':bar' ],",
")",
"java_lite_proto_library(name = 'bar_lite_pb2', deps = [':bar'])",
"proto_library(",
" name = 'bar',",
" srcs = [ 'bar.proto' ],",
" deps = [ ':baz' ],",
")",
"proto_library(",
" name = 'baz',",
" srcs = [ 'baz.proto' ],",
")");
{
JavaCompilationArgsProvider compilationArgsProvider =
getProvider(JavaCompilationArgsProvider.class, getConfiguredTarget("//x:foo_lite_pb2"));
Iterable<String> directJars =
prettyArtifactNames(compilationArgsProvider.getDirectCompileTimeJars());
assertThat(directJars).containsExactly("x/libfoo-lite-hjar.jar");
JavaSourceJarsProvider sourceJarsProvider =
getProvider(JavaSourceJarsProvider.class, getConfiguredTarget("//x:foo_lite_pb2"));
assertThat(sourceJarsProvider).isNotNull();
assertThat(prettyArtifactNames(sourceJarsProvider.getSourceJars()))
.containsExactly("x/libfoo-lite-src.jar");
}
{
JavaCompilationArgsProvider compilationArgsProvider =
getProvider(JavaCompilationArgsProvider.class, getConfiguredTarget("//x:bar_lite_pb2"));
Iterable<String> directJars =
prettyArtifactNames(compilationArgsProvider.getDirectCompileTimeJars());
assertThat(directJars).containsExactly("x/libbar-lite-hjar.jar");
JavaSourceJarsProvider sourceJarsProvider =
getProvider(JavaSourceJarsProvider.class, getConfiguredTarget("//x:bar_lite_pb2"));
assertThat(sourceJarsProvider).isNotNull();
assertThat(prettyArtifactNames(sourceJarsProvider.getSourceJars()))
.containsExactly("x/libbar-lite-src.jar");
}
}
/**
* Tests that a java_proto_library only provides direct jars corresponding on the proto_library
* rules it directly depends on, excluding anything that the proto_library rules depends on
* themselves. This does not concern strict-deps in the compilation of the generated Java code
* itself, only compilation of regular code in java_library/java_binary and similar rules.
*
* <p>Here, a java_lite_proto_library dependes on an alias proto. We make sure that the system
* behaves as if we depend directly on the aliased proto_library.
*/
@Test
public void jplCorrectlyDefinesDirectJars_strictDepsEnabled_aliasProto() throws Exception {
scratch.file(
"x/BUILD",
"load('//tools/build_rules/java_lite_proto_library:java_lite_proto_library.bzl',",
" 'java_lite_proto_library')",
"java_lite_proto_library(name = 'foo_java_proto_lite', deps = [':foo_proto'], ",
" strict_deps = 1)",
"proto_library(",
" name = 'foo_proto',",
" deps = [ ':bar_proto' ],",
")",
"proto_library(",
" name = 'bar_proto',",
" srcs = [ 'bar.proto' ],",
")");
JavaCompilationArgsProvider compilationArgsProvider =
getProvider(
JavaCompilationArgsProvider.class, getConfiguredTarget("//x:foo_java_proto_lite"));
Iterable<String> directJars =
prettyArtifactNames(compilationArgsProvider.getDirectCompileTimeJars());
assertThat(directJars).containsExactly("x/libbar_proto-lite-hjar.jar");
}
/**
* Tests that when strict-deps is disabled, java_lite_proto_library provides (in its "direct"
* jars) all transitive classes, not only direct ones. This does not concern strict-deps in the
* compilation of the generated Java code itself, only compilation of regular code in
* java_library/java_binary and similar rules.
*/
@Test
public void jplCorrectlyDefinesDirectJars_strictDepsDisabled() throws Exception {
scratch.file(
"x/BUILD",
"load('//tools/build_rules/java_lite_proto_library:java_lite_proto_library.bzl',",
" 'java_lite_proto_library')",
"java_lite_proto_library(name = 'foo_lite_pb', deps = [':foo'], strict_deps = 0)",
"proto_library(",
" name = 'foo',",
" srcs = [ 'foo.proto' ],",
" deps = [ ':bar' ],",
")",
"java_lite_proto_library(name = 'bar_lite_pb', deps = [':bar'])",
"proto_library(",
" name = 'bar',",
" srcs = [ 'bar.proto' ],",
" deps = [ ':baz' ],",
")",
"proto_library(",
" name = 'baz',",
" srcs = [ 'baz.proto' ],",
")");
{
JavaCompileAction action =
(JavaCompileAction)
getGeneratingAction(getConfiguredTarget("//x:foo_lite_pb"), "x/libfoo-lite.jar");
assertThat(prettyArtifactNames(getInputs(action, getDirectJars(action)))).isEmpty();
}
{
JavaCompileAction action =
(JavaCompileAction)
getGeneratingAction(getConfiguredTarget("//x:bar_lite_pb"), "x/libbar-lite.jar");
assertThat(prettyArtifactNames(getInputs(action, getDirectJars(action)))).isEmpty();
}
}
/** Tests that java_lite_proto_library's aspect exposes a Skylark provider named 'proto_java'. */
@Test
@Ignore
// TODO(elenairina): Enable this test when proto_java is returned from the aspect in Skylark
// version of java_lite_proto_library.
public void testJavaLiteProtoLibraryAspectProviders() throws Exception {
scratch.file(
"x/aspect.bzl",
"MyInfo = provider()",
"def _foo_aspect_impl(target,ctx):",
" proto_found = hasattr(target, 'proto_java')",
" if hasattr(ctx.rule.attr, 'deps'):",
" for dep in ctx.rule.attr.deps:",
" proto_found = proto_found or dep.proto_found",
" return MyInfo(proto_found = proto_found)",
"foo_aspect = aspect(_foo_aspect_impl, attr_aspects = ['deps'])",
"def _foo_rule_impl(ctx):",
" return MyInfo(result = ctx.attr.dep.proto_found)",
"foo_rule = rule(_foo_rule_impl, attrs = { 'dep' : attr.label(aspects = [foo_aspect])})");
scratch.file(
"x/BUILD",
"load(':aspect.bzl', 'foo_rule')",
"load('//tools/build_rules/java_lite_proto_library:java_lite_proto_library.bzl',",
" 'java_lite_proto_library')",
"java_lite_proto_library(name = 'foo_java_proto', deps = ['foo_proto'])",
"proto_library(name = 'foo_proto', srcs = ['foo.proto'], java_lib = ':lib')",
"foo_rule(name = 'foo_rule', dep = 'foo_java_proto')");
ConfiguredTarget target = getConfiguredTarget("//x:foo_rule");
Provider.Key key =
new SkylarkProvider.SkylarkKey(
Label.parseAbsolute("//x:aspect.bzl", ImmutableMap.of()), "MyInfo");
StructImpl myInfo = (StructImpl) target.get(key);
Boolean result = (Boolean) myInfo.getValue("result");
// "yes" means that "proto_java" was found on the proto_library + java_proto_library aspect.
assertThat(result).isTrue();
}
private static <P extends TransitiveInfoProvider> P getProvider(
Class<P> providerClass, ConfiguredTarget target) {
JavaInfo javaInfo = target.get(JavaInfo.PROVIDER);
return javaInfo.getProvider(providerClass);
}
}