-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Alan Jowett <[email protected]> Co-authored-by: Dave Thaler <[email protected]>
- Loading branch information
1 parent
3f524f4
commit 076ae86
Showing
3 changed files
with
645 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,304 @@ | ||
#!/usr/bin/env bash | ||
|
||
# Copyright (c) Prevail Verifier contributors. | ||
# SPDX-License-Identifier: MIT | ||
|
||
##============================================================================== | ||
## | ||
## Echo if verbose flag (ignores quiet flag) | ||
## | ||
##============================================================================== | ||
log_verbose() | ||
{ | ||
if [[ ${verbose} -eq 1 ]]; then | ||
echo "$1" | ||
fi | ||
} | ||
|
||
##============================================================================== | ||
## | ||
## Echo if whatif flag is specified but not quiet flag | ||
## | ||
##============================================================================== | ||
log_whatif() | ||
{ | ||
if [[ ${whatif} -eq 1 && ${quiet} -ne 1 ]]; then | ||
echo "$1" | ||
fi | ||
} | ||
|
||
##============================================================================== | ||
## | ||
## Process command-line options: | ||
## | ||
##============================================================================== | ||
|
||
for opt in "$@" | ||
do | ||
case $opt in | ||
-h | --help) | ||
usage=1 | ||
;; | ||
|
||
-v | --verbose) | ||
verbose=1 | ||
;; | ||
|
||
-q | --quiet) | ||
quiet=1 | ||
;; | ||
|
||
-w | --whatif) | ||
whatif=1 | ||
;; | ||
|
||
-s | --staged) | ||
# Split into array | ||
mapfile -t userFiles <<< "$(git diff --cached --name-only --diff-filter=ACMR)" | ||
;; | ||
|
||
--exclude-dirs=*) | ||
userExcludeDirs="${opt#*=}" | ||
;; | ||
|
||
--include-exts=*) | ||
userIncludeExts="${opt#*=}" | ||
;; | ||
|
||
--files=*) | ||
read -ra userFiles <<< "${opt#*=}" | ||
;; | ||
*) | ||
echo "$0: unknown option: $opt" | ||
exit 1 | ||
;; | ||
esac | ||
done | ||
|
||
##============================================================================== | ||
## | ||
## Display help | ||
## | ||
##============================================================================== | ||
|
||
if [[ ${usage} -eq 1 ]]; then | ||
cat<<EOF | ||
OVERVIEW: | ||
Formats all C/C++ source files based on the .clang-format rules | ||
$ format-code [-h] [-q] [-s] [-v] [-w] [--exclude-dirs="..."] [--include-exts="..."] [--files="..."] | ||
OPTIONS: | ||
-h, --help Print this help message. | ||
-q, --quiet Display only clang-format output and errors. | ||
-s, --staged Only format files which are staged to be committed. | ||
-v, --verbose Display verbose output. | ||
-w, --whatif Run the script without actually modifying the files | ||
and display the diff of expected changes, if any. | ||
--exclude-dirs Subdirectories to exclude. If unspecified, then | ||
./external, ./packages and ./x64 are excluded. | ||
All subdirectories are relative to the current path. | ||
--include-exts File extensions to include for formatting. If | ||
unspecified, then *.h, *.hpp, *.c, *.cpp, *idl, and | ||
*.acf are included. | ||
--files Only run the script against the specified files from | ||
the current directory. | ||
EXAMPLES: | ||
To determine what lines of each file in the default configuration would be | ||
modified by format-code, you can run from the root folder: | ||
$ ./scripts/format-code -w | ||
To update only all .c and .cpp files in src/ except for src/tools/netsh, you | ||
can run from the src folder: | ||
src$ ../scripts/format-code --exclude-dirs="tools/netsh" \ | ||
--include-exts="c cpp" | ||
To run only against a specified set of comma separated files in the current directory: | ||
$ ./scripts/format-code -w --files="file1 file2" | ||
EOF | ||
exit 0 | ||
fi | ||
|
||
##============================================================================== | ||
## | ||
## Check for acceptable version of clang-format | ||
## | ||
##============================================================================== | ||
check_version() | ||
{ | ||
# shellcheck disable=SC2206 | ||
v1=(${1//./ }) | ||
# shellcheck disable=SC2206 | ||
v2=(${2//./ }) | ||
if [[ ${#v1[@]} -ne ${#v2[@]} ]]; then | ||
echo "Cannot parse clang-format version number: $2" | ||
exit 1 | ||
fi | ||
for ((i=0; i<${#v1[@]}; i++)); | ||
do | ||
# Because clang-format is not invariant across versions, this | ||
# is an explicit equivalence check. | ||
if [[ ${v1[$i]} -ne ${v2[$i]} ]]; then | ||
echo "Warning: format-code prefers clang-format version $1, installed version is $2" | ||
fi | ||
done | ||
} | ||
|
||
##============================================================================== | ||
## | ||
## Check for installed clang-format tool | ||
## | ||
##============================================================================== | ||
check_clang-format() | ||
{ | ||
# First check explicitly for the clang-format-7 executable. | ||
cf=$(command -v clang-format-7 2> /dev/null) | ||
|
||
if [[ -x ${cf} ]]; then | ||
cf="clang-format-7" | ||
return | ||
else | ||
cf=$(command -v clang-format 2> /dev/null) | ||
if [[ ! -x ${cf} ]]; then | ||
echo "clang-format is not installed" | ||
exit 1 | ||
fi | ||
cf="clang-format" | ||
fi | ||
|
||
local required_cfver='17.0.3' | ||
# shellcheck disable=SC2155 | ||
local cfver=$(${cf} --version | grep -o -E '[0-9]+\.[0-9]+\.[0-9]+' | head -1) | ||
check_version "${required_cfver}" "${cfver}" | ||
} | ||
|
||
check_clang-format | ||
|
||
##============================================================================== | ||
## | ||
## Determine parameters for finding files to format | ||
## | ||
##============================================================================== | ||
|
||
findargs='find .' | ||
|
||
get_find_args() | ||
{ | ||
local defaultExcludeDirs='./.git ./external ./packages ./x64' | ||
local defaultIncludeExts='h hpp c cpp idl acf' | ||
|
||
if [[ -z ${userExcludeDirs} ]]; then | ||
read -r -a excludeDirs <<< "${defaultExcludeDirs}" | ||
else | ||
log_verbose "Using user directory exclusions: ${userExcludeDirs}" | ||
read -r -a excludeDirs <<< "${userExcludeDirs}" | ||
fi | ||
|
||
for exc in "${excludeDirs[@]}" | ||
do | ||
findargs="${findargs} ! \( -path '${exc}' -prune \)" | ||
done | ||
|
||
if [[ -z ${userIncludeExts} ]]; then | ||
# not local as this is used in get_file_list() too | ||
read -r -a includeExts <<< "${defaultIncludeExts}" | ||
else | ||
log_verbose "Using user extension inclusions: ${userIncludeExts}" | ||
read -r -a includeExts <<< "${userIncludeExts}" | ||
fi | ||
|
||
findargs="${findargs} \(" | ||
for ((i=0; i<${#includeExts[@]}; i++)); | ||
do | ||
findargs="${findargs} -iname '*.${includeExts[$i]}'" | ||
if [[ $((i + 1)) -lt ${#includeExts[@]} ]]; then | ||
findargs="${findargs} -o" | ||
fi | ||
done | ||
findargs="${findargs} \)" | ||
} | ||
|
||
get_find_args | ||
|
||
log_verbose "Query for files for format:" | ||
log_verbose "${findargs}" | ||
log_verbose "" | ||
|
||
##============================================================================== | ||
## | ||
## Call clang-format for each file to be formatted | ||
## | ||
##============================================================================== | ||
|
||
filecount=0 | ||
changecount=0 | ||
|
||
cfargs="${cf} -style=file" | ||
if [[ ${whatif} -ne 1 ]]; then | ||
cfargs="${cfargs} -i" | ||
fi | ||
|
||
get_file_list() | ||
{ | ||
if [[ -z "${userFiles[*]}" ]]; then | ||
mapfile -t file_list < <(eval "$findargs") | ||
if [[ ${#file_list[@]} -eq 0 ]]; then | ||
echo "No files were found to format!" | ||
exit 1 | ||
fi | ||
else | ||
log_verbose "Using user files: ${userFiles[*]}" | ||
file_list=() | ||
for file in "${userFiles[@]}"; do | ||
for ext in "${includeExts[@]}"; do | ||
if [[ ${file##*\.} == "$ext" ]]; then | ||
file_list+=( "$file" ) | ||
log_verbose "Checking user file: ${file}" | ||
break | ||
fi | ||
done | ||
done | ||
fi | ||
} | ||
|
||
get_file_list | ||
|
||
for file in "${file_list[@]}" | ||
do | ||
((filecount+=1)) | ||
cf="${cfargs} $file" | ||
|
||
log_whatif "Formatting $file ..." | ||
|
||
if [[ ${whatif} -eq 1 ]]; then | ||
${cf} | diff -u "$file" - | ||
else | ||
if [[ ${verbose} -eq 1 ]]; then | ||
${cf} | ||
else | ||
${cf} > /dev/null | ||
fi | ||
fi | ||
|
||
#shellcheck disable=SC2181 | ||
if [[ $? -ne 0 ]]; then | ||
if [[ ${whatif} -eq 1 ]]; then | ||
((changecount+=1)) | ||
else | ||
echo "clang-format failed on file: $file." | ||
fi | ||
fi | ||
done | ||
|
||
log_whatif "${filecount} files processed, ${changecount} changed." | ||
|
||
# If files are being edited, this count is zero so we exit with success. | ||
exit "$changecount" |
Oops, something went wrong.