Formatting Cookbook
Don’t know the option name? Start here. Each section shows a common formatting goal, the config to achieve it, and a before/after example.
All examples assume the default config unless stated otherwise. Click the config option name to jump to its full documentation in the Config Reference.
I want to control line width
Section titled “I want to control line width”Option: line_width (default: 80)
Narrower widths force more wrapping. Wider widths keep more on one line.
format: line_width: 40Before (line_width: 80):
target_link_libraries( mylib PUBLIC Boost::filesystem Boost::system fmt::fmt spdlog::spdlog)After (line_width: 40):
target_link_libraries( mylib PUBLIC Boost::filesystem Boost::system fmt::fmt spdlog::spdlog)I want to change indentation width
Section titled “I want to change indentation width”Option: tab_size (default: 2)
Controls how many spaces each indentation level uses. Larger values make nesting more visually distinct; smaller values save horizontal space.
format: tab_size: 4Before (tab_size: 2):
if(ENABLE_TESTS) add_executable(test_runner test_main.cc)endif()After (tab_size: 4):
if(ENABLE_TESTS) add_executable(test_runner test_main.cc)endif()I want to use tabs instead of spaces
Section titled “I want to use tabs instead of spaces”Option: use_tabs (default: false)
Some projects prefer tab characters for indentation so that each developer’s editor can display them at their preferred width.
format: use_tabs: trueIndentation uses tab characters instead of spaces. The visual width is
still controlled by tab_size.
I want to change command name casing
Section titled “I want to change command name casing”Option: command_case (default: lower)
CMake is case-insensitive for command names, so add_executable and
ADD_EXECUTABLE are identical. This option enforces a consistent style
across your project.
format: command_case: upperBefore:
cmake_minimum_required(VERSION 3.20)add_executable(myapp main.cc)After:
CMAKE_MINIMUM_REQUIRED(VERSION 3.20)ADD_EXECUTABLE(myapp main.cc)Set to unchanged to leave command names as they are in the source.
I want to change keyword casing
Section titled “I want to change keyword casing”Option: keyword_case (default: upper)
Keywords like PUBLIC, PRIVATE, VERSION, and FATAL_ERROR are also
case-insensitive in CMake. This option enforces consistent casing for
all recognized keywords and flags.
format: keyword_case: lowerBefore:
target_link_libraries(mylib PUBLIC dep1 dep2)cmake_minimum_required(VERSION 3.20 FATAL_ERROR)After:
target_link_libraries(mylib public dep1 dep2)cmake_minimum_required(version 3.20 fatal_error)I want the closing parenthesis on its own line
Section titled “I want the closing parenthesis on its own line”Option: dangle_parens (default: false)
Some teams prefer the closing ) on its own line for wrapped commands,
similar to how many C/C++ styles place } on its own line. This makes
diffs cleaner when adding arguments at the end.
format: dangle_parens: trueBefore:
target_link_libraries( mylib PUBLIC Boost::filesystem Boost::system fmt::fmt spdlog::spdlog)After:
target_link_libraries( mylib PUBLIC Boost::filesystem Boost::system fmt::fmt spdlog::spdlog)I want a space before ( in if() / foreach()
Section titled “I want a space before ( in if() / foreach()”Option: space_before_control_paren (default: false)
Some style guides prefer if (CONDITION) over if(CONDITION) to
visually distinguish control-flow statements from function calls.
format: space_before_control_paren: trueBefore:
if(ENABLE_TESTS) message(STATUS "running tests")endif()After:
if (ENABLE_TESTS) message(STATUS "running tests")endif ()I want to sort argument lists
Section titled “I want to sort argument lists”Options: enable_sort and autosort (both default: false)
Dependency lists and source file lists are often added to over time and end up in random order. Sorting them makes it easier to spot duplicates and find entries at a glance.
format: enable_sort: true autosort: trueBefore:
target_link_libraries( mylib PUBLIC spdlog::spdlog Boost::filesystem fmt::fmt Boost::system)After:
target_link_libraries( mylib PUBLIC Boost::filesystem Boost::system fmt::fmt spdlog::spdlog)autosort sorts keyword sections where all arguments are simple unquoted
tokens (no variables or generator expressions). Sorting is
case-insensitive.
For finer control, mark specific sections as sortable in a custom
command spec — then only those sections are sorted, regardless of
autosort:
format: enable_sort: true
commands: my_cmd: kwargs: SOURCES: nargs: "+" sortable: trueIf a section’s value list has positional semantics that sorting
would corrupt — for example, a label-then-values shape like
set_property(... PROPERTY <name> <values…>) — opt it out of the
autosort heuristic with no_autosort: true. The built-in specs for
set_property, set_target_properties, set_source_files_properties,
set_directory_properties, and set_tests_properties already carry
this marker. For your own custom specs:
commands: my_cmd: kwargs: PROPERTY: nargs: "+" no_autosort: trueI want to keep the variable name on the set() line
Section titled “I want to keep the variable name on the set() line”Option: wrap_after_first_arg (default for set(): true)
This is already enabled for set() by default. The variable name stays
on the set( line even when the rest wraps:
set(SOURCES main.cc utils.cc parser.cc formatter.cc config.cc)To disable it:
per_command_overrides: set: wrap_after_first_arg: falseWhich produces:
set( SOURCES main.cc utils.cc parser.cc)I want to limit blank lines
Section titled “I want to limit blank lines”Option: max_empty_lines (default: 1)
Over time, CMake files accumulate inconsistent blank-line spacing — some sections have one blank line, others have three or four. This option clamps consecutive blank lines to a consistent maximum.
format: max_empty_lines: 0Set to 0 to remove all blank lines between commands, 1 (the default)
for single spacing, or 2 to allow double spacing.
I want strict line-width enforcement in CI
Section titled “I want strict line-width enforcement in CI”Option: require_valid_layout (default: false)
When enabled, the formatter returns an error if any formatted line
exceeds line_width — for example because a quoted string or bracket
argument is too long to break. This guarantees that your CI pipeline
catches overlong lines instead of silently accepting them.
format: line_width: 40 require_valid_layout: trueExample error output when a line can’t fit within the configured width:
error: line 2 is 74 characters wide, exceeding the limit of 40hint: set line_width = 74 (or higher), add the command to always_wrap, or disable require_valid_layoutUseful in CI where you want a hard guarantee that no line exceeds the limit.
I want to force specific commands to always wrap
Section titled “I want to force specific commands to always wrap”Option: always_wrap (default: [])
Some commands are easier to read in vertical layout even when they’d
fit on one line — for example, target_link_libraries with a few short
dependencies. By default, the formatter keeps short calls inline. This
option forces named commands to always wrap vertically.
format: always_wrap: - target_link_librariesBefore:
target_link_libraries(mylib PUBLIC dep1 dep2)After:
target_link_libraries( mylib PUBLIC dep1 dep2)I want to override settings for one command
Section titled “I want to override settings for one command”Section: per_command_overrides
Sometimes a single command looks better with different settings — for
example, message() strings are often long and read better with a wider
line width, while the rest of your project uses the default.
per_command_overrides: message: line_width: 120Before (global line_width: 80):
message( STATUS "Building project ${PROJECT_NAME} version ${PROJECT_VERSION} for ${CMAKE_SYSTEM_NAME}")After (with message overridden to line_width: 120):
message( STATUS "Building project ${PROJECT_NAME} version ${PROJECT_VERSION} for ${CMAKE_SYSTEM_NAME}")Any format option can be overridden per command name. The override applies only to that command; all others use the global setting.
I want to disable formatting for a section
Section titled “I want to disable formatting for a section”Sometimes you have a block of CMake code with intentional non-standard formatting — a hand-aligned table, a generated section, or a legacy block you’re not ready to touch. Wrap it in a disable region:
# cmakefmt: offset(SPECIAL keep this exactly)# cmakefmt: onAlso supported: # cmake-format: off/on, # fmt: off/on, # ~~~.
See Disabled Regions for details.
I want to stop comment reflow
Section titled “I want to stop comment reflow”Option: enable_markup (default: true)
By default, cmakefmt reflows long comments to fit within line_width.
If your comments contain ASCII art, pre-formatted tables, license
headers, or other content that should not be rewrapped, disable this.
markup: enable_markup: falseWith enable_markup: true (default), a long comment is reflowed:
# This is a long comment that explains why we need this particular# dependency and what it does for the build systemset(FOO bar)With enable_markup: false, the same comment is preserved as written:
# This is a long comment that explains why we need this particular dependency and what it does for the build systemset(FOO bar)I want to teach cmakefmt my custom commands
Section titled “I want to teach cmakefmt my custom commands”Section: commands
Without a spec, custom commands format as flat token lists. With a spec, keywords and flags are recognized and arguments are grouped properly:
commands: my_add_test: kwargs: NAME: nargs: 1 SOURCES: nargs: "+" LIBRARIES: nargs: "+"See My Custom Command Formats Poorly for a full before/after example.
I want to review formatting changes side-by-side in meld / vimdiff / kdiff3
Section titled “I want to review formatting changes side-by-side in meld / vimdiff / kdiff3”cmakefmt --diff emits a unified diff to stdout, which is the right
format for terminal review and for piping into tools that consume
unified-diff input (delta, diff-so-fancy). Side-by-side tools such as
meld, vimdiff, and kdiff3 expect two file paths instead, and the formatter
does not launch them directly. Use shell process substitution (Bash or
Zsh) to feed cmakefmt’s formatted output to such tools as if it were a
file:
# Open meld with the original on the left and the formatted output on the rightmeld CMakeLists.txt <(cmakefmt CMakeLists.txt)
# Same idea with vimdiffvimdiff CMakeLists.txt <(cmakefmt CMakeLists.txt)
# Or kdiff3kdiff3 CMakeLists.txt <(cmakefmt CMakeLists.txt)To review every changed file in a directory in turn, combine
--list-changed-files with a shell loop:
for f in $(cmakefmt --list-changed-files .); do meld "$f" <(cmakefmt "$f")doneVisual Studio Code users get a built-in side-by-side diff with no shell dance:
code --diff CMakeLists.txt <(cmakefmt CMakeLists.txt)Process substitution (<(...)) is a Bash and Zsh feature; POSIX sh and
the Windows cmd shell do not support it. On Windows, use Git Bash, WSL,
or PowerShell with a temporary file:
$tmp = New-TemporaryFilecmakefmt CMakeLists.txt | Set-Content $tmpmeld CMakeLists.txt $tmpRemove-Item $tmp
Docs track main. For historical docs, check out a release tag in
the repository and build
docs/ locally.