#!/bin/bash
# dep - library dependency support
set -e
lib_load 'dev/package/files'
lib_load 'doc/pdf'
lib_load 'doc/tool/graphviz'
######
# Config
dev_package_dep_config_init() {
lib_setting_vars dep_extensions_show dep_extensions_expand
dep_extensions_show=${dep_extensions_show:-false}
dep_extensions_expand=${dep_extensions_expand:-false}
lib_setting_vars dep_graph_runtime
dep_graph_runtime=${dep_graph_runtime:-false}
}
dev_package_dep_config_check() {
:
}
######
# Dependencies
lib_dep_list() {
local file=$1
grep -Pe '^[#\t ]*lib_load ' "$file" \
| sed -e "s,^lib_load ['\"]*,!," \
-e "s,^.*lib_load ['\"]*,," \
-e "s,['\"] *$,,"
}
######
# Package Dependency CLI
package_dep_dispatch() { lib_cmd_dispatch package_dep "$@"; }
package_dep_usage() {
cat <<USAGE
<cmd> ...
Dependency Commands:
check [<file>+] Checks script dependencies
list [<file>+] Prints script dependencies
tree [<file>+] Prints dependency tree(s)
graph [<file>+] Generates and views a dependency graph
USAGE
}
package_dep_list() {
local -a files
package_sources_or_args files "$@"
# find only files that load libraries
local -a links
links=( $(grep -l 'lib_load' "${files[@]}" ) )
for_each _package_dep_print "${links[@]}"
}
_package_dep_print() {
local file=$1
echo -en "$file: "
lib_dep_list "$file" | xargs
}
package_dep_check() {
min_args 1 "$@"
# local -a files
# package_sources_or_args files "$@"
dep_check "$@" || error "dependency check failed"
}
package_dep_tree() {
verbose=true package_dep_check "$@"
}
######
# Package Dependency Tree
dep_check() {
local -a loaded visited
_dep_check "$@"
info "${#loaded[@]} libraries loaded"
info " ${loaded[*]}"
local n=$(( ${#package_libs[@]} - ${#loaded[@]} ))
if [ $n -gt 0 ]; then
local -a pkgs
for pkg in ${package_libs[@]}; do
if ! in_list "$pkg" "${loaded[@]}"; then
list_append pkgs "$pkg "
fi
done
info "${#pkgs[@]} libraries NOT loaded:"
info " ${pkgs[@]}"
fi
}
_dep_check() {
local okay=true
local file
for file in "$@"; do
local name
name=$(basename "$file" .in)
if [ ! -f "$file" ]; then
warn "${visited[*]} -> $name: library not found"
okay=false
continue
fi
if in_list $name "${visited[@]}"; then
error "cycle detected: ${visited[*]} $name"
fi
__dep_check "$file"
done
$okay
}
__dep_check() {
local file=$1
local name
name=$(basename "$file" .in)
local n_visited=${#visited[*]}
local -a old=( "${visited[@]}" )
local -a visited=( "${old[@]}" $name )
local -a deps
deps=( $(lib_dep_list "$file") )
local dir
dir=$(basename "$(dirname "$file")")
if [ "$dir" = 'src' ]; then
list_insert deps "!$runtime_name"
fi
local prefix='+-> '
[ $n_visited -gt 0 ] || prefix=''
local suffix=''
in_list $name "${loaded[@]}" || suffix=' *'
local indent_level=2
local indent=$(( $n_visited * $indent_level ))
info "$(printf "%${indent}s%s" '' "$prefix$name$suffix")"
if ! in_list $name "${loaded[@]}"; then
list_append loaded $name
fi
if ${dep_check_prune:-true} && [ -z "$suffix" ]; then
return
fi
if [ -z "${deps[*]}" ]; then
return
fi
local okay=true
local dname
local -a dfiles=()
for dname in "${deps[@]}"; do
dname=${dname#!}
list_append dfiles "libs/$dname.in"
done
_dep_check "${dfiles[@]}"
}
######
# Dependency Graphs
package_dep_graph() { cmd_dispatch "$@"; }
package_dep_graph_usage() {
cat <<USAGE
Dependency Graph Commands:
all Displays complete dependecy graph
tools Displays graph for all tools
libs Displays graphs for all libraries
runtime Displays graph for runtime libraries
files <file>+ Displays graph(s) for select files
USAGE
}
# TODO: rename these symbols and eliminate these placeholders
package_dep_graph_all() { dep_graph_view all; }
package_dep_graph_tools() { dep_graph_view tools; }
package_dep_graph_libs() { dep_graph_view libraries; }
package_dep_graph_runtime() { dep_graph_view runtime; }
package_dep_graph_files() { dep_graph_view files "$@"; }
######
# Dependency Graphs
dep_graph_view() {
local dotfile
dotfile=$(cmd_tempfile)
dep_graph_pdf "$dotfile" "$@"
run_pdf_viewer "$dotfile.pdf"
}
dep_graph_all() {
local dotfile=$1
shift
dep_graph "$dotfile.dot" "$@"
[ -s "$dotfile.dot" ] || return 0
local ext
for ext in svg pdf; do
graphviz_dot_$ext "$dotfile.dot" "$dotfile.$ext"
done
}
dep_graph_pdf() { dep_graph_write pdf "$@"; }
dep_graph_svg() { dep_graph_write svg "$@"; }
dep_graph_write() {
local ext=$1
local dotfile=$2
shift 2
dep_graph "$dotfile.dot" "$@"
[ -s "$dotfile.dot" ] || return 0
graphviz_dot_$ext "$dotfile.dot" "$dotfile.$ext"
}
# dep_graph() - Generates graphviz source code in the named file ($1)
# that will produce the specified kind ($2) of package dependency graph.
# $1 - Output file that will contain the required 'dot' graphing commands
# $2 - Kind of package graph (all, tools, libs, runtime, or files)
dep_graph() {
min_args 2 "$@"
local out=$1
shift
file_mkdir "$out"
dep_graph_kind "$@" >"$out"
}
dep_graph_kind() {
local kind=$1
shift
run_pushd "$package_repodir"
local -a files
case "$kind" in
(all)
dep_extensions_show=true
dep_extensions_expand=true
files=( $(find src libs -name '*.in') )
;;
(tools) files=( $(find src -name '*.in') ) ;;
(libraries) files=( $(find libs -name '*.in') ) ;;
(runtime) files=( "libs/$package_name.in" ) ;;
(files) files=( $(find "$@" -name '*.in') ) ;;
(*)
warn "'$kind' is not among ${dep_graph_kinds[*]}"
error "invalid graph type: '$kind'"
;;
esac
dot_digraph deps dep_graph_gen "${files[@]}"
run_popd
}
dep_graph_gen() {
local -a dep_graph_visited=()
for_each dep_graph_file "$@"
}
dep_graph_file() {
local file=$1
# avoid graphs cycles
! in_list "$file" "${dep_graph_visited[@]}" || return 0
dep_graph_visited+=( "$file" )
local src
src=${file#*/}
src=${src%.in}
# display tool different
if is_package_tool "$file"; then
src="$src*"
! $dep_extensions_show || dot_node_list "$src" "$package_name"
fi
# only display the include the runtime when indicated
if $dep_extensions_show && in_list $src "${package_extensions[@]}"; then
! $dep_extensions_expand || dep_graph_file "$runtime_file"
fi
local -A dep_arc_list=()
local dep
for dep in $(lib_dep_list "$file"); do
local style
if [ "${dep:0:1}" != '!' ]; then
style=dashed
else
style=solid
fi
local dot_node_attr="[style=$style]"
local dname=${dep#!}
[ "${dep_arc_list[$dep]}" ] || dot_node_list "$src" "$dname"
dep_arc_list[$dep]=1
if [ "${dname/\$/}" = "$dname" ]; then
dep_graph_file "libs/$dname.in"
else
debug "$dname: ignoring dynamic dependency"
fi
done
}
Generated on Thu May 4 18:59:41 PDT 2017 by mcsh i7 v0.19.0.