ardupilot/Tools/scripts/fix_libraries_includes.sh

239 lines
6.5 KiB
Bash
Raw Normal View History

#!/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) [OPTIONS] [--] [<pathspec>...]
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.
If pathspec is given then it's an argument passed directly to git-grep. See
git-grep(1) for more information on its format. In this case the changes will
apply only to files that match the pathspec. Otherwise changes will be made to
the entire repository.
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 $?
;;
--)
# remaining args are pathspecs
shift
break
;;
-*)
usage >&2
exit 1
;;
*)
# this and the remaining args are pathspecs
break
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