Project: /_project.yaml Book: /_book.yaml
New to Bazel? You’re in the right place. Follow this First Build tutorial for a simplified introduction to using Bazel. This tutorial defines key terms as they are used in Bazel’s context and walks you through the basics of the Bazel workflow. Starting with the tools you need, you will build and run three projects with increasing complexity and learn how and why they get more complex.
While Bazel is a build system that supports multi-language builds, this tutorial uses a C++ project as an example and provides the general guidelines and flow that apply to most languages.
Estimated completion time: 30 minutes.
Start by installing Bazel, if you haven’t already. This tutorial uses Git for source control, so for best results install Git as well.
Next, retrieve the sample project from Bazel's GitHub repository by running the following in your command-line tool of choice:
git clone https://github.com/bazelbuild/examples
The sample project for this tutorial is in the examples/cpp-tutorial
directory.
Take a look below at how it’s structured:
examples └── cpp-tutorial ├──stage1 │ ├── main │ │ ├── BUILD │ │ └── hello-world.cc │ └── WORKSPACE ├──stage2 │ ├── main │ │ ├── BUILD │ │ ├── hello-world.cc │ │ ├── hello-greet.cc │ │ └── hello-greet.h │ └── WORKSPACE └──stage3 ├── main │ ├── BUILD │ ├── hello-world.cc │ ├── hello-greet.cc │ └── hello-greet.h ├── lib │ ├── BUILD │ ├── hello-time.cc │ └── hello-time.h └── WORKSPACE
There are three sets of files, each set representing a stage in this tutorial. In the first stage, you will build a single [target] (https://bazel.build/reference/glossary#target) residing in a single [package] (https://bazel.build/reference/glossary#package). In the second stage, you will you will build both a binary and a library from a single package. In the third and final stage, you will build a project with multiple packages and build it with multiple targets.
By installing Bazel (and Git) and cloning the repository for this tutorial, you have laid the foundation for your first build with Bazel. Continue to the next section to define some terms and set up your workspace.
Before you can build a project, you need to set up its workspace. A workspace is a directory that holds your project‘s source files and Bazel’s build outputs. It also contains these significant files:
WORKSPACE
file , which identifies the directory and its contents as a Bazel workspace and lives at the root of the project's directory structure.BUILD
files , which tell Bazel how to build different parts of the project. A directory within the workspace that contains a BUILD file is a package. (More on packages later in this tutorial.)In future projects, to designate a directory as a Bazel workspace, create an empty file named WORKSPACE
in that directory. For the purposes of this tutorial, a WORKSPACE
file is already present in each stage.
NOTE: When Bazel builds the project, all inputs must be in the same workspace. Files residing in different workspaces are independent of one another unless linked. More detailed information about workspace rules can be found in this guide.
A BUILD
file contains several different types of instructions for Bazel. Each BUILD
file requires at least one rule as a set of instructions, which tells Bazel how to build the desired outputs, such as executable binaries or libraries. Each instance of a build rule in the BUILD
file is called a target and points to a specific set of source files and dependencies. A target can also point to other targets.
Take a look at the BUILD
file in the cpp-tutorial/stage1/main
directory:
cc_binary( name = "hello-world", srcs = ["hello-world.cc"], )
In our example, the hello-world
target instantiates Bazel's built-in cc_binary rule. The rule tells Bazel to build a self-contained executable binary from the hello-world.cc source file with no dependencies.
Now you are familiar with some key terms, and what they mean in the context of this project and Bazel in general. In the next section, you will build and test Stage 1 of the project.
It’s time to build the first part of the project. For a visual reference, the structure of the Stage 1 section of the project is:
examples └── cpp-tutorial └──stage1 ├── main │ ├── BUILD │ └── hello-world.cc └── WORKSPACE
Run the following to move to the cpp-tutorial/stage1
directory:
cd cpp-tutorial/stage1
Next, run:
bazel build //main:hello-world
In the target label, the //main:
part is the location of the BUILD
file relative to the root of the workspace, and hello-world
is the target name in the BUILD
file.
Bazel produces something that looks like this:
INFO: Found 1 target... Target //main:hello-world up-to-date: bazel-bin/main/hello-world INFO: Elapsed time: 2.267s, Critical Path: 0.25s
You just built your first Bazel target. Bazel places build outputs in the bazel-bin
directory at the root of the workspace.
Now test your freshly built binary, which is:
bazel-bin/main/hello-world
This results in a printed “Hello world
” message.
Here’s the dependency graph of Stage 1:
![Dependency graph for hello-world displays a single target with a single source file.] (/docs/images/cpp-tutorial-stage1.png “Dependency graph for hello-world displays a single target with a single source file.”)
Now that you have completed your first build, you have a basic idea of how a build is structured. In the next stage, you will add complexity by adding another target.
While a single target is sufficient for small projects, you may want to split larger projects into multiple targets and packages. This allows for fast incremental builds – that is, Bazel only rebuilds what's changed – and speeds up your builds by building multiple parts of a project at once. This stage of the tutorial adds a target, and the next adds a package.
This is the directory you are working with for Stage 2:
├──stage2 │ ├── main │ │ ├── BUILD │ │ ├── hello-world.cc │ │ ├── hello-greet.cc │ │ └── hello-greet.h │ └── WORKSPACE
Take a look below at the BUILD
file in the cpp-tutorial/stage2/main
directory:
cc_library( name = "hello-greet", srcs = ["hello-greet.cc"], hdrs = ["hello-greet.h"], ) cc_binary( name = "hello-world", srcs = ["hello-world.cc"], deps = [ ":hello-greet", ], )
With this BUILD
file, Bazel first builds the hello-greet
library (using Bazel's built-in cc_library rule), then the hello-world binary. The deps attribute in the hello-world target tells Bazel that the hello-greet library is required to build the hello-world binary.
Before you can build this new version of the project, you need to change directories, switching to the cpp-tutorial/stage2
directory by running:
cd ../stage2
Now you can build the new binary using the following familiar command:
bazel build //main:hello-world
Once again, Bazel produces something that looks like this:
INFO: Found 1 target... Target //main:hello-world up-to-date: bazel-bin/main/hello-world INFO: Elapsed time: 2.399s, Critical Path: 0.30s
Now you can test your freshly built binary, which returns another “Hello world
”:
bazel-bin/main/hello-world
If you now modify hello-greet.cc
and rebuild the project, Bazel only recompiles that file.
Looking at the dependency graph, you can see that hello-world depends on the same inputs as it did before, but the structure of the build is different:
You've now built the project with two targets. The hello-world
target builds one source file and depends on one other target (//main:hello-greet
), which builds two additional source files. In the next section, take it a step further and add another package.
This next stage adds another layer of complication and builds a project with multiple packages. Take a look below at the structure and contents of the cpp-tutorial/stage3
directory:
└──stage3 ├── main │ ├── BUILD │ ├── hello-world.cc │ ├── hello-greet.cc │ └── hello-greet.h ├── lib │ ├── BUILD │ ├── hello-time.cc │ └── hello-time.h └── WORKSPACE
You can see that now there are two sub-directories, and each contains a BUILD
file. Therefore, to Bazel, the workspace now contains two packages: lib
and main
.
Take a look at the lib/BUILD
file:
cc_library( name = "hello-time", srcs = ["hello-time.cc"], hdrs = ["hello-time.h"], visibility = ["//main:__pkg__"], )
And at the main/BUILD
file:
cc_library( name = "hello-greet", srcs = ["hello-greet.cc"], hdrs = ["hello-greet.h"], ) cc_binary( name = "hello-world", srcs = ["hello-world.cc"], deps = [ ":hello-greet", "//lib:hello-time", ], )
The hello-world
target in the main package depends on the hello-time
target in the lib
package (hence the target label //lib:hello-time
) - Bazel knows this through the deps
attribute. You can see this reflected in the dependency graph:
For the build to succeed, you make the //lib:hello-time
target in lib/BUILD
explicitly visible to targets in main/BUILD
using the visibility attribute. This is because by default targets are only visible to other targets in the same BUILD
file. Bazel uses target visibility to prevent issues such as libraries containing implementation details leaking into public APIs.
Now build this final version of the project. Switch to the cpp-tutorial/stage3
directory by running:
cd ../stage3
Once again, run the following command:
bazel build //main:hello-world
Bazel produces something that looks like this:
INFO: Found 1 target... Target //main:hello-world up-to-date: bazel-bin/main/hello-world INFO: Elapsed time: 0.167s, Critical Path: 0.00s
Now test the last binary of this tutorial for a final Hello world
message:
bazel-bin/main/hello-world
You've now built the project as two packages with three targets and understand the dependencies between them, which equips you to go forth and build future projects with Bazel. In the next section, take a look at how to continue your Bazel journey.
You’ve now completed your first basic build with Bazel, but this is just the start. Here are some more resources to continue learning with Bazel:
Happy building!