Bazel 101

Abhishek Bose
6 min readNov 2, 2019

--

understanding Bazel concepts by writing, building and running code

Bazel Official Image from Wikipedia

Reason to write

When I started learning and working on Bazel a couple of months back, using a few other build and test tools before, I found it a bit different from all those. I say different in terms of few things. This is just an attempt to collate my notes from here and there what I understood while going through the official docs and trying out Bazel hands-on on a project, we are working on.

Disclaimer(s):

This is a complete beginner’s guide — the Bazel pros might find it “very basic”.

As the name suggests, this article does not delve into any advanced concept. But, the attempt is to make simple concepts as “building blocks” for the advanced ones. I would try to put together a Bazel 102 for some of the advanced topics.

First things first

The 3 Wh- (What, Why and Where) answers for Bazel

For me, it’s an intelligent build tool. I used the word “intelligent” because of a few reasons:

  • It supports many languages e.g. Java, C/C++, Scala, Android, Haskell, Go, Javascript/Node.js etc. There is a full list of languages supported. (if you have visited that link, it mentions the concept of rules which we will talk about later)
  • Caches all the previously done work — e.g. tests or builds and so faster every time. The next build or test only are about the new changes. Fats and reliable.
  • Multi-platform — Bazel can build binaries and deployable packages for multiple platforms, including desktop, server, and mobile, from the same project.
  • Can work on a large codebase with multiple modules, submodules, sub projects etc.
  • Easy, reasonable to write build properties and declaring dependencies
  • Remote Build Cache & Execution — can run bazel commands on remote machines and cache command outputs, using REAPI and RWPI.

Hands on Bazel

In order to learn about a new thing — whether a new language, framework, tool etc — we need to read and understand the features and semantics but for me first thing is to playing with it and then coming back to read the theory and repeat the process!

The structure

A simple directory structure of a very simple Bazel based Java project

This is a simple Bazel based Java project. I know this statement can make all Non-Java folks reading this article a bit frustrated or sad. But, to be honest, it has nothing to do with Java, other than printing some lines on the console. Explaining the Bazel concepts are more important here and should be easy enough!

Okay, so as we see, we have a project/app directory named simple-java-bazel which is the root of the application. Let’s understand the main Bazel concept exploring what’s highlighted in the picture :

WORKSPACE — a blank text file named WORKSPACE, which identifies the directory and its contents as a Bazel workspace, being present at the root of the project’s directory structure. It could either be empty or could have references for external dependency downloads needed to build the project.

N.B. You can have multiple WORKSPACE files and a multi-workspace project. But, for the simplicity of this article, we are keeping one simple WORKSPACE file. Files residing in different workspaces are independent of one another unless linked,

BUILD.bazel — or simply BUILD file — this file has instructions (in form of rules) on how to run or build or test the project. This is equivalent to the pom.xml (Java) or build.sbt (Scala) or build.gradle (Java/Android) or package.json (NPM).

For every programming language, there are set of rules e.g. rules_java or rules_go etc. This rules are written using a DSL named Starlark. You can write your own rules, use them and publish them on Github. This flexibility of creating your own rules gives Bazel the flexibility to build project written in any programming language.

main-dir/another-dir — these are the names of sub-projects or modules or just subdirectories of the main directory simple-java-bazel. I used the term main-dir/another-dir just to keep it simple and language agnostic.

In Bazel terms, they should be called packages. Any directory which has a BUILD file in it becomes a Bazel package.

Let’s build the project by creating the folder and file structure.

First, the (trivial) Java part:

Animal.java

package com.abhi;

public interface Animal {
int numberOfLegs();
}

Human.java

package com.abhi;import com.abhi.Animal;class Human implements Animal {  public int numberOfLegs() {    return 2;  }  String sayHelloTo(String name) {    return “Hello “ + name;  }}

MyMain.java

package com.abhi;

import com.abhi.Animal;

public class MyMain {
public static void main(String args[]) {

Human someOne = new Human();
System.out.println("Hey, I have " +someOne.numberOfLegs() + "legs");
System.out.println(someOne.sayHelloTo("Mr. Pinkman"));

}
}

Just a bit of the code explanation — Animal is the specification or a trait or interface which says it has an abstract behaviour called numberOfLegs(). This interface is implemented by Human concrete class and overrides the behaviour numberOfLegs and returns 2 (obviously) and also adds a different concrete behaviour.

MyMain is the main class which is main runner of the project.

Next, the (important) Bazel part:

main-dir/BUILD.bazel

java_binary(
name = "mymain",
srcs = glob(["src/main/java/com/abhi/*.java"]),
main_class = "com.abhi.MyMain",
deps = ["//another-dir:animal"]
)

java_binary — this is a pre-defined rule found here. As the name suggests, it tells bazel to create a binary when a target is invoked

  • name - this is the target. This attribute is the ONLY mandatory one.
  • srcs - all source files, passed as glob here, inside the fully qualified directory names on classpath
  • main_class - the main runner class for the binary.
  • deps - dependent classes/interfaces to be included, not part of srcs.

another-dir/BUILD.bazel

java_library(
name = "animal",
srcs = ["src/main/java/com/abhi/Animal.java"],
visibility = ["//main-dir:__pkg__"]
)

java_library — this is pre-defined too, used to create library as the name suggests. Also found here.

  • visibility - As Animal is implemented in a different package (main-dir), it has to be visible to main-dir.

The commands

Now, to build the project, we have to install Bazel first.

Once installed, cd to Bazel workspace ,

$ cd simple-java-bazel
$ bazel build //main-dir:mymain

where,

//main-dir is a valid package name as it has a BUILD file

mymain is a target name inside the BUILD file of main-dir package

To build animal target, we have to do, (it can’t be run though)

$ bazel build //another-dir:animal

Output:

INFO: Analyzed target //main-dir:mymain (19 packages loaded, 504 targets configured).
INFO: Found 1 target…
Target //main-dir:mymain up-to-date:
bazel-bin/main-dir/mymain.jar
bazel-bin/main-dir/mymain
INFO: Elapsed time: 22.807s, Critical Path: 4.98s
INFO: 5 processes: 3 darwin-sandbox, 2 worker.
INFO: Build completed successfully, 9 total actions

mymair.jar and mymain executable is created inside bazel-bin/main-dir

Next, to run the project,

$ bazel run //main-dir:mymain

Output:

INFO: Analyzed target //main-dir:mymain (0 packages loaded, 0 targets configured).
INFO: Found 1 target…
Target //main-dir:mymain up-to-date:
bazel-bin/main-dir/mymain.jar
bazel-bin/main-dir/mymain
INFO: Elapsed time: 0.171s, Critical Path: 0.00s
INFO: 0 processes.
INFO: Build completed successfully, 1 total action
INFO: Build completed successfully, 1 total action
Hey, I have 2legs
Hello Mr. Pinkman

The main program has run:

Hey, I have 2legs
Hello Mr. Pinkman

Hurray! we have our first Bazel project. Now, you can choose your favourite language, pick up the rules from here and create your Bazel app!

Troubleshooting

You might need to add these below line at the top of the corresponding BUILD files, if the bazel build fails for not finding the rules_java rules.

load(“@rules_java//java:defs.bzl”, “java_binary”)
load(“@rules_java//java:defs.bzl”, “java_library”)

Conclusion:

  • we covered all the basic concepts around Bazel — WORKSPACE, packages, targets, rules, BUILD files etc
  • we have been able to create a Bazel based project, understanding them bit-by-bit.

I hope you all find it easy enough to follow and understand as a basic guide to start with Bazel. Please clap 👏 if you find it useful and leave your comments. This will encourage me to do a Bazel 102 😊.

This project could also be found on Github

--

--

Abhishek Bose

WIP: “Everything-as-Code” engineer | Practitioner: KISS, YAGNI and DRY | Music and film enthusiast