# $Id: librkcompsh,v 1.29 2007/03/22 16:51:34 tlaronde Exp $
#
# This is the KerGIS Tools' Bourne shell functions "library".
# It is used by `rkconfig' and `rkbuild'.
#
#  Copyright 2004 Thierry LARONDE <tlaronde@polynum.com>
#  All rights reserved. 
#  
#  This work is under the KerGIS Public Licence v1.0
# 
#  See the COPYRIGHT file at the root of the source directory or see
#  http://www.kergis.org/licence.txt for complete information.
# 
# !!!THIS SOFTWARE IS PROVIDED ``AS IS'' WITHOUT ANY WARRANTIES!!! 
#                      USE IT AT YOUR OWN RISK 

# This is handled internally.
#
rk_lddir=$OBJDIR/.rkcomp/ld

# add_once add a string only if not already present
# add_once VARIABLE STRING_TO_ADD
# returns new string (maybe unchanged).
#
rk_add_once()
{
	tmp=$(echo $1 | sed -e 's/^ *//' -e 's/ *$//' -e 's/ \{1,\}/ /')
	string=$(echo $2 | sed -e 's/^ *//' -e 's/ *$//' -e 's/ \{1,\}/ /')

	# XXX the dot is special in a regex so to allow relative path we
	# need to escape them.
	#
	regexp=$(echo $string | sed -e 's/\./\\./g' -e 's/\$/\\$/g')
	without=$(echo $tmp | sed -e "s@^$regexp\$@@" -e "s@^$regexp @@" -e "s@ $regexp\$@@" -e "s@ $regexp @@")
	if [ "x$tmp" != "x$without" ]; then
	  # Already here.
	  #
	  echo "$tmp"
	else
	  # Add it last.
	  #
	  echo "$tmp $string" | sed -e 's/^ *//' -e 's/ \{1,\}/ /'
	fi
}

# rk_check_required LIB_VAR_NAME [...]
# Verify that all the variables (specified by the var name i.e. MATHLIB
# and not $MATHLIB) are set i.e. non empty
# Check all with a debugging message and exits with error if one is
# missing.
# Calls the more low level rk__check_required() which specifies the 
# *.deps file from which to take description about the missing 
# dependencies.
#
rk_check_required()
{
	rk__check_required $PROJECT.deps "$@"
}

rk__check_required()
{
	info=$1
	shift
	crl_dep=
	missing=
	error=0
	while [ $# -gt 0 ]; do
	  crl_dep=$(eval echo \$$1)
	  if [ "x$crl_dep" != "x" ]; then
		msg="found ($crl_dep)"
	  else
		error=$(($error + 1))
		msg="not found!"
		missing="$missing $1"
	  fi
	  rk_debug L "Checking $1: $msg"
	  shift
	done
	if [ $error -gt 0 ]; then
	  rk_debug E "There were $error libes not found! Stop!"
	  rk_debug L ""
	  for missing_dep in $missing; do
	    debug L "$missing_dep is missing:"
	    debug L ""
	    sed -n /^$missing_dep/,'/^$/p' $info >&2
	    debug L ""
	  done
	  exit 1
	else
	  rk_debug L "OK. I continue..."
	fi
}

# rk_debug writes info (L for LOG), warning (W for WARNING), error
# (E for ERROR) or fatal (F for FATAL) for a programming unrecoverable
# error followed by the message on stderr.
#
rk_debug()
{
	case $1 in
	  A) echo "  ->" | tr -d '\n' 1>&2;;
	  E) echo "$program ERROR: " | tr -d '\n' 1>&2;;
	  F) echo "$program FATAL: " | tr -d '\n' 1>&2;;
	  L) echo "	" | tr -d '\n' 1>&2;;
	  W) echo "$program WARNING: " | tr -d '\n' 1>&2;;
	  *) echo "$1: " | tr -d '\n' 1>&2;;
	esac
    echo "$2" 1>&2
    return
}

# rk_derel suppress the ./ and ../ in a path
# rk_derel WORKING_DIR PATH_TO_DERELATIVATE
#
rk_derel()
{
	# If not beginning by a slash, put WORKING_DIR before.
	#
	derel_path=$(echo $2 | sed 's@^\([^/]\)@'$1'/\1@')
	derel_path=$(echo $derel_path | sed -e 's@/\./@/@g' -e 's@/\.$@@')
	if echo $derel_path | grep -q '^/\{0,1\}../'; then
	  error invalid_path $derel_path
	fi

	while echo $derel_path | grep -q '/../'; do
	  # Just the first occurrence will be replaced. We work from left
	  # to right.
	  #
	  derel_path=$(echo $derel_path | sed -e 's@/[^/]\{1,\}/\.\./@/@')
	  if echo $derel_path | grep -q '^/\{0,1\}../'; then
	    error invalid_path $derel_path
	  fi
	done
	
	# If there is a last '/..' take this into account.
	# Here, we are guaranteed that there is no more '/../' elsewhere
	# and that there is something before.
	#
	derel_path=$(echo $derel_path | sed 's@/[^/]\{1,\}/\.\.$@/@')

	# Suppress trailing '/' if something before.
	#
	derel_path=$(echo $derel_path | sed 's@\(.\)/$@\1@')

	echo $derel_path
}

# rk_deref : dereferencing symlinks.
# argument passed must be absolute.
#
rk_deref()
{
	pathname="$1"
	while [ -h "$pathname" ]; do
	  wd=$(dirname $pathname)
	  pathname=$(ls -l $pathname | sed 's/^.*-> *//')
	  pathname=$(derel $wd "$pathname")
	done

	echo $pathname
}

# rk_which_cmd_of search the first available occurrence of a space 
# separated list of cmds in the PATH.
#
rk_which_cmd_of()
{
	for cmd in $*; do
	  for path in `echo $RK_PATH | tr ':' ' '`; do
	    if [ -f "$path/$cmd" ] ; then
	      echo "$path/$cmd"
		  return
		fi
	  done
	done

	echo ""
}

# These functions translate the lib canonical name in the  TARGET
# static, static shared or dynamic shared (_?sh_) name.
#
rk_mk_ldname()
{
	basename $1 | sed 's@^lib\(.\{1,\}\)__[.0-9]*$@\1@'
}

rk_mk_ldaname()
{
	echo $1 | sed -e 's@__[.0-9]*$@__@' $LIB_A_TR
}

rk_mk_realaname()
{
	echo $1 | sed $LIB_A_TR
}

rk_mk_elf_realsoname()
{
	echo $1 | sed $LIB_DSH_TR
}

rk_mk_elf_soname()
{
	echo $1 | sed -e 's/__\([0-9]*\).*$/__\1/' $LIB_DSH_TR
}

rk_mk_elf_ldsoname()
{
	echo $1 | sed -e 's@__[0-9.]*$@__@' $LIB_DSH_TR
}

# rk_which_lib_of <library> <header>
# rk_which_lib_of first call rk_which_header_of to see if a header exists
# and if it founds one, it tries to locate a matching
# library with an "include/" subdir changed to "lib/". If it doesn't
# find one, it the tries the LIB_SEARCH_PATH in order.
# a "SYS" placed as a component of the colon separated search paths
# means to search the system paths. By default (set by the H_default),
# HEADER_SEARCH_PATH and LIB_SEARCH_PATH are set to the SYS.
# returns :
#	SUCCESS: echoes the fully qualified library pathname found and 
#   rk_add_lib with the infos if found;
#	FAILURE: echoes the empty string.
#
rk_which_lib_of()
{
	header="$2"
	# Normalized name.
	#
	nlib=$1

	# Static lib.
	#
	ralib=$(rk_mk_realaname $nlib)
	ldalib=$(rk_mk_ldaname $nlib)

	# Dynamic shared lib.
	#
	rsolib=$(rk_mk_elf_realsoname $nlib)
	solib=$(rk_mk_elf_soname $nlib)
	ldsolib=$(rk_mk_elf_ldsoname $nlib)

	if [ "$header" != "NULL" ]; then
	  header=$(rk_which_header_of $header)
	  [ "x$header" != "x"  ] || { echo ""; return; }
	fi

	# Now the library. Try first to find the library nearest the header.
	#
	guessed=$(dirname $header | sed 's@/include/@/lib/@')

	# Don't take '.' into account.
	#
	[ "$guessed" != "." ] || guessed=

	types=
	for path in `echo $guessed:$LIB_SEARCH_PATH | sed s@SYS@$SYS_LIB_PATH@ \
	 | tr ':' ' '`; do
	  [ ! -f "${M_CROSS_PATH_PREFIX}$path/$ralib" ] || types=static
	  [ ! -f "${M_CROSS_PATH_PREFIX}$path/$ldalib" ] || types=static
	  if [ "x$rsolib" != "x" -a -f "${M_CROSS_PATH_PREFIX}$path/$rsolib" ]; then
	    types="$types dshared"
	  fi
	  if [ "x$types" != "x" ]; then
		rk_add_lib ${M_CROSS_PATH_PREFIX}$path/$nlib $header $types
		#if cross path prefixed, add to map
		[ "x$M_CROSS_PATH_PREFIX" = "x" ] || rk_map_lib ${M_CROSS_PATH_PREFIX}$path/$nlib $path
	    return 
	  fi
	done

	# not found
	echo ""
}

# rk_which_header_of header.
#
rk_which_header_of()
{
	header=$1

	# Expand the search paths.
	# The headers are special in this sense that they do not mandatory
	# represent real files: they may be $CC artefacts. So no need to
	# define a SYS_HEADER_PATH, no -I will suffice. And we test via
	# pre preprocessor that is $CC -E.
	#
	echo "#include \"$header\"" > $tmpdir/$$.h
	for path in `echo $HEADER_SEARCH_PATH | tr ':' ' '`; do
	  if [ $path = "SYS" ]; then
		path=
	    flags=
	  else
	   flags=$(echo "-I$M_CROSS_PATH_PREFIX/$path" | sed 's@//@/@')
	  fi
	  if $CC -E $flags $tmpdir/$$.h >/dev/null 2>&1; then 
		# Headers may be relative and we will just use the path in
		# further processing. So the header is not usefull as is, but
		# only by its path. Hence the dot substitution.
		#
		header=$(echo $header | tr '/' '.')
		[ "x$path" = "x" ] || header="$path/$header"
		echo "$header" | tr -d '\n'
		return 
	  fi
	done

	echo ""
}

# rk_add_lib PATHNAME_TO_M_LIB PATHNAME_TO_HEADER.
# returns nothing.
# echoes the PATHNAME_TO_M_LIB.
#
# XXX PATHNAME means a fully qualified MATRIX pathname to the library. 
#
rk_add_lib()
{
	kal_lib="$1"
	kal_header="$2"
	shift; shift
	for type in "$@"; do
	  if [ ! -s $OBJDIR/.rkcomp/libes.$type ] || ! grep -q "^$kal_lib" $OBJDIR/.rkcomp/libes.$type; then
	    echo "$kal_lib" "$kal_header" >>$OBJDIR/.rkcomp/libes.$type
	  fi
	done
	echo "$kal_lib"
}

# rk_find_lib_rpath NORMALIZED_PATHNAME_OF_LIB.
# 	returns rpath.
#
# NOTE: lib must have been mapped (that is a correspondance between
# MATRIX used lib and TARGET pathname shall be recorded in PROJECT.map).
#
rk_find_lib_rpath()
{
	rkflr_nlib=$1

	# If there is no shared version, returns nothing.
	#
	grep -q "^$rkflr_nlib" $OBJDIR/.rkcomp/libes.dshared || { echo ""; return; }

	rkflr_lib=$(rk_mk_elf_realsoname  $1)
	rpath=`sed -n 's@^.\{4\}'$rkflr_lib' \([^ *]\{1,\}\) .*$@\1@p' $OBJDIR/.rkcomp/map | sed 1q`
	if [ "x$rpath" != "x" ]; then
	  rpath=$(dirname $rpath)
	else 
	  if [ -f "$OBJDIR/.rkcomp/libes_map" ]; then # try libes_map
	    rpath=`sed -n 's@^'$rkflr_lib' \([^ ]\{1,\}\)$@\1@p' $OBJDIR/.rkcomp/libes_map`
	  fi

	  # If not mapped, will use the dirname (identical mapping).
	  #
	  [ "x$rpath" != "x" ] || rpath=$(dirname $rkflr_lib)

	  # Add to the map to be verified at installation time.
	  #
	  echo '? f '$rkflr_lib' '$rpath/$(basename $rkflr_lib)' * *' >> $OBJDIR/.rkcomp/map 
	fi
	echo $rpath
}

# rk_map_lib NORMALIZED_PATHNAME_OF_LIB_ON_MATRIX  RPATH_ON_TARGET
# 	adds the rpath of the lib in .rkcomp/libes_map (map MATRIX present 
#	lib to TARGET present one).
#
rk_map_lib()
{
	echo "$1" "$2" >>$OBJDIR/.rkcomp/libes_map
}

# Symlinking stuff.
# We create a symlink in $rk_lddir pointing to the correct
# version of the lib (versioned static or shared).
#
rk_mk_link_ldaname()
{
	# Does a static version exist?
	#
	if [ -s $OBJDIR/.rkcomp/libes.static ] && grep -q "^$1" $OBJDIR/.rkcomp/libes.static; then
	  # Must take into account the fact that there is only a ldaname
	  # and no (rkcomp created) realaname.
	  #
	  if [ -e "$(rk_mk_realaname $1)" ]; then
	  ln -sf $(rk_mk_realaname $1) $rk_lddir/$(basename $(rk_mk_ldaname $1))
	  else
	    ln -sf $(rk_mk_ldaname $1) $rk_lddir/$(basename $(rk_mk_ldaname $1))
	  fi
	  rm -f $rk_lddir/$(basename $(rk_mk_elf_ldsoname $1))*
	else
	  rk_mk_link_elf_ldsoname $1
	fi
}

rk_mk_link_elf_ldsoname()
{
	# Does a dshared version exist?
	#
	if [ -s $OBJDIR/.rkcomp/libes.dshared ] && grep -q "^$1" $OBJDIR/.rkcomp/libes.dshared; then
	  ln -sf $(rk_mk_elf_realsoname $1) $rk_lddir/$(basename $(rk_mk_elf_ldsoname $1))
	  ln -sf $(rk_mk_elf_realsoname $1) $rk_lddir/$(basename $(rk_mk_elf_soname $1))
	  rm -f $rk_lddir/$(basename $(rk_mk_ldaname $1))
	else
	  rk_mk_link_ldaname $1
	fi
}
