blob: dfc34e4c53396d8feeb8d5c98c338bcfc2a9dcac [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 static com.google.devtools.build.lib.skyframe.BzlLoadValue.keyForBuild;
import com.google.common.collect.ImmutableList;
import com.google.common.eventbus.EventBus;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.Artifact.DerivedArtifact;
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.util.BuildViewTestCase;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.RepositoryName;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.packages.Provider;
import com.google.devtools.build.lib.packages.StarlarkProvider;
import com.google.devtools.build.lib.packages.StructImpl;
import com.google.devtools.build.lib.packages.util.MockProtoSupport;
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 java.io.IOException;
import java.util.List;
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 Starlark version of java_lite_proto_library rule. */
@RunWith(JUnit4.class)
public class StarlarkJavaLiteProtoLibraryTest extends BuildViewTestCase {
private ActionsTestUtil actionsTestUtil;
@Before
public final void setUpMocks() throws Exception {
useConfiguration(
"--proto_compiler=//proto:compiler",
"--proto_toolchain_for_javalite=//tools/proto/toolchains:javalite");
MockProtoSupport.setup(mockToolsConfig);
scratch.file(
"proto/BUILD",
"""
licenses(["notice"])
exports_files(["compiler"])
""");
mockToolchains();
invalidatePackages();
actionsTestUtil = actionsTestUtil();
}
private void mockToolchains() throws IOException {
mockRuntimes();
scratch.appendFile(
"tools/proto/toolchains/BUILD",
"""
package(default_visibility = ["//visibility:public"])
proto_lang_toolchain(
name = "javalite",
command_line = "--java_out=lite,immutable:$(OUT)",
progress_message = "Generating JavaLite proto_library %{label}",
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",
"""
java_lite_proto_library(
name = "lite_pb2",
deps = [":foo"],
)
proto_library(
name = "foo",
srcs = [
"bar.proto",
"foo.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("proto/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",
"""
java_lite_proto_library(
name = "lite_pb2",
deps = [":protolib"],
)
proto_library(
name = "protolib",
srcs = ["file.proto"],
)
""");
String genfilesDir = targetConfig.getGenfilesFragment(RepositoryName.MAIN).getPathString();
List<String> args =
getGeneratingSpawnAction(getConfiguredTarget("//x:lite_pb2"), "x/protolib-lite-src.jar")
.getRemainingArguments();
assertThat(args)
.containsAtLeast(
"--java_out=lite,immutable:" + genfilesDir + "/x/protolib-lite-src.jar",
"-I.",
"x/file.proto")
.inOrder();
}
@Test
public void testProtoLibraryBuildsCompiledJar() throws Exception {
ConfiguredTarget target =
scratchConfiguredTarget(
"java",
"lite_pb2",
"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",
"""
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",
"java_lite_proto_library(name = 'lite_pb2', deps = [':null_lib'])",
"proto_library(name = 'null_lib')");
JavaCompilationArgsProvider compilationArgsProvider =
JavaInfo.getProvider(JavaCompilationArgsProvider.class, target);
assertThat(compilationArgsProvider).isNotNull();
assertThat(compilationArgsProvider.getDirectCompileTimeJars()).isNotNull();
JavaSourceJarsProvider sourceJarsProvider =
JavaInfo.getProvider(JavaSourceJarsProvider.class, target);
assertThat(sourceJarsProvider).isNotNull();
assertThat(sourceJarsProvider.getSourceJars()).isNotNull();
}
@Test
public void testSameVersionCompilerArguments() throws Exception {
scratch.file(
"cross/BUILD",
"""
java_lite_proto_library(
name = "lite_pb2",
deps = ["bravo"],
)
proto_library(
name = "bravo",
srcs = ["bravo.proto"],
deps = [":alpha"],
)
proto_library(name = "alpha")
""");
String genfilesDir = targetConfig.getGenfilesFragment(RepositoryName.MAIN).getPathString();
ConfiguredTarget litepb2 = getConfiguredTarget("//cross:lite_pb2");
List<String> args =
getGeneratingSpawnAction(litepb2, "cross/bravo-lite-src.jar").getRemainingArguments();
assertThat(args)
.containsAtLeast(
"--java_out=lite,immutable:" + genfilesDir + "/cross/bravo-lite-src.jar",
"-I.",
"cross/bravo.proto")
.inOrder();
List<String> directJars =
prettyArtifactNames(
JavaInfo.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 Starlark 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",
"""
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",
"""
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",
extra_actions = [":xa"],
mnemonics = ["Javac"],
)
""");
useConfiguration(
"--experimental_action_listener=//xa:al",
"--proto_compiler=//proto:compiler",
"--proto_toolchain_for_javalite=//tools/proto/toolchains:javalite");
ConfiguredTarget ct = getConfiguredTarget("//x:lite_pb2");
NestedSet<DerivedArtifact> artifacts =
ct.getProvider(ExtraActionArtifactsProvider.class).getTransitiveExtraActionArtifacts();
Iterable<String> extraActionOwnerLabels =
transform(
artifacts.toList(),
(artifact) -> artifact == null ? null : artifact.getOwnerLabel().toString());
assertThat(extraActionOwnerLabels).contains("//x:foo");
}
/**
* Verify that a java_lite_proto_library exposes Starlark providers for the Java code it
* generates.
*/
@Test
public void testJavaProtosExposeStarlarkProviders() 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(
"protolib/BUILD",
"""
load("//proto:extensions.bzl", "custom_rule")
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("//protolib: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_compile_time_jars, outputs}, above, is defined.
}
@Test
public void testProtoLibraryInterop() throws Exception {
scratch.file(
"protolib/BUILD",
"""
proto_library(
name = "proto",
srcs = ["file.proto"],
)
java_lite_proto_library(
name = "lite_pb2",
deps = [":proto"],
)
""");
update(
ImmutableList.of("//protolib:lite_pb2"),
/* keepGoing= */ false,
/* loadingPhaseThreads= */ 1,
/* doAnalysis= */ true,
new EventBus());
}
/**
* 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",
"""
java_lite_proto_library(
name = "foo_java_proto_lite",
deps = [":foo_proto"],
)
proto_library(
name = "foo_proto",
deps = [":bar_proto"],
)
proto_library(
name = "bar_proto",
srcs = ["bar.proto"],
)
""");
JavaCompilationArgsProvider compilationArgsProvider =
JavaInfo.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
@Ignore("TODO(b/216484418): Systematize this test with its new version.")
public void jplCorrectlyDefinesDirectJars_strictDepsDisabled() throws Exception {
scratch.file(
"x/BUILD",
"""
java_lite_proto_library(
name = "foo_lite_pb",
deps = [":foo"],
)
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 Starlark provider named 'proto_java'. */
@Test
@Ignore
// TODO(elenairina): Enable this test when proto_java is returned from the aspect in Starlark
// 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")
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 StarlarkProvider.Key(keyForBuild(Label.parseCanonical("//x:aspect.bzl")), "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();
}
}