Tools: add script for standardize includes of libraries headers

This commit is contained in:
Gustavo Jose de Sousa 2015-07-08 19:07:43 -03:00 committed by Andrew Tridgell
parent ce0cb248ce
commit ae9a314295
1 changed files with 225 additions and 0 deletions

View File

@ -0,0 +1,225 @@
#!/bin/bash
src=$(realpath $(dirname $BASH_SOURCE)/../../)
base=$src/libraries
declare -A header_dirs
arg_verbose=false
arg_create_commits=false
usage(){
cat <<EOF
Usage: $(basename $BASH_SOURCE) [-h|--help]
Fix includes of libraries headers in source files to be as the following:
- If the header is in the same directory the source belongs to, then the
notation #include "" is used with the path relative to the directory
containing the source.
- If the header is outside the directory containing the source, then we use
the notation #include <> with the path relative to libraries folder.
The output is a log of the process.
Options:
-h,--help
Display this help message.
-v,--verbose
Not only log errors and warnings but also substitutions.
-c,--create-commits
Create commits in the end.
--commit
Assume that the user have run the substitutions beforehand - only
create the commits.
EOF
}
create_commits(){
for f in $(git diff-files --name-only); do
if [[ ${f%%/*} == "libraries" ]]; then
echo $f | sed "s,\(libraries/[^/]*\)/.*,\1,"
else
echo ${f%%/*}
fi
done | uniq | while read d; do
if [[ $d == libraries/* ]]; then
commit_base=${d#libraries/}
else
commit_base=$d
fi
cat >/tmp/commit_msg <<EOF
$commit_base: standardize inclusion of libaries headers
This commit changes the way libraries headers are included in source files:
- If the header is in the same directory the source belongs to, so the
notation '#include ""' is used with the path relative to the directory
containing the source.
- If the header is outside the directory containing the source, then we use
the notation '#include <>' with the path relative to libraries folder.
Some of the advantages of such approach:
- Only one search path for libraries headers.
- OSs like Windows may have a better lookup time.
EOF
git add -u $d
git commit -F /tmp/commit_msg
done
}
replace_include(){
local file=$1
local n=$2
local new_path=$3
local old_path=$4
local regex="\(#\s*include\s*\)[<\"].\+[>\"]"
[[ $new_path == $old_path ]] && return
$arg_verbose && echo "$file:$n: $old_path --> $new_path"
if ! sed -i "${n}s,$regex,\1$new_path," $file; then
echo Error on executing command: sed -i "${n}s,$regex,\1$new_path," $file >&2
kill -SIGINT $$
fi
}
fix_includes(){
local file=$1
local header=$2
local dirs=(${header_dirs[$header]})
local num_dirs=${#dirs[@]}
local regex="^\s*#\s*include\s*[<\"]\(.*/\)\?$header[>\"]"
grep -ahno $regex $file | while IFS=":" read n match; do
path=$(echo $match | sed "s/^\s*#\s*include\s*//g")
delim=${path:0:1}
path=${path:1:(${#path}-2)}
file_dir=$(realpath $(dirname $file))
if [[ $delim == "\"" ]]; then
localpath=$file_dir/$path
if [[ -f $localpath ]]; then
# verify if file is under to the file dir
localpath=$(realpath $localpath)
[[ $localpath == $file_dir* ]] && continue
# if not under file dir, check if $localpath is under $base
if [[ $localpath == $base* ]]; then
new_path=${localpath#$base/}
replace_include $file $n \<$new_path\> \"$path\"
continue
fi
fi
fi
match_count=0
possible_paths=()
for dir in "${dirs[@]}"; do
if [[ $dir/$header == *$path ]]; then
((match_count++))
new_path=$dir/$header
possible_paths[${#possible_paths[@]}]=$new_path
fi
done
if [[ $match_count -eq 0 ]]; then
echo "$file:$n: couldn't find a match for inclusion of $path"
elif [[ $match_count -eq 1 ]]; then
# check if included header is under file dir
if [[ -f $file_dir/$path ]]; then
new_path=\"$(realpath $file_dir/$path --relative-to $file_dir)\"
else
new_path=\<$new_path\>
fi
if [[ $delim == '"' ]]; then path=\"$path\"; else path=\<$path\>; fi
replace_include $file $n $new_path $path
else
echo "$file:$n: more than one match for inclusion of $path"
echo " possible paths:"
for p in "${possible_paths[@]}"; do
echo " $p"
done
fi
done
}
trap_reset_tree(){
echo
echo Process killed or interrupted! Reseting tree...
git -C $src reset --hard
exit 1
}
# parse args
while [[ -n $1 ]]; do
case "$1" in
-h|--help)
usage
exit 0
;;
-v|--verbose)
arg_verbose=true
;;
-c|--create-commits)
arg_create_commits=true
;;
--commit)
create_commits
exit $?
;;
*)
usage >&2
exit 1
;;
esac
shift
done
trap trap_reset_tree SIGINT SIGKILL
if ! git -C $src diff-files --quiet --exit-code; then
echo You have unstaged changes, please commit or stash them beforehand >&2
exit 1
fi
pushd $src > /dev/null
# collect all headers
git -C $base ls-files *.h > /tmp/headers
total=$(cat /tmp/headers | wc -l)
header_max_len=0
while read f; do
header=$(basename $f)
dir=$(dirname $f)
if [[ -z ${header_dirs[$header]} ]]; then
header_dirs[$header]=$dir
else
header_dirs[$header]+=" $dir"
fi
printf "\rCollecting header files paths... $((++i))/$total" >&2
[[ ${#header} -gt $header_max_len ]] && header_max_len=${#header}
done </tmp/headers
echo
total=${#header_dirs[@]}
i=0
for header in "${!header_dirs[@]}"; do
regex="#\s*include\s*[<\"]\(.*/\)\?$header[>\"]"
printf "\r($((++i))/$total) Fixing includes for header %-${header_max_len}s" $header >&2
# for each file that includes $header
git grep -l $regex | while read f; do
fix_includes $f $header
done
done
$arg_create_commits && create_commits
popd > /dev/null