#!/bin/sh # This purports to allow distributions to be built with shared libraries. # # I wrote it for Kpathsea and friends, but I don't think there's # anything TeX-specific in here. # # There is a much fancier libtool project underway by # , but I did not want to wait for that to be # completed, stable, and portable before releasing Web2c. The ideas are # based on Gord's Libtool, though, and you may find its documentation # interesting/useful reading. # # Porting this to other systems shouldn't be too hard, mostly because I # don't try to take advantage of all the fancy features offered by some # os's (like multiple version compatibility, encoding directory paths in # the binary, etc.) See the configure mode. I can send you the # hello,world Makefile I used for testing if you want it. # rcs_version='$Id: klibtool,v 1.11 2002/11/11 09:43:05 olaf Exp $' version=0.1 maint=tex-k@mail.tug.org help="Usage: $0 [OPTION]... MODE [ARG]... Help for building and linking with shared libraries. Modes: Environment variables used: configure [HOSTTYPE] RANLIB, LIBTOOL_OBJTYPES compile CC SOURCEFILE ARG... archive AR ARFLAGS LIBNAME ARG... link CC ARG... install-lib DIR LIBNAME... INSTALL_DATA install-prog DIR PROGNAME... INSTALL_PROGRAM version Options: --source-dir DIR --config-dir DIR -n, --dry-run --help --quiet, --silent -v, --verbose --version Email bugs to $maint. " bug_report="$0: Please report this bug to $maint. Please mention this is Klibtool version $version ($rcs_version), and your hardware/operating system (`uname -a`, at least). Running this same command ($0 $*) with --verbose and including the resulting output would be nice, but is not required." verbose=: chicken= show=echo config_dir= source_dir= # Yes, this option parsing is imperfect, e.g., -xcruddy will be # recognized as --config-dir. I don't think it's worth the trouble to # program correctly until somebody besides me uses this. while test $# -gt 0; do case "$1" in configure|compile|archive|link|install-lib|install-prog|version) mode=$1; break;; --source-dir) # --source-dir shift; source_dir=$1;; -*c*) # --config-dir shift; config_dir=$1;; -n|-*d*) # -n, --dry-run chicken=echo;; -*help) # --help echo "$help"; exit 0;; -*q|-*s) # --quiet, --silent show=:; verbose=:;; -v|-*verb*) # --verbose verbose=echo;; -*version) # --version echo "$0 version $version ($rcs_version)"; exit 0;; -*) echo "$0: Unknown option \`$1'. Try --help for info." >&2; exit 1;; *) echo "$0: Unknown mode \`$1'. Try --help for info." >&2; exit 1;; esac shift done # Read all the arguments. Peel off the mode. shift # # Read the configuration file unless we're configuring. # if test $mode != configure; then # Read configuration file. If we have it in the current directory, or # the user told us where it is, great. More likely, though, it's only # in the directory with the library that we're eventually going to # link with. We have no way of knowing what that is, so let's use the # location of this script itself as the default if not in `.', `..', # or `../..'. test -z "$config_dir" && config_dir=$KLIBTOOL_CONFIG_DIR if test -z "$config_dir"; then if test -r ./klibtool.config; then config_dir=. elif test -r ../klibtool.config; then config_dir=.. elif test -r ../../klibtool.config; then config_dir=../.. else dir=`echo $0 | sed 's,/[^/]*$,,'` test -r $dir/klibtool.config && config_dir=$dir fi fi if test -z "$config_dir"; then echo "$0: Cannot find klibtool.config in . or .. or $dir," >&2 echo "$0: and no --config-dir option specified" >&2 echo "$0: or KLIBTOOL_CONFIG_DIR environment variable set." >&2 exit 1 fi # Read the file. . $config_dir/klibtool.config if test -z "$LIBTOOL_OBJTYPES"; then echo "$0: Impossibly empty LIBTOOL_OBJTYPES!" >&2 echo "$bug_report" >&2 exit 1 fi # Copy the valid object type names from LIBTOOL_OBJTYPES into objtypes. $verbose "$0: checking LIBTOOL_OBJTYPES = $LIBTOOL_OBJTYPES." objtypes= for ot in `echo $LIBTOOL_OBJTYPES | tr : " "`; do case $ot in SHARED) if $shared_support; then objtypes=$objtypes:$ot else echo "$0: Shared libraries not supported on this system." >&2 fi ;; STATIC) objtypes=$objtypes:$ot;; "") true;; # don't worry about empty object types. *) echo "$0: Ignoring unknown libtool object type $objtype." >&2;; esac done # Remove the extra trailing colon from our list-building. objtypes=`echo $objtypes | sed 's/^://'` if test -z $objtypes; then # If they just took the default, we started with STATIC and so # wouldn't be here. echo "$0: No valid object types in $LIBTOOL_OBJTYPES, quitting." >&2 exit 1 fi $verbose "$0: final objtypes = $objtypes." fi # # Do the deed. # # I wish we had subroutines so we could make this readable, but shell # functions aren't portable enough, even nowadays. # $verbose "$0: mode = $mode." case $mode in # configure mode: [HOSTTYPE] configure) # If no config dir specified, use the script location. if test -z "$config_dir"; then if echo $0 | grep / >/dev/null; then config_dir=`echo $0 | sed 's,/[^/]*$,,'` else config_dir=. # $0 is just the script name, no directory part. fi fi if test -z "$source_dir"; then source_dir=$config_dir fi config_file=$config_dir/klibtool.config config_tmp=$config_dir/klt$$.tmp $verbose "$0: writing to config_file = $config_file." # If no specified HOSTTYPE, guess it. if test $# -eq 0; then config_guess=$source_dir/config.guess if test ! -r $config_guess; then echo "$0: config.guess not in $source_dir." >&2 echo "$0: Either specify a host type or get the scripts." >&2 exit 1 fi host_alias=`$config_guess` else test $# -eq 1 \ || echo "$0: Using $1 as host alias, ignoring other arguments ($*)." >&2 host_alias=$1 fi # Convert the original host type to canonical form. config_sub=$source_dir/config.sub if test ! -r $config_sub; then echo "$0: config.sub missing from $source_dir; it's required." >&2 exit 1 fi host_type=`$config_sub $host_alias` if test -z "$host_type"; then echo "$0: $host_alias not a recognizable host type." >&2 exit 1 fi # Define defaults, to be overridden in the system-specific cases. config_vars="LIBTOOL_OBJTYPES shared_support shared_ext libpath_var CC args_STATIC_compile args_SHARED_compile args_STATIC_archive STATIC_ranlib args_SHARED_archive args_SHARED_link SHARED_postinstall" for v in $config_vars; do # Preserve existing value of a couple variables. case $v in LIBTOOL_OBJTYPES|CC) true;; *) eval $v=;; esac done test -z "$LIBTOOL_OBJTYPES" && LIBTOOL_OBJTYPES=STATIC shared_ext=so libpath_var=LD_LIBRARY_PATH STATIC_ranlib=$RANLIB # The compiler. If the user set CC, take that, else use gcc if we # can find it, else use cc. Up to the user to avoid /usr/ucb/cc. if test -z "$CC"; then for dir in `echo $PATH | tr : ' '`; do test -z "$dir" && dir=. if test -f $dir/gcc; then CC=gcc break elif test -f $dir/gcc.exe; then CC=gcc break fi done fi test -z "$CC" && CC=cc # # But the real question is not the name of the command, it's whether # it is GNU C. We only distinguish gcc and system cc. We have to # assume that they use the same compiler at `klibtool compile' time # as we determine here; the whole point is that we don't want to do # this check before compiling every file (for speed). rm -f conftest.c ( echo "#ifdef __GNUC__" echo "yes;" echo "#endif" ) >conftest.c if eval "$CC -E conftest.c" | grep yes >/dev/null 2>&1; then compiler=gcc args_SHARED_compile=-fPIC # should we have an option for -fpic? args_SHARED_archive=-shared else compiler=cc fi rm -f conftest.c # Override defaults for this system. case $host_type in *-*-linux*) shared_support=true SHARED_postinstall='ldconfig $libdir' ;; *-*-solaris2*) shared_support=true if test $compiler = cc; then # /opt/SUNWspro/cc, that is. args_SHARED_compile=-KPIC args_SHARED_archive="-G -z text" # Perhaps should have -h. fi ;; *-*-sysv4.2*) shared_support=true if test $compiler = cc; then # /opt/ccs/bin/cc, that is. args_SHARED_compile=-KPIC args_SHARED_archive="-G -z text" fi ;; *-*-sunos4*) shared_support=true STATIC_ranlib=ranlib SHARED_postinstall='ldconfig $libdir' if test $compiler = cc; then args_SHARED_compile=-PIC prog_SHARED_archive=ld args_SHARED_archive="-assert pure-text" # gord has -Bstatic? fi ;; *-*-irix5*|*-*-irix6*) shared_support=true args_SHARED_compile= ;; *-*-*djgpp* | *-*-*go32* | *-*-msdos*) shared_support=false libpath_var=LIBRARY_PATH ;; *-*-freebsd*) shared_support=true SHARED_postinstall='ldconfig -m $libdir' ;; hppa*-*-hpux*) if test $compiler = gcc; then shared_support=true args_SHARED_archive='-shared -fPIC' shared_ext='sl' fi ;; *-*-darwin*) STATIC_postinstall='$STATIC_ranlib $libdir/$lib_basename' ;; *) echo "$0: $host_type not explicitly supported, using defaults." >&2 ;; esac # Finally, we'll be creating something. rm -f $config_tmp # Output values. for v in $config_vars; do config_line=$v=\'`eval echo '$'$v`\' $verbose "$0: writing config line $config_line." echo $config_line >>$config_tmp done # Check if this changed anything. if cmp -s $config_file $config_tmp 2>/dev/null; then $verbose "$0: $config_file is unchanged" rm -f $config_tmp else rm -f $config_file mv $config_tmp $config_file fi ;; # compile mode: CC SOURCEFILE [ARG]... compile) compiler=$1; shift # must assume it's what configure found sourcefile=$1 objname=`basename $sourcefile | sed 's/\.[^./]*$//'`.o $verbose "$0: object basename for source file $sourcefile = $objname." # for ot in `echo $objtypes | tr : " "`; do # Snarf arguments for this object type. ot_args=`eval echo '$'args_${ot}_${mode}` $verbose "$0: args_${ot}_${mode} = $ot_args." # Have to output into a subdirectory of current directory (not # source directory, which might be read-only). output_arg="-o $ot/$objname" if test ! -d $ot; then $show mkdir $ot $chicken mkdir $ot fi # Construct and run the cmd. cmd="$compiler ""$@"" $ot_args $output_arg" $show $cmd $chicken eval "$cmd" status=$? test $status -eq 0 || break done # end of objtypes loop for compile mode test $status -eq 0 && date >./"`echo $objname | sed 's/o$/lo/'`" exit $status ;; # archive mode archive) cmdname=$1; shift # the archiver for ot in `echo $objtypes | tr : " "`; do libname= args= if test $ot = SHARED; then # Can't generally use ar to make a shared library. old_ar="$cmdname $1" shift ot_args=`eval echo '$'args_SHARED_archive` ot_prog=`eval echo '$'prog_SHARED_archive` test -z "$ot_prog" && ot_prog=$CC cmdname="$ot_prog $ot_args -o" $verbose "$0: replaced $old_ar with $cmdname." fi # Now transform remaining arguments (presumably filenames). for arg in "$@"; do case "$arg" in *.l[ao]) # Remove the `l' from a .la or .lo filename. newarg=`echo $arg | sed 's/l\(.\)$/\1/'` $verbose "$0: transformed arg $arg to $newarg." # First .la file is the output library. if test -z "$libname" && echo $newarg | grep '\.a$'>/dev/null; then if test $ot = SHARED; then $verbose "$0: running $0 version $newarg." verstring=`$0 version $newarg` $verbose "$0: got version $verstring." libname=`echo $newarg | sed 's/\.a$/\.'$shared_ext$verstring/` else libname=$newarg fi if echo $libname | grep / >/dev/null; then lib_dir=`echo $libname | sed 's,/[^/]*$,,'` else lib_dir=. fi lib_basename=`basename $libname` lib_base=`echo $lib_basename | sed 's/[.0-9]*$//'` # We might have to run a command after making the library. post= if test $ot = SHARED; then # If it supports shared libraries, it supports symlinks. # Although this is unnecessary on (e.g.) SunOS, it # doesn't hurt, and it is necessary on (e.g.) Solaris. post="&& rm -f $lib_base && ln -s $lib_basename $lib_base" elif test $ot = STATIC; then ranlib=`eval echo '$'${ot}_ranlib` $verbose "${ot}_ranlib = $ranlib." test -n "$ranlib" && post="&& $ranlib $lib_basename" fi $verbose "$0: output library dir = $lib_dir." $verbose "$0: output library basename = $lib_basename." $verbose "$0: output library base = $lib_base." newarg=$lib_basename fi arg=$newarg ;; esac args="$args $arg" done if test -z "$libname"; then # FIXME: should check that the .la file was second arg. echo "$0 archive: .la (libtool archive) argument missing." >&2 exit 1 fi # Remove old archive file because we recommend `ar q', not `r', # and the user can't necessarily know the library name. We remove # both $lib_base and $lib_base* because on MS-DOS the latter may not # match the former. cmd="cd $lib_dir/$ot && rm -f $lib_base $lib_base* && $cmdname $args $post" $show $cmd $chicken eval "($cmd)" status=$? test $status -eq 0 || break # If making both objtypes, need original arguments next time through. if test $ot = SHARED; then cmdname=$old_ar else true # Don't think we failed to make the library just because # the last objtype was not SHARED. fi done # end of objtypes loop for archive mode # # Create the .la file we claimed that we would. test $status -eq 0 && date >./"`echo $libname | sed 's/\.[^/]*$/.la/'`" exit $status ;; # link mode link) cmdname=$1; shift # the linker # Do links using the first object type. linktype=`echo $objtypes | sed 's/:.*//'` $verbose "$0: linktype = $linktype." if test -z "$linktype"; then echo "$0: Impossibly empty linktype?!" >&2 exit 1 fi # # Need to know the output name if we generate the wrapper. looking_for_output=false real_output_name= libpath= # for arg in "$@"; do if $looking_for_output; then real_output_name=$arg arg=$real_output_name.exe looking_for_output=false fi case "$arg" in -o) test $linktype = SHARED && looking_for_output=true ;; *.l[ao]) # Find .la files in the linktype subdir # of the given directory, e.g., if the user says # ../foo/libfoo.la, transform into # ../foo/$linktype/libfoo.{a,so...}. We do the same for # libtool archive, although it's not as often necessary. newarg=`echo $arg | sed -e 's,\([^/]*\)$,'$linktype'/\1,' \ -e 's/l\(.\)$/\1/'` if test $linktype = SHARED \ && echo $newarg | grep '\.a$' >/dev/null; then # If shared, transform dir/foo.la into -Ldir -lfoo. dir=`echo $newarg | sed 's,/[^/]*$,,'` lib=`echo $newarg | sed -e 's,.*/\([^/]*\),\1,' \ -e 's/^lib//' -e 's/\.a$//'` newarg="-L$dir -l$lib" # Remember we will need this directory in LD_LIBRARY_PATH. if echo $dir | grep -v '^/' >/dev/null; then dir=`pwd`/$dir fi # Maybe had previous directories. test -n "$libpath" && libpath=$libpath: libpath=$libpath$dir fi $verbose "$0: transformed .la arg $arg to $newarg." arg=$newarg ;; *.lo) # .lo files have no directory stripping or suffix changing. newarg=`echo $arg | sed -e 's,\([^/]*\)$,'$linktype'/\1,' \ -e 's/l\(.\)$/\1/'` $verbose "$0: transformed .lo arg $arg to $newarg." arg=$newarg ;; esac args="$args $arg" done # Set up to generate wrapper shell script if shared. if test $linktype = SHARED; then if $looking_for_output; then echo "$0 link: -o requires an argument." >&2 exit 1 fi if test -z "$real_output_name"; then # If they didn't give -o at all, default to a.out. real_output_name=a.out args="$args -o $real_output_name.exe" fi fi # Do the link. cmd="$cmdname $args" $show $cmd $chicken eval "$cmd" status=$? if test $status -eq 0 && test -n "$real_output_name"; then $verbose "$0: creating wrapper $real_output_name." # We created the binary, so create the wrapper script. # Use as short a temporary suffix as we can to minimize the chance # of collisions on deficient systems. rm -f ${real_output_name} ${real_output_name}T ( libpath_var_val='$'$libpath_var echo "#! /bin/sh" echo "# Generated `date` by $0." echo "# Do not install this wrapper script." echo "# It's only here to enable running $real_output_name" echo "# before it's installed. (Use $0 install-progs to install it.)" echo echo "if test -z \"$libpath_var_val\"; then" echo " $libpath_var=\"$libpath\"" echo "else" echo " $libpath_var=\"$libpath:$libpath_var_val\"" echo "fi" echo "export $libpath_var" echo echo "thisdir=\`echo $""0 | sed 's%/[^/]*$%%'\`" echo 'test "x$thisdir" = "x$0" && thisdir=.' echo echo "exec \$thisdir/$real_output_name.exe \"\$@\"" ) >${real_output_name}T chmod +x ${real_output_name}T mv ${real_output_name}T ${real_output_name} fi exit $status ;; # install-lib mode: DIR LIBNAME... install-lib) if test $# -lt 2; then echo "$0 install-lib: Need directory and at least one library." >&2 exit 1 fi libdir=$1; shift if test ! -d $libdir; then echo "$0 install-lib: $1 not a directory." >&2 exit 1 fi for arg in "$@"; do # for each library... # Always having a directory part simplifies the code below. echo $arg | grep / >/dev/null || arg="./$arg" for ot in `echo $objtypes | tr : " "`; do # for each object type... case $ot in SHARED) # needs shared extension and version number. verstring=`$0 version $arg` libname=`echo $arg | sed 's/\.la$/\.'$shared_ext$verstring/` ;; STATIC) # just get rid of the `l'. libname=`echo $arg | sed 's/l\(.\)$/\1/'` ;; *) echo "$0: Impossible object type $ot!" >&2 echo "$bug_report" >&2 exit 1;; esac # Have to insert the object type directory. libname=`echo $libname | sed 's,\(/[^/]*\)$,/'$ot'\1,'` lib_basename=`basename $libname` $verbose "$0: library name = $libname." $verbose "$0: installation name = $libdir/$lib_basename." cmd="${INSTALL_DATA-cp} $libname $libdir/$lib_basename" # if test $ot = SHARED; then # Link libfoo.so to libfoo.so.1.2.3. lib_base=`echo $lib_basename | sed 's/[.0-9]*$//'` $verbose "$0: linking $libdir/$lib_base to $lib_basename" (cd $libdir && rm -f $lib_base && ln -s $lib_basename $lib_base) fi # # Run ldconfig, etc. postinstall=`eval echo '$'${ot}_postinstall` test -n "$postinstall" && cmd="$cmd && $postinstall" $show $cmd $chicken eval "$cmd" done done ;; # install-prog mode: DIR PROGNAME... install-prog) if test $# -lt 2; then echo "$0 install-prog: Need directory and at least one program." >&2 exit 1 fi dir=$1; shift if test ! -d $dir; then echo "$0 install-prog: $1 not a directory." >&2 exit 1 fi # The program got linked using the first object type, so # installation of the program will proceed accordingly. linktype=`echo $objtypes | sed 's/:.*//'` $verbose "$0: linktype = $linktype." if test -z "$linktype"; then echo "$0: Impossibly empty linktype?!" >&2 exit 1 fi if test $linktype = SHARED; then # Install the binary, not the wrapper script. suffix=.exe else suffix= fi for arg in "$@"; do # for each program... # FIXME: On SunOS, AIX, and HP-UX, relink to avoid hardwired libdirs. cmd="${INSTALL_PROGRAM-cp} $arg$suffix $dir/$arg" $show $cmd $chicken eval "$cmd" done ;; # version mode version) if test $# -ne 1; then echo "$0 version: Exactly one argument needed, not $# ($*)." >&2 exit 1 fi # Strip directory name, `lib' prefix, and any .suffix. dir=`echo $1 | sed 's,/[^/]*$,,'` test "x$dir" = "x$1" && dir=. libname=`basename $1 | sed -e 's,^lib,,' -e s',\..*$,,'` verfile=$dir/klibtool.version if test ! -r $verfile; then echo "$0 version: No file $verfile for $libname version info." >&2 echo "$0 version: Original argument was $1." >&2 exit 1 fi $verbose "$0: dir = $dir, libname = $libname." version=`awk '$1 == "'$libname'" { print "." $2 "." $3 "." $4 }' $verfile` $verbose "$0: version for $libname = $version." echo $version ;; # unknown mode *) echo "$0: Impossible mode \`$mode'!" >&2 echo "$bug_report" >&2 exit 1 ;; esac