Forked from
Core Modules / dune-common
5436 commits behind the upstream repository.
-
Martin Nolte authored
[[Imported from SVN: r6890]]
Martin Nolte authored[[Imported from SVN: r6890]]
dunemodules.lib 15.31 KiB
# -*-sh-*-
# vim: ft=sh
###############################################
###
### Configuration
###
# name of the "control" files
CONTROL="dune.module"
###############################################
###
### check for environment variables
###
if test -z $GREP; then
GREP=grep
fi
# SunOS [e]grep does not seem to comprehend character classes. Set up
# some variables to spell them out
UPPER=ABCDEFGHIJKLMNOPQRSTUVWXYZ
LOWER=abcdefghijklmnopqrstuvwxyz
ALPHA="$UPPER$LOWER"
DIGIT=0123456789
ALNUM="$ALPHA$DIGIT"
space=" "
formfeed=""
newline="
"
cr="
"
tab=" "
vtab=""
# $SPACE will unfortunately not work since grep will not accept an
# embedded newline. Instead one can often get away with using $BLANK
SPACE="$space$formfeed$newline$cr$tab$vtab"
BLANK="$space$tab"
#
# read paramters from a $CONTROL file
#
# paramters:
# $1 file to read
#
PARSER_TRIM="awk '{gsub(/^[[:space:]]+| +$/,\"\");printf(\"%s\", \$0);}'"
parse_control() {
# check file existence
if test ! -f "$1" -o "$(basename $1)" != "$CONTROL"; then
echo "ERROR: '$1' is no $CONTROL file" >&2
exit 1
fi
# reset information handling
module=""
module_inst="no"
# read parameters from control file
local name="$($GREP Module: "$1" | cut -d ':' -f2 | eval $PARSER_TRIM)"
if test "x$name" = "x"; then
echo "ERROR: $CONTROL files $1 does not contain a Module entry" >&2
exit 1
fi
# create and check variable name from module name
export module=$(fix_variable_name $name)
if ! check_modname "$module"; then
echo "ERROR: $CONTROL files $1 contains an invalid Module entry" >&2
exit 1
fi
# read dune.module file
local deps="$($GREP "^[$BLANK]*Depends:" "$1" | cut -d ':' -f2 | eval $PARSER_TRIM)"
local sugs="$($GREP "^[$BLANK]*Suggests:" "$1" | cut -d ':' -f2 | eval $PARSER_TRIM)"
local vers="$($GREP "^[$BLANK]*Version:" "$1" | cut -d ':' -f2 | eval $PARSER_TRIM)"
local main="$($GREP "^[$BLANK]*Maintainer:" "$1" | cut -d ':' -f2 | eval $PARSER_TRIM)"
# check whether the module is installed.
# - installed modules can be found via pkg-config
# - pkg-config --var=prefix should be the same as $path
#
# the path contains a different sub structure
# for installed and source modules
# - installed module: ${path}/lib/dunecontrol/${name}/dune.module
# and there is a file ${path}/lib/pkgconfig/${name}.pc
# - source module: ${path}/dune.module
# and there is a file ${path}/${name}.pc.in
local path="$(canonicalpath "$1")"
if pkg-config $name; then
local prefix="$(pkg-config --variable=prefix $name)"
local pkgpath=$(canonicalname "$prefix/lib/dunecontrol/$name")
if test x"$pkgpath" = x"$path"; then
path="$prefix"
module_inst="yes"
fi
fi
# avoid multiple definition of the same module
if eval test "x\$HAVE_$module" != "x"; then
# make sure we don't stumble over the same module twice
if eval test "\$PATH_$module" = "$path"; then
return
fi
local old_mod_inst
eval old_mod_inst=\$INST_$module
case "$old_mod_inst$module_inst" in
# multiple local modules are an error
# multiple installed modules are an error
nono|yesyes)
echo "ERROR: multiple definition of module $name" >&2
echo "previous defined in:" >&2
if eval test x\$INST_$module = "xyes"; then
echo " $(eval echo \$PATH_$module)/lib/dunecontrol/$name/$CONTROL" >&2
else
echo " $(eval echo \$PATH_$module)/$CONTROL" >&2
fi
echo "redefined in:" >&2
if test "$module_inst" = "yes"; then
echo " $path/lib/dunecontrol/$name/$CONTROL" >&2
else
echo " $path/$CONTROL" >&2
fi
exit 1
;;
# installed modules are superseded by locally built modules
noyes)
echo "WARNING: ignoring installed module file" >&2
echo " $path/lib/dunecontrol/$name/$CONTROL" >&2
echo "using previously found locally built module" >&2
echo " $(eval echo \$PATH_$module)/$CONTROL" >&2
module_inst="no"
return
;;
# local modules supersede installed modules
yesno)
echo "WARNING: not using installed module file" >&2
echo " $(eval echo \$PATH_$module)/lib/dunecontrol/$name/$CONTROL" >&2
echo "using locally built module" >&2
echo " $path/$CONTROL" >&2
true # do nothing, ignore the previously found module
;;
esac
fi
# set status variables
export HAVE_$module=yes
export PATH_$module="$path"
export VERS_$module="$vers"
export NAME_$module="$name"
export MAIN_$module="$main"
export DEPS_$module="$deps"
export INST_$module="$module_inst"
for name in $deps; do
mod=$(fix_variable_name $name)
export NAME_$mod="$name"
done
export SUGS_$module="$sugs"
for name in $sugs; do
mod=$(fix_variable_name $name)
export NAME_$mod="$name"
done
# update list of modules
if test "$module_inst" = "yes"; then
export INSTMODULES="$INSTMODULES$module "
else
export LOCALMODULES="$LOCALMODULES$module "
fi
}
#
# try to setup the control path
#
setup_control_path() {
if test -z $DUNE_CONTROL_PATH; then
DUNE_CONTROL_PATH=.
# try pkg-config locations
if pkg-config dune-common; then
# try usual locations of installed modules
for i in /usr/local/lib/dunecontrol/ /usr/lib/dunecontrol/; do
if test -d $i; then
DUNE_CONTROL_PATH=$DUNE_CONTROL_PATH:"$i"
fi
done
for i in `echo $PKG_CONFIG_PATH | tr ':' ' '`; do
if test -d "$i/../dunecontrol"; then
DUNE_CONTROL_PATH=$DUNE_CONTROL_PATH:"$i/../dunecontrol"
fi
done
fi
fi
# try to read DUNE_CONTROL_PATH from OPTS file
if test -n "$DUNE_OPTS_FILE"; then
DUNE_CONTROL_PATH="$(. $DUNE_OPTS_FILE; eval echo $DUNE_CONTROL_PATH)"
fi
# canonicalize path
local TMP=""
# foreach dir in $@
while read dir; do
TMP=$TMP:"$(canonicalname $dir)"
done <<EOF
$(echo $DUNE_CONTROL_PATH | sed -e 's/:\+/:/g' | tr ':' '\n')
EOF
# sort+uniq path
DUNE_CONTROL_PATH="$(echo $TMP | tr ':' '\n' | sort -u | tr '\n' ':' | sed -e 's/^://' -e 's/:$//')"
# safe result
export DUNE_CONTROL_PATH
}
#
# search for modules in each directory in DUNE_CONTROL_PATH
#
find_modules_in_path() {
setup_control_path
if test -z "$FOUND_MODULES"; then
# foreach dir in $@
while read dir; do
if test -d "$dir"; then
while read m; do
test -n "$m" && parse_control "$m"
done <<EOFM
$(find -H "$dir" -name $CONTROL | $GREP -v 'dune-[-_a-zA-Z]/dune-[-a-zA-Z_]*-[0-9]\{1,\}.[0-9]\{1,\}/')
EOFM
else
parse_control "$dir"
fi
done <<EOF
$(echo $DUNE_CONTROL_PATH | sed -e 's/:\+/:/g' | tr ':' '\n')
EOF
export MODULES="$LOCALMODULES$INSTMODULES"
export FOUND_MODULES="$MODULES"
else
export MODULES="$FOUND_MODULES"
fi
}
#
# sort $MODULES according to the dependencies
#
sort_modules() {
# reset lists
export SORTEDMODULES=""
export REVERSEMODULES=""
export SORTEDMODULES_SUB=""
# handle each modules passed as parameter
for m in "$@"; do
# did we find a module file for this module?
if eval test x\$HAVE_$m != x; then
_sort_module $m MAIN
else
echo "ERROR: could not find module $(eval echo \$NAME_$m)" >&2
exit 1
fi
done
# save result
export MODULES="$SORTEDMODULES"
# setup list of SUGS/DEPS and the INFO list
export SORTEDMODULES_INFO=""
export SORTEDMODULES_DEPS=""
export SORTEDMODULES_MAIN=""
export SORTEDMODULES_SUGS=""
local mode
for m in $MODULES; do
eval mode=\$MODE_$m
SORTEDMODULES_INFO="$SORTEDMODULES_INFO $m[$mode]"
eval SORTEDMODULES_$mode=\"\$SORTEDMODULES_$mode $m\"
done
export SORTEDMODULES_INFO
export SORTEDMODULES_DEPS
export SORTEDMODULES_SUGS
export SORTEDMODULES_MAIN
# clean up temporary variables
for m in $MODULES; do
export MODE_$m=""
export SORT_DONE_$m=""
export SORT_DEPS_DONE_$m=""
export SORT_SUGS_DONE_$m=""
done
}
_check_deps()
{
local module="$1"
local mode="$2"
local depmode="$3"
local report="ERROR"
local requires="requires"
local required="required"
local dependency="dependency"
if test "x$mode" = "xSUGS"; then
report="WARNING"
requires="suggests"
required="suggested"
dependency="suggestion"
fi
eval deps=\$${mode}_$module
#initially remove leading space
deps=`echo ${deps//^[, ]}`
while test -n "$deps"; do
#the end of the name is marked either by space, opening parenthesis,
#or comma
name="${deps%%[ (,]*}"
#remove the name and adjacent whitespace
deps=`echo "$deps" | sed 's/^[^ (,]* *//'`
#check whether there is a dependency version
case "$deps" in
'('*) deps="${deps#(}"
depver="${deps%%)*}"
deps="${deps#*)}"
;;
*) depver=
;;
esac
#remove any leading whitespace or commas for the next iteration
deps=`echo ${deps//^[, ]}`
dep=$(fix_variable_name $name)
if ! check_modname $dep; then
echo "ERROR: invalid module name $name ($dependency of $module)" >&2
exit 1
fi
if eval test x\$HAVE_$dep != "x"; then
eval ver=\$VERS_$dep
if test "$SKIPVERSIONCHECK" != "yes" && ! check_version "$ver" "$depver"; then
echo "$report: version mismatch." >&2
echo " $modname $requires $name $depver," >&2
echo " but only $name = $ver is available." >&2
if test "x$mode" = "xDEPS"; then
exit 1
else
echo "Skipping $name!" >&2
continue
fi
fi
_sort_module $dep $depmode
else
# perhaps this module is installed,
# then it should be handled via pkg-config
if ! pkg-config $name; then
echo "$report: could not find module $name," >&2
echo " module is also unknown to pkg-config." >&2
echo " Maybe you need to adjust PKG_CONFIG_PATH!" >&2
echo " $name is $required by $modname" >&2
if test "x$mode" = "xDEPS"; then
exit 1
else
echo "Skipping $name!" >&2
continue
fi
else
eval ver=$(pkg-config $name --modversion)
if test "$SKIPVERSIONCHECK" != "yes" && ! check_version "$ver" "$depver"; then
echo "$report: version mismatch." >&2
echo " $modname $requires $name $depver," >&2
echo " but only $name = $ver is installed." >&2
if test "x$mode" = "xDEPS"; then
exit 1
else
echo "Skipping $name!" >&2
continue
fi
fi
# update module list
parse_control $(pkg-config $name --variable=prefix)/lib/dunecontrol/$name/dune.module
_sort_module $dep $depmode
fi
fi
done
}
#
# recursive part of sort_modules
# evaluate dependencies of one module
#
# paramters:
# $1 name of the modules
# $2 parser mode:
# DEPS: search for dependencies
# SUSG: search for suggestions (DEPS of SUGS are handled as SUGS)
# MAIN: primary invocation of a DEPS search,
# MAIN modules are not added to the list of DEPS/SUGS
#
_sort_module() {
local module="$1"
local mode="$2"
test -n "$mode"
local modname=""
eval modname=\$NAME_$module
local deps=""
local name=""
local dep=""
local ver=""
local depver=""
shift 1
if ! check_modname $module; then
echo "ERROR: invalid module name $module" >&2
exit 1
fi
depmode=$(test $mode = SUGS && echo SUGS || echo DEPS)
if eval test "x\$SORT_${depmode}_DONE_$module" != "xyes"; then
# stop any recursion
export SORT_${depmode}_DONE_$module=yes
# resolve dependencies
_check_deps $module DEPS $depmode # it might happen that the DEPS are actually SUGS
# resolve suggestions
_check_deps $module SUGS SUGS
# remember mode of the module
if eval test "x\$MODE_$module" = xSUGS -o "x\$MODE_$module" = x; then
export MODE_$module=$mode
fi
# topological list of the module and its dependencies/suggestions
if eval test "x\$SORT_DONE_$module" != "xyes"; then
export SORT_DONE_$module=yes
export SORTEDMODULES="$SORTEDMODULES $module"
export REVERSEMODULES="$module $REVERSEMODULES"
fi
fi
}
#
# load the $CONTROL file, skip all control variables
# and run a command
#
# parameters:
# $1 command to execute
# $2 full path of the $CONTROL file
#
eval_control() {
local command="$1"
local file="$2"
shift 2
if test -f "$file"; then
# open subshell
(
set -e
# load functions defined in $file
# if $command is not defined in $file,
# then the default implementation will be executed
eval "$($GREP -v "^[-$ALNUM]\{1,\}:" $file)"
# execute $command
$command
) || false
else
echo "ERROR: could not find $file" >&2
exit 1
fi
}
#
# fix a value such that it is suitable for a variable name
#
# parameters:
# $1 value
#
fix_variable_name() {
echo ${@//[[:punct:]]/_}
}
#
# fix a value such that it is suitable for a variable name and assign it
#
# parameters:
# $1 name of variable
# $2 value
#
fix_and_assign() {
local name="$1"
if ! check_modname $name; then
echo "ERROR: error in assignment. $name is not a valid variabel name." >&2
fi
shift 1
export $name=$(fix_variable_name $@)
}
#
# make sure the module name fits the naming convention
# (we try to assign the name and report upon failure)
#
# parameters:
# $1 module name
#
check_modname() {
# magic pattern match, see http://www.linuxmisc.com/9-unix-questions/67d307ca51f16ed4.htm
[ -n "${1##*[!A-Za-z0-9_]*}" ] && [ -n "${1##[!A-Za-z_]*}" ]
}
#
# compare a sub part of the version string
#
# parameters:
# $1 version
# $2 part
#
# parts:
# 1: major
# 2: minor
# 3: revision
#
get_sub_version() {
#it would be nice to give the part to awk via a "-v FIELD=$2"
#command line argument. Unfortunatly, SunOS does not support this.
#Worse, we cannot use awks int() function here, since under SunOS it
#will always return 0 for string input.
echo $1 | cut -d. -f"$2" | sed 's/[^0-9].*$//;s/^$/0/'
}
#
# compare two versions
#
# parameters:
# $1 version1
# $2 version1
#
# return:
# 0: equal
# 1: v1 > v2
# 2: v1 < v2
#
compare_versions() {
local v1="$1"
local v2="$2"
for i in 1 2 3; do
compare_sub_version $v1 $v2 $i || return 0
done
echo "eq"
}
compare_sub_version() {
# compare sub version number
local sub1=`get_sub_version $1 $3`
local sub2=`get_sub_version $2 $3`
if test $sub1 -gt $sub2; then
echo "gt"
return 1
fi
if test $sub1 -lt $sub2; then
echo "lt"
return 1
fi
return 0
}
check_version() {
if test -z "$2"; then # if no constraint is given, check_version is true
return 0
fi
local v=$1
local PATTERN="^ *\([<>=]*\) *\([0-9.]*\)\(.*\)$"
if test x != `echo "$2" | sed -e "s/$PATTERN/x/"`; then
echo "ERROR: invalid version constraint $2" >&2
exit 1
fi
local op=`echo "$2" | sed -e "s/$PATTERN/\1/"`
local v2=`echo "$2" | sed -e "s/$PATTERN/\2/"`
local rest=`echo "$2" | sed -e "s/$PATTERN/\3/" -e 's/ //g'`
local result=1
local rel=`compare_versions $v $v2`
case $rel$op in
"eq<="|"eq="|"eq>="|\
"gt>="|"gt>"|\
"lt<="|"lt<")
result=0
;;
esac
if test -z "$rest"; then
return $result
fi
PATTERN="\([|&]\{2\}\)\(.*\)$"
if test xx != x`echo "$rest" | sed -e "s/$PATTERN/x/"`; then
echo "ERROR: invalid version constraint '$rest'" >&2
exit 1
fi
op=`echo "$rest" | sed -e "s/$PATTERN/\1/"`
v2=`echo "$rest" | sed -e "s/$PATTERN/\2/"`
if eval "test $result -eq 0" $op "check_version \"$v\" \"$v2\""; then
return 0
fi
return 1
}