Changelog
This project follows a simple changelog discipline:
- keep user-visible changes in
Unreleaseduntil the next cut - group entries by impact, not by file
- call out migration-impacting changes explicitly
Unreleased
Section titled “Unreleased”1.6.0 — 2026-05-17
Section titled “1.6.0 — 2026-05-17”- New
cmakefmt dump spec-coveragesubcommand that introspects the formatter’s command registry and reports, for every documented CMake command, how thoroughly it’s modelled. Classifications: missing (not in registry), stub (registered but no kwargs/flags), partial (1-3 kwargs+flags), full (>3 kwargs+flags). Supports--format human|jsonand--status missing|stub|partial|fullfor filtering. Reference list of CMake commands snapshot is atsrc/spec/cmake_commands.txt(CMake 4.3.1). Useful for introspecting “what does cmakefmt actually know about my CMake code?” before reporting a layout surprise as a bug, and for spec-completeness work tracked on the roadmap.
Changed
Section titled “Changed”- The
Error::Formatter(String)catch-all variant has been split into four structured sub-variants —Error::CliArg,Error::InvalidRegex(carrying the underlyingregex::Errorvia a#[source]chain),Error::Render, andError::LegacyMigration. Most priorError::Formattersites in the codebase migrated to one of the structured variants (the original variant remains for genuinely miscellaneous CLI/runtime conditions). Downstream Rust consumers matching onErrorcan now programmatically distinguish CLI-argument conflicts, regex compile failures, render failures, and legacy cmake-format migration errors instead of parsing message strings. All five variants are#[non_exhaustive]so further sub-splitting in future phases is patch-safe. Exit codes and user-facing error text are unchanged for all butInvalidRegex, which now renders the underlyingregex::Errorsource chain for clearer diagnostics.
- The playground at cmakefmt.dev now reliably reflects the
latest released version after a tag push, instead of whatever
was on
mainat the most recentpages.ymlrun. Previously, a non-release commit landing onmainbetween the Release commit and the tag could leave the playground silently advertising in-development formatter behaviour while the latest tagged release didn’t ship it.pages.ymlnow fires on tag pushes too, so the post-release playground deploy is rebuilt from the tagged commit’s source. (Phase 60.)
Internal
Section titled “Internal”- Added a
cargo-fuzzharness atfuzz/with two targets:parse(asserts the parser doesn’t panic on arbitrary bytes) andformat_roundtrip(assertsparser::parse → format_source → parser::parsecompletes without crashing). A weekly advisory cron in.github/workflows/fuzz.ymlexercises each target for 5 minutes by default, configurable viaworkflow_dispatchinput. Crash artifacts upload with 7-day retention. Coverage-guided fuzzing complements the existing proptest harness; together they form a defence-in-depth safety net for the parser’s hand-written scanner/grammar surface. (Phase 58.) run_config_subcommandandinstall_git_hooknow report failures throughErr(cmakefmt::Error::cli_arg(...))instead of the oldereprintln! + Ok(EXIT_ERROR)pair, so every error path in the binary routes through the samerender_cli_errorenvelope. User-facing message format unchanged; exit codes unchanged. (Phase 63 polish.)- Split the
comments_playground_preset_preserves_barriers_and_reflows_commentsmegablob snapshot intests/snapshots.rsinto five focused per-behaviour tests (long-comment reflow, bracket-comment attachment, trailing-comment wrap,# cmakefmt: offblock preservation, hash-banner preservation). Diff readability during review improves; behaviour coverage is unchanged. (Phase 58 cleanup.) - Split
src/main.rs(~4500 lines) into focused submodules undersrc/cli/. TheClidefinition, the fourArgssub-structs (InputSelectionArgs,OutputModesArgs,ExecutionArgs,ConfigOverridesArgs), the subcommand enums (CliCommand,ConfigAction,DumpAction),main(), and the top-levelrun()dispatcher stay inmain.rs(now under 1200 lines). Per-concern code moves to named submodules:cli/process.rs(path discovery,process_path,process_stdin),cli/report.rs(per-format builders for JSON / SARIF / Checkstyle / JUnit / GitHub / Edit reports),cli/commands.rs(subcommand handlers and watch mode),cli/runtime.rs(validate_cli, run-loop helpers, completion handling),cli/errors.rs(render_cli_errorand the parse / file / formatter renderers),cli/summary.rs(human + stat summary rendering with co-located tests), andcli/diff.rs(diff and highlight helpers,apply_line_ranges). Three function signatures narrowed from&Clito specificArgsgroup references where the call sites already destructured. No behaviour change; all 610 tests still pass, clippy strict still clean, codspeed will verify no perf regression. The Cli flatten refactor shipped in v1.5.0 was a deliberate precondition for this split.
1.5.0 — 2026-05-17
Section titled “1.5.0 — 2026-05-17”- New
cmakefmt manpagesubcommand that prints the roff man page to stdout, replacing the older--generate-man-pageflag at the top-level CLI surface. The flag remains accepted as a hidden deprecated alias so existing release scripts and distribution recipes keep working during the transition; packagers should migrate tocmakefmt manpage > cmakefmt.1when convenient.
Changed
Section titled “Changed”--fastis now a hidden alias of--no-verifyinstead of a visible one. Both flags continue to work; only the canonical--no-verifyis documented in--helpoutput and the CLI reference. Matches the “deprecated alias” framing the docs have used since v1.3.x.- Annotated several public spec and config-enum types with
#[non_exhaustive]so future field/variant additions stay patch-safe rather than breaking downstream consumers using exhaustive struct-literal construction ormatcharms:KwargSpec,CommandForm,LayoutOverrides,NArgs,CommandSpec,CaseStyle,LineEnding,FractionalTabPolicy,ContinuationAlign,DangleAlign, plus per-variant onError::IoAtandError::LayoutTooWide. TheConfig,PerCommandConfig, and AST node types (CommandInvocation,BracketArgument,File,Statement,Argument,Comment) intentionally stay un-annotated for now because external callers (tests, benchmarks, downstream tools) construct them via struct literals; their hardening is tracked on the roadmap for a later release that pairs the annotation with a builder API.
- The winget submission workflow (
publish-winget.yml) now actually fires on each tagged release. Previously therelease.ymljob that publishes the GitHub Release used the defaultGITHUB_TOKEN, and GitHub’s recursion guard prevents events emitted byGITHUB_TOKENfrom triggering downstream workflows — so every release sincev0.8.0published a GitHub Release without ever firing the winget submission. Confirmed against the workflow history: 10+ releases published, exactly onepublish-winget.ymlrun, which was a manualworkflow_dispatchfrom 2026-04-26 againstv1.3.0.release.ymlnow passesRELEASE_WORKFLOW_TOKENto thesoftprops/action-gh-releasestep, so the release-publish event comes from a PAT and downstream workflows trigger as intended. The token’s scope requirements are documented inRELEASING.md. - Files that begin with a UTF-8 byte-order mark (commonly written by MSVC / Visual Studio on Windows) now round-trip through the formatter with the BOM preserved. Previously the parser stripped the BOM during parsing and the formatter never re-prepended it, so every format pass silently changed the file’s leading bytes — invisible to most editors but enough to trip strict file-content checks or break a build’s encoding-detection step. Files without a BOM are unaffected (no BOM is ever added).
set_package_properties(PACKAGE … PROPERTIES <k> <v> …)is now exempt from the autosort heuristic, completing the v1.4.2 sweep across the property-family commands. The kwarg has the same<key> <value>pair structure asset_target_properties; the v1.4.2 fix missed it because it lives in theFeatureSummarymodule spec rather than the core property commands. As before, the previous behaviour produced syntactically valid CMake with corrupted semantics.
Internal
Section titled “Internal”- Removed a dead conditional in
formatter::node::format_section_inlinewhose two arms returned identical values; theheader_kindparameter became unused and was dropped from the signature. No behaviour change.
Documentation
Section titled “Documentation”- The Formatting Cookbook no longer recommends the renamed-away
config keys
use_tabcharsandseparate_ctrl_name_with_space; these are accepted as legacy aliases but the current spellings areuse_tabsandspace_before_control_paren. The recipe link to the custom-commands section of the config reference also got a corrected anchor (#custom-command-specs). - The FAQ no longer references a nonexistent
--command-specCLI flag (custom commands live in thecommands:section of the config file); the editor-integration link now points to the correct/editors/page; the FAQ is now reachable from the sidebar under Reference. - The Playground page now includes a short description of the source presets, config presets, shareable URLs, and auto-refresh behaviour, so the new feature surface is discoverable from outside the component itself.
- Bumped pinned-version examples in the CI guide from
1.3.0to1.4.2, and updated the JSON-LDsoftwareVersionblob on the homepage to match the latest release. - Troubleshooting guide now references
--no-verifyinstead of the deprecated--fastalias.
1.4.2 — 2026-05-16
Section titled “1.4.2 — 2026-05-16”set_property(... PROPERTY <name> <values…>)and theset_target_properties/set_source_files_properties/set_directory_properties/set_tests_propertiescommands are now exempt from the autosort heuristic. Previously, withautosortenabled, the formatter would flat-sort the tokens insidePROPERTYandPROPERTIESvalue lists — silently moving the property name out of its positional slot inset_property, and scrambling<key> <value>pairs in the*_propertiesfamily. The reordered output was still valid CMake syntax but changed the command’s semantics. These commands now round-trip unchanged regardless ofautosort.- Decorative banner comments composed entirely of
#characters (e.g.########################################) are preserved verbatim instead of being collapsed to a single#line by the comment reflow logic.
Documentation
Section titled “Documentation”- The browser playground now includes source presets for common formatter scenarios, config presets that keep the full default configuration visible, shareable URLs for the current source/config state, and automatic output refresh when switching presets.
- The homepage formatter animation now uses
astro-magic-move’s publicstep/totalStepsAPI, so the dependency can track upstream releases without relying on private component internals.
1.4.1 — 2026-05-14
Section titled “1.4.1 — 2026-05-14”- Linux wheels now install on glibc ≥ 2.28 (RHEL 8/9, Ubuntu 22.04+,
Debian 11+, SLES 15) instead of requiring glibc ≥ 2.39, which had
excluded every in-support enterprise distro. Wheels are now built
inside a
manylinux_2_28container. (#47) - Lowered
requires-pythonfrom>=3.11to>=3.8. The published wheel is a standalone Rust binary with no Python runtime dependency, so the previous floor was preventing installs on systems whose Python happened to be older even though nothing about the package needed 3.11. (#47)
1.4.0 — 2026-05-02
Section titled “1.4.0 — 2026-05-02”- Structurally-correct command specs for ~50 commonly-used CMake
module commands — the commands defined in CMake-bundled modules
that become available after
include(<Module>)orfind_package(<Module>). These previously formatted as flat positional lists; the formatter now recognises kwargs and flags for layout, autosort eligibility, and pair-aware rendering. Coverage spans:- FetchContent:
FetchContent_Declare(with the full source-fetch surface plusEXCLUDE_FROM_ALL/SYSTEM/OVERRIDE_FIND_PACKAGEflags andFIND_PACKAGE_ARGS),FetchContent_MakeAvailable,FetchContent_GetProperties,FetchContent_Populate,FetchContent_SetPopulated(corrected from a 3-positional misspelling to the documented kwarg form). - ExternalProject:
ExternalProject_Addwith all ~90 kwargs across the source-fetch, configure, build, install, and test phases — the largest single command spec in the project.ExternalProject_Add_Step,ExternalProject_Add_StepTargets,ExternalProject_Add_StepDependencies,ExternalProject_Get_Property. - Check* family:
check_language,check_compiler_flagand its per-language variants,check_include_file*,check_function_exists,check_library_exists,check_symbol_exists,check_cxx_symbol_exists,check_type_size,check_variable_exists, thecheck_<lang>_source_compiles/_runsfamily,check_source_compiles/_runs,check_linker_flag,check_prototype_definition,check_struct_has_member,check_ipo_supported,check_pie_supported,check_cxx_accepts_flag. - CMakePushCheckState:
cmake_push_check_state(withRESETflag),cmake_pop_check_state,cmake_reset_check_state. - CMakePackageConfigHelpers:
configure_package_config_file(withINSTALL_DESTINATION/PATH_VARS/INSTALL_PREFIXandNO_SET_AND_CHECK_MACRO/NO_CHECK_REQUIRED_COMPONENTS_MACROflags),write_basic_package_version_file,write_basic_config_version_file. - GoogleTest:
gtest_add_tests,gtest_discover_tests(with the fullEXTRA_ARGS/WORKING_DIRECTORY/TEST_PREFIX/PROPERTIES/DISCOVERY_TIMEOUT/DISCOVERY_MODEsurface). - GenerateExportHeader:
generate_export_header(with all 10 macro-naming kwargs andDEFINE_NO_DEPRECATEDflag). - CMakeFindDependencyMacro:
find_dependency(full forward offind_package’s flag and kwarg surface). - FindPackageHandleStandardArgs:
find_package_handle_standard_args,find_package_check_version,find_package_message. - FindPkgConfig:
pkg_check_modules,pkg_search_module,pkg_get_variable. - CMakeParseArguments:
cmake_parse_arguments(still available alongside the builtin since 3.7). - CMakePrintHelpers:
cmake_print_properties,cmake_print_variables. - CPackComponent:
cpack_add_component,cpack_add_component_group,cpack_add_install_type,cpack_configure_downloads. - CPackIFW:
cpack_ifw_configure_component,cpack_ifw_configure_component_group,cpack_ifw_add_repository,cpack_ifw_update_repository,cpack_ifw_add_package_resources. - CPackIFWConfigureFile:
cpack_ifw_configure_file. - BundleUtilities:
fixup_bundle,verify_app. - AndroidTestUtilities:
android_add_test_data. - Other:
test_big_endian,write_compiler_detection_header,cmake_dependent_option,processorcount,select_library_configurations.
- FetchContent:
Removed
Section titled “Removed”- The
--previewCLI flag and the[experimental]config section (and its underlyingExperimentalstruct) have been dropped. Both were no-ops on every release that exposed them — the formatter has no preview-gated behaviour at the moment, so passing the flag or populating the section did nothing. Removing them keeps the public surface honest. If a future release introduces opt-in preview behaviour, the gate will return; until then there is nothing for it to gate. - The
[markup] explicit_trailing_patternconfig option has been removed. It was wired into the config schema but never consulted by the formatter — no path read the user-supplied regex, so a custom pattern produced no observable effect. Removing the dead surface prevents config drift where users would set it and silently get the default behaviour.
- The Language Server Protocol entry points
(
textDocument/formatting,textDocument/rangeFormatting,textDocument/codeAction) now return proper error responses for malformed requests instead of silently dropping them. Failed parameter extraction surfaces asInvalidParams; failed JSON serialisation surfaces asInternalError. Editors and LSP test harnesses that wait for a response no longer hang on these paths. - A user-supplied command override declaring a
Discriminatedspec with an emptyformsmap and nofallbackno longer crashes the formatter. The lookup now degrades to a static emptyCommandFormrather than panicking via.expect().
Changed
Section titled “Changed”- Filesystem error messages now include the offending path. Failures
reading config files, source files, the cache, install-hook
destinations, or atomic-write tempfiles render as
error: I/O failure reading <path>: <reason>instead of a bare<reason>. Streaming I/O (stdin/stdout) is unaffected and continues to surface its native error. - The embedded command spec is now split across two YAML sources:
src/spec/builtins.yaml(commands listed bycmake --help-command-list) andsrc/spec/modules.yaml(commands defined in CMake-bundled modules). The runtime decodes both MessagePack blobs and merges them into a single command table at startup; spec consumers see no difference. The split mirrors the natural taxonomy users already use to think about CMake commands and keepsbuiltins.yamlfocused on the language surface. Module commands previously specced inbuiltins.yaml(the Check* family and friends) have been migrated verbatim tomodules.yaml.
Distribution
Section titled “Distribution”- Automated winget submissions on each release. A new
publish-winget.ymlworkflow fires onrelease: publishedand opens a PR againstmicrosoft/winget-pkgswith the new manifest, using a shared fork atcmakefmt/winget-pkgs. Manual retries are available viaworkflow_dispatch. The first run closed the pre-existing manifest gap (winget had been stuck at 0.3.0); from v1.3.0 onward,winget upgradetracks releases on the same cadence as Homebrew.
Documentation
Section titled “Documentation”- Overhauled the GitHub README. Trimmed from 356 lines to 130 by
removing content owned by the docs site (Common Workflows table,
Configuration section, Formatter Disable Regions, Library Usage
example, full performance fixture table, Project Layout, Development
commands). Added a focused GitHub Action section showcasing the new
mode/scopeinputs. Each remaining sentence is a complete thought rather than a colon-and-link fragment. - Rewrote the CI Integration page to use the new
cmakefmt-actionsurface. Themodeinput (check/diff/fix/setup) andscopeinput (all/changed/staged) replace the olderargs:-based examples throughout, withpaths,since,working-directory, andversioncovering the rest. Added a changed-file rollout example, a monorepo example, and an auto-format-and-commit example. Bumped the Docker tag pin from 0.4.0 to 1.3.0 to match the current release. - Tightened the winget messaging in the installation page. With manifest submission now automated, the “version updates may lag releases slightly” hedge has been removed and the support-levels table promotes winget from “Community maintained” to “Officially maintained”.
Performance
Section titled “Performance”- Single-file wall time on the 656-line
mariadb_serverfixture holds at 6.0 ms — unchanged from v1.3.0 despite the spec growing significantly with the new module command coverage. Build-time MessagePack pre-deserialisation (introduced in v1.2.0) continues to absorb the parse cost; the larger lookup table and MessagePack blob are decoded at startup with no measurable difference. Release binary size is unchanged at 4.7 MB. Methodology unchanged from v1.2.0:hyperfine --shell=none --style basic --warmup 100 --runs 200.
1.3.0 — 2026-04-25
Section titled “1.3.0 — 2026-04-25”- Structurally-correct command specs for ~60 previously-stubbed CMake
builtins. Each spec follows the canonical
cmake --help-commandsynopsis and gives the formatter keyword/flag awareness for layout decisions (inline packing, vertical wrapping, autosort eligibility, pair-aware rendering). Coverage spans:- Trivial commands:
break,continue,enable_testing,aux_source_directory,mark_as_advanced,add_compile_definitions,add_compile_options,add_definitions,add_dependencies,add_link_options,link_directories,link_libraries,include_directories,include_regular_expression,site_name,get_cmake_property,enable_language. - Single-form commands with kwargs/flags:
build_command,define_property,set_directory_properties,set_tests_properties,set_source_files_properties,target_compile_features,variable_watch,fltk_wrap_ui,qt_wrap_cpp,qt_wrap_ui,include_external_msproject,create_test_sourcelist,separate_arguments,cmake_host_system_information,cmake_file_api,get_target_property,get_test_property,get_source_file_property. - CTest family (full kwarg coverage on every
ctest_*builtin):ctest_build,ctest_configure,ctest_coverage,ctest_empty_binary_directory,ctest_memcheck,ctest_read_custom_files,ctest_run_script,ctest_sleep,ctest_start,ctest_submit,ctest_test,ctest_update,ctest_upload. - Multi-form discriminated commands:
add_test(NAME-form vs legacy positional fallback),cmake_policy(VERSION/SET/GET/PUSH/POP),source_group(TREE-form vs default fallback),cmake_path(full coverage of all 30+ subcommands: GET/HAS_/IS_/COMPARE/SET/APPEND/ APPEND_STRING/REMOVE_FILENAME/REPLACE_FILENAME/REMOVE_EXTENSION/ REPLACE_EXTENSION/NORMAL_PATH/RELATIVE_PATH/ABSOLUTE_PATH/ NATIVE_PATH/CONVERT/HASH). - Approximations of scope-discriminated commands:
get_directory_property,get_property,set_property,get_filename_component,load_cache,try_compile,try_run— modelled as single-form with combined kwargs/flags rather than full multi-scope discrimination, which is sufficient for current layout decisions but may be promoted to trueforms:later.
- Trivial commands:
- Discriminated command fallback dispatch.
add_testandsource_grouppreviously declared their non-discriminator signatures under a literally-namedDEFAULTform, butform_for()looks up forms by exact first-arg match. When the first arg wasn’tDEFAULTthe lookup fell through to the first form in insertion order rather than the intended catch-all. Forsource_group("Source Files" FILES … REGULAR_EXPRESSION …)this meant the TREE form was applied to a non-TREE call, soREGULAR_EXPRESSIONwas not recognised as a kwarg and got swallowed into theFILESvalue list. The catch-all now lives under the siblingfallback:field, whichform_for()consults via.or(fallback.as_ref()). Both commands round-trip correctly.
Documentation
Section titled “Documentation”- Annotated the 16 deprecated CMake commands that remain in
builtins.yamlaspargs: "*"stubs (e.g.install_files,install_programs,install_targets,subdirs,make_directory,exec_program,output_required_files,remove,use_mangled_mesa,utility_source,variable_requires,write_file,load_command,export_library_dependencies,subdir_depends,build_name) with# Deprecated since CMake X.YYAML comments pointing at their modern replacements. The gap is now explicit, not an oversight.
Internal
Section titled “Internal”- 12 new snapshot tests for the new specs, each using a narrow
line_width(40 or 50) to force wrapping and assert structural separation: flags rendered on their own lines, kwargs as separate keyword sections, multi-form fallback dispatch wired correctly. Round-tripping inline-only would have masked the fallback bug above.
Performance
Section titled “Performance”- Single-file wall time on the 656-line
mariadb_serverfixture drops from 6.6 ms (v1.2.0) to 6.0 ms — a 9% improvement despite the spec growing significantly with the Phase 47g coverage additions. Build-time MessagePack pre-deserialisation (introduced in v1.2.0) absorbs the larger spec at zero runtime cost; the speedup comes from incidental optimisations in the splitter and layout paths exercised by the new structural awareness. Methodology unchanged from v1.2.0:hyperfine --shell=none --style basic --warmup 100 --runs 200. Release binary size unchanged at 4.7 MB.
1.2.0 — 2026-04-25
Section titled “1.2.0 — 2026-04-25”-
continuation_alignconfig knob — controls how continuation lines indent when a wrapped subkwarg group overflowsline_width. Two modes:under-first-value(default — cmake-format-style hanging indent, aligned under the first value after the subkwarg) andsame-indent(continuation at the subkwarg’s own indent). Overridable per-command viaper_command_overridesor per-spec vialayout.continuation_align.Note: this is a behaviour change from v1.1.0. Before v1.2.0 the formatter always used same-indent continuation (the knob did not exist). If an install/DIRECTORY section in your codebase has a subkwarg value list long enough to wrap (e.g.
PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ), its continuation lines will now align under the first value column instead of wrapping to the subkwarg indent. Setcontinuation_align: same-indentto restore the previous layout. -
install(TARGETS ...)artifact-kind subgroups are now modelled as nested keyword sections per CMake’s documented signature. Each ofRUNTIME/LIBRARY/ARCHIVE/OBJECTS/FRAMEWORK/BUNDLE/PRIVATE_HEADER/ PUBLIC_HEADER/RESOURCE/FILE_SET/CXX_MODULES_BMIcarries its ownDESTINATION/PERMISSIONS/CONFIGURATIONS/COMPONENT/NAMELINK_COMPONENTsubkwargs andOPTIONAL/EXCLUDE_FROM_ALL/NAMELINK_ONLY/NAMELINK_SKIPsubflags.OBJECTSwas previously missing;FILE_SETnow correctly takes a positional set name. -
install(PROGRAMS)form (previously absent).install(SCRIPT|CODE)now acceptCOMPONENT,ALL_COMPONENTS,EXCLUDE_FROM_ALL.install(RUNTIME_DEPENDENCY_SET)fully modelled with per-artifact- kind subgroups plus the seven regex/file filter kwargs. -
install(IMPORTED_RUNTIME_ARTIFACTS)restructured with the same artifact-kind subgroup pattern as TARGETS. -
install(FILES)gainsTYPE,RENAME,EXCLUDE_FROM_ALL. -
install(DIRECTORY)gainsTYPE,DIRECTORY_PERMISSIONS,CONFIGURATIONS,MESSAGE_NEVER,EXCLUDE_FROM_ALL,FILES_MATCHING;PATTERN/REGEXsubgroups now take their pattern as a positional and acceptEXCLUDEplus a nestedPERMISSIONSsubkwarg. -
RUNTIME_DEPENDENCIESpromoted from an unstructured value list to a proper kwarg group withDIRECTORIES,PRE_INCLUDE_REGEXES,PRE_EXCLUDE_REGEXES,POST_INCLUDE_REGEXES,POST_EXCLUDE_REGEXES,POST_INCLUDE_FILES,POST_EXCLUDE_FILESsubkwargs.
Changed
Section titled “Changed”- Pair-aware vertical rendering for sections with nested subkwargs.
When a wrapped section contains subkwargs, each
subkwarg + valuepair renders as a single logical line at the nested indent — matching the layout shown incmake --help-command install. Non-grouped sections (PUBLIC/PRIVATE/INTERFACE, etc.) keep their existing flat packing. - Trailing comments attached to a keyword header (
RUNTIME # runtime artifacts) stay on the header line when they fit withinline_width, and reflow through the standard comment formatter when they don’t.
- Subkwarg argument values no longer collide with ancestor keyword
names.
install(TARGETS foo LIBRARY COMPONENT Runtime)previously reinterpretedRuntimeas theRUNTIMEartifact-kind subgroup; the splitter now force-attaches a kwarg’s required nargs as values regardless of token classification.OneOrMorenargs (CONFIGURATIONS Debug Runtime) are also protected. - Inline comments interleaved between a subkwarg and its value no
longer count toward the subkwarg’s nargs quota. The grouped
writer advances by non-comment tokens so
COMPONENT # comment\n RuntimekeepsRuntimeas theCOMPONENTvalue. #line comments appearing before a kwarg’s required positional (e.g.FILE_SET # set comment\n HEADERS …) no longer swallow the positional into the comment text. Positionals are always emitted before any comments on the header line.- Long trailing comments on a keyword header now respect
line_width— they break to a new line at the nested indent and reflow rather than producing an overlong line. autosortno longer scrambles kwarg sections whose spec declares nested subkwargs or flags. The gate applies equally to explicitly-sortable: truesections, so malformed specs can’t bypass it. Pure flat-list kwargs (PUBLIC, user ITEMS-style lists) still sort.- Removed an accidental top-level
PERMISSIONSentry frominstall(DIRECTORY). Per CMake docs,PERMISSIONSonly appears as a subkwarg of aPATTERN|REGEXsubgroup; the top-level permission kwargs areFILE_PERMISSIONSandDIRECTORY_PERMISSIONS.
Removed
Section titled “Removed”- Dormant
fuzz/directory (three cargo-fuzz targets plus the frozen pest parser comparison harness). Never wired into CI; the parser rewrite they guarded is shipped. Can be recreated if a fuzzing push returns.
Documentation
Section titled “Documentation”- Rustdoc / docs.rs surface polished: docs.rs now builds with
all-featuresand thedocsrscfg so feature-gated items (cli-only re-exports,lsp::run,wasm::format) render with feature badges. Added module-level docs forparser::ast, a# Featurestable and expanded Organisation section at the crate root, explicit 1-based line/column guarantees onParseDiagnosticandFileParseError, a “Loading from disk” section onConfig, per-variant docs onNArgs, resolution-order notes onCommandConfig, and minimal doctest examples onCommandRegistry::merge_toml_overrides/merge_yaml_overrides.
Performance
Section titled “Performance”- Faster startup. The embedded built-in command spec is now
pre-deserialised at build time (
build.rsreads the YAML source and emits a MessagePack blob intoOUT_DIR); the runtime decodes that blob viarmp-serdeinstead of parsing structured text on every invocation. Single-file wall time on the 656-line mariadb_server fixture drops from 6.8 ms (v1.1.0) to 6.6 ms under matched-methodology hyperfine measurements (--shell=none --style basic, 100 warmups, 200 runs). The improvement holds even though the install() restructure grew the spec ~4×.
Internal
Section titled “Internal”- Migrated the embedded built-in command spec from
src/spec/builtins.tomltosrc/spec/builtins.yamlfor human-edit ergonomics, then pre-deserialises the YAML at build time (see Performance, above). The runtime no longer parses text at all; it decodes a MessagePack blob produced once duringcargo build. User config and spec override files still accept both TOML and YAML — only the embedded baseline path changed.
1.1.0 — 2026-04-22
Section titled “1.1.0 — 2026-04-22”- Formatting Cookbook page — common formatting goals with before/after examples and config links
- Glossary page — definitions of all terminology used in docs, config, and parse tree dumps
- “Why
cmakefmt?” cards on the landing page - Playground CTA after the Performance section
- Community sidebar section (badge, projects using
cmakefmt)
Changed
Section titled “Changed”- Landing page restructured: explainer tagline, “Why” cards replace the old Features section, slimmed “Where to Go Next” with three sub-sections of two cards each
- Comparison page updated with parallelism, recursive discovery, config autocomplete, watch mode, and reordered table by user impact
- Mobile-friendly docs: responsive layouts, unified sidebar menu with TOC, chart view-transition support, “See all features” toggle on mobile
- Homepage animation: lazy panel initialization for production
hydration,
magic-moveproperty compatibility fix - “Getting Started” replaces “Installation” in the header nav
Changed
Section titled “Changed”- Real-world fixture corpus expanded with four large-file pins
(
opencv_root,blender_root,llvm_libc_math,grpc_root) so the per-file benchmark suite is fully reproducible fromtests/fixtures/real_world/manifest.toml - Refreshed headline performance numbers after the parser rewrite,
lazy-diff work, and related micro-optimisations:
- per-file geo-mean: 48× → 104×
- whole-repo geo-mean: 49× → 150× parallel, 19× → 95× serial
- fastest whole-repo speedup: 485× → 2,853× (opencv, 282 files, parallel)
- 55K-line gRPC root
CMakeLists.txt: 180 ms → 39 ms - aggregate corpus wall time: 252 s → 0.65 s (
--parallel, 14 repos)
- Standalone comment immediately following an argument with a trailing comment no longer merges into that trailing comment on a second format pass. The vertical-arguments writer now refuses to inline-attach a comment when the previous line already ends in a trailing comment.
- All references to the non-functional
cmakefmt initshorthand replaced with the canonicalcmakefmt config init(landing page, getting-started, migration guide, CLI reference, and the binary’s--helplong text). The barecmakefmt initform was already inert — the CLI parsesinitas a path argument — so docs that promised it were misleading. - Diff computation skipped when result is unused — 14s → 0.6s on a
55K-line file. The Myers diff algorithm was running eagerly on every
invocation even when
--diffwas not requested. autosortnow activates on keyword sections containing inline comments — previously any inline comment silently disabled the heuristic- Memoized sort keys to avoid repeated
to_ascii_lowercase()per comparison - Single-pass token classification (
classify_token) eliminates redundant case conversion in the per-argument hot path - Combined iterator passes in
try_format_inlineandtry_format_hanging(two.any()calls merged into one)
1.0.0 — 2026-04-14
Section titled “1.0.0 — 2026-04-14”--list-unknown-commandsflag — parse files and report commands that don’t match any built-in or user-defined spec, with file:line locations--previewflag — enable all experimental formatting options--report-format edit— editor-friendly JSON output with full-file replacements for each changed file[experimental]config section — placeholder for unstable formatting options gated behind--preview(no options yet)enable_sortconfig option — sort arguments in keyword sections markedsortablein the command spec (default: off)autosortconfig option — heuristically sort keyword sections where all arguments are simple unquoted tokens (default: off)sortableannotation for keyword specs — mark sections whose arguments may be sorted whenenable_sortis enabled- Stability contract published at
cmakefmt.dev/stability/ wrap_after_first_argconfig option and layout hint — keep the first positional argument on the command line when wrapping. Enabled by default forset()so the variable name always stays on theset(line.- Trailing inline comments now stay attached to their preceding argument in both packed and vertical layouts
- Trailing comment reflow — long trailing comments that exceed
line_widthare reflowed with continuation lines aligned to the#. The parser recognises column-aligned continuation comments and merges them back into the trailing comment on re-parse, ensuring idempotent round-trips. - Remaining positional arguments are packed inline after the first argument
when they fit within
line_width, avoiding unnecessary vertical layout cmakefmt dump astsubcommand — print the raw parser AST as a colored Unicode box-drawing tree for debugging parser behaviorcmakefmt dump parsesubcommand — print a spec-resolved parse tree showing keyword/flag/positional grouping and flow-control nesting. Nested keyword specs are resolved recursively (e.g.FORCEshows asFLAGunderCACHE).- Demo GIF on the README and Getting Started page
- Weekly scheduled benchmark CI for consistent performance tracking
- Large-file LSP timeout test (2000 lines, < 1 second)
Changed
Section titled “Changed”reflow_commentsconfig option merged intoenable_markup—enable_markup: truenow controls both markup handling and comment reflow. The standalonereflow_commentskey is no longer accepted in native config files (legacy conversion still maps it).set()CACHE spec updated:STRING/FILEPATH/etc. type argument usesnargs = 2andFORCEis a flag at theCACHElevel rather than a nested positional argument- Semantic verifier strips all comments from comparison — comments have no CMake semantic meaning and were causing false-positive verification failures when comment reflow changed their structure
- Default config example uses
my_add_testwith VERBOSE flag, matching the playground source - Playground loads default config from WASM
default_config_yaml()at runtime instead of a hardcoded string - README banner uses absolute URL for PyPI/crates.io rendering
autosortnow activates on keyword sections that contain inline comments — previously, the presence of any inline comment caused the heuristic to skip the section entirely
0.10.0 — 2026-04-12
Section titled “0.10.0 — 2026-04-12”--explainflag — show per-command formatting decisions (layout choice, config values, thresholds) for a single file--watchflag — watch directories for changes and reformat in-place automatically; press Ctrl+C to stop.editorconfigfallback — when no.cmakefmt.yamlis found,indent_styleandindent_sizefrom.editorconfigare used as defaults. Disable with--no-editorconfig--debugnow logs discovery context: active ignore sources,--path-regexfilter,--staged/--changedmode, and files filtered by--path-regex
Changed
Section titled “Changed”--progress-barno longer requires--in-place— works with--check,--summary,--quiet, and non-human report formats. Automatically suppressed when stdout streams to the terminal, with a warning explaining why- Stable Rust library API for embedding: parser internals are no longer part
of the public surface, parse/config/spec failures now use crate-owned error
types, and
CommandConfigno longer exposes internal representation fields - PyPI package now includes README as the long description on the project page
0.9.0 — 2026-04-12
Section titled “0.9.0 — 2026-04-12”Migration
Section titled “Migration”pip install cmakefmtis now CLI-only. It installs thecmakefmtexecutable into your environment rather than a Python module.- The
v0.8.0Python binding API is gone inv0.9.0, soimport cmakefmtno longer works on the new release line.
conda install -c conda-forge cmakefmt— now available on conda-forge- Animated before/after formatting demo on the landing page
- sdist smoke test in CI — verifies source distribution installs correctly
- Native aarch64 Linux wheel builds via
ubuntu-24.04-arm(no more cross-compilation)
Changed
Section titled “Changed”- Breaking:
pip install cmakefmtnow installs the CLI binary instead of a Python library.import cmakefmtno longer works — use the binary directly. --fastrenamed to--no-verify(--fastremains as a deprecated alias)--colourrenamed to--color(--colourremains as an alias)
--quiet/-qnow suppresses formatted output in stdout mode (previously only suppressed “would be reformatted” lines in--checkmode)pyproject.tomlnow included in sdist (fixes source builds on platforms without pre-built wheels)- PyPI wheel smoke test now runs on macOS aarch64 (previously skipped)
0.8.0 — 2026-04-11
Section titled “0.8.0 — 2026-04-11”- Python bindings via PyO3 —
pip install cmakefmtexposesformat_source(),is_formatted(), anddefault_config()as native Python functions with the same config schema as.cmakefmt.yaml - Python
configparameter accepts both YAML strings and Python dicts Config::from_yaml_str()public API for parsing config from YAML strings through the validatedFileConfigschema
Changed
Section titled “Changed”- Breaking:
command_caseandkeyword_casemoved fromstyle:toformat:section in config files. Thestyle:section is removed.
- Broken pipe error when piping output to
headorless - WASM binding now validates config through
FileConfigschema instead of silently accepting invalid fields via the flatConfigstruct
0.7.0 — 2026-04-11
Section titled “0.7.0 — 2026-04-11”--summaryflag — shows per-file status lines with change details, line counts, and formatting time; suppresses formatted output in stdout mode--sortedflag — sorts discovered files by path before processing for deterministic alphabetical output order- Short flags:
-s(summary),-q(quiet),-d(diff),-p(progress-bar) - Streaming output — per-file results now appear as each file completes instead of batching until the end, including in parallel mode
- Deterministic output order — parallel results are buffered and flushed in input order, so output is stable across runs
- Unclosed parenthesis diagnostics — parse errors now point to the
unmatched
(instead of the end of the file - CodSpeed CI benchmark tracking for automated performance regression detection on every PR
Changed
Section titled “Changed”- Parallel formatting is now the default — uses available CPUs minus one
(minimum 1). Use
--parallel 1to force serial processing.
0.6.0 — 2026-04-10
Section titled “0.6.0 — 2026-04-10”Removed
Section titled “Removed”--lspflag — usecmakefmt lspinstead--generate-completionflag — usecmakefmt completions <SHELL>instead--dump-configflag — usecmakefmt config dumpinstead--dump-schemaflag — usecmakefmt config schemainstead--check-configflag — usecmakefmt config checkinstead--show-configflag — usecmakefmt config showinstead--show-config-path/--find-config-pathflag — usecmakefmt config pathinstead--explain-configflag — usecmakefmt config explaininstead--convert-legacy-config/--convert-legacy-config-formatflags — usecmakefmt config convertinsteadcmakefmt init(top-level subcommand) — usecmakefmt config initinstead
cmakefmt config show,cmakefmt config path, andcmakefmt config explainnow accept an optional file path argument directly (e.g.cmakefmt config show src/CMakeLists.txt)- File-not-found validation for
config show,config path, andconfig explain— clear error message when the target file does not exist
Changed
Section titled “Changed”cmakefmt config dumpandcmakefmt config showformat is now specified via--formatflag (e.g.cmakefmt config dump --format toml) instead of a positional argument- 78
conflicts_withannotations removed from the CLI definition — subcommand structure now handles mutual exclusivity
0.5.0 — 2026-04-10
Section titled “0.5.0 — 2026-04-10”cmakefmt configsubcommand group —dump,schema,check,show,path,explain,convert, andinitsub-subcommands for config inspection and conversioncmakefmt lspsubcommand (replaces--lspflag, which is now deprecated)cmakefmt completions <SHELL>subcommand (replaces--generate-completionflag, which is now deprecated)cmakefmt install-hooksubcommand — one-command git pre-commit hook setup- LSP:
workspace/didChangeConfigurationsupport — live config reload when.cmakefmt.yamlchanges without restarting the server - LSP:
textDocument/codeAction— “Disable cmakefmt for selection” action that inserts# cmakefmt: off/onbarriers - LSP: 10-second timeout on formatting requests to prevent pathological inputs from freezing the editor
- Colored CLI help output (green headers, cyan flags)
- Cargo-fuzz targets for parser and formatter (
fuzz/) cmakefmt-fixpre-commit hook for auto-formatting (in addition to the existing check-onlycmakefmthook)- Docker image published to GHCR (
ghcr.io/cmakefmt/cmakefmt) on every release - Cross-platform output consistency CI workflow
- Real-world regression suite CI workflow (CMake, LLVM, OpenCV)
- SBOM generation (
cargo-cyclonedx) in the release workflow - Docker build CI workflow
- WASM API documentation page
- Docs site redesign: Plus Jakarta Sans headings, gradient hero with animated dot grid, animated link underlines, card lift-on-hover, sidebar active indicator, page load fade-in
- Benchmarks for config pattern validation, legacy conversion, and atomic writes
Changed
Section titled “Changed”- Unrecognized config keys now produce an error instead of being silently
ignored (
deny_unknown_fieldson all config sections) require_valid_layouterror now suggests the specificline_widthvalue needed to accommodate the offending line- Config regex patterns (
literal_comment_pattern,explicit_trailing_pattern, etc.) are now compiled once per formatting run instead of once per comment or command, improving performance on comment-heavy files
Deprecated
Section titled “Deprecated”--lspflag — usecmakefmt lspinstead--generate-completionflag — usecmakefmt completions <SHELL>instead--dump-configflag — usecmakefmt config dumpinstead--dump-schemaflag — usecmakefmt config schemainstead--check-configflag — usecmakefmt config checkinstead--show-configflag — usecmakefmt config showinstead--show-config-pathflag — usecmakefmt config pathinstead--explain-configflag — usecmakefmt config explaininstead--convert-legacy-configflag — usecmakefmt config convertinsteadcmakefmt init(top-level) — usecmakefmt config initinstead
0.4.0 — 2026-04-09
Section titled “0.4.0 — 2026-04-09”cmakefmt initsubcommand — generates a starter.cmakefmt.yamlin the current directory--check-configflag — validates a config file and exits without formatting--statflag — prints a git-style summary (3 files changed, 12 lines reformatted)- Elapsed time shown in the formatting summary (e.g.
in 0.42s) - Fix hint printed when
--checkfails (hint: run cmakefmt --in-place .) - User-friendly panic handler with structured bug report template
.pre-commit-hooks.yamlfor pre-commit integrationDockerfilefor CI usage- GitHub issue templates for bug reports and feature requests
- Architecture guide for contributors
- FAQ docs page
- LSP-mode editor configs for Neovim, Helix, and Zed
- Azure Pipelines and Bitbucket Pipelines examples in CI docs
- Migration guide expanded with key differences and unsupported options tables
--diffnow works with--checkand non-human--report-formatmodes; previously both suppressed the unified diff output
Security
Section titled “Security”- Config regex patterns (
literal_comment_pattern,explicit_trailing_pattern,fence_pattern,ruler_pattern) are now validated at config load time; previously invalid or pathological regexes were silently accepted and could cause CPU exhaustion (ReDoS) --in-placewrites are now atomic (write to temp file, then rename); previously a TOCTOU race could cause unintended overwrites if the target file was replaced with a symlink between read and write
0.3.0 — 2026-04-08
Section titled “0.3.0 — 2026-04-08”--dump-schemaflag — prints the JSON Schema for the.cmakefmt.yaml/.cmakefmt.tomlconfig file to stdout and exits; schema is also published atcmakefmt.dev/schemas/latest/schema.jsonfor zero-config YAML autocomplete in editors withredhat.vscode-yamlor similar plugins--lspflag — starts a stdio JSON-RPC Language Server Protocol server supportingtextDocument/formattingandtextDocument/rangeFormatting; enables format-on-save in any editor with LSP client support (Neovim, Helix, Zed, Emacs, …) without a dedicated extension- Guide pages on cmakefmt.dev: editor integration, CI integration, tool comparison, badge, and “Projects using cmakefmt”
0.2.0 — 2026-04-07
Section titled “0.2.0 — 2026-04-07”- interactive browser playground at cmakefmt.dev/playground — format CMake code, edit config, and define custom command specs entirely in the browser via WebAssembly
format.disableconfig option — global kill-switch that returns the source file unchanged; useful for temporarily opting out of formatting without removing the config fileformat.line_endingconfig option — controls output line endings:unix(LF, default),windows(CRLF), orauto(detects predominant ending in the input and preserves it)format.always_wrapconfig option — list of command names that are always rendered in vertical (wrapped) layout, never inline or hanging; thealways_wrapflag in per-command specs (commands:) is now also honouredformat.require_valid_layoutconfig option — whentrue, the formatter returns an error if any output line exceedsline_width; useful for strict CI enforcementformat.fractional_tab_policyconfig option — controls sub-tab-stop indentation remainders whenuse_tabsistrue:use-space(default) keeps them as spaces,round-uppromotes them to a full tabformat.max_rows_cmdlineconfig option — maximum number of rows a positional argument group may occupy before the hanging-wrap layout is rejected and vertical layout is used instead (default:2)markup.explicit_trailing_patternconfig option — regex pattern (default#<) that marks an inline comment as trailing its preceding argument, keeping it on the same line rather than wrapping to a new line
0.1.1 — 2026-04-06
Section titled “0.1.1 — 2026-04-06”- Homebrew installation support (
brew install cmakefmt/cmakefmt/cmakefmt) - shell completion installation instructions
- site metadata and crate status badge on docs.rs
Changed
Section titled “Changed”- improved docs.rs readability and tightened public API surface
- documentation clarity and wording improvements
0.1.0 — 2026-04-05
Section titled “0.1.0 — 2026-04-05”- full CLI workflow:
--check,--diff,--in-place,--staged,--changed,--files-from,--parallel,--dump-config,--list-input,--list-changed,--explain-config,--quiet,--keep-going - recursive file discovery with
.cmakefmtignoreand--exclude-regexsupport - YAML and TOML config file support with automatic discovery
- comment preservation and fence/barrier support (
# cmakefmt: off/on) - pragma-gated rollout mode
- formatter result caching
- colored diff output and in-place progress bar
- CI-oriented report formats (JSON, JUnit, SARIF, GitHub Actions, GitLab CI)
- legacy
cmake-formatconfig conversion (--convert-config) - built-in and module-command spec coverage audited against CMake 4.3.1
- custom command specifications via config
- real-world regression corpus covering LLVM, Qt, protobuf, and more
- performance benchmarks: ~20× geometric-mean speedup over
cmake-format - parallel formatting with
--parallel - comprehensive docs site at cmakefmt.dev
- shell completion generation (
--completions) - dual MIT/Apache-2.0 licensing with full REUSE compliance
- Windows, macOS, and Linux support
Compatibility Notes
Section titled “Compatibility Notes”cmakefmtaims to be easy to migrate to fromcmake-format, but output is not intended to be byte-for-byte identical- config option names differ from
cmake-formatin places; use--convert-configto migrate
Docs track main. For historical docs, check out a release tag in
the repository and build
docs/ locally.