Replace pre-commit hook with CMake-generated clang-format check (#40136)

* Replace pre-commit hook with CMake-generated clang-format check Replace the old pre-commit hook that shelled out to PowerShell and never blocked commits (-NoFail) with a CMake-generated hook that calls clang-format directly on staged C/C++ files. - Add tools/hooks/pre-commit.in as a CMake template - CMake resolves the clang-format path at configure time via LLVM_INSTALL_DIR, matching the existing FormatSource.ps1.in pattern - Hook blocks commits on formatting errors, skips gracefully if clang-format is not available (cmake not yet run) - ~5x faster than the old PowerShell approach (~0.5s vs ~2.6s) * Make pre-commit hook behavior configurable via WSL_PRE_COMMIT_MODE Add WSL_PRE_COMMIT_MODE CMake cache variable with three modes: - warn (default): report formatting issues without blocking commit - error: block commit when formatting issues are found - fix: auto-format files and re-stage them Also addresses PR feedback: - Generate hook into build tree, copy to source tree for out-of-source builds - Use repo-local tools/clang-format.exe instead of LLVM_INSTALL_DIR path - Use @ONLY in configure_file to avoid shell variable substitution issues - Document modes in dev-loop.md and UserConfig.cmake.sample Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Ben Hillis committed Apr 9, 2026 at 13:31 UTC b7735bba18736efe8b5b447e3f0d97df6cae1b22
6 files changed +82 -10
.gitignore
+2
@@ -62,6 +62,8 @@ msixinstaller/x64
62 package/x64
63 /appx-logs.txt
64 tools/clang-format.exe
65 +tools/hooks/pre-commit
66 +!/tools/hooks/pre-commit.in
67 /linux-crashes
68 doc/site/
69 directory.build.targets
CMakeLists.txt
+8
@@ -326,9 +326,17 @@ endif()
326 # Generate the clang-format script which contains a path to clang-format.exe
327 configure_file(${CMAKE_CURRENT_LIST_DIR}/tools/FormatSource.ps1.in ${CMAKE_BINARY_DIR}/FormatSource.ps1)
328
329 +# Pre-commit hook mode: warn (default), error, or fix
330 +set(WSL_PRE_COMMIT_MODE "warn" CACHE STRING "Pre-commit hook behavior: warn, error, or fix")
331 +
332 +# Generate the pre-commit hook into the build tree
333 +file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/tools/hooks)
334 +configure_file(${CMAKE_CURRENT_LIST_DIR}/tools/hooks/pre-commit.in ${CMAKE_BINARY_DIR}/tools/hooks/pre-commit @ONLY)
335 +
336 cmake_path(COMPARE "${wsl_SOURCE_DIR}" EQUAL "${wsl_BINARY_DIR}" BUILD_IN_SOURCE)
337 if (NOT ${BUILD_IN_SOURCE}) # Testing on 3.26 project_type_DIR paths appear canonicalized
338 file(CREATE_LINK ${LLVM_INSTALL_DIR}/clang-format.exe ${wsl_SOURCE_DIR}/tools/clang-format.exe COPY_ON_ERROR)
339 + file(COPY_FILE ${CMAKE_BINARY_DIR}/tools/hooks/pre-commit ${wsl_SOURCE_DIR}/tools/hooks/pre-commit ONLY_IF_DIFFERENT)
340 endif()
341
342 set(LINUXSDK_PATH ${LINUXSDK_SOURCE_DIR}/${LLVM_ARCH})
UserConfig.cmake.sample
+7 -1
@@ -43,4 +43,10 @@ endif()
43 # set(WSL_POST_BUILD_COMMAND "powershell;-ExecutionPolicy;Bypass;-NoProfile;-NonInteractive;./tools/deploy/deploy-to-host.ps1")
44
45 # # Uncomment to reduce the verbosity of the appx package build
46 -# set(WSL_SILENT_APPX_BUILD true)
\ No newline at end of file
46 +# set(WSL_SILENT_APPX_BUILD true)
47 +
48 +# # Uncomment to change the pre-commit hook behavior (default: warn)
49 +# # warn - report formatting issues without blocking the commit
50 +# # error - block the commit when formatting issues are found
51 +# # fix - automatically fix formatting and re-stage files
52 +# set(WSL_PRE_COMMIT_MODE "warn")
doc/docs/dev-loop.md
+8 -1
@@ -127,4 +127,11 @@ Also see:
127 Every pull request needs to be clang-formatted before it can be merged.
128
129 The code can be manually formatted by running: `powershell .\FormatSource.ps1 -ModifiedOnly $false`.
130 -To automatically check formatting when creating a commit, run: `tools\SetupClangFormat.bat`
130 +
131 +To automatically check formatting before each commit, run CMake configure (e.g. `cmake .`) and then: `tools\SetupClangFormat.bat`
132 +
133 +The pre-commit hook behavior can be configured by setting `WSL_PRE_COMMIT_MODE` in `UserConfig.cmake`:
134 +
135 +- `warn` (default) – report formatting issues without blocking the commit
136 +- `error` – block the commit when formatting issues are found
137 +- `fix` – automatically fix formatting and re-stage files
tools/hooks/pre-commit deleted
-8
@@ -1,8 +0,0 @@
1 -#!/bin/sh
2 -
3 -# run only on Windows
4 -if [[ "$OSTYPE" == "msys" ]]; then
5 - exec powershell.exe -NoProfile -ExecutionPolicy Bypass -Command ".\FormatSource.ps1" -Staged '$true' -Verify '$true' -NoFail '$true'
6 -fi
7 -
8 -exit 0
\ No newline at end of file
tools/hooks/pre-commit.in new
+57
@@ -0,0 +1,57 @@
1 +#!/bin/sh
2 +# Copyright (c) Microsoft Corporation. All rights reserved.
3 +# Licensed under the MIT License. See LICENSE in the project root for license information.
4 +#
5 +# Pre-commit hook: validates clang-format on staged C/C++ files.
6 +# Generated by CMake from tools/hooks/pre-commit.in
7 +# Install with: tools\SetupClangFormat.bat
8 +#
9 +# Mode: @WSL_PRE_COMMIT_MODE@ (set WSL_PRE_COMMIT_MODE in UserConfig.cmake)
10 +# warn - report formatting issues without blocking the commit (default)
11 +# error - block the commit when formatting issues are found
12 +# fix - automatically fix formatting and re-stage files
13 +
14 +MODE="@WSL_PRE_COMMIT_MODE@"
15 +REPO_ROOT="$(git rev-parse --show-toplevel)"
16 +CLANG_FORMAT="$REPO_ROOT/tools/clang-format.exe"
17 +
18 +# --- Collect staged C/C++ source files ---
19 +STAGED=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(c|cpp|cxx|h|hpp|hxx)$')
20 +if [ -z "$STAGED" ]; then
21 + exit 0
22 +fi
23 +
24 +# --- Verify clang-format is available ---
25 +if [ ! -f "$CLANG_FORMAT" ]; then
26 + echo "[pre-commit] clang-format not found, skipping. Run 'cmake .' to set up."
27 + exit 0
28 +fi
29 +
30 +# --- fix mode: format in-place and re-stage ---
31 +if [ "$MODE" = "fix" ]; then
32 + echo "[pre-commit] Fixing formatting..."
33 + echo "$STAGED" | while IFS= read -r file; do
34 + "$CLANG_FORMAT" --style=file -i "$REPO_ROOT/$file"
35 + git add "$REPO_ROOT/$file"
36 + done
37 + echo "[pre-commit] Formatting applied and files re-staged."
38 + exit 0
39 +fi
40 +
41 +# --- warn / error mode: check formatting ---
42 +echo "[pre-commit] Checking formatting (mode: $MODE)..."
43 +FILES=$(echo "$STAGED" | sed "s|^|$REPO_ROOT/|")
44 +echo "$FILES" | xargs "$CLANG_FORMAT" --style=file -Werror --dry-run 2>&1
45 +RESULT=$?
46 +
47 +if [ $RESULT -ne 0 ]; then
48 + echo ""
49 + echo "[pre-commit] Formatting errors found. Run to fix:"
50 + echo " powershell FormatSource.ps1 -Staged \$true"
51 + if [ "$MODE" = "error" ]; then
52 + exit 1
53 + fi
54 + echo "[pre-commit] Continuing commit (mode: warn)."
55 +else
56 + echo "[pre-commit] All checks passed."
57 +fi