Scalio

Bazel

Bazel with Android + Kotlin


Bazel with Android + Kotlin

Bazel is a flexible build tool from Google used for automating both building and testing software across a variety of languages and platforms. In this article, we will explore how to create a simple Android project with Kotlin 1.3 support.


TL;DR


The final project can be found in Scalio’s Android Kotlin Starter repository.


Let’s get started


 

Choose the Empty Activity project template in Android Studio:


 

Select `Empty Activity`Configure project setup

It’s time to replace the default Gradle configuration with some sweet Bazel!


 

Bazel configuration files

 

Bazel will require a slight adjustment if you’re coming from Gradle, which has a convention-based notion of a project where all required files reside. Bazel, on the other hand, specifies all relevant build files explicitly by defining a single WORKSPACE file with each package/target defined by a separate BUILD file.



WORKSPACE file


The main Bazel configuration is held in a single WORKSPACE file located at the project root. This is where we specify each external project dependency that will be used in the build targets.


We have a couple of options when defining the Android SDK within Bazel.


Assuming you already have your local ANDROID_HOME variable set as described in the documentation, you can import the Android SDK by simply using:


 

android_sdk_repository(    name = "androidsdk",    api_level = 29,    build_tools_version = "29.0.2" )

Android SDK Declaration (Implicit)


You can also import the Android SDK manually by specifying the path:


 

android_sdk_repository(    name = "androidsdk",    path = "/path/to/Android/sdk",    api_level = 29,    build_tools_version = "29.0.2" )

Android SDK Declaration (Explicit)


Since every Bazel target requires its own rules, we need to specify Android related rules as well. We can use the official Android rules directly from Google:


 

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive(    name = "rules_android",    urls = ["https://github.com/bazelbuild/rules_android/archive/v0.1.1.zip"],    sha256 = "cd06d15dd8bb59926e4d65f9003bfc20f9da4b2519985c27e190cddc8b7a7806",    strip_prefix = "rules_android-0.1.1", )

Base Android integration rule



WORKSPACE file — Kotlin 1.3


One issue we dealt with when we started using Bazel with Android is that there are no official Android rules that support Kotlin version 1.3+. Google’s latest official version only supports Kotlin 1.2 and has a number of unresolved issues. Since we want to use coroutines and all the latest Kotlin goodies, we at Scalio decided to write our own rules. Thanks to Google and Christian Edward, Kotlin 1.3 is supported now by default!

kotlin 1-2 to 1-3.jpg 51.09 KB
Include Kotlin 1.3.31 support in your projects with the following rule:


 

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") rules_kotlin_version = "legacy-1.3.0-rc1" rules_kotlin_sha = "9de078258235ea48021830b1669bbbb678d7c3bdffd3435f4c0817c921a88e42" http_archive(    name = "io_bazel_rules_kotlin",    urls = ["https://github.com/bazelbuild/rules_kotlin/archive/%s.zip" % rules_kotlin_version],    type = "zip",    strip_prefix = "rules_kotlin-%s" % rules_kotlin_version,    sha256 = rules_kotlin_sha ) load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kotlin_repositories", "kt_register_toolchains") kotlin_repositories() kt_register_toolchains()


Kotlin integration rules


If you require any Kotlin 1.3.* version, you can specify the compiler explicitly by modifying the rule:


 

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") kotlin_version = "1.3.61" kotlin_release_sha = "3901151ad5d94798a268d1771c6c0b7e305a608c2889fc98a674802500597b1c" rules_kotlin_compiler_release = {    "urls": [    "https://github.com/JetBrains/kotlin/releases/download/v{v}/kotlin-compiler-{v}.zip".format(v = kotlin_version),    ],    "sha256": kotlin_release_sha, } rules_kotlin_version = "legacy-1.3.0-rc1" rules_kotlin_sha = "9de078258235ea48021830b1669bbbb678d7c3bdffd3435f4c0817c921a88e42" http_archive(    name = "io_bazel_rules_kotlin",    urls = ["https://github.com/bazelbuild/rules_kotlin/archive/%s.zip" % rules_kotlin_version],    type = "zip",    strip_prefix = "rules_kotlin-%s" % rules_kotlin_version,    sha256 = rules_kotlin_sha ) load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kotlin_repositories", "kt_register_toolchains") kotlin_repositories(compiler_release = rules_kotlin_compiler_release) kt_register_toolchains()

Kotlin integration rules with custom compiler


The only thing left to do is to include other external dependencies. Android Studio uses AppCompat and ConstraintLayout in its samples, so we need to tell Bazel where to find those libraries:


 

RULES_JVM_EXTERNAL_TAG = "2.2" RULES_JVM_EXTERNAL_SHA = "f1203ce04e232ab6fdd81897cf0ff76f2c04c0741424d192f28e65ae752ce2d6" http_archive(    name = "rules_jvm_external",    strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG,    sha256 = RULES_JVM_EXTERNAL_SHA,    url = "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG, ) load("@rules_jvm_external//:defs.bzl", "maven_install") maven_install(    artifacts = [        "androidx.appcompat:appcompat:1.0.2",        "androidx.fragment:fragment:1.0.0",        "androidx.core:core:1.0.1",        "androidx.lifecycle:lifecycle-runtime:2.0.0",        "androidx.lifecycle:lifecycle-viewmodel:2.0.0",        "androidx.lifecycle:lifecycle-common:2.0.0",        "androidx.drawerlayout:drawerlayout:1.0.0",        "androidx.constraintlayout:constraintlayout:1.1.3",        "com.google.android.material:material:1.0.0",        "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2",        "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2"    ],    repositories = [        "https://maven.google.com",    ],    fetch_sources = True, )

External project dependencies


BUILD file


There is a single package in our sample, so we only need to create one BUILD file for Bazel located at the package root.


We first create the initial imports from the WORKSPACE file, set the resource target bazel_res, set the Kotlin target bazel_kt and finally compile it all together into the binary target bazel.


 

load("@rules_jvm_external//:defs.bzl", "artifact") load("@rules_android//android:rules.bzl", "android_library") package(default_visibility = ["//visibility:private"]) load("@io_bazel_rules_kotlin//kotlin:kotlin.bzl", "kt_android_library") PACKAGE = "io.scal.bazel" MANIFEST = "AndroidManifest.xml" android_library(    name = "bazel_res",    custom_package = PACKAGE,    manifest = MANIFEST,    resource_files = glob(["res/**"]),    enable_data_binding = False,    deps = [        artifact("androidx.constraintlayout:constraintlayout"),        artifact("com.google.android.material:material"),    ], ) kt_android_library(    name = "bazel_kt",    srcs = glob(["java/**/*.kt"]),    deps = [        ":bazel_res",        artifact("androidx.appcompat:appcompat"),        artifact("androidx.fragment:fragment"),        artifact("androidx.core:core"),        artifact("androidx.lifecycle:lifecycle-runtime"),        artifact("androidx.lifecycle:lifecycle-viewmodel"),        artifact("androidx.lifecycle:lifecycle-common"),        artifact("androidx.drawerlayout:drawerlayout"),    ] ) android_binary(    name = "bazel",    manifest = MANIFEST,    custom_package = PACKAGE,    manifest_values = {        "minSdkVersion": "21",        "versionCode" : "2",        "versionName" : "0.2",        "targetSdkVersion": "29",    },    deps = [        ":bazel_res",        ":bazel_kt",        artifact("androidx.appcompat:appcompat"),    ], )

Final Build File


That’s it! The only thing left to do is build our shiny, new project with Bazel by running:


 

bazel build //app/src/main:bazel

Bazel build command



Conclusion


Note that Android Studio has great integration with Bazel via its plugin if you would rather work outside the command line.


The final project can be found in Scalio’s Android Kotlin Starter repository. The Bazel Kotlin plugin is open source and can be found on Github.


A special thanks to Micah Lucas for edits and revisions.

How can we help you?