diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000000..634ba48ec9 --- /dev/null +++ b/.pre-commit-config.yaml @@ -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)$' diff --git a/README.md b/README.md index 50982be53a..3ac29e465f 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/docs/FORMATTING.md b/docs/FORMATTING.md new file mode 100644 index 0000000000..2208c3b3d9 --- /dev/null +++ b/docs/FORMATTING.md @@ -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. diff --git a/run-clang-format.sh b/run-clang-format.sh index 20129e63d2..25158178bb 100755 --- a/run-clang-format.sh +++ b/run-clang-format.sh @@ -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"