Dave Korn wrote: > [ Please Cc me on replies, I'm not subbed up to list. ] > Windows DLLs, unlike Linux DSOs, have to live in the bin directory (or > elsewhere on $PATH), not the lib dir. Libtool handles this at --link time by > setting the dlname in the generated .la file to a relative path > "../bin/$dlname", working on the assumption that the library .la file will be > installed into $prefix/lib, and this causes the DLL to be installed to > $prefix/bin. > > That heuristic fails
Note, for the rest of the libtool team -- there is also a pending patch not yet posted to this list, complementary to Dave's, from Yaakov Selkowitz. Yaakov's patch improves the heuristic for many common cases, eliminating some cases where Dave's -bindir option might be used. However, even Yaakov's improvement doesn't solve the specific case vis. gcc. Besides, no matter how smart a heuristic is, there are cases where it fails -- so providing a specific control to override the heuristic is kinda necessary. So, IMO, both Yaakov's and Dave's patch -- in some form, are appropriate and complementary. > The attached patch provides a way to resolve this problem, by adding a > "-bindir" option to libtool's --link mode command line. Dave, I'm sorry I wasn't able to get to this in time to save you the effort, but thanks for following thru. Comments inline, but I have no official authority to actually "approve" anything. > diff --git a/Makefile.am b/Makefile.am > index a18955e..129b890 100644 > --- a/Makefile.am > +++ b/Makefile.am > @@ -494,7 +494,8 @@ TESTSUITE_AT = tests/testsuite.at \ > tests/configure-iface.at \ > tests/stresstest.at \ > tests/cmdline_wrap.at \ > - tests/darwin.at > + tests/darwin.at \ > + tests/win32.at > > EXTRA_DIST += $(srcdir)/$(TESTSUITE) $(TESTSUITE_AT) > $(srcdir)/tests/package.m4 OK. > diff --git a/libltdl/config/general.m4sh b/libltdl/config/general.m4sh > old mode 100644 > new mode 100755 > index 4bc304c..96072e2 > --- a/libltdl/config/general.m4sh > +++ b/libltdl/config/general.m4sh > @@ -98,6 +98,49 @@ func_dirname_and_basename () > func_basename_result=`$ECHO "${1}" | $SED -e "$basename"` > } > > +# func_relative_path libdir bindir > +# generates a relative path from libdir to bindir, intended > +# for supporting installation of windows DLLs into -bindir. > +# call: > +# dirname: Compute the dirname of FILE. If nonempty, > +# add APPEND to the result, otherwise set result > +# to NONDIR_REPLACEMENT. > +# value returned in "$func_dirname_result" > +func_relative_path () > +{ > + local tlibdir tbindir tcancelled We can't use the 'local' keyword. You need to uglify these variables, e.g. func_relative_path_tlibdir func_relative_path_tbindir func_relative_path_tcancelled > + func_relative_path_result= > + tlibdir=${1%%/} > + tbindir=${2%%/} We can't use these, even tho they are technically supported by posix sh. (Actually, we kinda can -- IF m4 says so. What happens is you abstract the call into a function -- like func_stripname -- and then use m4 to generate the appropriate contents of that function. For cygwin/bash, it happens that the contents of func_stripname() actually do use the %, # posixisms. But, in "client" code, you have to do func_stripname '' '/' "$1" tlibdir=$func_stripname_result func_stripname '' '/' "$2" tbindir=$func_stripname_result > + while :; do > + # check if we have found a prefix of bindir > + tcancelled=${tbindir##$tlibdir} Again, we can't do this (nor ${foo:=.}). See below... > + if test x$tcancelled != x$tbindir ; > + then > + # Found a matching prefix, exit loop. > + func_relative_path_result="${func_relative_path_result:=.}" > + break > + fi > + func_dirname $tlibdir > + tlibdir=${func_dirname_result} > + if test x$tlibdir = x ; > + then > + # Have to descend all the way to the root! > + func_relative_path_result="../$func_relative_path_result" > + break > + fi > + func_relative_path_result="../$func_relative_path_result" > + done while :; do # check if we have found a prefix of bindir case "$tbindir" in $tlibdir* ) # found a matching prefix func_stripname "$tlibdir" '' "$tbindir" tcancelled=$func_stripname_result if test -z "$func_relative_path_result"; then func_relative_path_result=. fi break ;; *) func_dirname $tlibdir tlibdir=${func_dirname_result} if test x$tlibdir = x ; then # Have to descend all the way to the root! func_relative_path_result=../$func_relative_path_result break fi func_relative_path_result=../$func_relative_path_result ;; esac done > + # Now calculate path; take care to avoid doubling-up slashes. > + func_relative_path_result="${func_relative_path_result%%/}${tcancelled%%/}" and again: func_stripname '' '/' "$func_relative_path_result" func_relative_path_result=$func_stripname_result func_stripname '' '/' "$tcancelled" func_relative_path_result=${func_relative_path_result}${func_stripname_result} > + # Normalisation. If bindir is libdir, return empty string, > + # if subdir return string beginning './', else relative path > + # ending with a slash; either way, target file name can be > + # directly appended. > + func_relative_path_result="${func_relative_path_result:=.}/" > + func_relative_path_result="${func_relative_path_result##./}" And again... if test -z "$func_relative_path_result"; then func_relative_path_result=. else func_stripname './' '' "$func_relative_path_result" func_relative_path_result=$func_stripname_result fi > +} > + > # Generated shell functions inserted here AND...because you're (now) calling the generated functions from func_relative_path, then it needs to be defined below the '# Generated shell functions inserted here' marker, not above it. So, here's the revised function (w/o the varname uglification): func_relative_path () { local tlibdir tbindir tcancelled func_relative_path_result= func_stripname '' '/' "$1" tlibdir=$func_stripname_result func_stripname '' '/' "$2" tbindir=$func_stripname_result while :; do # check if we have found a prefix of bindir case "$tbindir" in $tlibdir* ) # found a matching prefix func_stripname "$tlibdir" '' "$tbindir" tcancelled=$func_stripname_result if test -z "$func_relative_path_result"; then func_relative_path_result=. fi break ;; *) func_dirname $tlibdir tlibdir=${func_dirname_result} if test x$tlibdir = x ; then # Have to descend all the way to the root! func_relative_path_result=../$func_relative_path_result break fi func_relative_path_result=../$func_relative_path_result ;; esac done func_stripname '' '/' "$func_relative_path_result" func_relative_path_result=$func_stripname_result func_stripname '' '/' "$tcancelled" func_relative_path_result=${func_relative_path_result}${func_stripname_result} if test -z "$func_relative_path_result"; then func_relative_path_result=./ else func_stripname './' '' "$func_relative_path_result/" func_relative_path_result=$func_stripname_result fi } > # The name of this program: > diff --git a/libltdl/config/ltmain.m4sh b/libltdl/config/ltmain.m4sh > old mode 100644 > new mode 100755 > index ebd3909..5403d80 > --- a/libltdl/config/ltmain.m4sh > +++ b/libltdl/config/ltmain.m4sh > @@ -1129,6 +1129,8 @@ The following components of LINK-COMMAND are treated > specially: > > -all-static do not do any dynamic linking at all > -avoid-version do not add a version suffix if possible > + -bindir BINDIR specify path to $prefix/bin (needed only when installing > + a Windows DLL) > -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime > -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols > -export-dynamic allow symbols from OUTPUT-FILE to be resolved with > dlsym(3) > @@ -3659,6 +3661,7 @@ func_mode_link () > new_inherited_linker_flags= > > avoid_version=no > + bindir= > dlfiles= > dlprefiles= > dlself=no > @@ -3751,6 +3754,11 @@ func_mode_link () > esac > > case $prev in > + bindir) > + bindir="$arg" > + prev= > + continue > + ;; > dlfiles|dlprefiles) > if test "$preload" = no; then > # Add the symbol object into the linking commands. > @@ -4012,6 +4020,11 @@ func_mode_link () > continue > ;; > > + -bindir) > + prev=bindir > + continue > + ;; > + > -dlopen) > prev=dlfiles > continue > @@ -7706,7 +7719,17 @@ EOF > # place dlname in correct position for cygwin > tdlname=$dlname > case $host,$output,$installed,$module,$dlname in > - *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | > *cegcc*,*lai,yes,no,*.dll) tdlname=../bin/$dlname ;; > + *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | > *cegcc*,*lai,yes,no,*.dll) > + # If a -bindir argument was supplied, place the dll there. > + if test "x$bindir" != x ; > + then > + func_relative_path "$install_libdir" "$bindir" > + tdlname="${func_relative_path_result}$dlname" > + else > + # Otherwise fall back on heuristic. NOTE: this is where Yaakov's patch would go... > + tdlname=../bin/$dlname > + fi > + ;; > esac > $ECHO > $output "\ > # $outputname - a libtool library file > diff --git a/tests/win32.at b/tests/win32.at > new file mode 100755 > index 0000000..1d36add > --- /dev/null > +++ b/tests/win32.at > @@ -0,0 +1,170 @@ > +# win32.at - tests specific to Cygwin and MinGW > +# > +# 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. > +#### > + > +noskip=: > +case "$host_os" in > +cygwin*|mingw*) ;; > +*) noskip=false ;; > +esac > + > +$noskip && { > + > +AT_BANNER([Win32 tests]) > +AT_SETUP([win32 compile check]) > + > +# Verify compiling works. > + > +AT_DATA([simple.c] ,[[ > +int main() { return 0;} > +]]) > + > +$noskip && { > +$CC $CPPFLAGS $CFLAGS -o simple simple.c 2>&1 > /dev/null || noskip=false > +rm -f simple > +} > + > +AT_CHECK([$noskip || (exit 77)]) > + > +AT_CLEANUP > + > +# Now the tests themselves. > + > +AT_SETUP([win32 dll basic]) > + > +AT_DATA([foo.c],[[ > +int x=0; > +]]) > + > +AT_DATA([baz.c],[[ > +int y=0; > +]]) > + > +AT_DATA([bar.c],[[ > +extern int x; > +int bar(void); > +int bar() { return x;} > +]]) > + > +AT_DATA([main.c],[[ > +extern int x; > +extern int y; > + > +int main() { > +return x+y; > +} > +]]) > + > +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 -shared -o > libfoo.la $CPPFLAGS $CFLAGS $LDFLAGS foo.lo baz.lo -rpath > /nonexistent],[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 -shared -o > libbar.la $CPPFLAGS $CFLAGS $LDFLAGS bar.lo libfoo.la -rpath > /nonexistent],[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],[0],[ignore],[ignore]) > + > +AT_CLEANUP > + > +AT_SETUP([win32 dll install to bindir]) > + > +AT_DATA([foo.c],[[ > +int x=0; > +]]) > + > +AT_DATA([bar.c],[[ > +extern int x; > +int bar(void); > +int bar() { return x;} > +]]) > + > +AT_DATA([baz.c],[[ > +int y=0; > +]]) > + > +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 -shared -o > libfoo.la $CPPFLAGS $CFLAGS $LDFLAGS foo.lo baz.lo -rpath > /nonexistent],[0],[ignore],[ignore]) > + > +AT_CHECK([$LIBTOOL --mode=compile --tag=CC $CC -c -o bar.lo $CPPFLAGS > $CFLAGS bar.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` > +libdir=${curdir}/usr/lib/gcc/i686-pc-cygwin/4.5.0 > + > +# Do a basic install with no -bindir option for reference > + > +AT_CHECK([$LIBTOOL --mode=link --tag=CC $CC -no-undefined -shared -o > libbar.la $CPPFLAGS $CFLAGS $LDFLAGS bar.lo libfoo.la -rpath > ${libdir}],[0],[ignore],[ignore]) I'm not sure "-rpath" is the right way to test, here. I know you're trying to avoid doing a full "configure --prefix= --bindir=" test, but I've only rarely seen the -rpath flag used on win32, so I'm not sure this test is exercising the normal usage pattern. > +rm -rf ${curdir}/usr > +mkdir -p ${libdir} > +AT_CHECK([$LIBTOOL --mode=install $lt_INSTALL libbar.la $libdir], > + [], [ignore], [ignore]) > + > +# And ensure it went where we expect. > +AT_CHECK(test -f $libdir/../bin/???bar-0.dll) > + > +for x 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 > + > + AT_CHECK([$LIBTOOL --mode=link --tag=CC $CC -no-undefined -bindir $x > -shared -o libbar.la $CPPFLAGS $CFLAGS $LDFLAGS bar.lo libfoo.la -rpath > ${libdir}],[0],[ignore],[ignore]) > + > + rm -rf ${curdir}/usr ${curdir}/bin /tmp/$$ > + mkdir -p ${libdir} $x ${curdir}/usr ${curdir}/bin /tmp/$$ > + AT_CHECK([$LIBTOOL --mode=install $lt_INSTALL libbar.la $libdir], > + [], [ignore], [ignore]) > + # Ensure it went to bindir this time. > + AT_CHECK(test -f $x/???bar-0.dll) > +done > + > +AT_CLEANUP > + > +} > + OK. Hope that helps, Dave. -- Chuck