diff options
Diffstat (limited to 'site/versions/master/docs/tutorial/java.md')
-rw-r--r-- | site/versions/master/docs/tutorial/java.md | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/site/versions/master/docs/tutorial/java.md b/site/versions/master/docs/tutorial/java.md new file mode 100644 index 0000000000..ce916667e1 --- /dev/null +++ b/site/versions/master/docs/tutorial/java.md @@ -0,0 +1,245 @@ +--- +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. + + |