blob: 7102217356e92443020c308bfabe6519a640f296 [file] [log] [blame]
#!/bin/bash
# git-verible-verilog-format.sh
#
# Copyright 2020 The Verible Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
script_name="$(basename $0)"
script_dir="$(realpath $(dirname $0))"
formatter="$(which verible-verilog-format)" || \
formatter="$script_dir"/verible-verilog-format
patch_tool="$(which verible-patch-tool)" || \
patch_tool="$script_dir"/verible-patch-tool
function usage() {
cat <<EOF
$0:
Performs incremental file formatting (verible-verilog-format) based on current diffs.
New files explicitly git-add-ed by the user are wholly formatted.
Actions:
1) Runs 'git add -u' to stage currently modified files to the index.
To format new files (wholly), 'git add' those before calling this script.
2) Runs 'git diff -u --cached' to generate a unified diff.
3) Diff is scanned to determine added or modified lines in each file.
4) Invokes 'verible-verilog-format --inplace' on all touched or new Verilog files,
but does not 'git add' so the changes may be examined and tested.
Formatting can be easily undone with:
'git diff | git apply --reverse -'.
usage: $0 [script options] [-- [verible-verilog-format options]]
(no positional arguments)
Run from anywhere inside a git project tree.
script options: (options with arguments can be: --flag=VALUE or --flag VALUE)
--help | -h : print help and exit
--verbose | -v : execute verbosely
--dry-run : stop before running formatter, and print formatting commands
--formatter TOOL : path to verible-verilog-format binary
[using: $formatter]
-- : stops option processing, and forwards remaining args as flags to the
underlying --formatter tool.
EOF
}
# self-identify message coming from this script
function msg() {
echo "[$script_name] " "$@"
}
verbose=0
dry_run=0
# 'args' will be forwarded to the formatter as additional tool options
args=()
# option processing
for opt
do
# handle: --option arg
if test -n "$prev_opt"
then
eval $prev_opt=\$opt
prev_opt=
shift
continue
fi
case $opt in
*=?*) optarg=$(expr "X$opt" : '[^=]*=\(.*\)') ;;
*=) optarg= ;;
esac
case $opt in
-- ) shift ; break ;; # stop option processing
--help | -h ) { usage ; exit ;} ;;
--verbose | -v ) verbose=1 ;;
--formatter ) prev_opt=formatter ;;
--formatter=* ) formatter="$optarg" ;;
--dry-run ) dry_run=1 ;;
--* | -* ) msg "Unknown option: $opt" ; exit 1 ;;
*) args=("${args[@]}" "$opt") ;;
esac
shift
done
# Check some requirements.
[[ -x "$formatter" ]] || {
msg "*** Unable to find executable 'verible-verilog-format'."
msg " Please specify formatter with: --formatter TOOL."
exit 1
}
[[ -x "$patch_tool" ]] || {
msg "*** Required executable '$patch_tool' is missing."
exit 1
}
# collect remainder after stopping option processing
args=("${args[@]}" "$@")
function verbose_command() {
if [[ "$dry_run" = 1 ]] || [[ "$verbose" = 1 ]]
then
msg "[command]: $@"
fi
[[ "$dry_run" = 1 ]] || "$@" || \
msg "Note: '$@' failed with status: $?"
}
# Switch to git root directory, so relative paths will be correct.
cd "$(git rev-parse --show-toplevel)"
msg "Working from git root: $PWD"
tempdir="$(mktemp -d --tmpdir "tmp.$script_name.XXXXXXX")"
msg "Temporary files in: $tempdir"
# Save current workspace in git index.
git add -u
# Examine current differences for changed lines.
# Strip the 'b/' from git diffs.
# Format only changed lines for each file, one file at a time.
git diff -u --cached | \
tee "$tempdir"/git-cached.diff | \
"$patch_tool" changed-lines - | \
sed -e 's|^b/||' | \
tee "$tempdir"/file-lines.txt | \
while read filename lines
do
# Check file extension: skip non-Verilog files.
case "$filename" in
*.v | *.vh | *.sv | *.svh ) ;;
* ) continue ;;
esac
# If $lines is blank, that implies a new file (format entire file).
# Otherwise it is the argument to --lines.
lines_flag=()
[[ -z "$lines" ]] || lines_flag=("--lines=$lines")
# Format one file at a time.
format_command=("$formatter" --inplace "$filename" "${lines_flag[@]}" "${args[@]}")
verbose_command "${format_command[@]}"
# Ignore exit statuses and keep going.
done
if [[ "$dry_run" = 0 ]]
then
cat <<EOF
[$script_name] Done formatting. You could run:
'git status' to see what files were formatted.
'git diff' to see detailed formatting changes.
'git add -u' to accept formatting changes for commit.
'git diff | git apply --reverse -' to undo formatting changes.
EOF
else
msg "Stopping before formatting due to --dry-run."
fi