A blazing-fast, workflow-first CMake formatter — built in Rust, built to last.
cmakefmt replaces the aging Python cmake-format tool with a
single native binary. Same spirit. No Python. No compromises.
Crates.io package name: cmakefmt-rust (CLI binary remains cmakefmt).
This project is independent from other Rust implementations, including:
azais-corentin/cmakefmt and
yamadapc/cmakefmt.
- Why
cmakefmt? - Performance
- Installation
- Quick Start
- Common Workflows
- Configuration
- Formatter Disable Regions
- Library Usage
- Documentation
- Project Layout
- Development
- Status
- License
Why cmakefmt?
- 20× faster — not a typo. Geometric-mean speedup of
20.69xovercmake-formaton real-world corpora. Pre-commit hooks that once made you wince now finish before you blink. - Zero dependencies. One binary. No Python environment, no virtualenv bootstrap, no dependency drift. Drop it in CI and forget about it.
- Built for actual workflows.
--check,--diff,--staged,--changed,--files-from,--show-config,--explain-config, semantic verification, JSON reporting — all first-class, not scripted workarounds. - Knows your commands. Teach
cmakefmtthe shape of your project's custom CMake functions and macros. No more generic token-wrapping for code you wrote. - Errors that actually help. Parse and config failures come with file/line context, source snippets, and reproduction hints — not opaque parser noise.
- Designed for real repositories. Comment preservation, disable-region passthrough, config discovery, ignore files, Git-aware file selection, and opt-in parallelism are core features, not afterthoughts.
Performance
| Fixture | Lines | cmakefmt ms |
cmake-format ms |
Speedup |
|---|---|---|---|---|
abseil/CMakeLists.txt |
280 | 5.804 | 168.570 | 29.04× |
catch2/CMakeLists.txt |
230 | 5.768 | 105.614 | 18.31× |
cli11/CMakeLists.txt |
283 | 5.570 | 120.994 | 21.72× |
cmake_cmbzip2/CMakeLists.txt |
25 | 5.042 | 61.751 | 12.25× |
googletest/CMakeLists.txt |
36 | 5.004 | 62.439 | 12.48× |
ggml/CMakeLists.txt |
498 | 7.773 | 210.200 | 27.04× |
llama_cpp/CMakeLists.txt |
286 | 6.257 | 126.584 | 20.23× |
llvm_tablegen/CMakeLists.txt |
83 | 5.172 | 75.429 | 14.58× |
mariadb_server/CMakeLists.txt |
656 | 9.774 | 473.879 | 48.49× |
nlohmann_json/CMakeLists.txt |
237 | 5.705 | 138.936 | 24.35× |
opencv_flann/CMakeLists.txt |
2 | 4.719 | 51.497 | 10.91× |
protobuf/CMakeLists.txt |
351 | 6.226 | 111.802 | 17.96× |
spdlog/CMakeLists.txt |
413 | 9.204 | 213.649 | 23.21× |
qtbase_network/CMakeLists.txt |
420 | 8.146 | 284.355 | 34.91× |
Geometric-mean speedup across the full corpus: 20.69×.
On a 220-file batch, --parallel 8 improves throughput by 3.80× vs serial.
Full methodology and profiler notes: cmakefmt.dev/performance.html.
Refresh the pinned local corpus and generate local before/after review artefacts with:
Installation
Homebrew (macOS and Linux):
Or in one step:
Cargo:
Build from source:
Verify:
Planned release channels and support levels are documented at cmakefmt.dev/release.html. Shell completion installation guidance lives in cmakefmt.dev/install.html.
Quick Start
1. Generate a starter config in your project root:
2. Dry-run — check your whole project without touching any files:
3. Apply formatting:
4. Format only the files you're about to commit:
Common Workflows
| Task | Command |
|---|---|
| Format file to stdout | cmakefmt CMakeLists.txt |
| Rewrite files in place | cmakefmt --in-place . |
| CI check | cmakefmt --check . |
| Preview which files would change | cmakefmt --list-changed-files . |
| See the exact patch | cmakefmt --diff CMakeLists.txt |
| Verify semantics while formatting to stdout | cmakefmt --verify CMakeLists.txt |
| Pre-commit guard (staged files only) | cmakefmt --staged --check |
| PR-scoped check | cmakefmt --changed --since origin/main --check |
| Machine-readable CI output | cmakefmt --check --report-format json . |
| GitHub Actions annotations | cmakefmt --check --report-format github . |
| Checkstyle / JUnit / SARIF output | cmakefmt --check --report-format checkstyle . |
| Pin the required binary version in CI | cmakefmt --required-version 0.1.1 --check . |
| Speed up repeated large-repo checks | cmakefmt --cache --check . |
| Roll out formatting file-by-file | cmakefmt --require-pragma --check . |
| Read from stdin | cat CMakeLists.txt | cmakefmt - |
Configuration
cmakefmt searches upward from each file for .cmakefmt.yaml, .cmakefmt.yml, or .cmakefmt.toml.
YAML is recommended for larger configs.
Example .cmakefmt.yaml:
format:
line_width: 100
tab_size: 4
style:
command_case: lower
keyword_case: upper
markup:
reflow_comments: true
Debug which config a file is actually using:
Migrate from an existing cmake-format config:
Full config reference: cmakefmt.dev/config.html.
Formatter Disable Regions
Selectively opt out of formatting with barrier comments.
There are three barrier styles:
-
Legacy
cmake-formatdirectives:# cmake-format: off set(MESSY_THING a b c) # kept verbatim # cmake-format: on -
Native directive barriers, using either
cmakefmtor the shorterfmtspelling:# cmakefmt: off set(MESSY_THING a b c) # kept verbatim # cmakefmt: on # fmt: off set(MESSY_THING a b c) # kept verbatim # fmt: on -
Fence barriers, which toggle formatting on and off each time
# ~~~appears:# ~~~ set(MESSY_THING a b c) # kept verbatim # ~~~
Use directive barriers when you want an explicit start/end marker, and fence barriers when you want a shorter toggle-style block.
Library Usage
cmakefmt is also available as a Rust library:
use ;
Full API docs: cmakefmt.dev/api.html.
Documentation
Start here: https://cmakefmt.dev.
| Doc | Description |
|---|---|
| Install | Install options, first-project setup, CI wiring |
| Coverage | How coverage is measured, published, and interpreted |
| Release Channels | Release contract, support levels, release artifacts, and shell completions |
| CLI Reference | Every flag, exit code, and discovery rule |
| Config Reference | Full config schema with examples |
| Formatter Behavior | How the formatter makes layout decisions |
Migration from cmake-format |
Incremental rollout guide and CLI mapping |
| Library API | Embedding cmakefmt in your own Rust tools |
| Troubleshooting | Common issues and debug workflow |
| Performance | Benchmark methodology and profiler notes |
| Contributing | How to contribute, run tests, and open PRs |
| Changelog | What's changed in each release |
Preview the full docs locally:
Project Layout
cmakefmt/
├── docs/ # mdBook source published to cmakefmt.dev
├── src/ # CLI, library API, parser, config, spec, formatter
├── tests/ # integration tests, snapshots, and fixtures
├── benches/ # Criterion benchmarks
├── scripts/ # repo maintenance and docs helpers
└── .github/ # CI and Pages workflows
Key modules under src/:
main.rs: CLI entry point and workflow orchestrationlib.rs: public library APIconfig/: config loading, merging, and legacy conversionparser/:pestgrammar, AST, and parse pipelinespec/: built-in and user-defined command registryformatter/: AST-to-doc formatting logic and comment handlingfiles.rs: file discovery and ignore handling
Development
Install pre-commit hooks:
Status
The repository is stable and actively maintained. cmakefmt is still
pre-1.0, so release packaging, package-manager distribution, and some output
or API details may continue to evolve. The built-in command registry is audited
through CMake 4.3.1.
Hit something unexpected? See Troubleshooting or run:
License
cmakefmt is dual-licensed under MIT or Apache-2.0 at your option.