mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-10 21:23:58 +01:00
f3c82c86f9
Previously, this script ran over every C file in our source code, which took up to a minute on my desktop. Instead, the script now has several modes that it can run in, to minimize the time spent and improve useful output. It should now be suitable for everyday use and git hooks. I hope. I've also renamed this script, so that we can keep using it in the future if we were to move to some tool other than clang-format.
233 lines
6.3 KiB
Bash
Executable File
233 lines
6.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Copyright 2020, The Tor Project, Inc.
|
|
# See LICENSE for licensing information.
|
|
|
|
#
|
|
# DO NOT COMMIT OR MERGE CODE THAT IS RUN THROUGH THIS TOOL YET.
|
|
#
|
|
# WE ARE STILL DISCUSSING OUR DESIRED STYLE AND ITERATING ON IT.
|
|
# (12 Feb 2020)
|
|
#
|
|
|
|
# This script runs "clang-format" and "codetool" in sequence over each of its
|
|
# arguments. It either replaces the original, or says whether anything has
|
|
# changed, depending on its arguments.
|
|
#
|
|
# We can't just use clang-format directly, since we also want to use codetool
|
|
# to reformat a few things back to how we want them, and we want avoid changing
|
|
# the mtime on files that didn't actually change.
|
|
#
|
|
# Use "-i" to edit the file in-place.
|
|
# Use "-c" to exit with a nonzero exit status if any file needs to change.
|
|
# Use "-d" to emit diffs.
|
|
#
|
|
# The "-a" option tells us to run over every Tor source file.
|
|
# The "-v" option tells us to be verbose.
|
|
|
|
set -e
|
|
|
|
ALL=0
|
|
GITDIFF=0
|
|
GITIDX=0
|
|
DIFFMODE=0
|
|
CHECKMODE=0
|
|
CHANGEMODE=0
|
|
|
|
SCRIPT_NAME=$(basename "$0")
|
|
SCRIPT_DIR=$(dirname "$0")
|
|
SRC_DIR="${SCRIPT_DIR}/../../src"
|
|
|
|
function usage() {
|
|
echo "$SCRIPT_NAME [-h] [-c|-d|-i] [-v] [-a|-G|files...]"
|
|
echo
|
|
echo " flags:"
|
|
echo " -h: show this help text"
|
|
echo " -c: check whether files are correctly formatted"
|
|
echo " -d: print a diff for the changes that would be applied"
|
|
echo " -i: change files in-place"
|
|
echo " -a: run over all the C files in Tor"
|
|
echo " -v: verbose mode"
|
|
echo " -g: look at the files that have changed in git."
|
|
echo " -G: look at the files that are staged for the git commit."
|
|
echo
|
|
echo "EXAMPLES"
|
|
echo
|
|
echo " $SCRIPT_NAME -a -i"
|
|
echo " rewrite every file in place, whether it has changed or not."
|
|
echo " $SCRIPT_NAME -a -d"
|
|
echo " as above, but only display the changes."
|
|
echo " $SCRIPT_NAME -g -i"
|
|
echo " update every file that you have changed in the git working tree."
|
|
echo " $SCRIPT_NAME -G -c"
|
|
echo " exit with an error if any staged changes are not well-formatted."
|
|
}
|
|
|
|
FILEARGS_OK=1
|
|
|
|
while getopts "acdgGhiv" opt; do
|
|
case "$opt" in
|
|
h) usage
|
|
exit 0
|
|
;;
|
|
a) ALL=1
|
|
FILEARGS_OK=0
|
|
;;
|
|
g) GITDIFF=1
|
|
FILEARGS_OK=0
|
|
;;
|
|
G) GITIDX=1
|
|
FILEARGS_OK=0
|
|
;;
|
|
c) CHECKMODE=1
|
|
;;
|
|
d) DIFFMODE=1
|
|
;;
|
|
i) CHANGEMODE=1
|
|
;;
|
|
v) VERBOSE=1
|
|
;;
|
|
*) echo
|
|
usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
# get rid of the flags; keep the filenames.
|
|
shift $((OPTIND - 1))
|
|
|
|
# Define a verbose function.
|
|
if [[ $VERBOSE = 1 ]]; then
|
|
function note()
|
|
{
|
|
echo "$@"
|
|
}
|
|
else
|
|
function note()
|
|
{
|
|
true
|
|
}
|
|
fi
|
|
|
|
# We have to be in at least one mode, or we can't do anything
|
|
if [[ $CHECKMODE = 0 && $DIFFMODE = 0 && $CHANGEMODE = 0 ]]; then
|
|
echo "Nothing to do. You need to specify -c, -d, or -i."
|
|
echo "Try $SCRIPT_NAME -h for more information."
|
|
exit 0
|
|
fi
|
|
|
|
# We don't want to "give an error if anything would change" if we're
|
|
# actually trying to change things.
|
|
if [[ $CHECKMODE = 1 && $CHANGEMODE = 1 ]]; then
|
|
echo "It doesn't make sense to use -c and -i together."
|
|
exit 0
|
|
fi
|
|
# It doesn't make sense to look at "all files" and "git files"
|
|
if [[ $((ALL + GITIDX + GITDIFF)) -gt 1 ]]; then
|
|
echo "It doesn't make sense to use more than one of -a, -g, or -G together."
|
|
exit 0
|
|
fi
|
|
|
|
if [[ $FILEARGS_OK = 1 ]]; then
|
|
# The filenames are on the command-line.
|
|
INPUTS=("${@}")
|
|
else
|
|
if [[ "$#" != 0 ]]; then
|
|
echo "Can't use -a, -g, or -G with additional command-line arguments."
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
if [[ $ALL = 1 ]]; then
|
|
# We're in "all" mode -- use find(1) to find the filenames.
|
|
mapfile -d '' INPUTS < <(find "${SRC_DIR}"/{lib,core,feature,app,test,tools} -name '[^.]*.[ch]' -print0)
|
|
elif [[ $GITIDX = 1 ]]; then
|
|
# We're in "git index" mode -- use git diff --cached to find the filenames
|
|
# that are changing in the index, then strip out the ones that
|
|
# aren't C.
|
|
mapfile INPUTS < <(git diff --name-only --cached --diff-filter=AMCR | grep '\.[ch]$')
|
|
elif [[ $GITDIFF = 1 ]]; then
|
|
# We are in 'git diff' mode -- we want everything that changed, including
|
|
# the index and the working tree.
|
|
#
|
|
# TODO: There might be a better way to do this.
|
|
mapfile INPUTS < <(git diff --name-only --cached --diff-filter=AMCR | grep '\.[ch]$'; git diff --name-only --diff-filter=AMCR | grep '\.[ch]$' )
|
|
fi
|
|
|
|
if [[ $GITIDX = 1 ]]; then
|
|
# If we're running in git mode, we need to stash all the changes that
|
|
# we don't want to look at. This is necessary even though we're only
|
|
# looking at the changed files, since we might have the file only
|
|
# partially staged.
|
|
note "Stashing unstaged changes"
|
|
git stash -q --keep-index
|
|
function restoregit() {
|
|
note "Restoring git state"
|
|
git stash pop -q
|
|
}
|
|
else
|
|
function restoregit() {
|
|
true
|
|
}
|
|
fi
|
|
|
|
ANY_CHANGED=0
|
|
|
|
tmpfname=""
|
|
|
|
#
|
|
# Set up a trap handler to make sure that on exit, we remove our
|
|
# tmpfile and un-stash the git environment (if appropriate)
|
|
#
|
|
trap 'if [ -n "${tmpfname}" ]; then rm -f "${tmpfname}"; fi; restoregit' 0
|
|
|
|
for fname in "${INPUTS[@]}"; do
|
|
note "Inspecting $fname..."
|
|
tmpfname="${fname}.$$.clang_fmt.tmp"
|
|
rm -f "${tmpfname}"
|
|
clang-format --style=file "${fname}" > "${tmpfname}"
|
|
"${SCRIPT_DIR}/codetool.py" "${tmpfname}"
|
|
|
|
changed=not_set
|
|
|
|
if [[ $DIFFMODE = 1 ]]; then
|
|
# If we're running diff for its output, we can also use it
|
|
# to compare the files.
|
|
if diff -u "${fname}" "${tmpfname}"; then
|
|
changed=0
|
|
else
|
|
changed=1
|
|
fi
|
|
else
|
|
# We aren't running diff, so we have to compare the files with cmp.
|
|
if cmp "${fname}" "${tmpfname}" >/dev/null 2>&1; then
|
|
changed=0
|
|
else
|
|
changed=1
|
|
fi
|
|
fi
|
|
|
|
if [[ $changed = 1 ]]; then
|
|
note "Found a change in $fname"
|
|
ANY_CHANGED=1
|
|
|
|
if [[ $CHANGEMODE = 1 ]]; then
|
|
mv "${tmpfname}" "${fname}"
|
|
fi
|
|
fi
|
|
|
|
rm -f "${tmpfname}"
|
|
done
|
|
|
|
exitcode=0
|
|
|
|
if [[ $CHECKMODE = 1 ]]; then
|
|
if [[ $ANY_CHANGED = 1 ]]; then
|
|
note "Found at least one misformatted file; check failed"
|
|
exitcode=1
|
|
else
|
|
note "No changes found."
|
|
fi
|
|
fi
|
|
|
|
exit $exitcode
|