Practical Bazel: Start with Genrules
Practical Bazel bazel libpng autoconf make
Published: 2020-10-21
Practical Bazel: Start with Genrules

Bazel is a powerful yet complicated system, and it can be intimidating to newcomers.

While the Bazel user guide and user manual preach the benefits of giving Bazel full control over your build process by rewriting all build processes using Bazel-native rulesets (as Google reportedly does internally), this is an immense amount of work. Specifically, if you are integrating third-party software into your Bazel-based build process, reverse engineering and rewriting the third-party project’s build system into Bazel can easily take days – and then you need to maintain it.

Start simple by prioritizing getting a working Bazel-based build system up and running as quickly as possible. For third-party software that already has an existing build system, start by using Bazel genrules to call into the third-party system. For code that you write, try the existing Bazel native rulesets first, but switch to genrules if they don’t meet your needs.

For example, let’s say I want to integrate libpng into my project’s monorepo. libpng has an autoconf-based build system which would require substantial engineering effort to replicate. Rather than spending days rewriting the build system, I’ll start by just calling into the libpng build process using http_archive and genrule, largely replicating what I would do if I built libpng by hand outside of Bazel.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# WORKSPACE
workspace(name = "start_with_genrules")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "libpng",
    urls = ["https://download.sourceforge.net/libpng/libpng-1.6.37.tar.gz"],
    sha256 = "daeb2620d829575513e35fecc83f0d3791a620b9b93d800b763542ece9390fb4",
    strip_prefix = "libpng-1.6.37",
    build_file = "@//:libpng.BUILD",
)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# libpng.BUILD
genrule(
    name = "build_libpng",
    srcs = glob(["**"]),
    outs = ["libpng16.a"],
    cmd = """
set -euo pipefail

# Avoid changing working directory because it breaks macros
# like location
( cd external/libpng && ./configure )
make -C external/libpng
cp external/libpng/.libs/libpng16.a $(location libpng16.a)
"""
)

See https://github.com/sengelha/practical_bazel_examples/tree/master/start_with_genrules for a full, working example.

Over time, as your Bazel experience increases and you identify your build process pain points, you can selectively rewrite your genrules into Bazel-native rules – potentially even creating your own rulesets along the way.