Clone the Bazel Github repo and run the provided compile script. Make sure that you are running Bazel on a supported platform and that you have installed other required software as described in the installation guide.
{% highlight bash %} $ git clone https://github.com/google/bazel.git $ cd bazel $ ./compile.sh {% endhighlight %}
./compile.sh
creates the bazel
executable in output/bazel
.
Note: Bazel may support a binary installation at a later time.
A workspace is a directory on your filesystem that contains source code for the software you want to build, as well symbolic links to directories that contain the build outputs (for example, bazel-bin
and bazel-out
). The location of the workspace directory is not significant, but it must contain an empty file called WORKSPACE
in the top-level directory. This file marks the directory as the workspace root.
One workspace can be shared among multiple projects if desired. To get started, we'll focus on a simple example with one project.
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.
To make sure everything is set up correctly in your build root, build one of the examples from the examples/
directory.
{% highlight bash %} $ cd ~/gitroot/my-project $ bazel build examples/java-native/src/main/java/com/example/myproject:hello-world Extracting Bazel installation... ........... INFO: Found 1 target... Target //examples/java-native/src/main/java/com/example/myproject:hello-world up-to-date: bazel-bin/examples/java-native/src/main/java/com/example/myproject/hello-world.jar bazel-bin/examples/java-native/src/main/java/com/example/myproject/hello-world INFO: Elapsed time: 3.040s, Critical Path: 1.14s $ bazel-bin/examples/java-native/src/main/java/com/example/myproject/hello-world Hello world {% endhighlight %}
Bazel puts binaries it has built under bazel-bin/
. Note that you can always look at the build
command's output to find output file paths.
Now you can create your own BUILD file and start adding build rules. This example assumes that my-project/
is a Java project. See the build encyclopedia for advice on adding build rules for other languages.
Note that when we ran “bazel build” above, the third argument started with a filesystem path (“examples/java”), followed by a colon. When you run bazel build examples/java-native/src/main/java/com/example/myproject:hello-world
, Bazel will look for a special file named BUILD in the examples/java-native/src/main/java/com/example/myproject/
subdirectory. This BUILD file defines rules about how Bazel should build things in this subdirectory.
Thus, to add build rules to my-project, create a file named BUILD
in the my-project/
directory. Add the following lines to this BUILD file:
{% highlight python %}
java_binary( name = “my-runner”, srcs = glob([“**/*.java”]), main_class = “com.example.ProjectRunner”, ) {% endhighlight %}
BUILD files are Python-like scripts. BUILD files cannot contain arbitrary Python, but each build rule looks like a Python function call and you can use “#” to start a single-line comment.
java_binary
is the type of thing this rule will build. name
is how you'll refer to the rule when you run “bazel build” (in the “examples/java:hello-world” build above the name
was “hello-world”). srcs
lists the Java source files Bazel should compile into a Java binary. glob(["**/*.java"])
is a handy shorthand for “recursively include every file that ends with .java” (see the user manual for more information about globbing). Replace com.example.ProjectRunner
with the class that contains the main method.
If you have no actual Java project you're using, you can use the following commands to make a fake project for this example:
{% highlight bash %} $ # If you're not already there, move to your build root directory. $ cd ~/gitroot/base_workspace $ mkdir -p my-project/java/com/example $ cat > my-project/java/com/example/ProjectRunner.java <<EOF package com.example;
public class ProjectRunner { public static void main(String args[]) { Greeting.sayHi(); } } EOF $ cat > my-project/java/com/example/Greeting.java <<EOF package com.example;
public class Greeting { public static void sayHi() { System.out.println(“Hi!”); } } EOF {% endhighlight %}
Now build your project:
{% highlight bash %} $ bazel build my-project:my-runner INFO: Found 1 target... Target //my-project:my-runner up-to-date: bazel-bin/my-project/my-runner.jar bazel-bin/my-project/my-runner INFO: Elapsed time: 1.021s, Critical Path: 0.83s $ bazel-bin/my-project/my-runner Hi! {% endhighlight %}
Congratulations, you've created your first Bazel BUILD file!
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 my-project/BUILD
file:
{% highlight python %} java_binary( name = “my-other-runner”, srcs = [“java/com/example/ProjectRunner.java”], main_class = “com.example.ProjectRunner”, deps = [“:greeter”], )
java_library( name = “greeter”, srcs = [“java/com/example/Greeting.java”], ) {% endhighlight %}
Now you can build and run my-project:my-other-runner
:
{% highlight bash %} $ bazel run my-project:my-other-runner INFO: Found 1 target... Target //my-project:my-other-runner up-to-date: bazel-bin/my-project/my-other-runner.jar bazel-bin/my-project/my-other-runner INFO: Elapsed time: 2.454s, Critical Path: 1.58s
INFO: Running command line: bazel-bin/my-project/my-other-runner Hi! {% endhighlight %}
If you edit ProjectRunner.java and rebuild my-other-runner
, only ProjectRunner.java
needs to be rebuilt (greeter is unchanged).
For larger projects, you will often be dealing with several directories. You can refer to targets defined in other BUILD files using the syntax //package-name:target-name
. For example, suppose my-project/java/com/example/
has a cmdline/
subdirectory with the following file:
{% highlight bash %} $ mkdir my-project/java/com/example/cmdline $ cat > my-project/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 %}
We could add a BUILD
file at my-project/java/com/example/cmdline/BUILD
that contained the following rule:
{% highlight python %}
java_binary( name = “runner”, srcs = [“Runner.java”], main_class = “com.example.cmdline.Runner”, deps = [“//my-project: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 my-project:greeter
. As is, if we build runner
we'll get a permissions error:
{% highlight bash %} $ bazel build my-project/java/com/example/cmdline:runner ERROR: /usr/local/google/home/kchodorow/gitroot/base_workspace/my-project/java/com/example/cmdline/BUILD:2:1: Target ‘//my-project:greeter’ is not visible from target ‘//my-project/java/com/example/cmdline:runner’. Check the visibility declaration of the former target if you think the dependency is legitimate. ERROR: Analysis of target ‘//my-project/java/com/example/cmdline:runner’ failed; build aborted. INFO: Elapsed time: 0.091s {% endhighlight %}
You can make a rule visibile to rules in other BUILD files by adding a visibility = level
attribute. Change the greeter
rule in my-project/BUILD to be visible to our new rule:
{% highlight python %} java_library( name = “greeter”, srcs = [“java/com/example/Greeting.java”], visibility = [“//my-project/java/com/example/cmdline:pkg”], ) {% endhighlight %}
This makes //my-project:greeter
visible to any rule in the //my-project/java/com/example/cmdline
package. Now we can build and run the binary:
{% highlight bash %} $ bazel run my-project/java/com/example/cmdline:runner INFO: Found 1 target... Target //my-project/java/com/example/cmdline:runner up-to-date: bazel-bin/my-project/java/com/example/cmdline/runner.jar bazel-bin/my-project/java/com/example/cmdline/runner INFO: Elapsed time: 1.576s, Critical Path: 0.81s
INFO: Running command line: bazel-bin/my-project/java/com/example/cmdline/runner Hi! {% endhighlight %}
See the build encyclopedia for more visibility options.
If you look at the contents of bazel-bin/my-project/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/my-project/java/com/example/cmdline/runner.jar META-INF/ META-INF/MANIFEST.MF com/ com/example/ com/example/cmdline/ com/example/cmdline/Runner.class {% endhighlight %}
To deploy a runner
binary, we need a self-contained jar. To build this, build runner_deploy.jar
(or, more generally, <target-name>_deploy.jar
):
{% highlight bash %} $ bazel build my-project/java/com/example/cmdline:runner_deploy.jar INFO: Found 1 target... Target //my-project/java/com/example/cmdline:runner_deploy.jar up-to-date: bazel-bin/my-project/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.
You can now create your own targets and compose them. See the build encyclopedia and Bazel user manual for more information. Let us know if you have any questions!