mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-12-11 05:03:34 +01:00
233 lines
6.3 KiB
Bash
233 lines
6.3 KiB
Bash
|
#!/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
|