rules_foreign_cc: running CMake, Make and Meson builds inside a Bazel graph
Build rules for interfacing with "foreign" (non-Bazel) build systems (CMake, configure-make, GNU Make, boost, ninja, Meson)
At a glance
- What is it?
- rules_foreign_cc is a set of Bazel rules for building C and C++ projects that come with their own build system, covering CMake, configure and make, GNU Make, Boost, Ninja and Meson. The repository is a community project rather than a Google product, and its version policy is written down explicitly.
- Who is it for?
- rules_foreign_cc is the right choice for a Bazel user who must consume a C or C++ dependency that insists on its own build system, especially when the same project has to be built in several environments, and who accepts a ruleset that sits outside Google's support commitment.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 7 days ago.
- What is it written in?
- Mainly Starlark, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The gap rules_foreign_cc fills: dependencies that ship their own build system
The one line summary in the README is the whole problem statement: rules for building C and C++ projects using foreign build systems inside Bazel projects. The topic list names the foreign systems it targets, CMake, configure and make, GNU Make, Boost, Ninja and Meson.
The reason this problem exists is not subtle. A large part of the C and C++ ecosystem predates Bazel and arrives with a build system of its own, and that build system is the only supported way to configure and compile it. Vendoring the source and writing your own BUILD file means reimplementing the configure logic, and you inherit the job of keeping that reimplementation correct across every version and every platform the project supports. Wrapping the original build system means the upstream project keeps doing the part it knows how to do.
So the audience is a Bazel user with a dependency that refuses to be a plain Bazel target. That is a common situation in a mixed shop: the rest of the repository is Starlark and C++ rules, and one library arrives as a tarball with a CMakeLists.txt and a pile of options.
The rules are written in Starlark, and the repository has a MODULE.bazel, a MODULE.bazel.lock, a WORKSPACE.bazel and a WORKSPACE.bzlmod. Having both the bzlmod file and the two WORKSPACE variants tells you the project supports the module system and still carries the legacy dependency mechanisms, which matters for anyone whose organisation has not finished migrating.
The documentation lives on its own site rather than in the README, and the README says that documentation for all rules and providers is available there. The README also points to a design document for external C and C++ library rules, which is where the reasoning behind the rule set is written down.
The examples directory is the real API documentation
The repository ships an examples tree that functions as a catalogue of use cases, and reading it tells you more about the design than any summary would.
For CMake there is a wide spread of cases: cmake_hello_world_lib, cmake_with_target, cmake_with_data, cmake_with_bazel_transitive, cmake_working_dir, cmake_defines, cmake_crosstool and cmake_synthetic. The synthetic one stands out, since it presumably exists to exercise the rules without a real project, which is what a test fixture looks like. cmake_with_bazel_transitive is the interesting name for a Bazel user, since it implies the case where a CMake project depends on something built by Bazel.
For the configure and make family there is configure_modify_input_source and configure_with_bazel_transitive. For Make there is make_simple. For Meson there is meson_simple and meson_with_requirements.
The rest of the examples tree is about how the rules behave rather than which build system they drive. current_toolchains suggests running against the toolchains the host provides, and integration_tests is a separate directory for the end-to-end checks. The mechanics are also visible in the configuration: examples/cc_wrapper.sh is a wrapper script, and there are separate .bazelrc, .bazelignore and .bazelversion files, plus a MODULE.bazel and a MODULE.bazel.lock in the examples directory itself. That last pair is the one to copy when starting your own integration, since it is the minimal bzlmod setup for the rules.
At the top level the repository also has toolchains/ and foreign_cc/ directories, an ARCHITECTURE.md, a CODEOWNERS file, a version.bzl file and a .bcr directory, the last of which is the conventional place for Bazel Central Registry submission metadata.
Version support is a written policy, not an accident
The README has a version compatibility section, and it is unusually concrete about what support means.
For Bazel itself, the intent is to support the latest minor release of all versions marked Active or Maintenance in the Bazel release model. The README is honest about the consequence: older Bazel versions may work, but they will not be tested and so they might break. That is a reasonable position for a community ruleset, and it tells you to keep Bazel current.
For the delegated build systems, the policy is stated as a logical equivalent of semantic versioning, since projects like CMake and Meson do not all use semver. Two rules apply. The first is the latest version that is upstream-supported, meaning upstream will still accept patches for it, and if upstream accepts patches for several release branches, the latest patch of each of those branches is supported. The second rule covers distribution LTS releases: the exact version and the latest patch version for supported LTS releases of distributions, on the reasoning that most packages only get tested with the versions in the distributions, so those are the versions most likely to work. Ubuntu and Fedora are named, and the README notes that versions can be referenced from Repology, giving cmake as the example.
The practical value of this section is that it tells you what to do when a foreign build system upgrades. There is a defined set of versions the ruleset aims to cover, and if your dependency pins something older or newer than that set, you are outside the tested envelope.
Release cadence is visible in the tags: 0.15.0 on 2025-06-05, 0.15.1 on 2025-06-24, and 0.16.0 on 2026-09-15. A gap of more than a year between the last two minor versions, with the last push to main on 2026-10-15, suggests work concentrated on a new minor line rather than frequent patches.
FreeBSD support is experimental, and that is stated up front
The caveats section is short and it leads with the platform gap. FreeBSD support is described as currently experimental and on a best-effort basis, because Google does not have a CI test environment for FreeBSD. The README points to an issue in the Bazel continuous integration repository and asks people to upvote it, which is a reasonable way to express demand for a CI environment.
The other structural caveat sits at the top of the README rather than at the bottom, next to the title. The project states that this is not an officially supported Google product, meaning support and new releases may be limited. That single line changes how you should read everything else in the repository, including the version compatibility policy and the release history.
It also explains the shape of the project. The repository is under the bazel-contrib organisation rather than the bazelbuild organisation, the CI badge points at Buildkite rather than a Google-run service, and the file layout includes the tooling of a carefully linted community project: a .pre-commit-config.yaml, a .clang-format file, .shellcheckrc, .yamllint.yml and .yamlfmt.yml.
For an organisation that has to answer a procurement or platform question, this is the sentence to quote. You can adopt a bazel-contrib ruleset, you can pin a version of it, and you should expect to own its maintenance. The alternative is a first-party Google ruleset, and for a project that has to live inside Google's support commitment, that is a different proposition.
How the rules differ from building the dependency yourself
The alternative to rules_foreign_cc is not another ruleset so much as a set of habits, and it is worth being precise about what changes.
Hand-rolled vendoring means the source lands in your repository and a BUILD file describes the outputs. You get full Bazel caching, a single dependency graph, and hermetic builds, at the cost of owning the mapping from the upstream build system to Bazel targets. Every upstream release that changes the library list, the generated headers or the compile flags is a patch you write and maintain, and you maintain it against every version you support.
A foreign build system wrapper runs the upstream build as a sub-build and exposes its outputs as Bazel actions. You stop maintaining the mapping and start maintaining the invocation, the environment the foreign build sees, and the set of outputs you declare. Sandboxing is the hard part in that model, because an upstream build system routinely assumes it can write into its own source tree, and the rules exist precisely to grant and contain that behaviour. The `cmake_with_bazel_transitive` and `configure_with_bazel_transitive` examples are evidence that the reverse direction is handled too, where a foreign project consumes a Bazel-built dependency.
The other difference is portability. Because the foreign build decides how to configure, the same ruleset can drive CMake on one platform and Meson on another, and the version policy section shows the ruleset is meant to cover several foreign build systems rather than one. The cost of that breadth is that the behaviour of your dependency depends on the behaviour of the foreign build system, which is exactly the variable the upstream project controls and you do not.
Apache-2.0, a community project, and what an upgrade costs you
The licence is Apache-2.0, which is a permissive licence carrying an express patent grant and requiring that changes to distributed copies are noted. That is a factual reading of the identifier rather than legal advice, and for a build ruleset the practical consequence is that you can vendor and modify it inside your own toolchain without the copyleft obligations that would apply under something like GPL.
The licence file sits alongside a CODEOWNERS file, a CONTRIBUTING.md and an AUTHORS file, which is the standard set of files for a project that wants outside contributions and wants them to go through a defined process.
The upgrade cost is the part to think about. Rules that drive another build system have a dependency surface of their own: the Bazel version, the version of the foreign build system, the version of the toolchain, and the pinned base image or CI environment that ties them together. When you move, the moving parts are the foreign build system's behaviour and the toolchain, and the rules version you need to track them.
The good news is that the repository keeps the pieces in the open: NEWS.md for generalised updates, the release notes linked from the README for per-version detail, ARCHITECTURE.md for the design, and a version.bzl file at the root. The reader who wants to know what changed in 0.16.0 compared with 0.15.1 has the places to look, and the doc site carries the per-rule and per-provider reference.
The honest summary is that you are adopting a well-organised community ruleset with a written compatibility policy, not a first-party product with a support commitment. That trade is usually worth it, as long as you make it deliberately.
Editorial conclusion
rules_foreign_cc is the right choice for a Bazel user who must consume a C or C++ dependency that insists on its own build system, especially when the same project has to be built in several environments, and who accepts a ruleset that sits outside Google's support commitment. It is the wrong choice for a repository that has no foreign build system to integrate, or for a team that needs a maintained support contract, since the README states plainly that this is not an officially supported Google product. Before adopting, read the version compatibility section and check your dependency against the supported upstream and distribution versions, then look at the examples directories for the closest match to your build system, since the repository carries separate examples for CMake variants, configure, make and Meson.
Frequently asked questions
What build systems does rules_foreign_cc support?
The project description names CMake, configure and make, GNU Make, Boost, Ninja and Meson. The examples tree covers most of these, including cmake_hello_world_lib, configure_modify_input_source, make_simple, meson_simple and meson_with_requirements.
Is rules_foreign_cc an officially supported Google product?
No. The README states plainly that this is not an officially supported Google product, meaning support and new releases may be limited. The repository lives under the bazel-contrib organisation and its CI badge points at Buildkite.
Which Bazel versions does rules_foreign_cc work with?
The intent is to support the latest minor of all Active or Maintenance Bazel versions. The README notes that older Bazel versions may work but will not be tested and so they might break, and for delegated build systems it supports the latest upstream-supported version plus exact and latest patch versions for supported distribution LTS releases.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/bazel-contrib-rules-foreign-cc)Was this useful?