aboutsummaryrefslogtreecommitdiffhomepage
path: root/site/docs/getting-started.md
blob: 1d029216d4613b91d2fc3acd538b651e78b8a713 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
---
layout: documentation
title: Getting Started
---

# Getting Started with Bazel

## Setup

Use the [installation instructions](/docs/install.html) to install a copy of
Bazel on your machine.

## Using a workspace

All Bazel builds take place in a [_workspace_](/docs/build-ref.html#workspaces),
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 a file called
`WORKSPACE` in the top-level directory. The `WORKSPACE` file may be an empty
file, or it may contain references to
[external dependencies](/docs/external.html) required to build the outputs.

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.

## 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 %}

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 the
identifier you'll use when you ask bazel to build the binary. `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
[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 visibile 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.

## Next Steps

You can now create your own targets and compose them. Next, check out the
[tutorial](/docs/tutorial/index.html) to learn how to build a server backend,
Android app, and iOS app with Bazel. Also see the
[build encyclopedia](be/overview.html) and
[user manual](bazel-user-manual.html) for more information.
[Let us know](https://groups.google.com/forum/#!forum/bazel-discuss) if you have
any questions!