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?

#!/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 "$@"

Comments

  1. Oops... it looks like the escaper has eaten my backspaces! I'll try to fix it soon.

    ReplyDelete

Post a Comment

Popular posts from this blog

Tftp secret of TL-WR740N uncovered

Hidden TFTP of TP-Link routers

Haskell for embedded: C output, compilers, monads, Timber