#922 got me interested in mingw, so I started playing around and reading. Here are some notes that may save others time and should be converted into a PR at some point. I’d appreciate any comments from people more familiar with the topic (@gmaxwell, @theuni).
secp256k1 has been written to support mingw builds. As demonstrated in #922, these builds (still) work in general. But a few caveats apply.
Here are some simple issues to start with:
- The Makefile is not written with Windows in mind. The most obvious issue is that they ignore the
.exe
extension, e.g., inmake clean
and forgen_context
. Automake has explicit support for this, which we can rely easily rely on. - When cross-compiling on Linux, it may happen that
configure
believes that we’re not cross-compiling because it’s possible to execute windows binaries with an installed Wine andbinfmt_misc
enables. (This happens on my system but not on CI). See https://stackoverflow.com/questions/13150631/cross-compiling-why-checking-whether-we-are-cross-compiling-no#comment40154987_13150651. This by itself is not an issue: There’s per se nothing wrong with configure buildinggen_context.exe
and using wine to execute it. It’s a little bit cumbersome though and together with the previous item that means that thegen_context
invocation will fail. (#919 may improve this but could create other problems because invoking Python portably could be difficult.)
The real fun starts with DLLs (shared libaries) which are a pain in the ass. When creating a DLL, one needs to declare exported symbols explictly using __declspec (dllexport)
. We currently do this via in secp256k1.h
via SECP256K1_API
when SECP256K1_BUILD
is defined.
When using the library (i.e., SECP256K1_BUILD
is not defined), one can declare the symbols explicitly using __declspec (dllimport)
. We currently don’t do this because the only problem is a small overhead when calling a DLL function but the code still works. The reason to avoid __declspec (dllimport)
is that static linking will fail. (I verified this by adding the declspec and configuring with --disable-shared
.) Unfortunately there’s no way of telling whether we’ll link statically or not, so the best possible thing is to have a macro that the user can set. (See also https://www.sourceware.org/autobook/autobook/autobook_137.html, https://autotools.io/libtool/windows.html, #314).
- We currently don’t have this macro. We could add it, maybe it’s useful for someone and it won’t create problems because it’s not set by default.
- In order to be able to create DLLs at all, we need to add
-no-undefined
to the appropriate libtool LDFLAGS likelibsecp256k1_la_LDFLAGS = -no-undefined
. Otherwise libtool will tell uslibtool: warning: undefined symbols not allowed in x86_64-w64-mingw32 shared libraries; building static only
as seen on CI in #922. A workaround for now and for testing is to call make withmake LDFLAGS=-no-undefined
. Apparently this flag does not hurt on Linux, and none of the guides I found mentions that this should be only set on Windows, so we can just set this unconditionally. - When building the benchmarks, we’ll still get warnings like
./.libs/lt-bench_internal.c:41:5: warning: '_putenv' redeclared without dllimport attribute: previous dllimport ignored [-Wattributes]
but these are in the C code of the libtool wrapper binary, not in our code. (On Windows, this is an entire wrapper binary and not just a sh script as on Linux). I have no idea if this is a libtool bug or the issue is on our side but apparently the wrapper binary works, so we can simply ignore these for now. Having warnings is not elegant though, so it may still be interesting to ask on the libtool list to see what’s going on. - According to the libtool and gnulib docs, we should add
win32-dll
toLT_INIT
. For me and CI, it seems to work without this, but I guess we should simply add it.
Uff.