#112 closed defect (migrated)

build shared libraries on Windows

Reported by: stefan Owned by: stefan
Priority: major Component: build system
Version: trunk Keywords: autotools-update


Make sure autotools can build proper DLLs on Windows. Probably restrict third-party codes (like Glpk), where we do not have control over the code, to static libraries, though.

COIN-OR package should make their API explicit by using __declspec(dllexport/dllimport) (MSVC) and __attribute ((visibility(...))) (GCC).

Then we should pass option win32-dll to LT_INIT (https://www.gnu.org/software/libtool/manual/html_node/LT_005fINIT.html).

Change History (11)

comment:1 Changed 21 months ago by stefan

  • Status changed from new to assigned
  • Version changed from 0.7 to trunk

comment:2 Changed 21 months ago by stefan

Should read chapter 25.4 DLLs with Libtool in Autobook.

Last edited 21 months ago by stefan (previous) (diff)

comment:3 Changed 21 months ago by stefan

Building a CoinUtils DLL with a static GLPK library (libcoinglpk.lib) is not possible:

*** Warning: This system cannot link to static lib archive /home/stefan/build-msvc/lib/libcoinglpk.la.
*** I have the capability to make that library automatically link in when
*** you link to this library.  But I can only do this if you have a
*** shared version of the library, which you do not appear to have.

So doing static lib only for third-party code will not work. Building a shared lib for Glpk could work if not setting the win32-dll flag of LT_INIT, since that is only C code. But it essentially means that using Glpk's configure will not work here, too.

comment:4 Changed 21 months ago by stefan

To have the OsiCommonTest DLL correctly link against the core OSI DLL, these changes where helpful: https://projects.coin-or.org/Osi/changeset/2152/

comment:5 Changed 21 months ago by stefan

Another problem that came up: After building CoinUtils?, automake only installs the static CoinUtils? lib and a CoinUtils? DLL, but it doesn't install the small LIB that would load the DLL. Thus, when building the Osi DLL, it pulls in the whole CoinUtils? LIB, instead of linking against the CoinUtils? DLL.

comment:6 Changed 21 months ago by stefan

Further, if disabling static libs, thus building only DLLs, libtool decides to do a cp libCoinUtils-0.dll libCoinUtils.lib...

comment:7 Changed 21 months ago by stefan

Regarding the previous comment. This is caused by the entry

# Names of this library.
library_names='libCoinUtils-0.dll libCoinUtils.lib'

in libCoinUtils.la. libtool --install installs libCoinUtils-0.dll as file and then creates for each following entry a symlink (which is cp on some Windows) to the first file. The corresponding libtool code is

	  if test "$#" -gt 0; then
	    # Delete the old symlinks, and create new ones.
	    # Try 'ln -sf' first, because the 'ln' binary might depend on
	    # the symlink we replace!  Solaris /bin/ln does not understand -f,
	    # so we also need to try rm && ln -s.
	    for linkname
	      test "$linkname" != "$realname" \
		&& func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })"

It seems wrong that libCoinUtils.lib is mentioned in library_names in libCoinUtils.la at the first place, which happens even if static libs are disabled.

library_names is setup early in libtool:

# List of archive names.  First name is the real one, the rest are links.
# The last name is the one that the linker finds with -lNAME
library_names_spec="\$libname\`echo \$release | \$SED -e s/[.]/-/g\`\$versuffix\$shared_ext \$libname.lib"

Thus, \$libname.lib at the end of the last line seems to be wrong here.

It is added by share/aclocal/libtool.m4. Line 2530 and following have

  case $GCC,$cc_basename in
    # gcc
    # Native MSVC
    soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext'
    # Assume MSVC wrapper
    library_names_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext $libname.lib'
    dynamic_linker='Win32 ld.exe'

So we seem to get into the last case instead of the "Native MSVC" one, which I presume is because we use the compile wrapper around cl.

Last edited 21 months ago by stefan (previous) (diff)

comment:8 Changed 21 months ago by stefan

This presumption is wrong. The compile wrapper is taken into consideration and cl is correctly detected. This results in the line

checking dynamic linker characteristics... Win32 link.exe

in the output of configure.

However, there is another round of checks coming right after, which seems to reset things to "Win32 ld.exe":

checking whether the /home/stefan/Osi-au/Osi/CoinUtils/compile cl linker (/bin/ld) supports shared libraries... yes
checking dynamic linker characteristics... Win32 link.exe
checking how to hardcode library paths into programs... immediate
checking if libtool supports shared libraries... yes
checking whether to build shared libraries... yes
checking whether to build static libraries... no
checking for unavailable option to produce PIC... -DDLL_EXPORT
checking if unavailable PIC flag -DDLL_EXPORT works... no
checking if unavailable static flag  works... no
checking if unavailable supports -c -o file.obj... no
checking if unavailable supports -c -o file.obj... (cached) no
checking whether the unavailable linker (/bin/ld) supports shared libraries... yes
checking dynamic linker characteristics... Win32 ld.exe

Maybe this is due to the Fortran compiler, which I do not have available on my setup.

comment:9 Changed 20 months ago by stefan

So, I made some progress on the DLL builds.

I threw out everything Fortran related in configure of CoinUtils. As CoinUtils does not build Fortran code, it should not have to check for a Fortran compiler. The check for Lapack doesn't assume anymore that the Lapack library is written in Fortran and so also does not assume anymore that the name mangling scheme of Fortran would apply to Lapack. Instead, we now check explicitly what name mangling scheme is used for the Lapack lib and set a corresponding C macro.

Also AC_COIN_FINALIZE_FLAGS now introduces a XYZ_EXPORT macro that is set to __declspec(dllimport) if building DLLs. For the build of XYZ itself, we have to redefine this as __declspec(dllexport), though (happens in CoinUtilsConfig.h).

With that, it seems that a proper CoinUtils DLL is build and installed. The CoinUtils tests manage to build, link, and run against it.

Osi is more of the same, but more complex, because it is not just one lib. I've set things up for the main OsiLib and OsiCommonTestsLib. This seems to build and link, also using a CoinUtils DLL.

Someone had the glorious idea a long while ago to declare Osi-dependent constructors for Coin{Pre|Post}SolveMatrix in CoinUtils and implement them in Osi! Such dirty stuff does not work easily for DLLs due to the distinction into dllimport and dllexport. So I removed these declarations from CoinUtils and made them ordinary C++ functions in OsiPresolve.

comment:10 Changed 20 months ago by stefan

Osi tests now also seems to work. Problem was that the main Osi DLL was passing a filepointer (FILE*) to the CoinUtils DLL for reading an .lp file. This does not seem to work (fscanf was crashing). The internet warns on problems when DLLs were build with different MSVC versions, but here it also seems to crash when using the same compilers. In this particular case, it was easy to work around, but this problem could pop up somewhere again.

comment:11 Changed 16 months ago by stefan

  • Resolution set to migrated
  • Status changed from assigned to closed

Ticket has been migrated to GitHub and will be resolved there: https://github.com/coin-or-tools/BuildTools/issues/112

Note: See TracTickets for help on using tickets.