docs: document clang-format 14 setup and add an opt-in pre-commit hook (#6741)

Recent distros and Homebrew ship only newer clang-format, so contributors
can't easily get the version CI uses (14, matching the OoT/MM decomp).

- docs/FORMATTING.md: how to get a 14.x binary (apt, AUR, muttleyxd static
  binaries, uvx/pipx wheel, brew) and how to run the formatter.
- run-clang-format.sh: default to clang-format-14 but honor $CLANG_FORMAT so
  any matching binary works. Any 14.x produces byte-identical output here.
- .pre-commit-config.yaml: opt-in hook to format staged C/C++ on commit.
- README: link the new doc.

The llvm@14 formula is available on Linux too, so filing it under a
macOS-only heading was too narrow.

* Support spaced paths in CLANG_FORMAT; document run-clang-format.ps1

eval the clang-format invocation so CLANG_FORMAT can carry arguments
(uvx clang-format@14) or a quoted path with spaces without one breaking
the other. The NUL-separated file list reaches xargs over the pipe, so
filenames never pass through eval.

Document the native Windows run-clang-format.ps1 path alongside the Git
Bash/WSL route.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
David Racine
2026-06-18 23:22:28 -04:00
committed by GitHub
parent 4ea17f6933
commit d912fd2c01
4 changed files with 89 additions and 2 deletions
+10
View File
@@ -0,0 +1,10 @@
# Local clang-format hook: formats staged C/C++ in soh/ on commit, using a
# pinned 14.x downloaded by pre-commit. Scope matches run-clang-format.sh / CI.
# pre-commit install --install-hooks # enable (downloads clang-format up front)
repos:
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v14.0.6
hooks:
- id: clang-format
files: '^soh/.*\.(c|cpp|h|hpp)$'
exclude: '^soh/assets/|^soh/(src|include)/.*\.(h|hpp)$'
+1
View File
@@ -104,6 +104,7 @@ More detailed documentation can be found in the 'docs' directory, including the
* [Credits](docs/CREDITS.md)
* [Custom Music](docs/CUSTOM_MUSIC.md)
* [Formatting](docs/FORMATTING.md)
* [Controller Mapping](docs/GAME_CONTROLLER_DB.md)
* [Modding](docs/MODDING.md)
* [Versioning](docs/VERSIONING.md)
+69
View File
@@ -0,0 +1,69 @@
# Formatting
Shipwright's C/C++ in `soh/` is formatted with **clang-format 14**, the version
used by the OoT/MM decompilation that the vendored `soh/src/` tree comes from.
clang-format's output is not stable across major versions, so the major version
matters: any **14.x** produces identical output for this tree (verified across
all formatted files), but clang-format 15+ will reformat differently and fail
CI. Patch version (14.0.0 vs 14.0.6) does not matter.
## Format the tree
```bash
./run-clang-format.sh
```
This formats every C/C++ file in `soh/` in place, skipping the decompiled
headers (`soh/src`, `soh/include`) and autogenerated assets (`soh/assets`),
matching what CI checks.
The script calls `clang-format-14` by default. If your clang-format 14 is named
or located differently, point `CLANG_FORMAT` at it:
```bash
CLANG_FORMAT=clang-format ./run-clang-format.sh # already 14.x
CLANG_FORMAT=/path/to/clang-format ./run-clang-format.sh # static binary
CLANG_FORMAT="uvx clang-format@14" ./run-clang-format.sh # uv wheel
CLANG_FORMAT='"/path with spaces/cf"' ./run-clang-format.sh # quote a spaced path
```
`CLANG_FORMAT` is treated as a command line, so it can carry arguments (the `uvx`
case) or a quoted path containing spaces.
On Windows you have two options. Run `run-clang-format.ps1` from PowerShell: it
downloads clang-format 14.0.6 itself (needs [7-Zip](https://www.7-zip.org/)
installed) and formats the same fileset, so you don't have to install
clang-format or pass `CLANG_FORMAT`. Or run `run-clang-format.sh` from Git Bash
(ships with Git for Windows) or WSL, since it needs a Unix shell for
`find`/`xargs`; get the binary from the `uvx` wheel or the Windows static binary
below.
## Getting clang-format 14
Recent distros and Homebrew often ship only newer clang-format. Any of these
gives you a 14.x binary:
- **Debian/Ubuntu**: `sudo apt-get install clang-format-14` (where the package
still exists).
- **Arch**: AUR [`clang-format-static-bin`](https://aur.archlinux.org/packages/clang-format-static-bin).
- **Any Linux/macOS/Windows, no install**: download a static binary from
[muttleyxd/clang-tools-static-binaries](https://github.com/muttleyxd/clang-tools-static-binaries/releases/tag/master-796e77c)
(e.g. `clang-format-14_linux-amd64`), `chmod +x`, and point `CLANG_FORMAT` at it.
- **Any OS via a Python wheel**: `uvx clang-format@14` (with
[uv](https://docs.astral.sh/uv/)), or `pipx install clang-format==14.0.6`.
- **Homebrew (macOS or Linux)**: `brew install llvm@14` and use its
`clang-format`, or use one of the cross-platform options above.
## Optional: format on commit
The repo ships a [pre-commit](https://pre-commit.com/) config
(`.pre-commit-config.yaml`) that auto-formats staged C/C++ with a pinned 14.x
before each commit, so you never get caught by the CI check. With pre-commit
[installed](https://pre-commit.com/#install), enable it once:
```bash
pre-commit install --install-hooks
```
`--install-hooks` downloads clang-format up front so your first commit isn't
slowed by it.
+9 -2
View File
@@ -1,3 +1,7 @@
# Default to clang-format-14; override CLANG_FORMAT to use another 14.x binary
# (distro pkg, muttleyxd static binary, uvx clang-format@14, ...). See docs/FORMATTING.md.
CLANG_FORMAT="${CLANG_FORMAT:-clang-format-14}"
# this line does quite a bit, so let's break it down
#
# find soh
@@ -21,9 +25,12 @@
# -print0
# separate paths with NUL bytes, avoiding issues with spaces in paths
#
# | xargs -0 clang-format-14 -i -verbose
# | eval "xargs -0 $CLANG_FORMAT -i --verbose"
# use xargs to take each path we've found
# and pass it as an argument to clang-format
# verbose to print files being formatted and X out of Y status
# eval so CLANG_FORMAT can carry arguments ("uvx clang-format@14")
# or a quoted path with spaces; the NUL-separated file list reaches
# xargs over the pipe, so it never passes through eval
find soh -type f \( -name "*.c" -o -name "*.cpp" -o \( \( -name "*.h" -o -name "*.hpp" \) ! -path "soh/src/*" ! -path "soh/include/*" \) \) ! -path "soh/assets/*" -print0 | xargs -0 clang-format-14 -i --verbose
find soh -type f \( -name "*.c" -o -name "*.cpp" -o \( \( -name "*.h" -o -name "*.hpp" \) ! -path "soh/src/*" ! -path "soh/include/*" \) \) ! -path "soh/assets/*" -print0 | eval "xargs -0 $CLANG_FORMAT -i --verbose"