| --- |
| layout: documentation |
| title: Build Java |
| --- |
| |
| Build Java |
| ========== |
| |
| You can use Bazel to build your Java application. In this tutorial you'll learn how to: |
| |
| * Build your first Java target |
| * Add dependencies to your target |
| * Use multiple packages |
| * Deploy your target |
| |
| ## Setting up your workspace |
| |
| Suppose that you have an existing project in a directory, say, |
| `~/gitroot/my-project/`. Create an empty file at |
| `~/gitroot/my-project/WORKSPACE` to show Bazel where your project's root is. |
| |
| ## Creating Your Own Build File |
| |
| Use the following commands to make a small Java project for this example: |
| |
| {% highlight bash %} |
| $ # If you're not already there, move to your workspace directory. |
| $ cd ~/gitroot/my-project |
| $ mkdir -p src/main/java/com/example |
| $ cat > src/main/java/com/example/ProjectRunner.java <<'EOF' |
| package com.example; |
| |
| public class ProjectRunner { |
| public static void main(String args[]) { |
| Greeting.sayHi(); |
| } |
| } |
| EOF |
| $ cat > src/main/java/com/example/Greeting.java <<'EOF' |
| package com.example; |
| |
| public class Greeting { |
| public static void sayHi() { |
| System.out.println("Hi!"); |
| } |
| } |
| EOF |
| {% endhighlight %} |
| |
| Bazel figures out what to build by looking for files named `BUILD` in your |
| workspace, so we'll create a `BUILD` file in the `~/gitroot/my-project` |
| directory. Add the following lines to this BUILD file: |
| |
| {% highlight python %} |
| # ~/gitroot/my-project/BUILD |
| java_binary( |
| name = "my-runner", |
| srcs = glob(["**/*.java"]), |
| main_class = "com.example.ProjectRunner", |
| ) |
| {% endhighlight %} |
| |
| `java_binary` is the type of thing this rule will build. |
| `glob(["**/*.java"])` is a handy shorthand for "recursively include every file |
| that ends with .java" (see the |
| [build encyclopedia](be/functions.html#glob) for more information about |
| globbing). `com.example.ProjectRunner` specifies the class that contains the |
| main method. |
| |
| Now you are ready to build your Java binary: |
| |
| {% highlight bash %} |
| $ cd ~/gitroot/my-project |
| $ bazel build //:my-runner |
| INFO: Found 1 target... |
| Target //:my-runner up-to-date: |
| bazel-bin/my-runner.jar |
| bazel-bin/my-runner |
| INFO: Elapsed time: 1.021s, Critical Path: 0.83s |
| $ bazel-bin/my-runner |
| Hi! |
| {% endhighlight %} |
| |
| Congratulations, you've just built your first Bazel target! |
| |
| ## Adding Dependencies |
| |
| Creating one rule to build your entire project may be sufficient for small |
| projects, but as projects get larger it's important to break up the build into |
| self-contained libraries that can be assembled into a final product. This way |
| the entire world doesn't need to be rebuilt on small changes and Bazel can |
| parallelize more of the build steps. |
| |
| To break up a project, create separate rules for each subcomponent and then |
| make them depend on each other. For the example above, add the following rules |
| to the `BUILD` file: |
| |
| {% highlight python %} |
| java_binary( |
| name = "my-other-runner", |
| srcs = ["src/main/java/com/example/ProjectRunner.java"], |
| main_class = "com.example.ProjectRunner", |
| deps = [":greeter"], |
| ) |
| |
| java_library( |
| name = "greeter", |
| srcs = ["src/main/java/com/example/Greeting.java"], |
| ) |
| {% endhighlight %} |
| |
| This builds the same files as before, but in a different way: now Bazel will |
| build the `greeter` library first and then build `my-other-runner`. Try building |
| and running `//:my-other-runner`: |
| |
| {% highlight bash %} |
| $ bazel run //:my-other-runner |
| INFO: Found 1 target... |
| Target //:my-other-runner up-to-date: |
| bazel-bin/my-other-runner.jar |
| bazel-bin/my-other-runner |
| INFO: Elapsed time: 2.454s, Critical Path: 1.58s |
| |
| INFO: Running command line: bazel-bin/my-other-runner |
| Hi! |
| {% endhighlight %} |
| |
| Now if you edit `ProjectRunner.java` and rebuild `my-other-runner`, |
| `Greeting.java` will not need to be recompiled. |
| |
| ## Using Multiple Packages |
| |
| For larger projects, you will often be dealing with several directories. You |
| can refer to targets defined in other BUILD files using the syntax |
| `//path/to/directory:target-name`. For example, suppose |
| `src/main/java/com/example/` has a `cmdline/` subdirectory with the following |
| file: |
| |
| {% highlight bash %} |
| $ mkdir -p src/main/java/com/example/cmdline |
| $ cat > src/main/java/com/example/cmdline/Runner.java <<'EOF' |
| package com.example.cmdline; |
| |
| import com.example.Greeting; |
| |
| public class Runner { |
| public static void main(String args[]) { |
| Greeting.sayHi(); |
| } |
| } |
| EOF |
| {% endhighlight %} |
| |
| `Runner.java` depends on `com.example.Greeting`, so we could add a `BUILD` file |
| at `src/main/java/com/example/cmdline/BUILD` that contained the following rule: |
| |
| {% highlight python %} |
| # ~/gitroot/my-project/src/main/java/com/example/cmdline/BUILD |
| java_binary( |
| name = "runner", |
| srcs = ["Runner.java"], |
| main_class = "com.example.cmdline.Runner", |
| deps = ["//:greeter"] |
| ) |
| {% endhighlight %} |
| |
| However, by default, build rules are _private_. This means that they can only be |
| referred to by rules in the same BUILD file. This prevents libraries that are |
| implementation details from leaking into public APIs, but it also means that you |
| must explicitly allow `runner` to depend on `//:greeter`. As is, if we |
| build `runner` we'll get a permissions error: |
| |
| {% highlight bash %} |
| $ bazel build //src/main/java/com/example/cmdline:runner |
| ERROR: /home/user/gitroot/my-project/src/main/java/com/example/cmdline/BUILD:2:1: |
| Target '//:greeter' is not visible from target '//src/main/java/com/example/cmdline:runner'. |
| Check the visibility declaration of the former target if you think the dependency is legitimate. |
| ERROR: Analysis of target '//src/main/java/com/example/cmdline:runner' failed; build aborted. |
| INFO: Elapsed time: 0.091s |
| {% endhighlight %} |
| |
| You can make a rule visible to rules in other BUILD files by adding a |
| `visibility = level` attribute. Change the `greeter` rule in |
| `~/gitroot/my-project/BUILD` to be visible to our new rule: |
| |
| {% highlight python %} |
| java_library( |
| name = "greeter", |
| srcs = ["src/main/java/com/example/Greeting.java"], |
| visibility = ["//src/main/java/com/example/cmdline:__pkg__"], |
| ) |
| {% endhighlight %} |
| |
| This makes `//:greeter` visible to any rule in the |
| `//src/main/java/com/example/cmdline` package. Now we can build and |
| run the `runner` binary: |
| |
| {% highlight bash %} |
| $ bazel run //src/main/java/com/example/cmdline:runner |
| INFO: Found 1 target... |
| Target //src/main/java/com/example/cmdline:runner up-to-date: |
| bazel-bin/src/main/java/com/example/cmdline/runner.jar |
| bazel-bin/src/main/java/com/example/cmdline/runner |
| INFO: Elapsed time: 1.576s, Critical Path: 0.81s |
| |
| INFO: Running command line: bazel-bin/src/main/java/com/example/cmdline/runner |
| Hi! |
| {% endhighlight %} |
| |
| See the [build encyclopedia](be/common-definitions.html#common.visibility) for more |
| visibility options. |
| |
| ## Deploying |
| |
| If you look at the contents of |
| _bazel-bin/src/main/java/com/example/cmdline/runner.jar_, you can see that it |
| only contains `Runner.class`, not its dependencies (`Greeting.class`): |
| |
| {% highlight bash %} |
| $ jar tf bazel-bin/src/main/java/com/example/cmdline/runner.jar |
| META-INF/ |
| META-INF/MANIFEST.MF |
| com/ |
| com/example/ |
| com/example/cmdline/ |
| com/example/cmdline/Runner.class |
| {% endhighlight %} |
| |
| This works for running locally (the `runner` script Bazel generates adds the |
| greeter jar to the classpath) but will not work if we want to copy `runner.jar` |
| to another machine and use it as a standalone binary. To build a self-contained |
| jar that can be deployed, build `runner_deploy.jar` (or, more generally, |
| `<target-name>_deploy.jar`): |
| |
| {% highlight bash %} |
| $ bazel build //src/main/java/com/example/cmdline:runner_deploy.jar |
| INFO: Found 1 target... |
| Target //src/main/java/com/example/cmdline:runner_deploy.jar up-to-date: |
| bazel-bin/src/main/java/com/example/cmdline/runner_deploy.jar |
| INFO: Elapsed time: 1.700s, Critical Path: 0.23s |
| {% endhighlight %} |
| |
| `runner_deploy.jar` will contain all of its dependencies. |
| |
| |