#!/bin/bash
#
# Abort a commit if the code style is incorrect.
#
DENYLIST=tools/uncrustify-denylist
UNCRUSTIFY="$(xcrun -f uncrustify)"
if git rev-parse --verify HEAD >/dev/null 2>&1 ; then
printf >&2 "Validating code style diff against previous commit...\n"
against=HEAD
else
# Initial commit: diff against an empty tree object
printf >&2 "Validating code style diff for entire source tree...\n"
against=$(git hash-object -t tree /dev/null)
fi
diff_with_stdin()
{
if which colordiff >/dev/null 2>&1; then
diff -u "$1" - | colordiff
else
diff -u "$1" -
fi
}
# Keep track of offending files
staged_paths_with_format_errors=()
# Note that we exclude staged deletions via --diff-filter
for path in $(git diff --staged --name-only --diff-filter="d" $against); do
# Parse our deny-list to find what to skip
while IFS= read -r deny_path; do
# Skip empty lines and comments
if [[ -z "$deny_path" || "$deny_path" == \#* ]]; then
continue
fi
# (Prepend ./ to the path in question to match the format used in the denylist)
# Note that excluded directories must specify a trailing slash (or the latter string here needs tweaking)
if [[ "./$path" == "$deny_path" || "./$path" == "$deny_path"* ]]; then
# (Continue outer loop of files to be committed)
continue 2
fi
done < "$DENYLIST"
# Skip non-C/++ files
case "$path" in
*.c|*.h|*.cpp)
;;
*)
continue
;;
esac
printf >&2 "Validating code style for $path: "
if "$UNCRUSTIFY" -q -c tools/xnu-uncrustify.cfg --check -f "$path" >/dev/null 2>&1; then
printf >&2 "\e[1;32mok\e[0m.\n"
else
printf >&2 "\e[1;31minvalid style\e[0m.\n"
"$UNCRUSTIFY" -q -c tools/xnu-uncrustify.cfg -f "$path" | diff_with_stdin "$path"
staged_paths_with_format_errors+=($path)
fi
done
if [ ${#staged_paths_with_format_errors[@]} -ne 0 ]; then
path_list="${staged_paths_with_format_errors[*]}"
printf >&2 "\e[1;31mSome files have invalid code style, aborting commit. To reformat:\n"
printf >&2 "$ $UNCRUSTIFY -q -c tools/xnu-uncrustify.cfg --replace --no-backup $path_list\e[0m\n"
exit 1
fi
exit 0