bazelizing, bazelization, to bazelize, bazelized: The act of converting some existing software artifact to be easily consumed by the Bazel build tool

This blog post is about bazelizing {fmt}. The public GitHub repository of {fmt} can be found here. In this blog post, I want to describe different approaches how to use {fmt} with Bazel.

### First approach: Inject a BUILD file

In your WORKSPACE.bazel file you can do something like this:

maybe(
new_git_repository,
name = "fmt",
branch = "master",
remote = "https://github.com/fmtlib/fmt",
build_file = "//third_party:fmt.BUILD",
)


The corresponding fmt.BUILD file can look like this:

cc_library(
name = "fmt",
srcs = [
#"src/fmt.cc", # No C++ module support
"src/format.cc",
"src/os.cc",
],
hdrs = [
"include/fmt/args.h",
"include/fmt/chrono.h",
"include/fmt/color.h",
"include/fmt/compile.h",
"include/fmt/core.h",
"include/fmt/format.h",
"include/fmt/format-inl.h",
"include/fmt/locale.h",
"include/fmt/os.h",
"include/fmt/ostream.h",
"include/fmt/printf.h",
"include/fmt/ranges.h",
"include/fmt/xchar.h",
],
includes = [
"include",
"src",
],
strip_include_prefix = "include",
visibility = ["//visibility:public"],
)


• fmt-8.01 does not have out-of-the-box for Bazel. This way Bazel can make use of {fmt} without the need that {fmt} knows anything about Bazel
• The original source code of fmt-8.0.1 needs not to be modified

• Reinvent the wheel: Every Bazel project that wants to use {fmt} has to reivent this fmt.BUILD file.
• Missing Knowledge: Maybe for some reason, it makes sense to define some special defines upfront, etc. It also takes some time and knowledge of {fmt} to set up such a BUILD file. What is the best practice to build this lib?
• Maintenance costs: If different Bazel projects want to adapt to future versions of {fmt} every single project has to do this maintenance on its own. Maybe new files will be introduced.

### Second approach: Bazelize {fmt}

Add a WORKSPACE.bazel and BUILD.bazel file to the {fmt} repository.

This way {fmt} gets bazelized and can be used in your Bazel builds.

Example

Create a WORKSPACE.bazel file with the following content:

load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")

# Fetch bazelized fmt
git_repository(
name = "fmt",
branch = "bazel-support", # A copy of master where BUILD.bazel, WORKSPACE.bazel, .bazelrc and .bazelversion are moved to root
remote = "https://github.com/<user_or_organisation>/fmt", # replace <user_or_organisation> by a valid account
)


Create a BUILD.bazel file and add a dependency to {fmt} (with the content of fmt.BUILD).

In favor of keeping the {fmt} project directory clean, those files were not added to the project root directory (see here for details).

### Third approach: Using the {fmt} repository with Bazel

Even though the {fmt} repository does not contain a WORKSPACE.bazel file in its root directory, there is an easy approach to use the {fmt} repository with Bazel out of the box. This is demonstrated in the following example.

Add to your WORKSPACE.bazel file:

load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository")

# Fetch all files from fmt including the BUILD file support/bazel/BUILD.bazel
new_git_repository(
name = "fmt_workaround",
branch = "master",
remote = "https://github.com/fmtlib/fmt/",
build_file_content = "# Empty build file on purpose"
)

# Now the BUILD file support/bazel/BUILD.bazel can be used:
new_git_repository(
name = "fmt",
branch = "master",
remote = "https://github.com/fmtlib/fmt/",
build_file = "@fmt_workaround//:support/bazel/BUILD.bazel"
)


Create a BUILD.bazel file and add a dependency to {fmt}:

cc_binary( # Build a binary
name = "Demo", # Name of the binary
srcs = ["main.cpp"], # List of files - we only have main.cpp
deps = ["@fmt//:fmt"], # Depend on fmt
)


Make use of {fmt} in main.cpp:

#include "fmt/core.h"C

int main() {
}


The expected output of this example is The answer is 42.

### Forth approach: Make use of patch_cmd

load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")

git_repository(
name = "fmt",
branch = "master",
patch_cmds = [
"mv support/bazel/.bazelrc .bazelrc",
"mv support/bazel/.bazelversion .bazelversion",
"mv support/bazel/BUILD.bazel BUILD.bazel",
"mv support/bazel/WORKSPACE.bazel WORKSPACE.bazel",
],
# Windows-related patch commands are only needed in the case MSYS2 is not installed
patch_cmds_win = [
"Move-Item -Path support/bazel/.bazelrc -Destination .bazelrc",
"Move-Item -Path support/bazel/.bazelversion -Destination .bazelversion",
"Move-Item -Path support/bazel/BUILD.bazel -Destination BUILD.bazel",
"Move-Item -Path support/bazel/WORKSPACE.bazel -Destination WORKSPACE.bazel",
],
remote = "https://github.com/fmtlib/fmt",
)


More details here.

• local_repository repository rule can not be used to embed {fmt} in your project, since it {fmt} needs to be patched first, which is not supported by this rule.