# bindir.at - Test the -bindir option # # Copyright (C) 2009 Free Software Foundation, Inc. # Written by Dave Korn, 2009 # # This file is part of GNU Libtool. # # GNU Libtool is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # # GNU Libtool is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNU Libtool; see the file COPYING. If not, a copy # can be downloaded from http://www.gnu.org/licenses/gpl.html, # or obtained by writing to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #### #### # In this testcase, and in the chunk of code that makes use # of $bindir in ltmain.m4sh, we would very much have liked to # automatically decide which systems require dynamically-loaded # libraries to be installed to a directory in $PATH according # to the libtool properties that tell us that "the system provides # no way to hard-code library paths into executables, and also # has no $shlibpath_var independent of the PATH variable", in # Ralf's words. But it turns out this is not possible, as:- # #> * Dave Korn wrote on Fri, Aug 14, 2009 at 04:30:27AM CEST: #>>> Ralf Wildenhues wrote: #>>> #>>>>> But in this particular case, I would argue that either you look at #>>>>> the libtool variables shlibpath_var and hardcode_action for "PATH" #>>>>> and "unsupported". #>>> #>>> On Cygwin, $hardcode_action = "immediate" in the generated libtool #>>> script. Did you perhaps mean $hardcode_shlibpath_var, which is indeed #>>> "unsupported"? #> #> Oh brother, yes, I forgot about those bogus hardcode_libdir_flag_spec #> settings. They fool _LT_LINKER_HARDCODE_LIBPATH. I don't remember whether #> they were needed in order to not break some semantics in ltmain (they #> probably were). #> #> Anyway, $hardcode_action = "immediate" is definitely wrong, but fixing that #> now is also way out of scope of this patch. So I guess we need to stick to #> host matching in the code, and work out a separate fix for the setting of #> $hardcode_libdir_flag_spec. # # So alas we punt for now, and just hardcode the relevant OSs that require # this functionality. That's Cygwin, MinGW and CeGCC for now; see the case # statement in libtool.m4sh around where the 'tdlname' variable is set. #### # Verify compiling works, and skip remaining tests if not. # AT_SETUP([bindir compile check]) AT_DATA([simple.c] ,[[ int main() { return 0;} ]]) $CC $CPPFLAGS $CFLAGS -o simple simple.c || noskip=false rm -f simple AT_CHECK([$noskip || (exit 77)]) AT_CLEANUP #### # Now run the tests themselves. First a simple test that we can # build and run an executable with a couple of tiny libraries. AT_SETUP([bindir basic lib test]) bindirneeded=: case "$host_os" in cygwin*|mingw*|cegcc*) ;; *) bindirneeded=false ;; esac #### # These routines save the PATH before a test and restore it after, # prepending a chosen directory to the path on the platforms where # -bindir is needed after saving. # func_save_and_prepend_path () { save_PATH=$PATH if $bindirneeded ; then PATH=$1:$PATH fi export PATH } func_restore_path () { PATH=$save_PATH export PATH } AT_DATA([foo.c],[[ int x=11; ]]) AT_DATA([baz.c],[[ extern int x; int baz (void); int baz (void) { return x;} ]]) AT_DATA([bar.c],[[ extern int baz (void); int y=3; int bar (void); int bar (void) { return y + baz ();} ]]) AT_DATA([main.c],[[ #include extern int baz (void); extern int bar (void); int main() { if (baz () + bar () - 25) abort (); return 0; } ]]) curdir=`pwd` eval "`$LIBTOOL --config | grep '^objdir='`" AT_CHECK([$LIBTOOL --mode=compile --tag=CC $CC -c -o foo.lo $CPPFLAGS $CFLAGS foo.c],[0],[ignore],[ignore]) AT_CHECK([$LIBTOOL --mode=compile --tag=CC $CC -c -o baz.lo $CPPFLAGS $CFLAGS baz.c],[0],[ignore],[ignore]) AT_CHECK([$LIBTOOL --mode=link --tag=CC $CC -no-undefined -o libfoo.la $CPPFLAGS $CFLAGS $LDFLAGS foo.lo baz.lo -rpath $curdir/$objdir],[0],[ignore],[ignore]) AT_CHECK([$LIBTOOL --mode=compile --tag=CC $CC -c -o bar.lo $CPPFLAGS $CFLAGS bar.c],[0],[ignore],[ignore]) AT_CHECK([$LIBTOOL --mode=link --tag=CC $CC -no-undefined -o libbar.la $CPPFLAGS $CFLAGS $LDFLAGS bar.lo libfoo.la -rpath $curdir/$objdir],[0],[ignore],[ignore]) AT_CHECK([$LIBTOOL --mode=compile --tag=CC $CC -c -o main.lo $CPPFLAGS $CFLAGS main.c],[0],[ignore],[ignore]) AT_CHECK([$LIBTOOL --mode=link --tag=CC $CC -o main$EXEEXT $CPPFLAGS $CFLAGS $LDFLAGS main.lo libbar.la libfoo.la],[0],[ignore],[ignore]) # Check both static and shared versions run. We don't install them # here, that will be covered by the later tests; we've rpath'd things # so that they can all be run in situ. LT_AT_NOINST_EXEC_CHECK([$LIBTOOL], [], [0], [ignore], [ignore], [--mode=execute ./main$EXEEXT]) # Ensure libraries can be found on PATH, if we are on one # of the affected platforms, before testing the shared version. func_save_and_prepend_path $curdir/$objdir $bindirneeded && { LT_AT_NOINST_EXEC_CHECK([$LIBTOOL], [], [0], [ignore], [ignore], [--mode=execute $objdir/main$EXEEXT]) } # In fact, prepending the PATH as above is superfluous on the windows # platforms that this feature is primarily aimed at, as the DLL search # path always includes the directory from which the app was launched. # To make sure it still works even when not side-by-side, we'll install # the main executable and execute it from there while the PATH still # points to the shared libs in the .libs subdir. On other platforms, # the rpaths we set at link time will guarantee it runs from the bindir. mkdir $curdir/bin AT_CHECK([$LIBTOOL --mode=install $lt_INSTALL main$EXEEXT $curdir/bin/main$EXEEXT], [], [ignore], [ignore]) LT_AT_EXEC_CHECK([$curdir/bin/main$EXEEXT], [0], [ignore], [ignore], []) func_restore_path AT_CLEANUP #### # This is the main testcase. For a variety of bindir and libdir # settings, it verifies that all the files get installed exactly # where we want them to go, and that they can be executed once # installed. # AT_SETUP([bindir install tests]) bindirneeded=: case "$host_os" in cygwin*|mingw*|cegcc*) ;; *) bindirneeded=false ;; esac eval "`$LIBTOOL --config | grep '^build_libtool_libs='`" AT_CHECK([test "$build_libtool_libs" = yes || (exit 77)]) #### # These routines save the PATH before a test and restore it after, # prepending a chosen directory to the path on the platforms where # -bindir is needed after saving. # func_save_and_prepend_path () { save_PATH=$PATH if $bindirneeded ; then PATH=$1:$PATH fi export PATH } func_restore_path () { PATH=$save_PATH export PATH } AT_DATA([foo.c],[[ int x=11; ]]) AT_DATA([baz.c],[[ extern int x; int baz (void); int baz (void) { return x;} ]]) AT_DATA([bar.c],[[ extern int baz (void); int y=3; int bar (void); int bar (void) { return y + baz ();} ]]) AT_DATA([main.c],[[ #include extern int baz (void); extern int bar (void); int main() { if (baz () + bar () - 25) abort (); return 0; } ]]) # We only need to compile once, but we'll need to relink for each different value # of libdir in order to set the rpath, and we'll install for each combination of # libdir and bindir. AT_CHECK([$LIBTOOL --mode=compile --tag=CC $CC -c -o foo.lo $CPPFLAGS $CFLAGS foo.c],[0],[ignore],[ignore]) AT_CHECK([$LIBTOOL --mode=compile --tag=CC $CC -c -o baz.lo $CPPFLAGS $CFLAGS baz.c],[0],[ignore],[ignore]) AT_CHECK([$LIBTOOL --mode=compile --tag=CC $CC -c -o bar.lo $CPPFLAGS $CFLAGS bar.c],[0],[ignore],[ignore]) AT_CHECK([$LIBTOOL --mode=compile --tag=CC $CC -c -o main.lo $CPPFLAGS $CFLAGS main.c],[0],[ignore],[ignore]) # Now try installing the libs. There are the following cases: # No -bindir # -bindir below lib install dir # -bindir is lib install dir # -bindir beside lib install dir # -bindir above lib dir # -bindir above and beside lib dir # -bindir in entirely unrelated prefix. curdir=`pwd` for libdir in \ $curdir/usr/lib/gcc/i686-pc-cygwin/4.5.0 \ $curdir/usr/lib/gcc/../gcc/.//i686-pc-cygwin/4.5.0/../../././//. \ $curdir/usr/lib/ \ $curdir/usr/lib \ $curdir/baz \ $curdir/baz/lib/ ; do # Do a basic install with no -bindir option for reference. We use the sbin/ # dir for the main exe to avoid the potential "this only works because it's # side-by-side with the libs" default DLL search path problem mentioned above. rm -rf $libdir $curdir/bin $curdir/sbin $curdir/baz $curdir/usr AS_MKDIR_P($libdir) AS_MKDIR_P($curdir/sbin) AT_CHECK([$LIBTOOL --mode=link --tag=CC $CC -no-undefined -o libfoo.la $CPPFLAGS $CFLAGS $LDFLAGS foo.lo bar.lo baz.lo -rpath $libdir],[0],[ignore],[ignore]) AT_CHECK([$LIBTOOL --mode=link --tag=CC $CC -o main$EXEEXT $CPPFLAGS $CFLAGS $LDFLAGS main.lo libfoo.la -rpath $libdir],[0],[ignore],[ignore]) AT_CHECK([$LIBTOOL --mode=install $lt_INSTALL libfoo.la $libdir], [], [ignore], [ignore]) AT_CHECK([$LIBTOOL --mode=install $lt_INSTALL main$EXEEXT $curdir/sbin/main$EXEEXT], [], [ignore], [ignore]) # And ensure it went where we expect. Could be looking for any of 'cygfoo-0.dll', # 'libfoo-0.dll', or 'libfoo.so.0'. We'll simplify this check by taking advantage # of the fact that if it's a DLL, it has to go in bindir, so we'll not check for # both forms in libdir. AT_CHECK([$bindirneeded && { test -f $libdir/../bin/???foo-0.dll || ls $libdir/../bin/*foo*0* 2>/dev/null ; } || ls $libdir/*foo*0* 2>/dev/null], [], [ignore], [ignore]) # And that it can be executed. extrapath= $bindirneeded && extrapath=$libdir/../bin func_save_and_prepend_path $extrapath LT_AT_EXEC_CHECK([$curdir/sbin/main$EXEEXT], [0], [ignore], [ignore], []) func_restore_path for bindir in \ $curdir/usr/lib/gcc/i686-pc-cygwin/4.5.0/bin/ \ $curdir/usr/lib/gcc/i686-pc-cygwin/4.5.0/bin \ $curdir/usr/lib/gcc/i686-pc-cygwin/4.5.0/ \ $curdir/usr/lib/gcc/i686-pc-cygwin/4.5.0 \ $curdir/usr/lib/gcc/i686-pc-cygwin/bin/ \ $curdir/usr/lib/gcc/i686-pc-cygwin/bin \ $curdir/usr/lib/gcc/i686-pc-cygwin/ \ $curdir/usr/lib/gcc/i686-pc-cygwin \ $curdir/usr/lib/bin/ \ $curdir/usr/lib/bin \ $curdir/usr/bin/ \ $curdir/usr/bin \ $curdir/bin/ \ $curdir/bin \ /tmp/foo/bar ; do # Clear any old stuff out before we install. Because bindir # may be in /tmp, we have to take care to create it securely # and not to delete and recreate it if we do. rm -rf $libdir $curdir/bin $curdir/sbin $curdir/baz $curdir/usr tmp= case $bindir in /tmp*) # Create a temporary directory $tmp in $TMPDIR (default /tmp). # Use mktemp if possible; otherwise fall back on mkdir, # with $RANDOM to make collisions less likely. : ${TMPDIR=/tmp} { tmp=` (umask 077 && mktemp -d "$TMPDIR/fooXXXXXX") 2>/dev/null ` && test -n "$tmp" && test -d "$tmp" } || { tmp=$TMPDIR/foo$$-$RANDOM (umask 077 && mkdir "$tmp") } || exit 77 bindir=$tmp/bar ;; *) # Clear any old stuff out before we install. rm -rf $bindir AS_MKDIR_P($bindir) ;; esac # Relink with new rpaths. AT_CHECK([$LIBTOOL --mode=link --tag=CC $CC -no-undefined -bindir $bindir -o libfoo.la $CPPFLAGS $CFLAGS $LDFLAGS foo.lo bar.lo baz.lo -rpath $libdir],[0],[ignore],[ignore]) AT_CHECK([$LIBTOOL --mode=link --tag=CC $CC -o main$EXEEXT $CPPFLAGS $CFLAGS $LDFLAGS main.lo libfoo.la],[0],[ignore],[ignore]) # Recreate directories (bindir already done) and install. AS_MKDIR_P($libdir) AS_MKDIR_P($curdir/sbin) AT_CHECK([$LIBTOOL --mode=install $lt_INSTALL libfoo.la $libdir], [], [ignore], [ignore]) AT_CHECK([$LIBTOOL --mode=install $lt_INSTALL main$EXEEXT $curdir/sbin/main$EXEEXT], [], [ignore], [ignore]) # Ensure it went to bindir rather than default dir this time. AT_CHECK([$bindirneeded && { test -f $bindir/???foo-0.dll || ls $bindir/*foo*0* 2>/dev/null ; } || ls $libdir/*foo*0* 2>/dev/null], [], [ignore], [ignore]) # And that it can be executed. extrapath= $bindirneeded && extrapath=$bindir func_save_and_prepend_path $extrapath LT_AT_EXEC_CHECK([$curdir/sbin/main$EXEEXT], [0], [ignore], [ignore], []) func_restore_path # Clean up if we made a temp dir. Subdirs under our testdir get rm'd # and recreated at the top of the loop. Securely created subdirs under # /tmp get created precisely once and rm'd when we're done with them. if test ! -z "$tmp" ; then rm -rf "$tmp" fi done done AT_CLEANUP