Previous attempts:
The result is as follows:
- Simple, concise and extensively documented code.
- Explicitly documented use cases with no ambiguities.
- No workarounds for linker warnings.
- Solves one item in #1235.
164+ * library. */
165+# elif !defined(SECP256K1_STATIC)
166+ /* Consuming libsecp256k1 as a DLL.
167+ * It does not depend on the Libtool's assumption that if a DLL is being built
168+ * (DLL_EXPORT is defined) then that DLL is going to consume any dependent libraries
169+ * as DLLs. See "Windows DLLs" in the Libtool manual. */
173 #ifndef SECP256K1_API
174 # if defined(__GNUC__) && (__GNUC__ >= 4) && defined(SECP256K1_BUILD)
175-# define SECP256K1_API __attribute__ ((visibility ("default")))
176-# define SECP256K1_API_VAR extern __attribute__ ((visibility ("default")))
177+ /* Building libsecp256k1 on non-Windows using GCC or compatible. */
178+# define SECP256K1_API __attribute__ ((visibility ("default")))
extern
in SECP256K1_API
. Then we don’t need to add it for variables. (And it doesn’t hurt in case of functions.)
159-# define SECP256K1_API_VAR extern __declspec (dllimport)
160-# elif defined DLL_EXPORT
161-# define SECP256K1_API __declspec (dllimport)
162-# define SECP256K1_API_VAR extern __declspec (dllimport)
163+ /* The user must define SECP256K1_STATIC when consuming libsecp256k1 as a static
164+ * library. */
0 /* The user must define SECP256K1_STATIC when consuming libsecp256k1 as a static
1 * library on Windows. */
146+ * exactly equivalent to) __attribute__ ((visibility("default"))), and so we
147+ * actually want __declspec even on GCC, see "Microsoft Windows Function
148+ * Attributes" in the GCC manual and the recommendations in
149+ * https://gcc.gnu.org/wiki/Visibility. */
150+# if defined(SECP256K1_BUILD)
151+# if defined(DLL_EXPORT) || defined(SECP256k1_DLL_EXPORT)
0# if defined(DLL_EXPORT) || defined(SECP256K1_DLL_EXPORT)
capital “K” :) Also in the comment below.
148+ * Attributes" in the GCC manual and the recommendations in
149+ * https://gcc.gnu.org/wiki/Visibility. */
150+# if defined(SECP256K1_BUILD)
151+# if defined(DLL_EXPORT) || defined(SECP256k1_DLL_EXPORT)
152+ /* Building libsecp256k1 as a DLL.
153+ * 1. If using Libtool, it defines DLL_EXPORT internally.
0 * 1. If using Libtool, it defines DLL_EXPORT automatically.
nit: I think this is slightly clearer. “Internally” sounds like we don’t see the definition here.
19@@ -20,10 +20,9 @@ if(SECP256K1_ASM STREQUAL "arm32")
20 target_link_libraries(secp256k1_asm INTERFACE secp256k1_asm_arm)
21 endif()
22
23-# Define our export symbol only for Win32 and only for shared libs.
140@@ -141,10 +141,10 @@ typedef int (*secp256k1_nonce_function)(
141 # define SECP256K1_API __declspec (dllexport)
142 # define SECP256K1_API_VAR extern __declspec (dllexport)
143 # endif
144-# elif defined _MSC_VER
145-# define SECP256K1_API
146-# define SECP256K1_API_VAR extern __declspec (dllimport)
147-# elif defined DLL_EXPORT
148+ /* The user must define SECP256K1_STATIC when consuming libsecp256k1 as a static
It is. Just try (with the reverted change in Makefile.am
):
0./configure --host=x86_64-w64-mingw32 --enable-examples && make
@theuni This also removes this branch:
0# elif defined _MSC_VER
1# define SECP256K1_API
2# define SECP256K1_API_VAR extern __declspec (dllimport)
This was libtool’s hack for making the build work on MSVC with static and dynamic linkage and without explicit config by the user. But this comes at the expense of confusing linker warnings with static linkage and other potential issues:
With Microsoft tools you typically get away with always compiling the code such that variables are expected to be imported from a DLL and functions are expected to be found in a static library. The tools will then automatically import the function from a DLL if that is where they are found. If the variables are not imported from a DLL as expected, but are found in a static library that is otherwise pulled in by some function, the linker will issue a warning (LNK4217) that a locally defined symbol is imported, but it still works. In other words, this scheme will not work to only consume variables from a library. There is also a price connected to this liberal use of imports in that an extra indirection is introduced when you are consuming the static version of the library. That extra indirection is unavoidable when the DLL is consumed, but it is not needed when consuming the static library.
https://www.gnu.org/software/libtool/manual/html_node/Windows-DLLs.html#Windows-DLLs
@hebasto and I now believe that it’s cleaner and simpler to require the user to set SECP256K1_STATIC
explicitly when static linkage is desired. Such a macro seems to be common on Windows.
23@@ -24,6 +24,7 @@ endif()
24 # This matches libtool's usage of DLL_EXPORT
25 if(WIN32)
26 set_target_properties(secp256k1 PROPERTIES DEFINE_SYMBOL "DLL_EXPORT")
27+ target_compile_definitions(secp256k1 INTERFACE $<$<NOT:$<BOOL:${BUILD_SHARED_LIBS}>>:SECP256K1_STATICLIB>)
SECP256K1_STATIC
?
The Windows CMake task didn’t catch this mistake (cirrus-ci.com/task/6074245453709312) because it uses DLLs. Would it make sense to add a CI task that uses static linkage?
The similar idea came to my head :) Implemented.
I haven’t followed the other attempts, but as far as I can see this doesn’t break anything or make me grumpy :)
Left some notes, including a real bug I believe.
13@@ -14,6 +14,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
14 - Document `doc/ellswift.md` which explains the mathematical background of the scheme.
15 - The [paper](https://eprint.iacr.org/2022/759) on which the scheme is based.
16
17+#### Changed
18+ - The user must define the `SECP256K1_STATIC` macro when consuming libsecp256k1 as a static library for their Windows builds.
nit:
0 - The user must now define the `SECP256K1_STATIC` macro before including `secp256k1.h` when consuming libsecp256k1 as a static library for their Windows builds.
Or stylistic variant that is easier to parse:
0 - When consuming libsecp256k1 as a static library on Windows, the user must now define the `SECP256K1_STATIC` macro before including `secp256k1.h`.
Updated bdcd794ef6d6a2e634b9bcc83b0b09fb4e6b5cd1 -> b688658d0a6449702448f2c0ff4f7f1ef9c2bfd1 (pr1367.05 -> pr1367.07, diff).
Recent @real-or-random’s comments have been addressed:
utACK b688658d0a6449702448f2c0ff4f7f1ef9c2bfd1.
It’s a lot to keep paged-in at once, so I can’t say I’m completely convinced, but generally lgtm and I appreciate the research and testing the two of you have been doing :)
Hopefully we can put this behind us now, heh.
It is a non-Libtool-specific way to explicitly specify the user's
intention to consume a static `libseck256k1`.
This change allows to get rid of MSVC linker warnings LNK4217 and
LNK4286. Also, it makes possible to merge the `SECP256K1_API` and
`SECP256K1_API_VAR` into one.
This change provides a way to build a shared library that is not tired
to the Libtool-specific `DLL_EXPORT` macro.