2023-12-04 05:55:08 -04:00
|
|
|
#!/usr/bin/env bash
|
2015-07-08 19:07:43 -03:00
|
|
|
|
|
|
|
src=$(realpath $(dirname $BASH_SOURCE)/../../)
|
|
|
|
base=$src/libraries
|
|
|
|
declare -A header_dirs
|
|
|
|
|
|
|
|
arg_verbose=false
|
|
|
|
arg_create_commits=false
|
|
|
|
|
|
|
|
usage(){
|
|
|
|
cat <<EOF
|
2015-08-15 20:13:55 -03:00
|
|
|
Usage: $(basename $BASH_SOURCE) [OPTIONS] [--] [<pathspec>...]
|
2015-07-08 19:07:43 -03:00
|
|
|
|
|
|
|
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.
|
|
|
|
|
2015-08-15 20:13:55 -03:00
|
|
|
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.
|
|
|
|
|
2015-07-08 19:07:43 -03:00
|
|
|
The output is a log of the process.
|
|
|
|
|
2015-08-15 20:13:55 -03:00
|
|
|
OPTIONS:
|
2015-07-08 19:07:43 -03:00
|
|
|
-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 $?
|
|
|
|
;;
|
2015-08-15 20:13:55 -03:00
|
|
|
--)
|
|
|
|
# remaining args are pathspecs
|
|
|
|
shift
|
|
|
|
break
|
|
|
|
;;
|
|
|
|
-*)
|
2015-07-08 19:07:43 -03:00
|
|
|
usage >&2
|
|
|
|
exit 1
|
|
|
|
;;
|
2015-08-15 20:13:55 -03:00
|
|
|
*)
|
|
|
|
# this and the remaining args are pathspecs
|
|
|
|
break
|
2015-07-08 19:07:43 -03:00
|
|
|
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
|
2015-08-15 20:13:55 -03:00
|
|
|
git grep -l $regex -- "$@" | while read f; do
|
2015-07-08 19:07:43 -03:00
|
|
|
fix_includes $f $header
|
|
|
|
done
|
|
|
|
done
|
|
|
|
|
|
|
|
$arg_create_commits && create_commits
|
|
|
|
|
|
|
|
popd > /dev/null
|