Nice and clean bash scripting for dialyzer dependencies
edit2: New escaper run.
edit: Backslash is fixed now.
I made a nice script that checks an Erlang source tree for base library dependencies. I needed this in order to speed up the system library PLT building analysis phase of the Dialyzer on my old computer.
The trivially implemented algorithm is only an approximation based on sed-processed program text, but according to my review it's should almost never generate a smaller set of packages than a proper parser. Note that even a proper parser would have problems with the pathological cases which this simple one would fail on.
The style is, however, much more interesting if you take a look at the attached code. What do you think about it?
edit: Backslash is fixed now.
I made a nice script that checks an Erlang source tree for base library dependencies. I needed this in order to speed up the system library PLT building analysis phase of the Dialyzer on my old computer.
The trivially implemented algorithm is only an approximation based on sed-processed program text, but according to my review it's should almost never generate a smaller set of packages than a proper parser. Note that even a proper parser would have problems with the pathological cases which this simple one would fail on.
The style is, however, much more interesting if you take a look at the attached code. What do you think about it?
#!/bin/dash
##### library code
###
# unpure!
debug(){
[ -n "$NDEBUG" ] &&
echo "DEBUG:" "$@" >> $LOG
}
# unpure!
warning(){
echo "warning:" "$@" >> $LOG
}
# unpure!
error(){
echo "error: ${1}!" >&2
sleep 1
kill $$
sleep 10
exit 1
}
# possibly unpure!
args(){
GOT=$1
NEED=$2
SUB=$3
[ $NEED -ne $GOT ] && error "${SUB}() needs $NEED args"
}
# possibly unpure!
argsmin(){
GOT=$1
NEED=$2
SUB=$3
[ $NEED -gt $GOT ] && error "${SUB}() needs at least $NEED args"
}
# possibly unpure!
ck(){
$@ || error "return code=$? while executing '$*'"
}
any(){ argsmin $# 1 any
CMD="$*"
while read R
do
$CMD "$R" && exit 0
done
exit 1
}
older(){ args $# 2 older
[ $2 -nt $1 ]
}
# possibly unpure!
sk(){ argsmin $# 2 sk
F="$1"
shift
FUN=$1
shift
ARG="$*"
SK_DOTEST="1"
if [ -f "$F" ]
then
echo "$ARG" |
sed "s~ ~\n~g" |
any older "$F" || SK_DOTEST=""
fi
if [ ${SK_DOTEST} ]
then
echo "starting $FUN $ARG $F" >&2
$FUN $ARG "$F" || {
C=$?
rm -v "$F"
error "return code=$C while executing '$FUN $ARG $F'"
}
ck test -f "$F"
else
echo "skipping $FUN $ARG $F" >&2
fi
}
###
##### end of library
list_erl(){ args $# 2 list_erl
find $1 -type f -iname "*.erl" > $2
}
# unpure!
cat_files(){ args $# 2 cat_files
cat $1 |
while read R
do
ck cat "$R"
done > $2
}
fun_calls(){ args $# 2 fun_calls
grep ":" $1 |
sed "s~^~ ~;
s~\<[a-z][a-z0-9_]*:[a-z][a-z0-9_]*\>~\n&\n ~g" |
grep "^[^ ]" |
sort |
uniq > $2
}
call_mods(){ args $# 2 call_mods
sed "s~:[^:]*$~~" $1 |
sort |
uniq > $2
}
erl_libs(){ args $# 2 erl_libs
find $1 -mindepth 3 -maxdepth 3 -type f -iname "*.erl" |
grep "^$1/[^/][^/]*/src/" > $2
}
used_libs(){ args $# 4 used_libs
USED_LIBS_MOD=$1
USED_LIBS_LIB=$2
USED_LIBS_SRC=$3
USED_LIBS_OUT=$4
cat $USED_LIBS_MOD |
while read R
do
P="^.*/\([^/][^/]*\)/src/$R\.erl$"
if
grep -q "$P" $USED_LIBS_LIB
then
grep "$P" $USED_LIBS_LIB |
sed "s~$P~\1~"
else
grep -q "/$R\.erl$" $USED_LIBS_SRC ||
warning "$R not found!"
fi
done |
sort |
uniq > $USED_LIBS_OUT
}
main(){ args $# 0 "main"
SRCDIR=$HOME/erl/trunk/tool/
SRCDIRLS=_srcls.txt.tmp
ERLTEXT=_source.txt.tmp
CALLS=_calls.txt.tmp
MODS=_mods.txt.tmp
LIBS=_libs.txt.tmp
USEDLIBS=usedlibs.txt
LOG="error.log"
sk $SRCDIRLS list_erl $SRCDIR
sk $ERLTEXT cat_files $SRCDIRLS
sk $CALLS fun_calls $ERLTEXT
sk $MODS call_mods $CALLS
sk $LIBS erl_libs /usr/lib/erlang/lib
sk $USEDLIBS used_libs $MODS $LIBS $SRCDIRLS
echo ok
}
main "$@"
Oops... it looks like the escaper has eaten my backspaces! I'll try to fix it soon.
ReplyDelete