Photo by Eric Welch on Unsplash

Getting started with Bazel for Android

Bazel for Android

Pavlo Stavytskyi
ProAndroidDev
Published in
7 min readMar 22, 2021

--

Bazel for Android — is a series of blog posts that shows the basics of building Android projects with Bazel build system.

Bazel — is an open-source build system developed by Google. It provides advanced local and distributed caching, optimized dependency analysis, and parallel execution. It supports a wide variety of languages and platforms including Android. You can learn more about Bazel from its official page.

In this part, we will see how to create a simple Android application built with Bazel completely from scratch. No automatic wizard or project template will be used to get started. We will create an empty project directory and populate it with source files one by one until the project is built and launched with Bazel.

You can find the source code from this post on GitHub:

Setting up Bazel

When working with Bazel, it is recommended to use Bazelisk, a wrapper for Bazel that can manage its version and automatically download the right binary if required.

Since Bazelisk is just a wrapper around Bazel, you can use bazelisk CLI the same way as you would use bazel one.

Install with brew on MacOS.

$ brew install bazelisk

Install with npm.

$ npm install -g @bazel/bazelisk

You can learn more about installation options for Bazelisk from its official documentation.

Setting up Android Studio

As we know, Android Studio provides various features that boost developer productivity significantly such as code completion, code analysis, refactoring tools, etc.

To use this functionality in Bazel projects, we need to install a corresponding plugin to Android Studio. To do so, follow these steps:

  • Click File > Settings from the menu bar
    (or Android Studio > Preferences on macOS).
  • Click Plugins > Marketplace.
  • Type Bazel and install the official Bazel plugin.
Installing Bazel plugin in Android Studio

Creating Bazel workspace

In Bazel, each project is defined by the WORKSPACE file that must be located in its root directory. The WORKSPACE file allows you to fetch external extensions to Bazel (so-called rule sets) that know how to build various programming languages and platforms including Android, Kotlin, etc.

Follow these steps to set up our project workspace:

  • Create an empty folder on your computer. It will be the root directory of our project. Let’s call it bazel-for-android.
  • Inside this folder create a file named WORKSPACE.
  • Open the terminal at the project directory and execute the command below.
$ bazelisk info workspace

If all is done correctly, it should print the path to your current project directory.

Importing project in Android Studio

Now, we need to enable Android Studio support for our project. To do this, follow these steps:

  • Open Android Studio.
  • Click File > Import Bazel Project…
  • Select path to the root project directory.
  • Select Crete from scratch.
  • Replace all the code in the pop-up window with the snippet below.
Android Studio Bazel plugin configuration
  • Click Finish.

Now Android Studio is able to recognize our Bazel project.

We won’t be diving deep into the Bazel plugin configuration but you can find more details about it in the official documentation.

You can always edit the configuration of an Android Studio Bazel plugin though. It is located in the .aswb/.bazelproject file (or .ijwb/.bazelproject if you’re using IntelliJ IDEA).

Writing code in WORKSPACE file

As you might remember, we use Groovy or Kotlin languages to configure build scripts in Gradle. Bazel uses Starlark language for this purpose. Starlark is a dynamically typed language that is a dialect of Python but with several restrictions. You can learn more about Starlark from its official documentation.

We need to write some scripts to tell Bazel how to build Android projects. To do this, we need to download a corresponding set of build rules into our Bazel project.

Build rules concept in Bazel is pretty similar to plugins in Gradle. Their main purpose is to extend the functionallity of the build system.

We will write those scripts in WORKSPACE file using Starlark language. Open WORKSPACE file and add populate it with the code below.

WORKSPACE file contents

Here we added a set of android rules to our project and called them rules_android. (Basically, this is another Bazel workspace that we’ve fetched into our project)

In rules_android workspace that we’ve just downloaded, we need to find android/rules.bzl file and import android_sdk_repository rule from it.

Then we use android_sdk_repository rule to set up Android SDK in our Bazel project.

Android SDK location

By default, android_sdk_repository will try to find an Android SDK on your machine by looking for $ANDROID_HOME environment variable.

Alternatively, you can explicitly specify the path to the Android SDK on your machine.

Using android_sdk_repository rule

Optionally, it is possible to specify api_level and build_tools_version but make sure those specific versions are installed on your machine. Otherwise, Bazel will try to use the latest ones on your computer. You can use SDK Manager in Android Studio to install required versions of Android SDK and Build Tools.

Creating folder structure

Next, we need to set up a folder layout for our project. To do so, follow these steps:

  • Create app folder in the root project directory.
  • Create app/src folder that will contain the source code of our Android application.
  • Under app/src add io/morfly/bfa packages.
  • Also, create app/res/layout folders that will contain project resources and layouts in particular.

Here is the project structure so far:

bazel-for-android
├── app
│ ├── src
│ │ └── io
│ │ └── morfly
│ │ └── bfa
│ └── res
│ └── layout

└── WORKSPACE

Writing source code

Now, we are ready to add the source code for our Android application.

Under the app/src/io/morfly/bfa directory create a MainActivity.java file and fill it with the code below.

MainActivity.java file contents

As you can see, it’s just a simple Activity written in Java.

Next, we will create a layout file for our activity. Under app/res/layout create a activity_main.xml with the following content.

activity_main.xml file contents

Finally, under app directory create AndroidManifest.xml with the code below.

AndroidManifest.xml file contents

This is the minimal set of files to get our Android app working. The only thing left, is to configure Bazel to build our project which we will do next.

Here is the project structure so far:

bazel-for-android 
├── app
│ ├── src
│ │ └── io/morfly/bfa
│ │ └── MainActivity.java
│ ├── res
│ │ └── activity_main.xml
│ │
│ └── AndroidManifest.xml

└── WORKSPACE

Note. Bazel is more flexible in terms of project structure, so you don’t necessarily need to follow the structure used in Gradle Android projects.

Building the project

Now, we need to configure Bazel, so that we can build our project with it.

In Gradle, we use build.gradle or build.gradle.kts files do define build configuration for our modules. In case of Bazel, we need to use BUILD files.

Under the app directory create a file named BUILD. We would also write a Starlark code in this file.

If you remember, we have already fetched rules_android in WORKSPACE file earlier. Now, we need to load another rule from it called android_binary. This rule will help us to configure an executable target for our Android app.

app/BUILD file contents

In Bazel, every rule has a different set of parameters. In case of android_binary apart from the unique name we should also specify:

  • custom_pagkage — make sure it’s the same as in AndroidManifest.xml file.
  • manifest — location of AndroidManifest.xml.
  • manifest_values — similarly to Gradle, we can set manifest values. In our case, we can just specify versions of Android SDK for our project. Make sure you have them installed on your machine.
  • srcs, resource_files — list of source and resource files respectively.

For file locations, we can either list them explicitly or use glob function with patterns. In the example above, we take all files located in res directory as resource_files. Learn more about glob patterns here.

You can learn more about Android rules and their parameters from the official documentation.

Now, open your terminal at the root directory of your workspace and run the command below to build the project.

$ bazelisk build //app:bin

Now, launch the emulator or connect the real mobile device to your computer and run the command below.

$ bazelisk mobile-install //app:bin --start_app

If the build was successful, you will see the Android application launched on your device showing “Bazel for Android 🍃” text on the screen.

Building the project using Android Studio

Alternatively, you can build the project directly from Android Studio using Bazel plugin that we’ve installed earlier.

Use a green arrow near the android_binary target to run the project.

Running the project in Android Studio

Here is the project structure so far:

bazel-for-android 
├── app
│ ├── src
│ │ └── io/morfly/bfa
│ │ └── MainActivity.java
│ ├── res
│ │ └── activity_main.xml
│ │
│ ├── BUILD
│ └── AndroidManifest.xml

└── WORKSPACE

Understanding Bazel labels

Before we wrap up, let’s analyze a command that we used to build the project.

$ bazelisk build //app:bin

We have used //app:bin label to refer to the android_binary target that we defined earlier. Let’s deconstruct it into components:

  • // — means that we start referring to our target from the root of the workspace.
  • app — is the Bazel package that contains the target we want to build.
  • :bin — is the name of the target that we want to build.

Note: Bazel package is the directory that holds a BUILD file. Therefore, Bazel package name is defined by the relative path from the project root to the directory that holds BUILD file.

Conclusion

This is the bare minimum required for building Android applications with Bazel. You can find source code on GitHub at the link below.

In the next parts, we will see how to add multiple modules to the Android project, add external dependencies and use Kotlin language.

--

--

Google Developer Expert for Android, Kotlin | Sr. Staff Software Engineer at Turo | Public Speaker | Tech Writer