[libc-commits] [libc] [libc] Add CMake formatting utility (PR #213102)

Jeff Bailey via libc-commits libc-commits at lists.llvm.org
Fri Aug 7 00:30:53 PDT 2026


https://github.com/kaladron updated https://github.com/llvm/llvm-project/pull/213102

>From 4ec7639843330d96519ce2c76fdde2a93eaa448c Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Sat, 1 Aug 2026 21:11:50 +0100
Subject: [PATCH] [libc][NFC] Add CMake formatting utility

Added libc/utils/cmake_format.py and unit tests in
libc/utils/cmake_format_test.py for LLVM libc CMake files.

Assisted-by: Automated tooling, human reviewed.
---
 libc/utils/cmake_format.py      | 1515 +++++++++++++++++++++++++++++++
 libc/utils/cmake_format_test.py |  463 ++++++++++
 2 files changed, 1978 insertions(+)
 create mode 100755 libc/utils/cmake_format.py
 create mode 100644 libc/utils/cmake_format_test.py

diff --git a/libc/utils/cmake_format.py b/libc/utils/cmake_format.py
new file mode 100755
index 0000000000000..2c77bc3ac5ed9
--- /dev/null
+++ b/libc/utils/cmake_format.py
@@ -0,0 +1,1515 @@
+#!/usr/bin/env python3
+"""
+LLVM and Subproject CMake Formatter Utility
+
+Token-stream formatter with schema-aware keyword handling, based on the
+`cmake-language(7)` EBNF grammar.
+
+Architecture Overview:
+  1. Lexer (tokenize): Converts raw CMake text into a flat list of typed Token
+     objects according to the cmake-language(7) EBNF. Handles bracket comments,
+     bracket arguments, quoted arguments, whitespace, newlines, and parens.
+  2. Schema Scanner (scan_dynamic_schemas): Walks the token stream to learn
+     custom keyword schemas from cmake_parse_arguments() calls and
+     set(*_ARGS ...) variable conventions, populating a FormatterContext.
+  3. Formatter (format_cmake_content): Iterates the token stream line-by-line,
+     applying indentation, keyword casing, comment buffering, and multi-line
+     argument layout rules using the populated FormatterContext.
+
+Minimum Python Version: 3.12 (uses pathlib.Path.walk(), added in 3.12).
+
+Grammar (from cmake-language(7)):
+  file                ::= file_element*
+  file_element        ::= command_invocation line_ending | (bracket_comment|space)* line_ending
+  line_ending         ::= line_comment? newline
+  command_invocation  ::= space* identifier space* '(' arguments ')'
+  arguments           ::= argument? separated_arguments*
+  separated_arguments ::= separation+ argument? | separation* '(' arguments ')'
+  argument            ::= bracket_argument | quoted_argument | unquoted_argument
+
+Keyword Schema Classification:
+  1. Options / Flags (0 values): e.g. `OUTPUT_STRIP_TRAILING_WHITESPACE`, `EXCLUDE_FROM_ALL`, `POST_BUILD`, `PARENT_SCOPE`, `FORCE`, `PARSE_ARGV`, `PARSE_ARGN`.
+     - Option keywords consume 0 arguments and close immediately unless inside an active multi-value list keyword scope.
+  2. Single-Value Keywords (1 value): e.g. `RESULT_VARIABLE`, `OUTPUT_VARIABLE`, `TARGET`, `WORKING_DIRECTORY`, `ALIAS`, `SUITE`, `CACHE`, `DEPFILE`.
+     - After value is consumed, keyword closes and subsequent keywords/values align at top-level keyword indent (+2 spaces).
+  3. Multi-Value List Keywords (1+ values): e.g. `SRCS`, `HDRS`, `DEPENDS`, `FULL_BUILD_DEPENDS`, `COMPILE_OPTIONS`, `LINK_LIBRARIES`, `LOADER_ARGS`, `ARGS`, `ENV`, `PROPERTIES`, `OBJECT`, `STATIC`, `SHARED`, `MODULE`.
+     - Keywords on the command header line (`cmd(KEYWORD...`) do NOT grant extra nesting to child lines (+2 spaces relative to call base).
+     - Keywords on their own separate line (`\n KEYWORD...`) indent child list items +4 spaces (+2 relative to keyword line).
+  4. Dynamic Schema Learning:
+     - Automatically parses `cmake_parse_arguments(...)` calls in `function(...)` / `macro(...)` AST blocks.
+     - Automatically learns custom option, single-value, and multi-value argument lists from `set(...)` calls using standard naming conventions (`*_OPTION_ARGS`, `*_SINGLE_VALUE_ARGS`, `*_MULTI_VALUE_ARGS`).
+
+Formatting Rules Enforced:
+  1. Command Casing: Built-in language commands cased in lowercase (`add_entrypoint_object`, `set`, `if`); module commands (like `ExternalProject_Add`) and custom functions retain canonical/declared casing.
+  2. Parenthesis Spacing: No space between command name and opening `(`. Collapses multiple spaces between arguments down to a single space.
+  3. Quoted String Immutability: Quoted arguments (`"..."`) and bracket arguments (`[=[...]=]`) are single immutable AST tokens. Multi-line quoted strings are preserved 100% untouched.
+  4. Empty Closures: `endif()`, `else()`, `endfunction()`, `endmacro()`, `endforeach()`, `endwhile()`.
+  5. Schema-Aware Keyword Casing: Keywords in command schema upper-cased; positional args, function parameters & file paths untouched.
+  6. Multi-line Argument Layout: Keywords and positional args indented +2 spaces relative to call base; multi-value list items indented +4 spaces; closing `)` at +0 spaces.
+  7. Control Block Indentation: 2-space indentation inside `if`/`foreach`/`function`/`macro`.
+  8. Comment Formatting: Line comments buffer and align with the indentation level of the code element immediately following them, unless separated by a blank line (standalone comments) or immediately preceding a closing parenthesis `)`.
+  9. Cleanliness: Trailing whitespace stripped, single trailing newline for non-empty files; empty files preserved 0-byte.
+
+Usage:
+  cmake_format.py [options] <file|directory>...
+
+Options:
+  -i, --inplace, --fix  Format files in-place.
+  -n, --dry-run         Check formatting without modifying files (Evaluation Mode).
+  --diff                Output unified diffs for files that need formatting.
+  -j, --jobs N          Number of parallel worker processes to use (default: 1).
+  -h, --help            Show this help message.
+"""
+
+import sys
+import os
+import re
+import argparse
+import difflib
+from dataclasses import dataclass, field
+from enum import Enum
+from pathlib import Path
+import copy
+from typing import NamedTuple
+
+
+class LexError(ValueError):
+    """Raised by tokenize() when the input CMake source is malformed.
+
+    Attributes:
+        msg:  Human-readable description of the problem.
+        line: 1-based line number where the unterminated token started.
+        col:  1-based column number where the unterminated token started.
+    """
+
+    def __init__(self, msg: str, line: int, col: int) -> None:
+        super().__init__(f"line {line}, col {col}: {msg}")
+        self.msg = msg
+        self.line = line
+        self.col = col
+
+
+# Control block commands
+CONTROL_START_BLOCKS = {"if", "function", "macro", "foreach", "while"}
+CONTROL_MIDDLE_BLOCKS = {"elseif", "else"}
+EMPTY_CLOSE_BLOCKS = {
+    "endif",
+    "else",
+    "endfunction",
+    "endmacro",
+    "endforeach",
+    "endwhile",
+}
+
+# Target creation commands
+TARGET_CREATION_COMMANDS = {"add_library", "add_executable", "add_custom_target"}
+
+# Formatting Constants
+INDENT_WIDTH = 2
+INDENT_STR = " " * INDENT_WIDTH
+# Indent levels relative to a command's base_indent:
+#   +1 level = keyword line or positional arg line (+2 spaces)
+#   +2 levels = multi-value list item under a keyword on its own line (+4 spaces)
+KEYWORD_INDENT_LEVELS = 1
+LIST_ITEM_INDENT_LEVELS = 2
+
+
+ at dataclass
+class FormatterContext:
+    """Holds learned schema state for formatting operations to ensure isolation and thread-safety."""
+
+    learned_options: set[str] = field(default_factory=set)
+    learned_one_value: set[str] = field(default_factory=lambda: {"ALIAS"})
+    learned_multi_value: set[str] = field(default_factory=set)
+    list_keywords: set[str] = field(
+        default_factory=lambda: {
+            "SRCS",
+            "HDRS",
+            "DEPENDS",
+            "FULL_BUILD_DEPENDS",
+            "COMPILE_OPTIONS",
+            "LINK_LIBRARIES",
+            "LINK_LIBS",
+            "FLAGS",
+            "SOURCES",
+            "BYPRODUCTS",
+            "BUILD_BYPRODUCTS",
+            "CMAKE_ARGS",
+            "CMAKE_CACHE_ARGS",
+            "LOADER_ARGS",
+            "ARGS",
+            "ENV",
+            "COMPILE_DEFINITIONS",
+            "PROPERTIES",
+            "OBJECT",
+            "STATIC",
+            "SHARED",
+            "MODULE",
+        }
+    )
+    dynamic_schemas: dict[str, "CommandSchema"] = field(default_factory=dict)
+    # Per-context cache for get_schema_for_cmd() results. Keyed by lowercased
+    # command name. Invalidated whenever the context is mutated by
+    # scan_dynamic_schemas(). Excluded from __init__ so it doesn't appear in
+    # the constructor or backward-compat aliases.
+    _schema_cache: dict[str, "CommandSchema"] = field(
+        default_factory=dict, init=False, repr=False, compare=False
+    )
+
+    def clone(self) -> "FormatterContext":
+        """Returns a deep copy of this context.
+
+        Uses copy.deepcopy() so that any new fields added to FormatterContext
+        or CommandSchema are automatically included — no lockstep update needed.
+        """
+        return copy.deepcopy(self)
+
+
+WORKSPACE_CONTEXT = FormatterContext()
+
+
+def _init_worker(ctx: FormatterContext) -> None:
+    """Initializer for ProcessPoolExecutor workers.
+
+    Replaces WORKSPACE_CONTEXT in the worker process with the fully
+    pre-scanned context from the main process. Using an initializer
+    (rather than relying on fork memory inheritance) ensures the
+    pre-scanned schemas are available regardless of the multiprocessing
+    start method ('fork' on Linux, 'spawn' on macOS/Windows).
+    """
+    global WORKSPACE_CONTEXT
+    WORKSPACE_CONTEXT = ctx
+
+
+# Pre-compiled regular expressions for Lexer, Schema Scanner, and Formatter.
+
+# Matches uppercase identifier tokens (A-Z, 0-9, _) — used to extract
+# keyword names from set(*_ARGS ...) string values.
+RE_IDENTIFIER_TOKENS = re.compile(r"[A-Z0-9_]+")
+# Matches mixed-case identifiers (a-zA-Z, 0-9, _) — used to extract
+# keyword names from cmake_parse_arguments() string arguments, which may
+# use any casing.
+RE_IDENTIFIER_WORDS = re.compile(r"[a-zA-Z0-9_]+")
+
+RE_OPTION_ARGS = re.compile(
+    r"set\s*\(\s*([A-Za-z0-9_]*(?:OPTION|OPTIONAL)_ARGS)\s+([^)]+)\)", re.IGNORECASE
+)
+RE_SINGLE_VALUE_ARGS = re.compile(
+    r"set\s*\(\s*([A-Za-z0-9_]*(?:SINGLE|ONE)_VALUE_ARGS)\s+([^)]+)\)", re.IGNORECASE
+)
+RE_MULTI_VALUE_ARGS = re.compile(
+    r"set\s*\(\s*([A-Za-z0-9_]*(?:MULTI_VALUE|LIST)_ARGS)\s+([^)]+)\)", re.IGNORECASE
+)
+
+RE_BRACKET_COMMENT_START = re.compile(r"#\[(=*)\[")
+RE_BRACKET_ARG_START = re.compile(r"\[(=*)\[")
+
+RE_COMMENT_HASH = re.compile(r"^(#+)(.*)$")
+
+RE_ARG_FORWARDING_VAR = re.compile(r'^"?\$(?:\{|\()ARG[NV]\d*(?:\}|\))"?$')
+RE_COMMAND_INVOCATION = re.compile(r"^(\s*)([a-zA-Z0-9_]+)(\s*)\((.*)$", re.DOTALL)
+# Matches the CMake CACHE keyword as a whole word, used to exclude CACHE variable
+# declarations from dynamic schema learning.
+RE_CACHE = re.compile(r"\bCACHE\b")
+
+
+ at dataclass
+class CommandSchema:
+    """Represents option, single-value, and multi-value keyword argument schemas for a CMake command."""
+
+    options: set[str] = field(default_factory=set)
+    one_value: set[str] = field(default_factory=set)
+    multi_value: set[str] = field(default_factory=set)
+    explicit_keywords: set[str] | None = None
+    all_keywords: set[str] = field(default_factory=set, init=False)
+
+    def __post_init__(self) -> None:
+        self.options = set(self.options)
+        self.one_value = set(self.one_value)
+        self.multi_value = set(self.multi_value)
+        self.all_keywords = self.options | self.one_value | self.multi_value
+        if self.explicit_keywords is None:
+            self.explicit_keywords = set(self.all_keywords)
+        else:
+            self.explicit_keywords = set(self.explicit_keywords)
+
+
+_IF_OPTIONS = {
+    "NOT",
+    "AND",
+    "OR",
+    "COMMAND",
+    "POLICY",
+    "TARGET",
+    "EXISTS",
+    "IS_DIRECTORY",
+    "IS_SYMLINK",
+    "IS_ABSOLUTE",
+    "MATCHES",
+    "LESS",
+    "GREATER",
+    "EQUAL",
+    "LESS_EQUAL",
+    "GREATER_EQUAL",
+    "STRLESS",
+    "STRGREATER",
+    "STREQUAL",
+    "STRLESS_EQUAL",
+    "STRGREATER_EQUAL",
+    "VERSION_LESS",
+    "VERSION_GREATER",
+    "VERSION_EQUAL",
+    "VERSION_LESS_EQUAL",
+    "VERSION_GREATER_EQUAL",
+    "IN_LIST",
+    "DEFINED",
+}
+
+# Official Built-in CMake Command Schema Registry
+BUILTIN_COMMAND_SCHEMAS = {
+    "function": CommandSchema(),
+    "macro": CommandSchema(),
+    "foreach": CommandSchema(options={"IN", "LISTS", "ITEMS", "ZIP_LISTS"}),
+    "while": CommandSchema(),
+    "if": CommandSchema(options=_IF_OPTIONS),
+    "elseif": CommandSchema(options=_IF_OPTIONS),
+    "else": CommandSchema(),
+    "endif": CommandSchema(),
+    "endfunction": CommandSchema(),
+    "endmacro": CommandSchema(),
+    "endforeach": CommandSchema(),
+    "endwhile": CommandSchema(),
+    "cmake_parse_arguments": CommandSchema(options={"PARSE_ARGV", "PARSE_ARGN"}),
+    "execute_process": CommandSchema(
+        options={
+            "OUTPUT_STRIP_TRAILING_WHITESPACE",
+            "ERROR_STRIP_TRAILING_WHITESPACE",
+            "OUTPUT_QUIET",
+            "ERROR_QUIET",
+            "ECHO_OUTPUT_VARIABLE",
+            "ECHO_ERROR_VARIABLE",
+        },
+        one_value={
+            "WORKING_DIRECTORY",
+            "TIMEOUT",
+            "RESULT_VARIABLE",
+            "RESULTS_VARIABLE",
+            "OUTPUT_VARIABLE",
+            "ERROR_VARIABLE",
+            "INPUT_FILE",
+            "OUTPUT_FILE",
+            "ERROR_FILE",
+            "ENCODING",
+            "COMMAND_ERROR_IS_FATAL",
+        },
+        multi_value={"COMMAND"},
+    ),
+    "try_compile": CommandSchema(
+        options={"GLOBAL_ERROR", "NO_CACHE"},
+        one_value={"OUTPUT_VARIABLE", "COPY_FILE", "COPY_FILE_ERROR"},
+        multi_value={
+            "SOURCES",
+            "COMPILE_DEFINITIONS",
+            "LINK_LIBRARIES",
+            "LINK_OPTIONS",
+            "CMAKE_FLAGS",
+        },
+    ),
+    "add_custom_command": CommandSchema(
+        options={
+            "POST_BUILD",
+            "PRE_BUILD",
+            "PRE_LINK",
+            "VERBATIM",
+            "APPEND",
+            "USES_TERMINAL",
+            "COMMAND_EXPAND_LISTS",
+        },
+        one_value={
+            "TARGET",
+            "MAIN_DEPENDENCY",
+            "WORKING_DIRECTORY",
+            "COMMENT",
+            "DEPFILE",
+            "JOB_POOL",
+            "JOB_SERVER_AWARE",
+        },
+        multi_value={"COMMAND", "OUTPUT", "BYPRODUCTS", "DEPENDS", "IMPLICIT_DEPENDS"},
+    ),
+    "add_custom_target": CommandSchema(
+        options={"ALL", "VERBATIM", "USES_TERMINAL", "COMMAND_EXPAND_LISTS"},
+        one_value={"WORKING_DIRECTORY", "COMMENT"},
+        multi_value={"COMMAND", "DEPENDS", "BYPRODUCTS", "SOURCES"},
+    ),
+    "ExternalProject_Add": CommandSchema(
+        options={"EXCLUDE_FROM_ALL"},
+        one_value={
+            "PREFIX",
+            "SOURCE_DIR",
+            "BINARY_DIR",
+            "INSTALL_DIR",
+            "DOWNLOAD_COMMAND",
+            "CONFIGURE_COMMAND",
+            "BUILD_COMMAND",
+            "INSTALL_COMMAND",
+        },
+        multi_value={
+            "BUILD_BYPRODUCTS",
+            "CMAKE_ARGS",
+            "CMAKE_CACHE_ARGS",
+            "STEP_TARGETS",
+            "INDEPENDENT_STEP_TARGETS",
+            "DEPENDS",
+        },
+    ),
+    "find_package": CommandSchema(
+        options={"EXACT", "QUIET", "REQUIRED", "CONFIG", "NO_MODULE"},
+        multi_value={"COMPONENTS", "OPTIONAL_COMPONENTS"},
+    ),
+    "add_library": CommandSchema(
+        options={"EXCLUDE_FROM_ALL", "GLOBAL", "IMPORTED"},
+        one_value={"ALIAS"},
+        multi_value={
+            "STATIC",
+            "SHARED",
+            "MODULE",
+            "OBJECT",
+            "PUBLIC",
+            "PRIVATE",
+            "INTERFACE",
+            "SOURCES",
+        },
+    ),
+    "add_executable": CommandSchema(
+        options={"WIN32", "MACOSX_BUNDLE", "EXCLUDE_FROM_ALL", "GLOBAL", "IMPORTED"},
+        one_value={"ALIAS"},
+        multi_value={"SOURCES"},
+    ),
+    "target_link_libraries": CommandSchema(
+        multi_value={"PUBLIC", "PRIVATE", "INTERFACE", "LINK_PRIVATE", "LINK_PUBLIC"}
+    ),
+    "target_include_directories": CommandSchema(
+        options={"BEFORE", "SYSTEM"}, multi_value={"PUBLIC", "PRIVATE", "INTERFACE"}
+    ),
+    "target_compile_options": CommandSchema(
+        options={"BEFORE"}, multi_value={"PUBLIC", "PRIVATE", "INTERFACE"}
+    ),
+    "set_target_properties": CommandSchema(multi_value={"PROPERTIES"}),
+    "set_source_files_properties": CommandSchema(multi_value={"PROPERTIES"}),
+    "set_directory_properties": CommandSchema(multi_value={"PROPERTIES"}),
+    "set_property": CommandSchema(
+        options={
+            "GLOBAL",
+            "DIRECTORY",
+            "TARGET",
+            "SOURCE",
+            "INSTALL",
+            "TEST",
+            "CACHE",
+            "INHERITED",
+        },
+        one_value={"PROPERTY"},
+        multi_value={"APPEND", "APPEND_STRING"},
+    ),
+    "get_target_property": CommandSchema(),
+    "get_property": CommandSchema(
+        options={
+            "GLOBAL",
+            "DIRECTORY",
+            "TARGET",
+            "SOURCE",
+            "INSTALL",
+            "TEST",
+            "CACHE",
+            "SET",
+            "DEFINED",
+            "BRIEF_DOCS",
+            "FULL_DOCS",
+        },
+        one_value={"PROPERTY"},
+    ),
+    "list": CommandSchema(
+        options={
+            "APPEND",
+            "PREPEND",
+            "POP_BACK",
+            "POP_FRONT",
+            "REMOVE_AT",
+            "REMOVE_ITEM",
+            "REMOVE_DUPLICATES",
+            "TRANSFORM",
+            "SORT",
+            "REVERSE",
+            "JOIN",
+            "SUBLIST",
+            "FILTER",
+            "FIND",
+            "GET",
+            "LENGTH",
+            "INSERT",
+        }
+    ),
+    "set": CommandSchema(options={"PARENT_SCOPE", "FORCE"}, one_value={"CACHE"}),
+}
+
+CANONICAL_CMD_CASING = {}
+for k in BUILTIN_COMMAND_SCHEMAS.keys():
+    if k != k.lower():
+        CANONICAL_CMD_CASING[k.lower()] = k
+
+
+def get_schema_for_cmd(
+    cmd_name: str, ctx: FormatterContext | None = None
+) -> CommandSchema:
+    """Resolves official built-in or dynamically learned argument schema for a given CMake command."""
+    cmd_lower = cmd_name.lower()
+
+    # Standard built-in CMake commands use exact official schemas
+    if cmd_lower in BUILTIN_COMMAND_SCHEMAS:
+        return BUILTIN_COMMAND_SCHEMAS[cmd_lower]
+
+    context = ctx if ctx is not None else WORKSPACE_CONTEXT
+
+    # Return cached schema if available, avoiding repeated set allocations.
+    cached = context._schema_cache.get(cmd_lower)
+    if cached is not None:
+        return cached
+
+    options = set(context.learned_options)
+    one_value_args = set(context.learned_one_value)
+    multi_value_args = set(context.list_keywords) | set(context.learned_multi_value)
+
+    explicit_kws = None
+    if cmd_lower in context.dynamic_schemas:
+        ds = context.dynamic_schemas[cmd_lower]
+        options.update(ds.options)
+        one_value_args.update(ds.one_value)
+        multi_value_args.update(ds.multi_value)
+        explicit_kws = set(ds.explicit_keywords)
+
+    # Resolve overlaps so `multi_value` and `one_value` take precedence over `options`.
+    options -= multi_value_args | one_value_args
+    one_value_args -= multi_value_args
+
+    schema = CommandSchema(
+        options=options,
+        one_value=one_value_args,
+        multi_value=multi_value_args,
+        explicit_keywords=explicit_kws if explicit_kws is not None else set(),
+    )
+    context._schema_cache[cmd_lower] = schema
+    return schema
+
+
+class TokenType(str, Enum):
+    IDENTIFIER = "IDENTIFIER"
+    LPAREN = "LPAREN"
+    RPAREN = "RPAREN"
+    QUOTED_ARG = "QUOTED_ARG"
+    BRACKET_ARG = "BRACKET_ARG"
+    UNQUOTED_ARG = "UNQUOTED_ARG"
+    LINE_COMMENT = "LINE_COMMENT"
+    BRACKET_COMMENT = "BRACKET_COMMENT"
+    WHITESPACE = "WHITESPACE"
+    NEWLINE = "NEWLINE"
+
+
+class Token(NamedTuple):
+    """Represents a single AST token with its TokenType and string payload."""
+
+    type: TokenType
+    value: str
+
+
+class KeywordType(str, Enum):
+    ONE_VALUE = "ONE_VALUE"
+    MULTI_VALUE = "MULTI_VALUE"
+    OPTION = "OPTION"
+
+
+def tokenize(text: str) -> list[Token]:
+    """Formal Lexer based on cmake-language(7) EBNF specification.
+
+    Raises:
+        LexError: If the input contains an unterminated quoted argument,
+            bracket argument, or bracket comment.
+    """
+    tokens = []
+    i = 0
+    n = len(text)
+    # Track line/col for error reporting.  line and col are 1-based.
+    line = 1
+    line_start = 0
+
+    while i < n:
+        col = i - line_start + 1
+
+        # Bracket comment or line comment starting with '#'
+        if text[i] == "#":
+            m = RE_BRACKET_COMMENT_START.match(text, pos=i)
+            if m:
+                eq_len = len(m.group(1))
+                close_pat = "]" + "=" * eq_len + "]"
+                end_idx = text.find(close_pat, i)
+                if end_idx == -1:
+                    raise LexError(
+                        f"unterminated bracket comment '#[{'=' * eq_len}['",
+                        line,
+                        col,
+                    )
+                end_pos = end_idx + len(close_pat)
+                tokens.append(Token(TokenType.BRACKET_COMMENT, text[i:end_pos]))
+                # Advance line/col tracking over the consumed span.
+                newlines = text[i:end_pos].count("\n")
+                if newlines:
+                    line += newlines
+                    line_start = end_pos - len(text[i:end_pos].rsplit("\n", 1)[-1])
+                i = end_pos
+                continue
+
+            # Line comment: #...
+            end_idx = text.find("\n", i)
+            if end_idx == -1:
+                end_idx = n
+            tokens.append(Token(TokenType.LINE_COMMENT, text[i:end_idx]))
+            i = end_idx
+            continue
+
+        # Bracket argument: [=[...]=]
+        if text[i] == "[":
+            m = RE_BRACKET_ARG_START.match(text, pos=i)
+            if m:
+                eq_len = len(m.group(1))
+                close_pat = "]" + "=" * eq_len + "]"
+                end_idx = text.find(close_pat, i)
+                if end_idx == -1:
+                    raise LexError(
+                        f"unterminated bracket argument '[{'=' * eq_len}['",
+                        line,
+                        col,
+                    )
+                end_pos = end_idx + len(close_pat)
+                tokens.append(Token(TokenType.BRACKET_ARG, text[i:end_pos]))
+                newlines = text[i:end_pos].count("\n")
+                if newlines:
+                    line += newlines
+                    line_start = end_pos - len(text[i:end_pos].rsplit("\n", 1)[-1])
+                i = end_pos
+                continue
+
+        # Quoted argument: "..." (Single immutable token!)
+        if text[i] == '"':
+            j = i + 1
+            closed = False
+            while j < n:
+                if text[j] == "\\":
+                    j = min(n, j + 2)
+                elif text[j] == '"':
+                    j += 1
+                    closed = True
+                    break
+                else:
+                    j += 1
+            if not closed:
+                raise LexError("unterminated quoted argument", line, col)
+            token_val = text[i:j]
+            tokens.append(Token(TokenType.QUOTED_ARG, token_val))
+            newlines = token_val.count("\n")
+            if newlines:
+                line += newlines
+                line_start = j - len(token_val.rsplit("\n", 1)[-1])
+            i = j
+            continue
+
+        # Newline
+        if text[i] == "\n":
+            tokens.append(Token(TokenType.NEWLINE, "\n"))
+            i += 1
+            line += 1
+            line_start = i
+            continue
+
+        # Whitespace
+        if text[i] in " \t\r":
+            j = i
+            while j < n and text[j] in " \t\r":
+                j += 1
+            tokens.append(Token(TokenType.WHITESPACE, text[i:j]))
+            i = j
+            continue
+
+        # Parens
+        if text[i] == "(":
+            tokens.append(Token(TokenType.LPAREN, "("))
+            i += 1
+            continue
+        if text[i] == ")":
+            tokens.append(Token(TokenType.RPAREN, ")"))
+            i += 1
+            continue
+
+        # Unquoted argument / Identifier
+        j = i
+        while j < n and text[j] not in ' \t\r\n()#"':
+            if text[j] == "\\":
+                j = min(n, j + 2)
+            else:
+                j += 1
+        token_val = text[i:j]
+        tokens.append(Token(TokenType.UNQUOTED_ARG, token_val))
+        i = j
+
+    return tokens
+
+
+def scan_dynamic_schemas(
+    content: str,
+    ctx: FormatterContext | None = None,
+    tokens: list[Token] | None = None,
+) -> None:
+    """Scans cmake_parse_arguments and set(..._ARGS) calls to learn keyword schemas dynamically.
+
+    Mutates ctx in-place: appends newly discovered keywords to ctx.learned_options,
+    ctx.learned_one_value, ctx.learned_multi_value, ctx.list_keywords, and
+    ctx.dynamic_schemas. If ctx is None, mutates the module-level WORKSPACE_CONTEXT.
+    """
+    if "_ARGS" not in content and "cmake_parse_arguments" not in content:
+        return
+    context = ctx if ctx is not None else WORKSPACE_CONTEXT
+    # Any mutation of the context invalidates previously cached merged schemas.
+    context._schema_cache.clear()
+
+    # 1. Parse set(*_OPTION_ARGS ...), set(*_SINGLE_VALUE_ARGS ...), set(*_MULTI_VALUE_ARGS ...), etc.
+    schema_patterns = [
+        (RE_OPTION_ARGS, context.learned_options),
+        (RE_SINGLE_VALUE_ARGS, context.learned_one_value),
+        (RE_MULTI_VALUE_ARGS, context.learned_multi_value),
+    ]
+    for regex, target_set in schema_patterns:
+        for m in regex.finditer(content):
+            if not RE_CACHE.search(m.group(2)):
+                words = set(RE_IDENTIFIER_TOKENS.findall(m.group(2)))
+                target_set.update(words)
+                if target_set is context.learned_multi_value:
+                    context.list_keywords.update(words)
+
+    # 2. Token-based scanning for function()/macro() declarations and cmake_parse_arguments
+    if tokens is None:
+        tokens = tokenize(content)
+    n = len(tokens)
+    current_fn_name = None
+
+    for i in range(n):
+        tok = tokens[i]
+        if tok.type != TokenType.UNQUOTED_ARG:
+            continue
+
+        cmd_lower = tok.value.lower()
+
+        # Track function/macro start
+        if cmd_lower in ("function", "macro"):
+            for j in range(i + 1, n):
+                tok_j = tokens[j]
+                if tok_j.type in (
+                    TokenType.WHITESPACE,
+                    TokenType.NEWLINE,
+                    TokenType.LINE_COMMENT,
+                    TokenType.BRACKET_COMMENT,
+                ):
+                    continue
+                if tok_j.type == TokenType.LPAREN:
+                    for k in range(j + 1, n):
+                        tok_k = tokens[k]
+                        if tok_k.type in (
+                            TokenType.WHITESPACE,
+                            TokenType.NEWLINE,
+                            TokenType.LINE_COMMENT,
+                            TokenType.BRACKET_COMMENT,
+                        ):
+                            continue
+                        if tok_k.type in (TokenType.UNQUOTED_ARG, TokenType.QUOTED_ARG):
+                            current_fn_name = tok_k.value.strip('"')
+                        break
+                break
+
+        # Track function/macro end
+        elif cmd_lower in ("endfunction", "endmacro"):
+            current_fn_name = None
+
+        # Parse cmake_parse_arguments(...) call inside function/macro
+        elif cmd_lower == "cmake_parse_arguments" and current_fn_name:
+            arg_tokens = []
+            in_parens = False
+            paren_depth = 0
+            for j in range(i + 1, n):
+                tok_j = tokens[j]
+                if tok_j.type in (
+                    TokenType.WHITESPACE,
+                    TokenType.NEWLINE,
+                    TokenType.LINE_COMMENT,
+                    TokenType.BRACKET_COMMENT,
+                ):
+                    continue
+                if tok_j.type == TokenType.LPAREN:
+                    in_parens = True
+                    paren_depth += 1
+                    continue
+                if in_parens:
+                    if tok_j.type == TokenType.RPAREN:
+                        paren_depth -= 1
+                        if paren_depth <= 0:
+                            break
+                    else:
+                        arg_tokens.append(tok_j.value)
+
+            # cmake_parse_arguments positional layout:
+            #   cmake_parse_arguments(<prefix> <options> <one_value> <multi_value> <args>...)
+            #   cmake_parse_arguments(PARSE_ARGV <n> <prefix> <options> <one_value> <multi_value>)
+            # When PARSE_ARGV/PARSE_ARGN is present, the first two tokens are the
+            # mode keyword and the index argument; the prefix/options/... follow at +2.
+            _CPA_MIN_ARGS = 4  # prefix + options + one_value + multi_value
+            _CPA_PARSE_ARGV_EXTRA = 2  # extra tokens: mode keyword + integer index
+            _CPA_PARSE_ARGV_MIN_ARGS = _CPA_MIN_ARGS + _CPA_PARSE_ARGV_EXTRA
+
+            if len(arg_tokens) >= _CPA_MIN_ARGS:
+                offset = 0
+                if (
+                    arg_tokens[0].upper() in ("PARSE_ARGV", "PARSE_ARGN")
+                    and len(arg_tokens) >= _CPA_PARSE_ARGV_MIN_ARGS
+                ):
+                    offset = _CPA_PARSE_ARGV_EXTRA
+
+                if len(arg_tokens) >= offset + _CPA_MIN_ARGS:
+                    # arg_tokens[offset + 0] is the prefix — not needed for schema building
+                    opt_str = arg_tokens[offset + 1]
+                    one_str = arg_tokens[offset + 2]
+                    multi_str = arg_tokens[offset + 3]
+
+                    def _extract_literal_kws(arg_s: str) -> set[str]:
+                        cleaned = arg_s.strip('"\t\r\n ')
+                        if cleaned.startswith("${") or cleaned.startswith("$"):
+                            return set()
+                        non_var = re.sub(r"\$\{[^}]*\}", "", cleaned)
+                        return set(RE_IDENTIFIER_WORDS.findall(non_var))
+
+                    opts = _extract_literal_kws(opt_str)
+                    ones = _extract_literal_kws(one_str)
+                    multis = _extract_literal_kws(multi_str)
+                    context.list_keywords.update(multis)
+                    context.dynamic_schemas[current_fn_name.lower()] = CommandSchema(
+                        options=opts, one_value=ones, multi_value=multis
+                    )
+
+
+# Filesystem markers that indicate a project root boundary. Used by pre_scan_workspace_modules
+# to stop walking upward before escaping the repository tree.
+_PROJECT_ROOT_SENTINELS = frozenset({".git", ".gitmodules"})
+
+
+def pre_scan_workspace_modules(
+    paths: list[str], ctx: FormatterContext | None = None
+) -> None:
+    """Pre-scans all CMake module files in the repository directory tree to learn dynamic schemas before formatting."""
+    context = ctx if ctx is not None else WORKSPACE_CONTEXT
+    module_files = set()
+    scanned_base_dirs = set()
+
+    for p in paths:
+        path = Path(p)
+        base_dir = path.resolve() if path.is_dir() else path.resolve().parent
+        if base_dir in scanned_base_dirs:
+            continue
+        scanned_base_dirs.add(base_dir)
+
+        # Walk upward to find cmake/modules/, stopping at recognized repository
+        # root boundaries (.git, .gitmodules) so we don't accidentally scan
+        # unrelated cmake/modules/ directories in parent directories.
+        curr = base_dir
+        while curr and curr != curr.parent:
+            mod_dir = curr / "cmake" / "modules"
+            if mod_dir.is_dir():
+                module_files.update(str(f) for f in mod_dir.glob("*.cmake"))
+                break
+            # Stop after checking current dir if we've reached a repository root boundary
+            if any((curr / sentinel).exists() for sentinel in _PROJECT_ROOT_SENTINELS):
+                break
+            curr = curr.parent
+
+    for mf in module_files:
+        try:
+            with open(mf, "r", encoding="utf-8") as f:
+                scan_dynamic_schemas(f.read(), ctx=context)
+        except (OSError, UnicodeDecodeError, LexError):
+            pass
+
+
+# Regex matching directive comments that should not have a space inserted after '#'
+# (e.g., native CMake template directives like #cmakedefine / #cmakedefine01, tool directives
+# like # cmake-lint / # cmake-format / #clang-format / #nolint).
+#
+# NOTE: C preprocessor directives (#include, #define, #pragma, #ifdef, etc.) are included
+# here to support embedded C code snippets and C header template code written inside
+# CMake files and CMake comments, ensuring that formatting does not corrupt the embedded C.
+RE_DIRECTIVE_COMMENT = re.compile(
+    r"^#(?:[ \t]*cmake-lint|[ \t]*cmake-format|cmakedefine01|cmakedefine|nolint|pragma|include|clang-format|define|undef|ifdef|ifndef|error|warning)\b",
+    re.IGNORECASE,
+)
+
+
+def _format_line_comment(comment_text: str) -> str:
+    """Formats a single line comment, ensuring a space after '#' while preserving special markers (#---, #===, #!).
+
+    This function is only called for LINE_COMMENT tokens. Bracket-style comments
+    (#[=[...]=]) are handled separately by the lexer as BRACKET_COMMENT tokens and
+    are never passed here.
+    """
+    m_hash = RE_COMMENT_HASH.match(comment_text)
+    if m_hash:
+        hash_prefix, rest_comment = m_hash.groups()
+        if not (
+            comment_text.startswith("#---")
+            or comment_text.startswith("#===")
+            or comment_text.startswith("#!")
+            or RE_DIRECTIVE_COMMENT.match(comment_text)
+        ):
+            if rest_comment and not (
+                rest_comment.startswith(" ") or rest_comment.startswith("\t")
+            ):
+                return f"{hash_prefix} {rest_comment}"
+    return comment_text
+
+
+ at dataclass
+class CallState:
+    """Tracks formatter state across lines during a multi-line command invocation.
+
+    Lifecycle:
+        Created when the opening line of a multi-line command is encountered
+        (i.e., the line's paren balance is still positive after the command).
+        Destroyed when the closing ``)``) line is processed or paren balance
+        drops to zero.
+
+    Key state variables:
+        cmd_name: The command being formatted (lowercase).
+        base_indent: Indent level of the command itself (used to compute child indents).
+        paren_balance: Running count of unmatched ``(`` tokens; zero signals end of call.
+        schema: Resolved CommandSchema providing options/one_value/multi_value sets.
+        active_kw: The currently open keyword (None if no keyword is active).
+        active_kw_type: ONE_VALUE, MULTI_VALUE, or None — governs indent of child lines.
+        active_kw_on_cmd_line: True if active_kw was opened on the command line itself,
+            which suppresses extra nesting for its child list items.
+        one_value_count: Number of values consumed under the current ONE_VALUE keyword;
+            the keyword closes after the first value is seen.
+    """
+
+    cmd_name: str = ""
+    base_indent: int = 0
+    paren_balance: int = 0
+    schema: CommandSchema | None = None
+    arg_count: int = 0
+    first_arg_on_separate_line: bool = False
+    active_kw: str | None = None
+    active_kw_type: KeywordType | None = None
+    active_kw_on_cmd_line: bool = False
+    one_value_count: int = 0
+    last_line_was_keyword: bool = False
+    in_call: bool = True
+
+    def __post_init__(self) -> None:
+        if self.schema is None:
+            self.schema = CommandSchema()
+
+    def reset_kw(self) -> None:
+        self.active_kw = None
+        self.active_kw_type = None
+        self.active_kw_on_cmd_line = False
+
+    def is_keyword_token(self, clean_word: str) -> bool:
+        """Determines contextually whether an unquoted identifier token is a keyword or a positional argument."""
+        if not clean_word.isidentifier():
+            return False
+        clean_upper = clean_word.upper()
+        if clean_upper in self.schema.explicit_keywords:
+            return True
+        if clean_upper in self.schema.all_keywords:
+            if self.arg_count > 0 or self.cmd_name in BUILTIN_COMMAND_SCHEMAS:
+                return True
+        return False
+
+    def process_tokens(self, tokens: list[Token], is_cmd_line: bool = False) -> str:
+        """Processes line tokens contextually, updating active keyword state and reconstructing formatted text."""
+        reconstructed_parts = []
+        for tok in tokens:
+            if tok.type == TokenType.WHITESPACE:
+                reconstructed_parts.append(" ")
+            elif tok.type in (
+                TokenType.QUOTED_ARG,
+                TokenType.BRACKET_ARG,
+            ) or self.cmd_name in {"add_subdirectory", "include"}:
+                reconstructed_parts.append(tok.value)
+                if RE_ARG_FORWARDING_VAR.match(tok.value):
+                    self.reset_kw()
+                elif self.active_kw_type == KeywordType.ONE_VALUE:
+                    self.one_value_count += 1
+                    if self.one_value_count >= 1:
+                        self.reset_kw()
+            elif tok.type == TokenType.UNQUOTED_ARG:
+                # Strip trailing syntactic noise (`,`, `;`, `)`) and leading `(` that
+                # CMake allows to attach to unquoted argument tokens.
+                clean_word = tok.value.rstrip("),;").lstrip("(")
+                if RE_ARG_FORWARDING_VAR.match(clean_word):
+                    reconstructed_parts.append(tok.value)
+                    self.reset_kw()
+                elif self.is_keyword_token(clean_word):
+                    kw = clean_word.upper()
+                    formatted_tok = tok.value.replace(clean_word, kw, 1)
+                    reconstructed_parts.append(formatted_tok)
+                    if kw in self.schema.options:
+                        if self.active_kw_type != KeywordType.MULTI_VALUE:
+                            self.reset_kw()
+                        self.one_value_count = 0
+                    elif kw in self.schema.one_value:
+                        self.active_kw = kw
+                        self.active_kw_type = KeywordType.ONE_VALUE
+                        self.active_kw_on_cmd_line = is_cmd_line
+                        self.one_value_count = 0
+                    elif kw in self.schema.multi_value:
+                        self.active_kw = kw
+                        self.active_kw_type = KeywordType.MULTI_VALUE
+                        self.active_kw_on_cmd_line = is_cmd_line
+                        self.one_value_count = 0
+                else:
+                    reconstructed_parts.append(tok.value)
+                    if self.active_kw_type == KeywordType.ONE_VALUE:
+                        self.one_value_count += 1
+                        if self.one_value_count >= 1:
+                            self.reset_kw()
+            else:
+                reconstructed_parts.append(tok.value)
+
+        return "".join(reconstructed_parts)
+
+
+ at dataclass
+class _FormatterState:
+    """Mutable state carried through a single formatting pass over a CMake file.
+
+    Encapsulates all the variables that must be shared across line iterations
+    (output buffer, pending comments, indent level, active call tracker) so
+    that the per-line helper functions can read and update them without relying
+    on Python closure capture.
+    """
+
+    formatted_lines: list[str] = field(default_factory=list)
+    pending_comments: list[str] = field(default_factory=list)
+    block_indent_level: int = 0
+    current_call_state: CallState | None = None
+
+    def flush_comments(self, indent_str: str) -> None:
+        """Appends all buffered comments at the given indent level, then clears the buffer."""
+        for ctext in self.pending_comments:
+            self.formatted_lines.append(f"{indent_str}{ctext}")
+        self.pending_comments.clear()
+
+
+def _trim_line_toks(line_toks: list[Token]) -> list[Token]:
+    """Returns a slice of line_toks with leading and trailing WHITESPACE tokens removed."""
+    start = next(
+        (i for i, t in enumerate(line_toks) if t.type != TokenType.WHITESPACE),
+        len(line_toks),
+    )
+    end = next(
+        (
+            i
+            for i in range(len(line_toks) - 1, start - 1, -1)
+            if line_toks[i].type != TokenType.WHITESPACE
+        ),
+        start - 1,
+    )
+    return line_toks[start : end + 1]
+
+
+def _process_call_continuation_line(
+    trimmed_line_toks: list[Token],
+    state: _FormatterState,
+) -> None:
+    """Handles a continuation line inside an active multi-line command invocation (section 3).
+
+    Updates paren_balance on the active CallState. If the line starts with ')' the
+    call is closed. Otherwise the indent level is computed from the active keyword
+    context, the line is formatted via CallState.process_tokens(), and the result
+    is appended to state.formatted_lines.
+    """
+    cs = state.current_call_state
+    if cs is None:
+        raise RuntimeError(
+            "_process_call_continuation_line called with no active CallState"
+        )
+
+    # Safe: the lexer only emits LPAREN/RPAREN for syntactic parens,
+    # never inside quoted arguments, bracket arguments, or comments.
+    lparens = sum(1 for tok in trimmed_line_toks if tok.type == TokenType.LPAREN)
+    rparens = sum(1 for tok in trimmed_line_toks if tok.type == TokenType.RPAREN)
+
+    # Check if this line is a standalone closing parenthesis line (e.g. ')' or ') # comment').
+    non_comment_toks = [
+        tok
+        for tok in trimmed_line_toks
+        if tok.type not in (TokenType.WHITESPACE, TokenType.LINE_COMMENT)
+    ]
+    is_standalone_close_paren = (
+        len(non_comment_toks) == 1 and non_comment_toks[0].type == TokenType.RPAREN
+    )
+
+    if is_standalone_close_paren and (cs.paren_balance - 1) <= 0:
+        cs.in_call = False
+        comment_indent = INDENT_STR * (
+            cs.base_indent + LIST_ITEM_INDENT_LEVELS
+            if cs.last_line_was_keyword
+            else cs.base_indent + KEYWORD_INDENT_LEVELS
+        )
+        line_indent = INDENT_STR * cs.base_indent
+        cs.reset_kw()
+        state.flush_comments(comment_indent)
+        reconstructed_close = "".join(tok.value for tok in trimmed_line_toks).rstrip()
+        state.formatted_lines.append(f"{line_indent}{reconstructed_close}")
+        state.current_call_state = None
+        return
+
+    cs.paren_balance += lparens - rparens
+
+    # Determine whether the first meaningful token on this line is a keyword or
+    # an argument-forwarding variable (${ARGN} etc.), as both affect indentation.
+    first_unquoted: str | None = None
+    for tok in trimmed_line_toks:
+        if tok.type in (
+            TokenType.UNQUOTED_ARG,
+            TokenType.QUOTED_ARG,
+            TokenType.BRACKET_ARG,
+        ):
+            first_unquoted = tok.value
+            break
+
+    line_starts_new_keyword = False
+    is_arg_var_line = False
+    if first_unquoted:
+        clean_first = first_unquoted.rstrip("),;").lstrip("(")
+        if RE_ARG_FORWARDING_VAR.match(clean_first):
+            is_arg_var_line = True
+        elif cs.is_keyword_token(clean_first):
+            line_starts_new_keyword = True
+
+    cs.last_line_was_keyword = line_starts_new_keyword
+
+    # Compute indentation level for this continuation line.
+    if line_starts_new_keyword or is_arg_var_line:
+        indent_str = INDENT_STR * (cs.base_indent + KEYWORD_INDENT_LEVELS)
+    elif cs.active_kw is not None:
+        if cs.active_kw_on_cmd_line:
+            indent_str = INDENT_STR * (cs.base_indent + KEYWORD_INDENT_LEVELS)
+        else:
+            indent_str = INDENT_STR * (cs.base_indent + LIST_ITEM_INDENT_LEVELS)
+    elif cs.cmd_name == "set" and cs.arg_count >= 1 and cs.first_arg_on_separate_line:
+        indent_str = INDENT_STR * (cs.base_indent + LIST_ITEM_INDENT_LEVELS)
+    else:
+        indent_str = INDENT_STR * (cs.base_indent + KEYWORD_INDENT_LEVELS)
+
+    reconstructed_line = cs.process_tokens(trimmed_line_toks, is_cmd_line=False)
+    cs.arg_count += sum(
+        1
+        for tok in trimmed_line_toks
+        if tok.type
+        in (TokenType.QUOTED_ARG, TokenType.BRACKET_ARG, TokenType.UNQUOTED_ARG)
+    )
+
+    state.flush_comments(indent_str)
+    state.formatted_lines.append(f"{indent_str}{reconstructed_line}".rstrip())
+
+    if cs.paren_balance <= 0:
+        cs.in_call = False
+        state.current_call_state = None
+
+
+def _process_command_invocation_line(
+    non_ws_toks: list[Token],
+    trimmed_line_toks: list[Token],
+    state: _FormatterState,
+    file_ctx: FormatterContext,
+) -> None:
+    """Handles the opening line of a CMake command invocation (section 4).
+
+    Applies command casing, strips arguments from empty-closure commands,
+    computes the opening line's paren balance to decide if a CallState is
+    needed for continuation lines, and adjusts block_indent_level for
+    control-flow commands.
+    """
+    cmd_name = non_ws_toks[0].value
+    lower_cmd = cmd_name.lower()
+
+    # Apply canonical casing: module commands keep declared case, built-ins go lowercase.
+    if lower_cmd in CANONICAL_CMD_CASING:
+        cmd_name = CANONICAL_CMD_CASING[lower_cmd]
+    elif (
+        lower_cmd in BUILTIN_COMMAND_SCHEMAS
+        or lower_cmd in EMPTY_CLOSE_BLOCKS
+        or lower_cmd in CONTROL_START_BLOCKS
+    ):
+        cmd_name = lower_cmd
+
+    # Find the opening '(' in the trimmed token list.
+    lparen_idx = next(
+        (i for i, tok in enumerate(trimmed_line_toks) if tok.type == TokenType.LPAREN),
+        -1,
+    )
+    rest_tokens = trimmed_line_toks[lparen_idx + 1 :] if lparen_idx != -1 else []
+
+    # Empty-closure commands (endif, else, …) have their arguments stripped while preserving line comments.
+    if lower_cmd in EMPTY_CLOSE_BLOCKS:
+        rparen_seen = False
+        filtered_tokens = []
+        for rt in rest_tokens:
+            if not rparen_seen:
+                if rt.type == TokenType.RPAREN:
+                    filtered_tokens.append(rt)
+                    rparen_seen = True
+            else:
+                filtered_tokens.append(rt)
+        rest_tokens = filtered_tokens
+
+    lparens = sum(1 for tok in rest_tokens if tok.type == TokenType.LPAREN)
+    rparens = sum(1 for tok in rest_tokens if tok.type == TokenType.RPAREN)
+    line_paren_balance = 1 + lparens - rparens
+    is_single_line = line_paren_balance <= 0
+
+    # Compute indent for this line, adjusting block level for end/middle commands.
+    if lower_cmd.startswith("end"):
+        state.block_indent_level = max(0, state.block_indent_level - 1)
+        indent_str = INDENT_STR * state.block_indent_level
+    elif lower_cmd in CONTROL_MIDDLE_BLOCKS:
+        indent_str = INDENT_STR * max(0, state.block_indent_level - 1)
+    else:
+        indent_str = INDENT_STR * state.block_indent_level
+
+    cmd_schema = get_schema_for_cmd(lower_cmd, ctx=file_ctx)
+    rest_arg_count = sum(
+        1
+        for tok in rest_tokens
+        if tok.type
+        in (TokenType.QUOTED_ARG, TokenType.BRACKET_ARG, TokenType.UNQUOTED_ARG)
+    )
+    first_arg_on_separate_line = rest_arg_count == 0
+
+    temp_state = CallState(cmd_name=lower_cmd, schema=cmd_schema)
+    rest_formatted = temp_state.process_tokens(rest_tokens, is_cmd_line=True)
+    reconstructed = f"{indent_str}{cmd_name}({rest_formatted}".rstrip()
+
+    state.flush_comments(indent_str)
+    state.formatted_lines.append(reconstructed)
+
+    if not is_single_line:
+        state.current_call_state = CallState(
+            cmd_name=lower_cmd,
+            base_indent=state.block_indent_level,
+            paren_balance=line_paren_balance,
+            schema=cmd_schema,
+            arg_count=rest_arg_count,
+            first_arg_on_separate_line=first_arg_on_separate_line,
+            active_kw=temp_state.active_kw,
+            active_kw_type=temp_state.active_kw_type,
+            active_kw_on_cmd_line=temp_state.active_kw_on_cmd_line,
+            one_value_count=temp_state.one_value_count,
+        )
+
+    if lower_cmd in CONTROL_START_BLOCKS:
+        state.block_indent_level += 1
+
+
+def format_cmake_content(content: str, ctx: FormatterContext | None = None) -> str:
+    """Formats CMake file content using AST token-stream parser.
+
+    Applies lowercasing for built-in commands, proper indent nesting for control blocks,
+    schema-aware keyword upper-casing, multi-line argument indentation (+2 / +4 spaces),
+    and lookahead comment buffering alignment.
+
+    The function dispatches each source line to one of five handlers:
+      1. Empty line  — flush buffered comments and emit a blank line.
+      2. Comment line — buffer the comment for lookahead alignment.
+      3. Call continuation line — delegate to _process_call_continuation_line().
+      4. Command invocation line — delegate to _process_command_invocation_line().
+      5. Raw fallback — emit the line unchanged (bracket comments, bare tokens).
+    """
+    if not content.strip():
+        return ""
+
+    file_ctx = ctx.clone() if ctx is not None else WORKSPACE_CONTEXT.clone()
+    tokens = tokenize(content)
+    scan_dynamic_schemas(content, ctx=file_ctx, tokens=tokens)
+
+    # Group tokens into lines (separated by TokenType.NEWLINE).
+    lines_tokens: list[list[Token]] = []
+    curr_line: list[Token] = []
+    for tok in tokens:
+        if tok.type == TokenType.NEWLINE:
+            lines_tokens.append(curr_line)
+            curr_line = []
+        else:
+            curr_line.append(tok)
+    if curr_line or not lines_tokens:
+        lines_tokens.append(curr_line)
+
+    state = _FormatterState()
+
+    for line_toks in lines_tokens:
+        # Strip leading whitespace tokens for line classification.
+        non_ws_toks = [t for t in line_toks if t.type != TokenType.WHITESPACE]
+        in_call = (
+            state.current_call_state is not None and state.current_call_state.in_call
+        )
+
+        # 1. Empty line — flush buffered comments and emit a blank line.
+        if not non_ws_toks:
+            if state.pending_comments:
+                state.flush_comments(
+                    INDENT_STR * (state.block_indent_level + (1 if in_call else 0))
+                )
+            state.formatted_lines.append("")
+            if (
+                state.current_call_state
+                and state.current_call_state.in_call
+                and state.current_call_state.paren_balance <= 0
+            ):
+                state.current_call_state.in_call = False
+                state.current_call_state = None
+            continue
+
+        # 2. Comment line — buffer for lookahead alignment.
+        if non_ws_toks[0].type == TokenType.LINE_COMMENT:
+            state.pending_comments.append(_format_line_comment(non_ws_toks[0].value))
+            continue
+
+        trimmed_line_toks = _trim_line_toks(line_toks)
+
+        # 3. Continuation line inside an active multi-line call.
+        if state.current_call_state and state.current_call_state.in_call:
+            _process_call_continuation_line(trimmed_line_toks, state)
+            continue
+
+        # 4. Command invocation line (identifier followed by '(').
+        if (
+            non_ws_toks[0].type == TokenType.UNQUOTED_ARG
+            and len(non_ws_toks) >= 2
+            and non_ws_toks[1].type == TokenType.LPAREN
+        ):
+            _process_command_invocation_line(
+                non_ws_toks, trimmed_line_toks, state, file_ctx
+            )
+            continue
+
+        # 5. Raw token line fallback (bracket comments, bare unrecognised tokens, etc.).
+        raw_line_text = "".join(t.value for t in line_toks).rstrip()
+        state.flush_comments(
+            INDENT_STR * (state.block_indent_level + (1 if in_call else 0))
+        )
+        state.formatted_lines.append(raw_line_text)
+
+    if state.pending_comments:
+        state.flush_comments(INDENT_STR * state.block_indent_level)
+
+    return "\n".join(state.formatted_lines).rstrip() + "\n"
+
+
+def process_file(
+    filepath: str, inplace: bool = False, dry_run: bool = False, show_diff: bool = False
+) -> bool | None:
+    """Reads, formats, and optionally writes back a single CMake file.
+
+    Returns:
+        True   — file had formatting changes (or would have, in dry-run mode).
+        False  — file is already correctly formatted; no changes needed.
+        None   — a read or write error occurred; the file was not modified.
+    """
+    try:
+        with open(filepath, "r", encoding="utf-8") as f:
+            original = f.read()
+    except Exception as e:
+        print(f"Error reading file {filepath}: {e}", file=sys.stderr)
+        return None
+
+    try:
+        formatted = format_cmake_content(original)
+    except LexError as e:
+        print(f"Error parsing file {filepath}: {e}", file=sys.stderr)
+        return None
+
+    has_changes = original != formatted
+
+    if has_changes:
+        if show_diff:
+            diff = difflib.unified_diff(
+                original.splitlines(keepends=True),
+                formatted.splitlines(keepends=True),
+                fromfile=f"a/{filepath}",
+                tofile=f"b/{filepath}",
+            )
+            sys.stdout.writelines(diff)
+
+        if inplace and not dry_run:
+            temp_file = None
+            try:
+                target_path = Path(filepath)
+                temp_file = target_path.with_name(
+                    f".{target_path.name}.tmp_{os.getpid()}"
+                )
+                with open(temp_file, "w", encoding="utf-8") as f:
+                    f.write(formatted)
+                os.replace(temp_file, target_path)
+                print(f"Formatted {filepath}")
+            except Exception as e:
+                if temp_file and os.path.exists(temp_file):
+                    try:
+                        os.remove(temp_file)
+                    except OSError:
+                        pass
+                print(f"Error writing file {filepath}: {e}", file=sys.stderr)
+                return None
+        elif dry_run:
+            print(f"Formatting needed: {filepath}")
+
+    return has_changes
+
+
+def find_cmake_files(paths: list[str]) -> list[str]:
+    """Recursively discovers CMake files (CMakeLists.txt and *.cmake) under the given paths.
+
+    Directories starting with '.' and directories named 'build' are excluded from traversal.
+    Plain file paths are included directly without filtering.
+    Returns files in sorted order for deterministic output across platforms and runs.
+    """
+    cmake_files = []
+    for p in paths:
+        path = Path(p)
+        if path.is_file():
+            cmake_files.append(str(path))
+        elif path.is_dir():
+            for root, dirs, files in path.walk():
+                dirs[:] = sorted(
+                    d for d in dirs if not d.startswith(".") and d != "build"
+                )
+                for f in sorted(files):
+                    if f == "CMakeLists.txt" or f.endswith(".cmake"):
+                        cmake_files.append(str(root / f))
+    return cmake_files
+
+
+def main() -> None:
+    """Entry point: parses CLI arguments and drives file discovery, pre-scanning, and formatting."""
+    parser = argparse.ArgumentParser(
+        description="LLVM and LLVM-libc CMake Formatter Utility",
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        epilog=__doc__,
+    )
+    parser.add_argument("paths", nargs="*", help="Files or directories to format.")
+    parser.add_argument(
+        "-i", "--inplace", "--fix", action="store_true", help="Format files in-place."
+    )
+    parser.add_argument(
+        "-n",
+        "--dry-run",
+        action="store_true",
+        help="Check formatting without modifying files (Evaluation Mode).",
+    )
+    parser.add_argument(
+        "--diff", action="store_true", help="Show unified diffs of formatting changes."
+    )
+
+    parser.add_argument(
+        "-j",
+        "--jobs",
+        type=int,
+        default=1,
+        help="Number of parallel worker processes to use (default: 1).",
+    )
+
+    args = parser.parse_args()
+
+    if not args.paths:
+        if not sys.stdin.isatty():
+            content = sys.stdin.read()
+            try:
+                formatted = format_cmake_content(content)
+            except LexError as e:
+                print(f"Error parsing stdin: {e}", file=sys.stderr)
+                sys.exit(1)
+            if args.dry_run:
+                if content != formatted:
+                    if args.diff:
+                        diff = difflib.unified_diff(
+                            content.splitlines(keepends=True),
+                            formatted.splitlines(keepends=True),
+                            fromfile="stdin",
+                            tofile="formatted",
+                        )
+                        sys.stdout.writelines(diff)
+                    sys.exit(1)
+                else:
+                    sys.exit(0)
+            else:
+                sys.stdout.write(formatted)
+                sys.exit(0)
+        else:
+            parser.print_help()
+            sys.exit(1)
+
+    cmake_files = find_cmake_files(args.paths)
+    if not cmake_files:
+        print("No CMake files found.", file=sys.stderr)
+        sys.exit(0)
+
+    # Mutates WORKSPACE_CONTEXT in-place so that subsequent format_cmake_content()
+    # calls (which clone it per-file) inherit the pre-scanned module schemas.
+    pre_scan_workspace_modules(args.paths)
+
+    files_needing_format = 0
+    if args.jobs > 1 and len(cmake_files) > 1:
+        from concurrent.futures import ProcessPoolExecutor, as_completed
+
+        # Each worker process receives the pre-scanned WORKSPACE_CONTEXT via
+        # the initializer, which pickles it once per worker. This works
+        # correctly with any start method ('fork' on Linux, 'spawn' on
+        # macOS/Windows) — no need to force 'fork'.
+        with ProcessPoolExecutor(
+            max_workers=args.jobs,
+            initializer=_init_worker,
+            initargs=(WORKSPACE_CONTEXT,),
+        ) as executor:
+            future_to_file = {
+                executor.submit(
+                    process_file, fpath, args.inplace, args.dry_run, args.diff
+                ): fpath
+                for fpath in cmake_files
+            }
+            for future in as_completed(future_to_file):
+                fpath = future_to_file[future]
+                try:
+                    result = future.result()
+                    if result is True:
+                        files_needing_format += 1
+                except Exception as e:
+                    print(f"Error processing {fpath}: {e}", file=sys.stderr)
+    else:
+        for fpath in cmake_files:
+            changed = process_file(
+                fpath, inplace=args.inplace, dry_run=args.dry_run, show_diff=args.diff
+            )
+            if changed is True:
+                files_needing_format += 1
+
+    if args.dry_run and files_needing_format > 0:
+        print(f"\n{files_needing_format} file(s) need formatting.")
+        sys.exit(1)
+
+    sys.exit(0)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/libc/utils/cmake_format_test.py b/libc/utils/cmake_format_test.py
new file mode 100644
index 0000000000000..a2b3452cab0b6
--- /dev/null
+++ b/libc/utils/cmake_format_test.py
@@ -0,0 +1,463 @@
+#!/usr/bin/env python3
+"""
+Unit tests for LLVM and Subproject CMake Formatter Utility (`libc/utils/cmake_format.py`).
+
+Verifies all 9 formatting rules, dynamic schema learning, codebase idempotency,
+and golden output snapshots.
+"""
+
+import unittest
+import os
+import sys
+
+# Add the directory containing cmake_format.py to sys.path
+SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
+sys.path.insert(0, SCRIPT_DIR)
+
+import cmake_format
+
+
+class TestRule1_CommandCasing(unittest.TestCase):
+    """Rule 1: Command Casing."""
+
+    def test_1_1_builtin_lowercase(self):
+        """Rule 1.1: Built-in language commands are cased in lowercase."""
+        code = "SET(FOO bar)\nIF(FOO)\n  ADD_LIBRARY(lib STATIC)\nENDIF()\n"
+        expected = "set(FOO bar)\nif(FOO)\n  add_library(lib STATIC)\nendif()\n"
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+    def test_1_2_module_canonical_casing(self):
+        """Rule 1.2: Module commands retain canonical declared casing."""
+        code = "externalproject_add(\n  proj\n  PREFIX\n  dir\n)\n"
+        expected = "ExternalProject_Add(\n  proj\n  PREFIX\n  dir\n)\n"
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+    def test_1_3_custom_function_casing(self):
+        """Rule 1.3: Custom user functions/macros retain their original casing."""
+        code = "add_libc_unittest(\n  my_test\n  SRCS\n    my_test.cpp\n)\n"
+        expected = "add_libc_unittest(\n  my_test\n  SRCS\n    my_test.cpp\n)\n"
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+
+class TestRule2_ParenthesisSpacingAndWhitespace(unittest.TestCase):
+    """Rule 2: Parenthesis Spacing & Whitespace Collapsing."""
+
+    def test_2_1_zero_space_before_opening_paren(self):
+        """Rule 2.1: Zero space between command identifier and opening '('."""
+        code = "set  (FOO bar)\n"
+        expected = "set(FOO bar)\n"
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+    def test_2_2_collapse_arg_whitespace(self):
+        """Rule 2.2: Collapses multiple spaces between single-line arguments down to one."""
+        code = "add_entrypoint_object(target_name    DEPENDS   dep1    dep2)\n"
+        expected = "add_entrypoint_object(target_name DEPENDS dep1 dep2)\n"
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+
+class TestRule3_QuotedAndBracketImmutability(unittest.TestCase):
+    """Rule 3: Quoted String & Bracket Immutability."""
+
+    def test_3_1_quoted_and_bracket_tokens(self):
+        """Rule 3.1: Quoted arguments and bracket arguments are single immutable tokens."""
+        code = 'set(MSG "Hello \\"World\\"")\nset(CODE [=[raw string\n  content]=])\n'
+        expected = (
+            'set(MSG "Hello \\"World\\"")\nset(CODE [=[raw string\n  content]=])\n'
+        )
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+    def test_3_2_multiline_string_preservation(self):
+        """Rule 3.2: Multi-line quoted strings are preserved 100% untouched."""
+        code = 'set(DOC "\n  Line 1\n    Line 2\n")\n'
+        expected = 'set(DOC "\n  Line 1\n    Line 2\n")\n'
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+
+class TestRule4_EmptyClosures(unittest.TestCase):
+    """Rule 4: Empty Closures."""
+
+    def test_4_1_empty_closing_parens(self):
+        """Rule 4.1: Control block closures strip old condition arguments down to empty ()."""
+        code = "if(FOO)\n  set(BAR 1)\nelse(FOO)\n  set(BAR 0)\nendif(FOO)\n"
+        expected = "if(FOO)\n  set(BAR 1)\nelse()\n  set(BAR 0)\nendif()\n"
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+
+class TestRule5_SchemaAwareKeywordCasing(unittest.TestCase):
+    """Rule 5: Schema-Aware Keyword Casing."""
+
+    def test_5_1_keyword_uppercasing(self):
+        """Rule 5.1: Schema keywords are upper-cased."""
+        code = "add_entrypoint_object(\n  target\n  srcs\n    src.cpp\n  depends\n    dep\n)\n"
+        expected = "add_entrypoint_object(\n  target\n  SRCS\n    src.cpp\n  DEPENDS\n    dep\n)\n"
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+    def test_5_2_positional_args_untouched(self):
+        """Rule 5.2: Positional arguments, file paths, and target names are left untouched."""
+        code = "add_header_macro(\n  string\n  ../libc/include/string.yaml\n  string.h\n  DEPENDS\n    .llvm_libc_common_h\n)\n"
+        expected = "add_header_macro(\n  string\n  ../libc/include/string.yaml\n  string.h\n  DEPENDS\n    .llvm_libc_common_h\n)\n"
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+    def test_5_3_custom_function_positional_args_untouched(self):
+        """Rule 5.3: Positional output_var parameters in custom functions retain casing."""
+        code = '_get_common_compile_options(compile_options "${ADD_OBJECT_FLAGS}")\n'
+        expected = (
+            '_get_common_compile_options(compile_options "${ADD_OBJECT_FLAGS}")\n'
+        )
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+
+class TestRule6_MultilineArgumentLayout(unittest.TestCase):
+    """Rule 6: Multi-line Argument Layout & Keyword Schema Mechanics."""
+
+    def test_6_1_option_keyword_scope(self):
+        """Rule 6.1: Option keywords (0 values) close immediately."""
+        code = "add_library(\n  target\n  ALIAS\n    real_target\n)\n"
+        expected = "add_library(\n  target\n  ALIAS\n    real_target\n)\n"
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+    def test_6_2_single_value_keyword_scope(self):
+        """Rule 6.2: Single-value keywords consume 1 argument then close."""
+        code = "execute_process(\n  WORKING_DIRECTORY\n    /tmp\n  OUTPUT_VARIABLE\n    out\n)\n"
+        expected = "execute_process(\n  WORKING_DIRECTORY\n    /tmp\n  OUTPUT_VARIABLE\n    out\n)\n"
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+    def test_6_3a_multivalue_separate_line(self):
+        """Rule 6.3a: Multi-value keyword on new line indents items +4 spaces (+2 relative to keyword)."""
+        code = "add_entrypoint_object(\n  target\n  SRCS\n    s1.cpp\n    s2.cpp\n)\n"
+        expected = (
+            "add_entrypoint_object(\n  target\n  SRCS\n    s1.cpp\n    s2.cpp\n)\n"
+        )
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+    def test_6_3b_multivalue_header_line(self):
+        """Rule 6.3b: Multi-value keyword on header line indents items +2 spaces relative to call base."""
+        code = "add_entrypoint_object(target SRCS s1.cpp s2.cpp)\n"
+        expected = "add_entrypoint_object(target SRCS s1.cpp s2.cpp)\n"
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+    def test_6_4_argument_forwarding_vars(self):
+        """Rule 6.4: Argument forwarding variables (${ARGN}, ${ARGV}) align at +2 spaces and reset active keyword."""
+        code = "add_entrypoint_object(\n  target\n  DEPENDS\n    dep1\n  ${ARGN}\n)\n"
+        expected = (
+            "add_entrypoint_object(\n  target\n  DEPENDS\n    dep1\n  ${ARGN}\n)\n"
+        )
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+    def test_6_5_closing_paren_indent(self):
+        """Rule 6.5: Closing parenthesis ')' aligns at +0 spaces (call base)."""
+        code = "add_entrypoint_object(\n  target\n  SRCS\n    s1.cpp\n)\n"
+        expected = "add_entrypoint_object(\n  target\n  SRCS\n    s1.cpp\n)\n"
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+
+class TestRule7_ControlBlockIndentation(unittest.TestCase):
+    """Rule 7: Control Block Indentation."""
+
+    def test_7_1_nested_control_blocks(self):
+        """Rule 7.1: Nests 2 spaces per control block level."""
+        code = "if(A)\nif(B)\nset(C 1)\nendif()\nendif()\n"
+        expected = "if(A)\n  if(B)\n    set(C 1)\n  endif()\nendif()\n"
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+    def test_7_2_middle_blocks(self):
+        """Rule 7.2: else() and elseif() align at block base level."""
+        code = "if(A)\nset(X 1)\nelseif(B)\nset(X 2)\nelse()\nset(X 3)\nendif()\n"
+        expected = (
+            "if(A)\n  set(X 1)\nelseif(B)\n  set(X 2)\nelse()\n  set(X 3)\nendif()\n"
+        )
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+
+class TestRule8_CommentFormatting(unittest.TestCase):
+    """Rule 8: Comment Formatting & Lookahead Buffering."""
+
+    def test_8_1_lookahead_comment_buffering(self):
+        """Rule 8.1: Line comment aligns with the indentation of the upcoming code line."""
+        code = "add_entrypoint_object(\n  target\n  SRCS\n    # Comment for s1\n    s1.cpp\n)\n"
+        expected = "add_entrypoint_object(\n  target\n  SRCS\n    # Comment for s1\n    s1.cpp\n)\n"
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+    def test_8_2_standalone_comments_blank_line(self):
+        """Rule 8.2: Standalone comments followed by a blank line flush at current base indent."""
+        code = "# Standalone comment\n\nadd_library(target s1.cpp)\n"
+        expected = "# Standalone comment\n\nadd_library(target s1.cpp)\n"
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+    def test_8_3a_closing_paren_keyword_comment(self):
+        """Rule 8.3a: Comment following a keyword before closing ')' indents under keyword (+4 spaces).
+
+        In practice, UNIT_TEST_ONLY is learned as an option from cmake_parse_arguments in
+        LLVMLibCTestRules.cmake via pre_scan_workspace_modules. We supply an equivalent
+        inline definition so the test is self-contained.
+        """
+        # Inline definition matching the real LLVMLibCTestRules.cmake declaration.
+        schema_stub = """
+function(add_libc_test test_name)
+  cmake_parse_arguments(
+    "LIBC_TEST"
+    "UNIT_TEST_ONLY;HERMETIC_TEST_ONLY"
+    ""
+    ""
+    ${ARGN}
+  )
+endfunction()
+"""
+        ctx = cmake_format.FormatterContext()
+        cmake_format.scan_dynamic_schemas(schema_stub, ctx=ctx)
+        code = "add_libc_test(\n  hash_test\n  UNIT_TEST_ONLY\n    # Explanation for option\n)\n"
+        expected = "add_libc_test(\n  hash_test\n  UNIT_TEST_ONLY\n    # Explanation for option\n)\n"
+        self.assertEqual(cmake_format.format_cmake_content(code, ctx=ctx), expected)
+
+    def test_8_3b_closing_paren_list_comment(self):
+        """Rule 8.3b: Comment following a list item before closing ')' aligns at top-level call (+2 spaces)."""
+        code = "add_libc_test(\n  stdbit_test\n  DEPENDS\n    dep1\n  # Top level call comment\n)\n"
+        expected = "add_libc_test(\n  stdbit_test\n  DEPENDS\n    dep1\n  # Top level call comment\n)\n"
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+    def test_8_4_comment_space_hygiene(self):
+        """Rule 8.4: Missing space after '#' is added; special markers (#---, #!) preserved."""
+        code = "#no space\n#--- header ---\n#!shebang\n"
+        expected = "# no space\n#--- header ---\n#!shebang\n"
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+
+class TestRule9_CleanlinessAndFileHygiene(unittest.TestCase):
+    """Rule 9: Cleanliness & File Hygiene."""
+
+    def test_9_1_strip_trailing_whitespace(self):
+        """Rule 9.1: Trailing whitespace on all lines is stripped."""
+        code = "set(FOO bar)   \nset(BAR baz)  \n"
+        expected = "set(FOO bar)\nset(BAR baz)\n"
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+    def test_9_2_single_trailing_newline(self):
+        """Rule 9.2: Non-empty files end with exactly one trailing newline."""
+        code = "set(FOO bar)\n\n\n"
+        expected = "set(FOO bar)\n"
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+    def test_9_3_empty_file_preservation(self):
+        """Rule 9.3: Empty 0-byte files are preserved as 0-byte."""
+        self.assertEqual(cmake_format.format_cmake_content(""), "")
+        self.assertEqual(cmake_format.format_cmake_content("   \n\n  \t "), "")
+
+
+class TestDynamicSchemaLearning(unittest.TestCase):
+    """Dynamic Schema Learning Tests."""
+
+    def test_function_block_parser(self):
+        """Verifies cmake_parse_arguments extraction inside function/macro AST blocks."""
+        code = """
+function(custom_target_func target)
+  cmake_parse_arguments(
+    "FUNC"
+    "OPTION1;OPTION2"
+    "SINGLE1"
+    "MULTI1;MULTI2"
+    ${ARGN}
+  )
+endfunction()
+"""
+        cmake_format.scan_dynamic_schemas(code)
+        schema = cmake_format.get_schema_for_cmd("custom_target_func")
+        self.assertIn("OPTION1", schema.options)
+        self.assertIn("SINGLE1", schema.one_value)
+        self.assertIn("MULTI1", schema.multi_value)
+
+    def test_variable_list_scanner(self):
+        """Verifies dynamic learning from set(*_OPTION_ARGS), set(*_SINGLE_VALUE_ARGS), set(*_MULTI_VALUE_ARGS)."""
+        code = """
+set(CUSTOM_OPTION_ARGS OPT_A OPT_B)
+set(CUSTOM_SINGLE_VALUE_ARGS SING_A)
+set(CUSTOM_MULTI_VALUE_ARGS MULT_A MULT_B)
+"""
+        ctx = cmake_format.FormatterContext()
+        cmake_format.scan_dynamic_schemas(code, ctx=ctx)
+        self.assertIn("OPT_A", ctx.learned_options)
+        self.assertIn("SING_A", ctx.learned_one_value)
+        self.assertIn("MULT_A", ctx.learned_multi_value)
+
+    def test_cache_variable_exclusion(self):
+        """Verifies cache variable declarations like set(FOO "" CACHE STRING "...") are excluded."""
+        code = 'set(LIBC_COMPILE_OPTIONS_DEFAULT "" CACHE STRING "Docstring")\n'
+        ctx = cmake_format.FormatterContext()
+        cmake_format.scan_dynamic_schemas(code, ctx=ctx)
+        self.assertNotIn("STRING", ctx.learned_options)
+
+
+class TestCodebaseIdempotency(unittest.TestCase):
+    """Codebase Idempotency Test."""
+
+    def test_libc_codebase_idempotency(self):
+        """Verifies format(format(content)) == format(content) over libc/ files."""
+        libc_dir = os.path.join(SCRIPT_DIR, "..")
+        cmake_files = cmake_format.find_cmake_files([libc_dir])
+        self.assertGreater(len(cmake_files), 0, "No CMake files found under libc/")
+
+        cmake_format.pre_scan_workspace_modules([libc_dir])
+
+        for fpath in cmake_files:
+            with self.subTest(file=fpath):
+                with open(fpath, "r", encoding="utf-8") as f:
+                    original = f.read()
+                formatted = cmake_format.format_cmake_content(original)
+                reformatted = cmake_format.format_cmake_content(formatted)
+                self.assertEqual(
+                    formatted, reformatted, f"Formatting is not idempotent for {fpath}"
+                )
+
+
+class TestEdgeCasesAndIsolation(unittest.TestCase):
+    """Edge cases for parenthesis counting in comments/strings and state isolation."""
+
+    def test_parens_in_comments_and_strings(self):
+        """Verifies parentheses inside comments and strings do not corrupt paren_balance or indentation."""
+        code = (
+            "add_custom_target(\n"
+            "  mytarget\n"
+            "  # Comment with parentheses () and (extra)\n"
+            '  "file_with_(parens).cpp"\n'
+            "  DEPENDS\n"
+            "    dep1\n"
+            ")\n"
+        )
+        expected = (
+            "add_custom_target(\n"
+            "  mytarget\n"
+            "  # Comment with parentheses () and (extra)\n"
+            '  "file_with_(parens).cpp"\n'
+            "  DEPENDS\n"
+            "    dep1\n"
+            ")\n"
+        )
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+    def test_bracket_comment_with_equals(self):
+        """Verifies bracket comments with '=' delimiters (#[=[...]=]) are properly preserved."""
+        code = (
+            "#[=[\n"
+            "  Multi-line comment with equals\n"
+            "  set(FOO bar)\n"
+            "]=]\n"
+            "set(BAR baz)\n"
+        )
+        expected = (
+            "#[=[\n"
+            "  Multi-line comment with equals\n"
+            "  set(FOO bar)\n"
+            "]=]\n"
+            "set(BAR baz)\n"
+        )
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+    def test_unterminated_quoted_argument_raises_lexerror(self):
+        """Verifies unterminated quoted string arguments raise LexError."""
+        code_quote = 'set(FOO "unterminated string'
+        with self.assertRaises(cmake_format.LexError) as cm:
+            cmake_format.format_cmake_content(code_quote)
+        self.assertIn("unterminated quoted argument", str(cm.exception))
+
+    def test_unterminated_bracket_comment_raises_lexerror(self):
+        """Verifies unterminated bracket comments raise LexError."""
+        code_bc = "#[=[ unterminated bracket comment"
+        with self.assertRaises(cmake_format.LexError) as cm:
+            cmake_format.format_cmake_content(code_bc)
+        self.assertIn("unterminated bracket comment", str(cm.exception))
+
+    def test_unterminated_bracket_argument_raises_lexerror(self):
+        """Verifies unterminated bracket arguments raise LexError."""
+        code_ba = "set(FOO [=[unterminated bracket arg)"
+        with self.assertRaises(cmake_format.LexError) as cm:
+            cmake_format.format_cmake_content(code_ba)
+        self.assertIn("unterminated bracket argument", str(cm.exception))
+
+    def test_global_state_isolation(self):
+        """Verifies custom schemas learned in File A do not leak into formatting of File B when using isolated contexts."""
+        file_a = (
+            "set(MY_CUSTOM_OPTION_ARGS OPT_ISOLATED_A OPT_ISOLATED_B)\n"
+            "my_custom_cmd(OPT_ISOLATED_A valA OPT_ISOLATED_B valB)\n"
+        )
+        file_b = "my_other_cmd(opt_isolated_a vala opt_isolated_b valb)\n"
+        ctx_a = cmake_format.FormatterContext()
+        ctx_b = cmake_format.FormatterContext()
+        cmake_format.format_cmake_content(file_a, ctx=ctx_a)
+        result_b = cmake_format.format_cmake_content(file_b, ctx=ctx_b)
+        expected_b = "my_other_cmd(opt_isolated_a vala opt_isolated_b valb)\n"
+        self.assertEqual(result_b, expected_b)
+
+    def test_pre_scan_workspace_modules(self):
+        """Verifies pre_scan_workspace_modules discovers and scans modules in cmake/modules/."""
+        import tempfile
+
+        with tempfile.TemporaryDirectory() as tmpdir:
+            mod_dir = os.path.join(tmpdir, "cmake", "modules")
+            os.makedirs(mod_dir, exist_ok=True)
+            mod_file = os.path.join(mod_dir, "CustomModule.cmake")
+            with open(mod_file, "w", encoding="utf-8") as f:
+                f.write("set(MY_MOD_OPTION_ARGS MOD_OPT1 MOD_OPT2)\n")
+
+            ctx = cmake_format.FormatterContext()
+            cmake_format.pre_scan_workspace_modules([tmpdir], ctx=ctx)
+            self.assertIn("MOD_OPT1", ctx.learned_options)
+            self.assertIn("MOD_OPT2", ctx.learned_options)
+
+    def test_stdin_pipe_formatting(self):
+        """Verifies formatting unformatted CMake code via stdin."""
+        code = "add_library(\n foo\n   STATIC\n   s1.cpp\n )\n"
+        expected = "add_library(\n  foo\n  STATIC\n    s1.cpp\n)\n"
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+
+class TestParsingBugs(unittest.TestCase):
+    """Regression tests for parsing and formatting bugs."""
+
+    def test_bug_inline_comment_on_empty_closures(self):
+        """Empty closure commands (endif, else, endfunction, etc.) preserve inline comments."""
+        code = "if(FOO)\n  set(BAR 1)\nelse() # else comment\n  set(BAR 0)\nendif() # endif comment\n"
+        expected = "if(FOO)\n  set(BAR 1)\nelse() # else comment\n  set(BAR 0)\nendif() # endif comment\n"
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+    def test_bug_escaped_chars_in_unquoted_args(self):
+        """Unquoted args containing escaped characters (e.g. \\#, \\(, \\), \\", \\ ) are preserved as single tokens."""
+        code = "add_entrypoint_object(\n  target\n  SRCS\n    bar\\#baz\n    bar\\(baz\\)\n    C:\\Program\\ Files\\Foo\n)\n"
+        expected = "add_entrypoint_object(\n  target\n  SRCS\n    bar\\#baz\n    bar\\(baz\\)\n    C:\\Program\\ Files\\Foo\n)\n"
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+    def test_bug_continuation_line_starting_with_paren(self):
+        """Continuation lines starting with ')' when paren balance > 0 do not prematurely close call state."""
+        code = "if(\n  (A AND B)\n  ) AND (C AND D)\n)\n  set(X 1)\nendif()\n"
+        expected = "if(\n  (A AND B)\n  ) AND (C AND D)\n)\n  set(X 1)\nendif()\n"
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+    def test_bug_multiline_set_schema_learning(self):
+        """Multi-line set(*_MULTI_VALUE_ARGS ...) definitions are parsed correctly by schema scanner."""
+        code = "set(CUSTOM_MULTI_VALUE_ARGS\n  MULT_LINE1\n  MULT_LINE2\n)\n"
+        ctx = cmake_format.FormatterContext()
+        cmake_format.scan_dynamic_schemas(code, ctx=ctx)
+        self.assertIn("MULT_LINE1", ctx.learned_multi_value)
+        self.assertIn("MULT_LINE2", ctx.learned_multi_value)
+
+    def test_bug_cmake_parse_arguments_var_refs(self):
+        """Variable references like ${MY_OPTIONS} in cmake_parse_arguments are not registered as literal keywords."""
+        code = """
+function(my_func)
+  cmake_parse_arguments("ARG" "${MY_OPTIONS}" "${MY_ONE_VAL}" "${MY_MULTI_VAL}" ${ARGN})
+endfunction()
+"""
+        ctx = cmake_format.FormatterContext()
+        cmake_format.scan_dynamic_schemas(code, ctx=ctx)
+        schema = cmake_format.get_schema_for_cmd("my_func", ctx=ctx)
+        self.assertNotIn("MY_OPTIONS", schema.options)
+        self.assertNotIn("MY_ONE_VAL", schema.one_value)
+        self.assertNotIn("MY_MULTI_VAL", schema.multi_value)
+
+    def test_bug_directive_comment_preservation(self):
+        """Directives like #nolint, #pragma, #include, # cmake-lint, # cmake-format in comments are preserved without forced spaces."""
+        code = "#nolint\n#pragma once\n#include <foo>\n# cmake-lint: disable=syntax-error\n# cmake-format: off\n#cmake-lint: disable=syntax-error\n"
+        expected = "#nolint\n#pragma once\n#include <foo>\n# cmake-lint: disable=syntax-error\n# cmake-format: off\n#cmake-lint: disable=syntax-error\n"
+        self.assertEqual(cmake_format.format_cmake_content(code), expected)
+
+
+if __name__ == "__main__":
+    unittest.main()



More information about the libc-commits mailing list