[libc-commits] [libc] f72b2a3 - [libc][CI] Add CMake format check to code-format-helper (#228526)
via libc-commits
libc-commits at lists.llvm.org
Fri Oct 9 00:20:07 PDT 2026
Author: Jeff Bailey
Date: 2026-10-09T08:19:57+01:00
New Revision: f72b2a332cdc62c87a90cf4d578872f445d2bab0
URL: https://github.com/llvm/llvm-project/commit/f72b2a332cdc62c87a90cf4d578872f445d2bab0
DIFF: https://github.com/llvm/llvm-project/commit/f72b2a332cdc62c87a90cf4d578872f445d2bab0.diff
LOG: [libc][CI] Add CMake format check to code-format-helper (#228526)
Added CMakeFormatHelper to llvm/utils/git/code-format-helper.py so the
existing GitHub Actions code formatting workflow checks modified CMake
files under libc/ using libc/utils/cmake_format.py.
When formatting issues are found, both the GitHub Actions log and the
automated pull request comment display the exact command to fix the
files in-place alongside the command to preview the diff locally.
Updated libc/utils/cmake_format.py so --dry-run --diff emits only the
unified diff on stdout and exits non-zero on lexer or file errors.
Assisted-by: Automated tooling, human reviewed.
Added:
Modified:
libc/utils/cmake_format.py
libc/utils/cmake_format_test.py
llvm/utils/git/code-format-helper.py
Removed:
################################################################################
diff --git a/libc/utils/cmake_format.py b/libc/utils/cmake_format.py
index 9087bc40ce4875..bea148b3586a15 100755
--- a/libc/utils/cmake_format.py
+++ b/libc/utils/cmake_format.py
@@ -1392,7 +1392,7 @@ def process_file(
pass
print(f"Error writing file {filepath}: {e}", file=sys.stderr)
return None
- elif dry_run:
+ elif dry_run and not show_
diff :
print(f"Formatting needed: {filepath}")
return has_changes
@@ -1496,6 +1496,7 @@ def main() -> None:
pre_scan_workspace_modules(args.paths)
files_needing_format = 0
+ had_errors = False
if args.jobs > 1 and len(cmake_files) > 1:
from concurrent.futures import ProcessPoolExecutor, as_completed
@@ -1520,8 +1521,11 @@ def main() -> None:
result = future.result()
if result is True:
files_needing_format += 1
+ elif result is None:
+ had_errors = True
except Exception as e:
print(f"Error processing {fpath}: {e}", file=sys.stderr)
+ had_errors = True
else:
for fpath in cmake_files:
changed = process_file(
@@ -1529,9 +1533,16 @@ def main() -> None:
)
if changed is True:
files_needing_format += 1
+ elif changed is None:
+ had_errors = True
+
+ if had_errors:
+ sys.exit(1)
if args.dry_run and files_needing_format > 0:
- print(f"\n{files_needing_format} file(s) need formatting.")
+ if not args.
diff :
+ print(f"\n{files_needing_format} file(s) need formatting.")
+ print("Run with -i / --inplace to format files in-place.")
sys.exit(1)
sys.exit(0)
diff --git a/libc/utils/cmake_format_test.py b/libc/utils/cmake_format_test.py
index b699f1e5da9b2e..4e181a43b186b8 100644
--- a/libc/utils/cmake_format_test.py
+++ b/libc/utils/cmake_format_test.py
@@ -545,6 +545,61 @@ def test_stdout_write_keeps_lf_endings(self):
)
self.assertEqual(proc.stdout, expected)
+ def test_dry_run_
diff _outputs_pure_
diff (self):
+ """--dry-run --
diff exits 1 and outputs only the unified
diff ."""
+ import subprocess
+ import tempfile
+
+ code = "add_library(foo STATIC\n a.c\n)\n"
+
+ with tempfile.TemporaryDirectory() as tmpdir:
+ path = os.path.join(tmpdir, "CMakeLists.txt")
+ with open(path, "w", encoding="utf-8", newline="\n") as f:
+ f.write(code)
+
+ proc = subprocess.run(
+ [
+ sys.executable,
+ os.path.join(SCRIPT_DIR, "cmake_format.py"),
+ "--dry-run",
+ "--
diff ",
+ path,
+ ],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ text=True,
+ )
+ self.assertEqual(proc.returncode, 1)
+ self.assertTrue(proc.stdout.startswith("--- a/"))
+ self.assertNotIn("Formatting needed:", proc.stdout)
+ self.assertNotIn("file(s) need formatting.", proc.stdout)
+
+ def test_parse_error_exits_nonzero(self):
+ """A file with a CMake syntax error causes the CLI to exit with code 1."""
+ import subprocess
+ import tempfile
+
+ with tempfile.TemporaryDirectory() as tmpdir:
+ path = os.path.join(tmpdir, "CMakeLists.txt")
+ with open(path, "w", encoding="utf-8", newline="\n") as f:
+ f.write('set(FOO "unterminated)\n')
+
+ proc = subprocess.run(
+ [
+ sys.executable,
+ os.path.join(SCRIPT_DIR, "cmake_format.py"),
+ "--dry-run",
+ "--
diff ",
+ path,
+ ],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ text=True,
+ )
+ self.assertEqual(proc.returncode, 1)
+ self.assertEqual(proc.stdout, "")
+ self.assertIn("Error parsing file", proc.stderr)
+
if __name__ == "__main__":
unittest.main()
diff --git a/llvm/utils/git/code-format-helper.py b/llvm/utils/git/code-format-helper.py
index 40d6709682d2cd..e7a82b8766d7f9 100755
--- a/llvm/utils/git/code-format-helper.py
+++ b/llvm/utils/git/code-format-helper.py
@@ -22,8 +22,8 @@
check the coding style before submitting it. The canonical source of this script
is in the LLVM source tree under llvm/utils/git.
-For C/C++ code it uses clang-format and for Python code it uses darker (which
-in turn invokes black).
+For C/C++ code it uses clang-format, for Python code it uses darker (which
+in turn invokes black), and for LLVM-libc CMake files it uses cmake_format.py.
You can learn more about the LLVM coding style on llvm.org:
https://llvm.org/docs/CodingStandards.html
@@ -33,8 +33,9 @@
ln -s $(pwd)/llvm/utils/git/code-format-helper.py .git/hooks/pre-commit
-You can control the exact path to clang-format or darker with the following
-environment variables: $CLANG_FORMAT_PATH and $DARKER_FORMAT_PATH.
+You can control the exact path to clang-format, darker, or cmake_format.py with
+the following environment variables: $CLANG_FORMAT_PATH, $DARKER_FORMAT_PATH,
+and $CMAKE_FORMAT_PATH.
"""
@@ -466,7 +467,123 @@ def format_run(self, changed_files: List[str], args: FormatArgs) -> Optional[str
return report
-ALL_FORMATTERS = (DarkerFormatHelper(), ClangFormatHelper(), UndefGetFormatHelper())
+class CMakeFormatHelper(FormatHelper):
+ name = "cmake-format"
+ friendly_name = "CMake code formatter"
+
+ def _construct_command(self) -> List[str]:
+ return [
+ sys.executable,
+ self.cmake_fmt_path,
+ "--dry-run",
+ "--
diff ",
+ ] + self._cmake_files
+
+ @property
+ def instructions(self) -> str:
+ return " ".join(["python3", self.cmake_fmt_path, "-i"] + self._cmake_files)
+
+ @property
+ def check_instructions(self) -> str:
+ return " ".join(
+ ["python3", self.cmake_fmt_path, "--dry-run", "--
diff "] + self._cmake_files
+ )
+
+ def pr_comment_text_for_
diff (self,
diff : str) -> str:
+ return f"""
+:warning: {self.friendly_name}, {self.name} found issues in your code. :warning:
+
+<details>
+<summary>
+You can fix this locally with the following command:
+</summary>
+
+``````````bash
+{self.instructions}
+``````````
+
+You can also preview the formatting
diff without modifying files:
+
+``````````bash
+{self.check_instructions}
+``````````
+
+</details>
+
+<details>
+<summary>
+View the
diff from {self.name} here.
+</summary>
+
+``````````
diff
+{
diff }
+``````````
+
+</details>
+"""
+
+ def filter_changed_files(self, changed_files: List[str]) -> List[str]:
+ filtered_files = []
+ for path in changed_files:
+ if not path.startswith("libc/"):
+ continue
+ if os.path.basename(path) == "CMakeLists.txt" or path.endswith(".cmake"):
+ if os.path.exists(path):
+ filtered_files.append(path)
+ return filtered_files
+
+ @property
+ def cmake_fmt_path(self) -> str:
+ if "CMAKE_FORMAT_PATH" in os.environ:
+ return os.environ["CMAKE_FORMAT_PATH"]
+ return "libc/utils/cmake_format.py"
+
+ def has_tool(self) -> bool:
+ return os.path.isfile(self.cmake_fmt_path)
+
+ def format_run(self, changed_files: List[str], args: FormatArgs) -> Optional[str]:
+ cmake_files = self.filter_changed_files(changed_files)
+ if not cmake_files:
+ return None
+ self._cmake_files = cmake_files
+ cmake_cmd = self._construct_command()
+ if args.verbose:
+ print(f"Running: {' '.join(cmake_cmd)}")
+ proc = subprocess.run(cmake_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ if args.verbose:
+ sys.stdout.write(proc.stderr.decode("utf-8"))
+
+ if proc.returncode != 0:
+ # formatting needed, or the command otherwise failed
+ stdout = proc.stdout.decode("utf-8")
+ if args.verbose:
+ print(f"error: {self.name} exited with code {proc.returncode}")
+ if stdout:
+ print(
+ "error: CMake files are not formatted according to "
+ "LLVM-libc style."
+ )
+ print(f"To fix this locally, run:\n {self.instructions}")
+ print(
+ "To preview the formatting
diff locally, run:\n"
+ f" {self.check_instructions}"
+ )
+ # Print the
diff in the log so that it is viewable there
+ print(stdout)
+ elif stdout:
+ print(f"To fix CMake formatting, run:\n {self.instructions}")
+ return stdout
+ else:
+ sys.stdout.write(proc.stdout.decode("utf-8"))
+ return None
+
+
+ALL_FORMATTERS = (
+ DarkerFormatHelper(),
+ ClangFormatHelper(),
+ CMakeFormatHelper(),
+ UndefGetFormatHelper(),
+)
def hook_main():
More information about the libc-commits
mailing list