CMake
Bazel
build systems
software development
cross-platform development

Building a CMake library within a Bazel project

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Integrating a CMake-built library into a Bazel project is a common migration step when adopting Bazel incrementally. Many teams have existing native dependencies with CMake scripts they cannot rewrite immediately. The key is to make Bazel own orchestration while isolating non-hermetic CMake behavior as much as possible. A solid integration strategy defines inputs/outputs clearly, caches artifacts predictably, and provides a path to eventual native Bazel rules.

Core Sections

Integration patterns

Common approaches:

  • prebuild library outside Bazel and import via cc_import,
  • invoke CMake through genrule or repository rule,
  • use external rulesets that bridge CMake.

A simple import pattern after prebuild:

python
1cc_import(
2    name = "mylib",
3    hdrs = glob(["include/**/*.h"]),
4    static_library = "lib/libmylib.a",
5)

This is stable when binary artifacts are versioned and reproducible.

Invoking CMake from Bazel

For tighter integration, use a rule that runs CMake configure and build.

python
1genrule(
2    name = "build_mylib",
3    srcs = glob(["cmake_src/**"]),
4    outs = ["libmylib.a"],
5    cmd = "cmake -S $(location cmake_src) -B $(@D)/build && "
6          "cmake --build $(@D)/build --config Release && "
7          "cp $(@D)/build/libmylib.a $@",
8)

This is workable but can reduce hermeticity and portability if toolchains are not pinned.

Toolchain consistency

Bazel and CMake may use different compilers or flags by default. Align ABI-related settings like C++ standard library, RTTI, exceptions, and optimization flags to avoid linker/runtime issues.

Header and dependency boundaries

Expose only stable public headers through Bazel targets. Avoid leaking generated private include paths from CMake internals unless necessary.

Migration strategy

Treat bridge integration as transitional. As critical libraries stabilize, port build definitions to native Bazel rules to gain better caching and reproducibility.

Common Pitfalls

  • Mixing incompatible compiler toolchains between CMake and Bazel outputs.
  • Relying on environment-specific CMake paths that break in CI or remote executors.
  • Hiding undeclared headers or generated files from Bazel dependency graph.
  • Overusing ad hoc genrule wrappers without deterministic outputs.
  • Keeping bridge layer permanently and accumulating unmaintainable build complexity.

Verification Workflow

Build from a clean checkout in local and CI environments, then validate binary linkage and ABI compatibility with integration tests. Track reproducibility by comparing build metadata across runs. Document toolchain versions and required CMake options in the repository so new engineers can reproduce builds quickly.

text
11. Clean build in Bazel sandbox
22. Build in CI with same toolchain
33. Run linkage and smoke tests
44. Check deterministic outputs
55. Document and version build flags

Production Readiness Checklist

Before considering the implementation complete, run a repeatable readiness pass that validates correctness, failure handling, and operational behavior in the same environment class where this solution will run. Start with a deterministic happy-path example and then exercise one malformed input and one resource-constrained scenario. Capture structured output such as status codes, key counters, and timing metrics so regressions are visible across revisions.

Document expected behavior boundaries in plain language so future maintainers can quickly understand what is guaranteed and what is best-effort. If configuration affects behavior, include the exact setting names and safe defaults in your runbook. For team workflows, add one lightweight automated check in CI to enforce these expectations on every change and keep debugging effort low when dependencies or runtime versions change.

text
11. Validate normal input path
22. Validate malformed or missing input path
33. Validate constrained-resource behavior
44. Record timing and error metrics
55. Confirm rollback or fallback behavior
66. Add CI smoke check for regression detection

Summary

Building a CMake library inside Bazel is feasible and useful for incremental migration. Keep boundaries explicit, align toolchains, and prioritize deterministic artifacts. Use bridging mechanisms pragmatically, but plan toward native Bazel rules for long-term maintainability.


Course illustration
Course illustration

All Rights Reserved.