Project

General

Profile

The following shell script (developed by Uli Schlachter) can be executed in the root directory of DCMTK's source code. It uses Graphviz and some common unix tools to generate the following output image:

This image represents the dependencies between the individual modules of the DCMTK. As such, the dependencies of the individual apps are ignore and only the libraries are analyzed.

What you can see is, for example, that the module ''dcmsr'' depends on the modules ''dcmimage'', ''dcmimgle'', ''dcmdata'', ''oflog'', ''ofstd'' and ''config''. On the other hand, the module ''dcmpstat'' needs ''dcmsr''.

#!/bin/sh

# List of modules to parse
MODS="ofstd oflog dcmdata dcmiod dcmfg dcmseg dcmimgle dcmimage dcmjpeg dcmjpls dcmtls dcmnet dcmsr dcmsign dcmwlm dcmqrdb dcmpstat dcmrt dcmtract dcmpmap" 

# List of dependencies to ignore. This is necessary because e.g. oftest.h in
# ofstd/ includes a header from dcmdata, but only if some define is set first,
# which it never is in ofstd.
# This works by ignoring a dependency from A to B if "A B" is in this list
IGNORE="ofstd dcmdata|ofstd oflog|dcmnet dcmtls" 

DOT=$(mktemp)
trap 'rm $DOT' EXIT

graph()
{
    echo "$@" >> $DOT
}

graph "digraph deps {" 
graph " concentrate=true;" 

DIRS="" 
for x in ${MODS}
do
    DIRS="${DIRS} ${x}/libsrc/ ${x}/include/" 
done

echo "Parsing includes..." 

for x in $(find ${DIRS} -name '*.cc' -or -name '*.h')
do
        grep -EH '^ *#include ["<](../../[d]|dcmtk)' "${x}" | 
        sed -e 's_\([a-z0-9]*\)/[^:]*:#include .dcmtk/\([a-z0-9]*\)/.*$_\1 \2_' | 
        sed -e 's_\([a-z0-9]*\)/[^:]*:#include .../../\([a-z0-9]*\)/.*$_\1 \2_'
done | sort -u | grep -vE "$IGNORE" | 
    awk '
        {
            if ($1 != $2)
                deps[$1] = deps[$1] " " $2
        }
        END {
            for (moda in deps) {
                split(deps[moda], dep, " ")
                for (modb in dep) {
                    # Ok, moda depends on dep[modb], but does some other module
                    # moda depends on depend on dep[modb] too...
                    need = 1
                    for (modc in dep) {
                        if (modb == modc)
                            continue
                        split(deps[dep[modc]], d, " ")
                        for (modd in d) {
                            if (d[modd] == dep[modb]) {
                                # moda depends on dep[modb]
                                # moda depends on dep[modc]
                                # dep[modc] depends on dep[modb]
                                # -> Another module pulls this in
                                # (Or we have a cyclic graph and not a DAG)
                                print "# " moda " needs " dep[modb] " but " dep[modc] " already pulls it in" 
                                need = 0
                                break
                            }
                        }
                        if (!need)
                            break
                    }
                    if (need)
                        print "    " moda " -> " dep[modb] ";" 
                }
            }
        }' >> $DOT

graph "}" 

cat $DOT

echo "Generated input, now generating output" 

EXT=svgz
#EXT=png
OUT=deps.$EXT
dot -o "$OUT" -T$EXT < $DOT && echo "Generated $OUT"