#!/bin/bash
#
# bld -- Build archives, shared libraries and executables (like a mini libtool)
# Copyright (c) Mbedthis LLC, 2003-2004. All Rights Reserved.
#
# It is expected that bld is only invoked from the Mbedthis build systems. It
# expects make.var and make.rules to have defined all the necessary compile 
# and link flag environment variables.
#
#
###############################################################################
#
#	Must exit if anything goes wrong
#
set -e 

###############################################################################

. ${BLD_TOP}/config.sh

###############################################################################

usage() {
    cat <<!EOF
bld: usage: bld [options] objects ....
    Options:
    --debug                 Not implemented
    --dry-run               Trace commands but do not execute
    --entry name            Shared library entry point
    --executable name       Name of executable to build
    --graphical             Create a windowed program instead of a console.
    --help                  Print usage information
    --library name          Name of library to link
    --libraryPath "paths"   Paths to search for the libraries
    --libs libraries        Extra libraries to link with
    --native                Build for local execution. Use BLD_LD_FOR_BUILD.
    --nostdlib              Don't use standard libraries (UNIX only)
    --objects "objects..."  String containing objects to link
    --objectsList path      File containing list of objects to link
    --objectsDir path       Directory to find / store objects
    --quiet                 Run quietly without tracing actions to stdout
    --preferShared          Link with shared libraries by preference
    --preferStatic          Link with static libraries by preference
    --resources file        Resource file (menus and icons for Windows)
    --rpath path            Specify the executable run-time library search path
    --shared                Only make a shared library
    --soname                Create a versioned shared library with soname
    --static                Only make a static library
    --smartLibs libraries   Use shared or static intelligently depending on
                            --preferShared, --preferStatic and 
                            BLD_FEATURE_STATIC and BLD_FEATURE_SHARED
    --version               Print the bld version

    Environment variables used:
      BLD_TOP               Top of the source tree
      BLD_FEATURE_SHARED    Build shared libraries where relevant.
      BLD_FEATURE_STATIC    Build static libraries where relevant.

    Configuration files used:
        config.sh
!EOF
    exit 255
}

###############################################################################
#
#	Some shells don't have a standard getopts. Must supply our own.
#
#	Spec chars: ';' == no arg, ':' required arg, '.' optional arg
#

getoptex()
{
	let $# || return 1
	local optlist="${1#;}"
	let OPTIND || OPTIND=1
	[ $OPTIND -lt $# ] || return 1
	shift $OPTIND
	if [ "$1" != "-" -a "$1" != "${1#-}" ]
	then OPTIND=$[OPTIND+1]; if [ "$1" != "--" ]
	then
		local o
		o="-${1#-$OPTOFS}"
		for opt in ${optlist#;}
		do
			OPTOPT="${opt%[;.:]}"
			unset OPTARG
			local opttype="${opt##*[^;:.]}"
			[ -z "$opttype" ] && opttype=";"
			if [ ${#OPTOPT} -gt 1 ]
			then # long-named option
				case $o in
				"--$OPTOPT")
					if [ "$opttype" != ":" ]; then return 0; fi
					OPTARG="$2"
					if [ "$OPTARG" != "${OPTARG#-}" ];
					then # error: must have an agrument
						let OPTERR && \
							echo "$0: error: $OPTOPT must have an argument" >&2
						OPTARG="$OPTOPT";
						OPTOPT="?"
						return 1;
					fi
					OPTIND=$[OPTIND+1] # skip option's argument
					return 0
				;;
				"--$OPTOPT="*)
					if [ "$opttype" = ";" ];
					then	# error: must not have arguments
						let OPTERR && \
						  echo "$0: error: $OPTOPT must not have arguments" >&2
						OPTARG="$OPTOPT"
						OPTOPT="?"
						return 1
					fi
					OPTARG=${o#"--$OPTOPT="}
					return 0
				;;
				esac
			else # short-named option
				case "$o" in
					"-$OPTOPT")
						unset OPTOFS
						[ "$opttype" != ":" ] && return 0
						OPTARG="$2"
						if [ -z "$OPTARG" ]
						then
							echo "$0: error: -$OPTOPT must have an argument" >&2
							OPTARG="$OPTOPT"
							OPTOPT="?"
							return 1
						fi
						OPTIND=$[OPTIND+1] # skip option's argument
						return 0
					;;
					"-$OPTOPT"*)
						if [ $opttype = ";" ]
						then 
							# option with no argument is in a chain of options
							# move to the next option in the chain
							# the chain still has other options
							OPTOFS="$OPTOFS?" 
							OPTIND=$[OPTIND-1] 
							return 0
						else
							unset OPTOFS
							OPTARG="${o#-$OPTOPT}"
							return 0
						fi
					;;
				esac
			fi
		done
		echo "$0: error: invalid option: $o"
	fi; fi
	OPTOPT="?"
	unset OPTARG
	return 1
}

###############################################################################
#
#	Main
#

if [ -x ${BLD_TOOLS_DIR}/bldout ]
then LOG=bldout
else LOG=cat
fi

echo
echo "    # bld $@" | $LOG

ARCHIVE=
DEBUG=
DO=
ENTRY=
EXECUTABLE=
FILE_LIST=
GRAPHICAL=
LIBRARY=
LIBRARY_PATHS=
LIBS=
MAKE_SHARED=0
MAKE_STATIC=0
NATIVE=0
NOSTDLIB=0
RESOURCES=
OBJECTS=
PREFER_SHARED=0
PREFER_STATIC=0
RPATH=
SHARED_LIBRARY=
SMART_LIBS=
SONAME=0
VERSION=1.0

#
#	Reset default directory locations. We require explicit --objectsDir
#
BLD_OBJ_DIR=.

while getoptex "\
	debug \
	dry-run \
	entry: \
	executable: \
	graphical \
	help \
	libs: \
	library: \
	libraryPath: \
	native \
	nostdlib \
	objects: \
	objectsDir: \
	objectList: \
	preferShared \
	preferStatic \
	quiet \
	resources: \
	rpath: \
	shared \
	smartLibs: \
	soname \
	static \
	version" \
"$@"
do
	case "$OPTOPT" in 
	debug)
		DEBUG=1
		echo "Not yet implemented"
		;;
	dry-run)
		DO=:
		;;
	entry)
		ENTRY="$OPTARG"
		;;
	executable)
		EXECUTABLE="$OPTARG"
		;;
	graphical)
		GRAPHICAL=1
		;;
	help)
		usage
		;;
	library)
		LIBRARY="$OPTARG"
		;;
	libraryPath)
		LIBRARY_PATHS="$LIBRARY_PATHS $OPTARG"
		;;
	libs)
		LIBS="$LIBS $OPTARG"
		;;
	native)
		BLD_CC=$BLD_CC_FOR_BUILD
		BLD_LD=$BLD_LD_FOR_BUILD
		BLD_CFLAGS=
		BLD_IFLAGS=
		BLD_LDFLAGS=
		NATIVE=1
		;;
	nostdlib)
		NOSTDLIB=1
		;;
	objects)
		OBJECTS="$OBJECTS ${OPTARG}"
		;;
	objectsDir)
		#
		#	Objects may use this variable in their names
		#
		BLD_OBJ_DIR="${OPTARG}"
		;;
	objectList)
		FILE_LIST="$FILE_LIST $OPTARG"
		;;
	preferShared)
		PREFER_SHARED=1
		;;
	preferStatic)
		PREFER_STATIC=1
		;;
	quiet)
		;;
	resources)
		RESOURCES="$OPTARG"
		;;
	rpath)
		RPATH="$OPTARG"
		;;
	shared)
		MAKE_SHARED=1
		;;
	smartLibs)
		SMART_LIBS="$SMART_LIBS $OPTARG"
		;;
	soname)
		SONAME=1
		;;
	static)
		MAKE_STATIC=1
		;;
	version)
		echo $VERSION
		exit 0
		;;
	*)	echo "bld: bad option: $OPTOPT"
		exit 2
		;;
	esac
done

shift $[OPTIND-1]

if [ "$LIBRARY" -a $MAKE_STATIC = 0 -a $MAKE_SHARED = 0 ]
then
	echo "Must specify either --static or --shared or both switches".
	exit 255
fi

if [ $BLD_FEATURE_SHARED = 0 -a $PREFER_SHARED = 1 ]
then
	PREFER_STATIC=1
fi

if [ "${FILE_LIST}" ]
then
	for f in ${FILE_LIST}
	do
		OBJECTS="$OBJECTS `cat ${f}`"
	done
fi

#
#	Expand make variables in the objects
#
OBJECTS=`eval echo ${OBJECTS} $*`

#
#	Prepare for action
#
if [ "$BLD_OS" = "WIN" ]
then
	paths=
	for p in ${LIBRARY_PATHS} 
	do
		paths="${paths} -libpath:${p}"
	done
	LIBRARY_PATHS=${paths}

	LIB_LIST=
	for l in ${SMART_LIBS} 
	do
		if [ $BLD_FEATURE_STATIC = 1 -a $PREFER_STATIC = 1 ]
		then
			lib=`echo lib${l}Static${BLD_ARCHIVE} | \
				sed 's/StaticStatic/Static/'`
			LIB_LIST="${LIB_LIST} ${lib}"
		else
			LIB_LIST="${LIB_LIST} lib${l}${BLD_SHLIB}"
		fi
	done

	for l in ${LIBS} 
	do
		LIB_LIST="${LIB_LIST} lib${l}${BLD_SHLIB}"
	done

	if [ "$LIBRARY" != "" ]
	then
		if [ $MAKE_SHARED = 1 -a "$ENTRY" = "" ]
		then
			ENTRY="_DllMainCRTStartup@12"
		fi
	fi

	if [ "$EXECUTABLE" ]
	then
		if [ "$GRAPHICAL" ]
		then 
			ENTRY=WinMainCRTStartup
			SUBSYSTEM="WINDOWS"
		else 
			ENTRY=mainCRTStartup
			SUBSYSTEM="CONSOLE"
		fi
	fi

else
	#
	#	Unix / Linux
	#
	paths=
	for p in ${LIBRARY_PATHS} 
	do
		paths="${paths} -L${p}"
	done
	LIBRARY_PATHS=${paths}

	LIB_LIST=
	for l in ${SMART_LIBS}
	do
		if [ $BLD_FEATURE_STATIC = 1 -a $PREFER_STATIC = 1 ]
		then
			lib=`echo -l${l}Static | sed 's/StaticStatic/Static/'`
			LIB_LIST="${LIB_LIST} ${lib}"
		else
			LIB_LIST="${LIB_LIST} -l${l}"
		fi
	done

	for l in ${LIBS}
	do
		LIB_LIST="${LIB_LIST} -l${l}"
	done

	if [ "$RPATH" != "" ]
	then
		if [ "$BLD_OS" = "SOLARIS" ]
		then
			RPATH="-R$RPATH"
		else
			RPATH="-Wl,--enable-new-dtags -Wl,-rpath=$RPATH"
		fi
	fi
fi

if [ "$NOSTDLIB" = "0" -o "$BLD_UNIX" != 1 ]
then
	if [ "$NATIVE" = "1" ]
	then
		STND_LIBS=$_NATIVE_LIBS
	else
		if [ "$BLD_FEATURE_STATIC_LINK_LIBC" = "1" ]
		then
			STND_LIBS=$_STATIC_LIBS
		else
			STND_LIBS=$_SHARED_LIBS
		fi
	fi
fi

#
#	Now we do the actual work
#
if [ "$BLD_OS" = "WIN" ]
then
	if [ "$LIBRARY" != "" ]
	then
		if [ $MAKE_STATIC = 1 -a $BLD_FEATURE_STATIC = 1 ]
		then
			ARCHIVE=${LIBRARY}${BLD_ARCHIVE}
			echo "    ${BLD_AR} -nologo -out:${ARCHIVE} ${OBJECTS}" | $LOG
			${DO} ${BLD_AR} -nologo "-out:${ARCHIVE}" ${OBJECTS}
		fi

		if [ $MAKE_SHARED = 1 -a $BLD_FEATURE_SHARED = 1 ]
		then
			DEF=${LIBRARY}.def
			SHARED_LIBRARY=${LIBRARY}.dll
			echo "dumpext -o ${DEF} `basename ${SHARED_LIBRARY}` \
				${OBJECTS}" | $LOG
			dumpext -o ${DEF} `basename ${SHARED_LIBRARY}` ${OBJECTS}
			echo "    ${BLD_LD} -out:${SHARED_LIBRARY} ${_LD_DLL} ${_LD_OPT} \
				-entry:${ENTRY} -def:${DEF} ${MAKE_LDFLAGS} ${BLD_LDFLAGS} \
				${_LDFLAGS} ${LIBRARY_PATHS} -libpath:${_LDPATH} \
				${OBJECTS} ${LIB_LIST} ${STND_LIBS}" | $LOG
			${DO} ${BLD_LD} "-out:${SHARED_LIBRARY}" ${_LD_DLL} ${_LD_OPT} \
				-entry:${ENTRY} -def:${DEF} ${MAKE_LDFLAGS} ${BLD_LDFLAGS} \
				${_LDFLAGS} ${LIBRARY_PATHS} "-libpath:${_LDPATH}" \
				${OBJECTS} ${LIB_LIST} ${STND_LIBS} 
		fi
	fi

	if [ "$EXECUTABLE" ]
	then
		if [ "$RESOURCES" ]
		then
			RES_OBJ=`echo $RESOURCES | sed s/\.rc//`.res
			echo "    rc -fo $RES_OBJ $RESOURCES"
			rc -fo $RES_OBJ $RESOURCES
		else 
			RES_OBJ=
		fi

		echo "    ${BLD_LD} -out:${EXECUTABLE} ${_LD_OPT} -entry:${ENTRY} \
			-subsystem:${SUBSYSTEM} ${RES_OBJ} ${MAKE_LDFLAGS} ${BLD_LDFLAGS} \
			${_LDFLAGS} ${LIBRARY_PATHS} -libpath:${_LDPATH} ${OBJECTS} \
			${LIB_LIST} ${STND_LIBS}" | $LOG
		${DO} ${BLD_LD} "-out:${EXECUTABLE}" ${_LD_OPT} -entry:${ENTRY} \
			-subsystem:${SUBSYSTEM} ${RES_OBJ} ${MAKE_LDFLAGS} ${BLD_LDFLAGS} \
			${_LDFLAGS} ${LIBRARY_PATHS} "-libpath:${_LDPATH}" ${OBJECTS} \
			${LIB_LIST} ${STND_LIBS}
	fi

else
	#
	#	Unix (Linux or Solaris)
	#
	if [ "$LIBRARY" ]
	then
		if [ $MAKE_STATIC = 1 ]
		then
			ARCHIVE=${LIBRARY}${BLD_ARCHIVE}
			echo "    ${BLD_AR} cr ${ARCHIVE} ${OBJECTS}" | $LOG
			${DO} ${BLD_AR} cr ${ARCHIVE} ${OBJECTS}
		fi

		if [ $MAKE_SHARED = 1 ]
		then
			LOBJS=`echo ${OBJECTS} | sed "s/\.o/.lo/g"`
			if [ $SONAME = 1 ]
			then
				SHARED_LIBRARY=${LIBRARY}.so.${BLD_LIB_VERSION}
				SONAME_LIBRARY=${LIBRARY}.so.${BLD_LIB_VERSION%????}
				SONAME_SWITCH="-Wl,-soname=`basename $SONAME_LIBRARY`"
			else
				SHARED_LIBRARY=${LIBRARY}.so
				SONAME_SWITCH=""
			fi
			echo "    ${BLD_CC} -o ${SHARED_LIBRARY} ${SONAME_SWITCH} \
				${_LD_DLL} ${_LD_OPT} \
				${MAKE_LDFLAGS} ${BLD_LDFLAGS} ${_LDFLAGS} \
				${LIBRARY_PATHS} ${_LDPATH} ${RPATH} ${LOBJS} ${LIB_LIST}" \
				| $LOG
			eval ${DO} ${BLD_CC} -o ${SHARED_LIBRARY} ${SONAME_SWITCH} \
				${_LD_DLL} ${_LD_OPT} \
				${MAKE_LDFLAGS} ${BLD_LDFLAGS} ${_LDFLAGS} \
				${LIBRARY_PATHS} ${_LDPATH} ${RPATH} ${LOBJS} ${LIB_LIST}
			if [ $SONAME = 1 ]
			then
				rm -f ${SONAME_LIBRARY}
				ln -s `basename ${SHARED_LIBRARY}` ${SONAME_LIBRARY}
				LINK_LIBRARY=${LIBRARY}.so
				rm -f ${LINK_LIBRARY}
				ln -s `basename ${SONAME_LIBRARY}` ${LINK_LIBRARY}
			fi
		fi
	fi

	if [ "$EXECUTABLE" ]
	then
		if [ "$BLD_FEATURE_STATIC_LINK_LIBC" = "1" ]
		then
			echo "    ${BLD_CC} -o ${EXECUTABLE} ${_WARNING} ${_LD_OPT} \
				-static ${MAKE_LDFLAGS} ${BLD_LDFLAGS} ${_LDFLAGS} \
				${LIBRARY_PATHS} ${_LDPATH} ${RPATH} ${OBJECTS} \
				${LIB_LIST} ${STND_LIBS}" | $LOG
			eval ${DO} ${BLD_CC} -o ${EXECUTABLE} ${_WARNING} ${_LD_OPT} \
				-static ${MAKE_LDFLAGS} ${BLD_LDFLAGS} ${_LDFLAGS} \
				${LIBRARY_PATHS} ${_LDPATH} ${RPATH} ${OBJECTS} \
				${LIB_LIST} ${STND_LIBS}
		else
			echo "    ${BLD_CC} -o ${EXECUTABLE} ${_WARNING} ${_LD_OPT} \
				${MAKE_LDFLAGS} ${BLD_LDFLAGS} ${_LDFLAGS} \
				${LIBRARY_PATHS} ${_LDPATH} ${RPATH} ${OBJECTS} \
				${LIB_LIST} ${STND_LIBS}" | $LOG
			eval ${DO} ${BLD_CC} -o ${EXECUTABLE} ${_WARNING} ${_LD_OPT} \
				${MAKE_LDFLAGS} ${BLD_LDFLAGS} ${_LDFLAGS} \
				${LIBRARY_PATHS} ${_LDPATH} ${RPATH} ${OBJECTS} \
				${LIB_LIST} ${STND_LIBS}
		fi
	fi
fi

exit 0
