I wrote a small application and used java_binary as follows:
java_binary(
name = "MyCommand",
srcs = [
"MyCommand.java",
],
visibility = ["//visibility:public"],
deps = [
"//src/main/java/com/example/two",
],
)
It depends on a java_library
target //src/main/java/com/example/two
I then wrote a java_test
as follows:
java_test(
name = "TestMyCommand",
size = "small",
srcs = [
"TestMyCommand.java",
],
deps = [
":MyCommand",
],
)
The test is pretty simple and just does new MyCommand()
.
This unfortunately fails quickly with a ClassDefNotFoundException
with a class file found in //src/main/java/com/example/two
. Setting a breakpoint it looks like that library is not included in the ClassPath.
HOWEVER, if I change my java_test
to depend on a java_library
THEN the transitive dependency of //src/main/java/com/example/two
is included.
I could not find anything in the documentation explaining why?
This is because java_binary
is a "binary" rule, i.e., it outputs an executable, or another way, it's typically a "terminal" or "root" rule. So it doesn't pass along the info to add its deps to the final classpath, because it doesn't typically expect to be in the dependencies[1] of other targets (even java_test
).
You could refactor the BUILD file so that MyCommand.java
is in a java_library
, and the java_test
and the java_binary
targets depend on that:
java_binary(
name = "MyCommandMain",
runtime_deps = [":MyCommand"],
main_class = "com.example.one.MyCommand",
)
java_library(
name = "MyCommand",
srcs = [
"MyCommand.java",
],
visibility = ["//visibility:public"],
deps = [
"//src/main/java/com/example/two",
],
)
1: But you do sometimes see things like this:
# or other kinds of binary rules (cc_binary, py_binary, etc)
java_binary(
name = "my_java_bin",
...,
)
sh_binary(
name = "my_shell_script",
srcs = ["script.sh"],
data = [":my_java_bin"],
)
and script.sh
runs my_java_bin
as part of whatever it's accomplishing. The difference here is that my_shell_script
is using the whole binary, rather than programming against code within it.