Java project structure

854 views
Skip to first unread message

Paul Gross

unread,
May 16, 2015, 1:23:06 AM5/16/15
to bazel-...@googlegroups.com
I'm trying to convert a maven/gradle project to bazel, and I'm looking for guidance about how to structure the build and test commands.

I created an example repo to help illustrate my questions:

https://github.com/pgr0ss/bazel_java_example

The overall structure is a single git repo with subdirectories for libraries and applications (my real project has many of each kind).

Here are my main questions:

1. Where should I define build commands?

I added a BUILD file per subdirectory, and each of these includes a `java_library` and `java_test` (and one includes a `java_binary`). Is this the "right way?" What do people normally name these commands (e.g. I just called mine `test`.)?

2. If #1 is correct, what's the right way to run tests? Currently, I can do:
 
  bazel test app:test
  bazel test persistence:test

Is there a shortcut to run all tests? Or run dependent tests? For example, in order to gain confidence in an app, I'd like to run the tests for that app and its dependencies.

3. Do you have to duplicate dependencies between java_library and java_test?

It seems like if you use a library in both test and app code, including the app as a dependency in the test is not sufficient. You have to include the library again in the test.

For example:

https://github.com/pgr0ss/bazel_java_example/blob/f9f4976e9096e8e5c27ac42b9361194ad69a88fd/app/BUILD#L13
https://github.com/pgr0ss/bazel_java_example/blob/f9f4976e9096e8e5c27ac42b9361194ad69a88fd/app/BUILD#L22

Is this expected?

Hopefully these questions and the example app show you a rough idea of what I'm trying to do. I'm excited to use bazel and I want to make sure I'm using it correctly.

Thanks,
Paul

Denis Lila

unread,
May 16, 2015, 7:52:47 PM5/16/15
to bazel-...@googlegroups.com
>1 

This is ok, but I don't see a good reason to have different directories. I put all my java code in a "java"
folder the contents of which mirror the package path. So if I have packages "org.dlila.x" and "org.dlila.y.z",
my directory structure will look like:

java
  org
    dlila
      x
        *.java
      y
        *.java
        z
          *.java

I see that you have defined BUILD files in "app" and "persistence" and are globbing your
sources over multiple directories. In larger code bases it's probably better to have one BUILD
file per directory, and only include sources from that directory in the srcs attribute.

So, in my example above, my build files would be:
java/org/dlila/x/BUILD
java/org/dlila/y/BUILD
java/org/dlila/y/z/BUILD

and the srcs for each of these would be "srcs = glob(["*.java"])", unless I had a test app or something
that I wanted to not include in the library, in which case I would add an exclude for that file.

> 2

You can do
bazel test <directory>/...

> 3

Yes, this is expected. In general, there shouldn't be that many shared direct dependencies between your test target and
the library target. If there are, and you don't want to repeat them, you can always define a new target with the shared
dependencies and use that in both your java_library and the corresponding java_test

Paul Gross

unread,
May 17, 2015, 12:50:22 AM5/17/15
to Denis Lila, bazel-...@googlegroups.com
> 1

This example is super simple, but in our real code base, we have many apps and many libraries. These apps are deployed separately, and so it seems to make sense to make each its own module (directory). Each library is used by several apps.

What's the advantage of many BUILD files vs a single glob?

> 2

I want to run the tests across directories, not just within a directory. For example, if I change a library I want to run all of the tests of all of the applications that depend on it.

> 3

This makes sense.

Thanks,
Paul

--
You received this message because you are subscribed to the Google Groups "bazel-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bazel-discus...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/bazel-discuss/46c72893-886e-4886-b70a-b5e78a026eb6%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Damien Martin-guillerez

unread,
May 18, 2015, 2:29:45 AM5/18/15
to Paul Gross, Denis Lila, bazel-...@googlegroups.com
On Sun, May 17, 2015 at 6:50 AM Paul Gross <pgr...@gmail.com> wrote:
> 1

This example is super simple, but in our real code base, we have many apps and many libraries. These apps are deployed separately, and so it seems to make sense to make each its own module (directory). Each library is used by several apps.

What's the advantage of many BUILD files vs a single glob?

There is no "right way" to do but It is better to decompose in smaller target so that Bazel can recompile only the target that needs to be recompiled. If you keep on single glob then it basically means that for i in that glob you are going to rebuild all the java files every time.

Internally we try to have a build file per java package, even if that don't reflect in Bazel build really well.

I don't think there are common guidelines on how to names target except that you generally want to have something more precise than test. (For instance we have skyframe_test, shell_test, window_test, etc... in the list of bazel tests).
   

> 2

I want to run the tests across directories, not just within a directory. For example, if I change a library I want to run all of the tests of all of the applications that depend on it.

 bazel test //... will run all the tests in your workspace. You can play around with bazel query (http://bazel.io/docs/query.html) also to list the target that execute. For instance, something like bazel 'kind(test, rdeps(//..., deps(//path/to:mytarget), 1)' should returns the list of java test target that have a direct dependency on any dependency of //path/to:mytarget (I just wrote down the query, not sure of it). So you can do it a one-liner to executing the test that depends directly on dependencies of your target with:
bazel test $(bazel 'kind(test, rdeps(//..., deps(//path/to:mytarget), 1)')

Paul Gross

unread,
May 18, 2015, 11:55:08 AM5/18/15
to Damien Martin-guillerez, Denis Lila, bazel-...@googlegroups.com
That's really helpful. Thanks everyone!

Paul Gross

unread,
May 20, 2015, 1:11:18 AM5/20/15
to Damien Martin-guillerez, Denis Lila, bazel-...@googlegroups.com
I've been trying to adjust the example repo based on the comments here, and I have a few more questions (pardon the terrible examples):

If you have a BUILD per package, how do you reference these packages in other apps or libraries? Do you list all of the packages out in the deps like this?

https://github.com/pgr0ss/bazel_java_example/blob/5b7019f6e881a7b7f423d216ddb9a54a2c783991/app/BUILD#L13-L14

Or do you group them together somehow and reference something like "//logging"? I tried a few ways of grouping but I couldn't get it to work:

  java_library(
    name = "logging",
    deps = [
      "//logging/src/main/java/logging",
      "//logging/src/main/java/logging/implementations",
    ],
  )

This gave me the error "deps not allowed without srcs; move to runtime_deps?"

  filegroup(
    name = "logging",
    srcs = [
      "//logging/src/main/java/logging",
      "//logging/src/main/java/logging/implementations",
    ],
  )

This gave me the error "filegroup rule is misplaced here (expected cc_binary, cc_library, genrule, genproto, java_import, java_library, sh_binary or sh_library)."

Are there open source java projects that use bazel? I would love a larger, real example.

Thanks,
Paul

Damien Martin-guillerez

unread,
May 20, 2015, 4:27:06 AM5/20/15
to Paul Gross, Denis Lila, bazel-...@googlegroups.com
You can include directly //logging/src/main/java/logging. If you want to avoid having to specify both dependencies everywhere, use the exports attribute in //logging/src/main/java/logging:logging :
java_library(name = "logging", ..., exports = ["//logging/src/main/java/logging/implementations"])

But I believe nobody has to call implementations class directly, right? So you probably don't want to do exports and restrict visiblily of implementations only to logging.
--
-- Damien

Damien Martin-guillerez

unread,
May 20, 2015, 4:28:01 AM5/20/15
to Paul Gross, Denis Lila, bazel-...@googlegroups.com
For the list of OSS project: http://bazel.io/users.html
--
-- Damien
Reply all
Reply to author
Forward
0 new messages