#!/bin/bash
# Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
# taken from: https://raw.githubusercontent.com/andrewseidl/githook-clang-format/master/clang-format.hook
# - with just added check for cpp file & check for proper version of clang-format
# - with use of clang-format diff.py - for changed chunk changes only
# might be worth to consider: https://github.com/Sarcasm/run-clang-format
L_GIT_DIR=$(git rev-parse --show-toplevel)
source $L_GIT_DIR/config/format-config.sh
RED='\e[38;2;255;13;0m'
YELLOW="\e[38;2;255;232;0m"
GREEN="\e[38;2;0;222;27m"
ORANGE="\e[38;2;255;100;0m"
OK="${GREEN}OK!\e[0m"
ERROR="${RED}Error!\e[0m"
FIXED="${YELLOW}Fixed!\e[0m"
# if autoformatting was disabled by user - then ignore this commit hook
if [[ $DISABLE_AUTO_FORMATTING ]]; then
[[ $VERBOSE ]] && echo "auto formatting is disabled"
exit 0
fi
# check if clang-format in path is in proper version, version is 3rd column in `clang-format --version`
CVER=$( [[ $(which clang-format) ]] && echo $(clang-format --version | cut -d ' ' -f 3 | cut -d '.' -f 1) || echo "0")
# check for either clang-format or clang-format-9
if [[ $CVER -lt 10 && ! $(which clang-format-10) ]]; then
cat << EOF >&1
Either install:
clang-format in at least version 10 and set as default"
or
clang-format-10
Your clang format version in path used:
$(clang-format --version):
$(clang-format-10 --version)
git commit aborted"
EOF
exit 1
fi
get_clang_format_script() {
local declare searchpaths=(
$(which "clang-format-diff.py") # clang-format-diff in path
"/usr/share/clang/clang-format-10/clang-format-diff.py" # clang-format-diff location for clang format 9 Ubuntu/Debian 18.04
"/usr/share/clang/clang-format-${CVER}/clang-format-diff.py" # clang-format-diff location on Ubuntu/Debian
"/usr/share/clang/clang-format-diff.py" # clang-format_diff location on Arch last resort
)
local tool=""
for el in ${searchpaths[@]}; do
if [[ -f ${el} ]]; then
tool=${el}
break
fi
done
if [[ ${tool} == "" ]]; then
echo clang-format-diff not found in path and: ${sarchpaths[@]} >2
fi
echo "${tool}"
}
L_CLANG_DIFF_TOOL=$(get_clang_format_script)
if ! [[ $L_CLANG_DIFF_TOOL ]] || [[ $L_CLANG_DIFF_TOOL == "" ]]; then
exit 1
fi
check_file() {
file="${1}"
last_commit="${2}"
if [ -f $file ]; then
#[[ $VERBOSE ]] && echo -en "Checking: \e[33m${file}\e[0m :\t"
results["${file}"]="${OK}"
case ${last_commit} in
"True")
if [[ ${FIX,,} == "true" ]]; then
git diff -U0 --no-color remotes/origin/master...HEAD ${file} | ${L_CLANG_DIFF_TOOL} -style file -p1 -i
STATUS=$(git status --short -- ${file})
if [ -n "${STATUS}" ]; then
git add "${file}"
results["${file}"]="${FIXED}";
fi
else
OUT=$(git diff -U0 --no-color remotes/origin/master...HEAD ${file} | ${L_CLANG_DIFF_TOOL} -style file -p1 )
if [ -n "${OUT}" ]; then
results["${file}"]="${ERROR}"
return 1
fi
fi
;;
"Stage")
if [[ ${FIX,,} == "true" ]]; then
git diff -U0 --no-color --cached ${file} | ${L_CLANG_DIFF_TOOL} -style file -p1 -i
STATUS=$(git status --short -- ${file})
if [ -n "${STATUS}" ]; then
git add "${file}"
results["${file}"]="${FIXED}";
fi
else
OUT=$(git diff -U0 --no-color --cached ${file} | ${L_CLANG_DIFF_TOOL} -style file -p1)
if [ -n "${OUT}" ]; then
results["${file}"]="${ERROR}"
return 1
fi
fi
;;
*)
OUT=$(git diff -U0 --no-color --cached ${file} | ${L_CLANG_DIFF_TOOL} -style file -p1 )
if [ -n ${OUT} ]; then
results["${file}"]="${ERROR}"
return 1
fi
;;
esac
fi
return 0
}
# bash function using above config function
shouldnt_ignore() {
# change full name path to path relative to root git dir
local fname=${1/"$L_GIT_DIR"/"./"}
for el in ${ignore_paths[@]}; do
if [[ ${fname} =~ ^${el}.* ]]; then
[[ $VERBOSE ]] && echo "Ignore: ${fname} formatting due to: $el match!"
return 1
fi
done
return 0
}
function help() {
echo "Runs clang-format on source files"
cat <<- EOM
ussge:
$0 [option]
--about does nothing
--last checks current branch against origin/master - doesn't fix
--branch-fix checks current branch against origin/master and fixes errors - you have to 'git add' and 'git commit' them
--fix fix style in currently staged files (ignores user.fixinstage variale), this is usefull for manual style applying
If you would like to automatially fix style before commit
add to your git config "user.fixinstage" variable with value "True"
by calling:
git config user.fixinstage True
EOM
}
case "${1}" in
--about|--help )
help
FILES=''
LAST=""
exit 0
;;
--last)
FILES=$(git diff --name-only remotes/origin/master...HEAD)
LAST="True"
FIX="false"
;;
--branch-fix)
FILES=$(git diff --name-only remotes/origin/master...HEAD)
LAST="True"
FIX="true"
;;
--fix)
FILES=$(git diff-index --cached --name-only HEAD)
LAST="Stage"
FIX=true
;;
*)
if [[ $# -ne 0 ]]; then
echo "unknown parameters: '$@'"
help
exit 1
fi
FILES=$(git diff-index --cached --name-only HEAD)
LAST="Stage"
FIX=$(git config user.fixinstage)
FIX=${FIX:-false}
;;
esac
declare -A results
EXIT_CODE=0
for file in ${FILES}; do
if [[ ${file} =~ ^.*\.(cpp|hpp|c|h|cxx|gcc|cc)$ ]] && shouldnt_ignore ${file}; then
check_file "${file}" ${LAST}
RESULT=$?
if [[ ${RESULT} -eq 1 ]]; then
EXIT_CODE=1
fi
fi
done
for FILE in "${!results[@]}"
do
echo -e " ${FILE} : ${results[${FILE}]}"
done
echo "exit: ${EXIT_CODE}"
exit ${EXIT_CODE}