Getting started with Bazel for Android
Bazel for Android
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.
- Part 1: Getting started with Bazel for Android — this post
- Part 2: Building multi-module Android apps with Bazel
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.
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.
- 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.
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.
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
addio/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.
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.
Finally, under app
directory create AndroidManifest.xml
with the code below.
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.
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 inAndroidManifest.xml
file.manifest
— location ofAndroidManifest.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 inres
directory asresource_files
. Learn more aboutglob
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.
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 holdsBUILD
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.