Building Kotlin in a Bazel environment

Kotlin on Bazel

I’ve worked on the Advent of Code problems in a variety of languages. I prefer to have them in one consistent build environment, so I standardized on Bazel. There are several guides online which attempt to explain this process, but when trying to set up a project from scratch rather than modify an existing Java (or other) project I found that none of them seemed to capture everything that was required. These are the steps which worked for me.

Why bazel?

Bazel is a powerful cross-language tool to generate consistent veriable build artifacts from source. It can be painful to set up initially, but once it’s running it’s straightforward to extend and add new directories to. It is likely overkill for most projects, but as I’ve worked with it in the past it is my preferred solution for multi-language builds.

Final outcome

If you’d like to skip the explanation and see an example of a Kotlin bazel project, see this commit where I added a barebones Kotlin project to my Advent of Code Project

Adding a Kotlin build rule

Setting up the JRE

Kotlin requires a Java runtime. This is configured at the project level rather than in a BUILD file. The official way to do this is by specifying a Java version in your .bazelrc file. I find that using a remote JRE is sufficient unless you have specific reasons to use a local version.

# .bazelrc
build --java_language_version=11 --java_runtime_version=remotejdk_11

Adding Kotlin to your workspace

The bazelbuild Github page has instructions for how to add the Kotlin rules to your WORKSPACE and BUILD files.

In your WORKSPACE you will add something like the following (IMPORTANT: I recommend getting the latest version from the Github above)

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

rules_kotlin_version = "1.9.0"
rules_kotlin_sha = "5766f1e599acf551aa56f49dab9ab9108269b03c557496c54acaf41f98e2b8d6"
    name = "rules_kotlin",
    urls = ["" % rules_kotlin_version],
    sha256 = rules_kotlin_sha,

load("@rules_kotlin//kotlin:repositories.bzl", "kotlin_repositories")
kotlin_repositories() # if you want the default. Otherwise see custom kotlinc distribution below

load("@rules_kotlin//kotlin:core.bzl", "kt_register_toolchains")
kt_register_toolchains() # to use the default toolchain, otherwise see toolchains below

Build Rules

You should now be able to import and use the kt_jvm_library and kt_jvm_binary rules in your build files in a manner identical to how you would use the Java rules

load("@rules_kotlin//kotlin:jvm.bzl", "kt_jvm_binary")

    name = "01",
    srcs = glob(["*.kt"]),
    main_class = "beckbria.aoc2023.day01.MainKt",
    data = ["input.txt"],
    deps = ["//2023/utils"],
    visibility = ["//visibility:public"],


The main_class for a binary references the class which contains a main function to be invoked. The namespace segments and class file name must begin with a lowercase letter. beckbria.aoc2023.day01.MainKt will match a file named main.kt but not a file named Main.kt.