diff --git a/clangify.sh b/clangify.sh index bad86ca42..1f58e93b8 100755 --- a/clangify.sh +++ b/clangify.sh @@ -1,22 +1,184 @@ #!/bin/bash # This script will run clang-format on all modified, non-3rd-party C++/Header files. +# Never, ever, should this recieve a path with a newline in it. Don't bother proofing it for that. -set -e -if hash clang-format 2>/dev/null && hash git 2>/dev/null; then - files_to_clean=($(git diff --name-only $(git merge-base origin/master HEAD))) +# use script dir and return on exit +current=$PWD +function cleanup { + cd $current +} +trap cleanup EXIT +cd "${BASH_SOURCE%/*}/" - printf "%s\n" ${files_to_clean[@]} | \ - xargs -I{} find '{}' \( -name "*.cpp" -o -name "*.h" \) \ - -not -path "./cockatrice/src/qt-json/*" \ - -not -path "./servatrice/src/smtp/*" \ - -not -path "./common/sfmt/*" \ - -not -path "./oracle/src/zip/*" \ - -not -path "./build*/*" \ - -exec clang-format -style=file -i {} \; - echo "Successfully formatted following files:" - printf "%s\n" ${files_to_clean[@]} -else - echo "Please install clang-format and git to use this program" +# defaults +include=("common" \ +"cockatrice/src" \ +"oracle/src" \ +"servatrice/src") +exclude=("cockatrice/src/qt-json" \ +"servatrice/src/smtp" \ +"common/sfmt" \ +"oracle/src/zip") +exts=("cpp" "h") +cf_cmd="clang-format" +branch="origin/master" + +# parse options +while [[ $@ ]]; do + case "$1" in + '-b'|'--branch') + branch=$2 + set_branch=1 + shift 2 + ;; + '-c'|'--color-diff') + color=" --color=always" + mode=diff + shift + ;; + '-d'|'--diff') + mode=diff + shift + ;; + '-h'|'--help') + cat <s are given, all source files in those directories are checked, recursively. + +USAGE: $0 [option] [-b[ranch] ] [ ...] + +DEFAULTS: +Default includes are: + ${include[@]/%/ + } +Default excludes are: + ${exclude[@]/%/ + } +OPTIONS: + -b, --branch + Compare to this git branch and format only files that differ. + If unspecified it defaults to origin/master. + To not compare to a branch this has to be explicitly set to "". + When not comparing to a branch git will not be used at all and all valid files will be parsed. + + -c, --color-diff + Display a colored diff. Implies --diff. + Only available on systems which support 'diff --color'. + + -d, --diff + Display a diff. Implies --test. + + -h, --help + Display this message and exit. + + -n, --names + Display a list of filenames that require formatting. Implies --test. + + -t, --test + Do not edit files in place. Set exit code to 1 if changes are required. + +EXIT CODES: + 0 on a successful format or if no files require formatting. + 1 if a file requires formatting. + 2 if given incorrect arguments. + 3 if clang-format could not be found. +EOM + exit 0 + ;; + '-n'|'--names') + mode=name + shift + ;; + '-t'|'--test') + mode=code + shift + ;; + '--') + shift + ;; + *) + include=() # empty includes + while [[ $@ ]]; do + if [[ -d $1 ]]; then + include+=("$1") + shift + else + echo "error in parsing arguments of $0: $1 is not a directory" >&2 + exit 2 # input error + fi + done + if ! [[ $set_branch ]]; then + unset branch # unset branch if not set explicitly + fi + break + ;; + esac +done + +# check availability of clang-format +if ! hash $cf_cmd 2>/dev/null; then + echo "could not find $cf_cmd" >&2 + # find any clang-format-x.x in /usr/bin + cf_cmd=$(find /usr/bin -regex '.*/clang-format-[0-9]+\.[0-9]+' -print -quit) + if [[ $cf_cmd ]]; then + echo "found $cf_cmd instead" >&2 + else + exit 3 # special exit code for missing dependency + fi fi + +if [[ $branch ]]; then + # get all dirty files through git + if ! base=$(git merge-base ${branch} HEAD); then + echo "could not find git merge base" >&2 + exit 2 # input error + fi + declare -a reg + for ex in ${exts[@]}; do + reg+=(${include[@]/%/.*\\.$ex\$}) + done + names=$(git diff --name-only $base | grep ${reg[@]/#/-e ^}) +else + names=$(find ${include[@]} -type f -false ${exts[@]/#/-o -name *\\.}) +fi + +# filter excludes +names=$(<<<"$names" grep -v ${exclude[@]/#/-e ^}) + +if ! [[ $names ]]; then + exit 0 # nothing to format means format is successful! +fi + +# format +case $mode in + diff) + declare -i code=0 + for name in ${names[@]}; do + if ! $cf_cmd "$name" | diff "$name" - -p $color; then + code=1 + fi + done + exit $code + ;; + name) + declare -i code=0 + for name in ${names[@]}; do + if ! $cf_cmd "$name" | diff "$name" - -q >/dev/null; then + echo "$name" + code=1 + fi + done + exit $code + ;; + code) + for name in ${names[@]}; do + $cf_cmd "$name" | diff "$name" - -q >/dev/null || exit 1 + done + ;; + *) + $cf_cmd -i $names + ;; +esac