Catch2
Catch2 copied to clipboard
Tests fail to link with v3.1.0 on windows when built as shared library
Describe the bug
Version v3.1.0
enables building catch2
as a shared lib but does not export/import static
symbols in header files.
Without proper annotations, link.exe
on windows will not automatically import static
variables even when they are exported by CMake
. Currently Selftest
cannot be built against a shared version of catch2
.
The following linker error is produced:
InternalBenchmark.tests.cpp.obj : error LNK2001: unresolved external symbol "private: static class Catch::IMutableContext * Catch::IMutableContext::currentContext" (?currentContext@IMutableContext@Catch@@0PEAV12@EA)
Benchmark.tests.cpp.obj : error LNK2001: unresolved external symbol "private: static class Catch::IMutableContext * Catch::IMutableContext::currentContext" (?currentContext@IMutableContext@Catch@@0PEAV12@EA)
ToStringGeneral.tests.cpp.obj : error LNK2001: unresolved external symbol "public: static int Catch::StringMaker<float,void>::precision" (?precision@?$StringMaker@MX@Catch@@2HA)
ToStringGeneral.tests.cpp.obj : error LNK2001: unresolved external symbol "public: static int Catch::StringMaker<double,void>::precision" (?precision@?$StringMaker@NX@Catch@@2HA)
tests\SelfTest.exe : fatal error LNK1120: 3 unresolved externals
ninja: build stopped: subcommand failed.
Expected behavior
No linker errors when linking against a shared library build of catch2
.
Reproduction steps Compile and build the project as a shared library:
mkdir build && cd build
cmake .. -DBUILD_SHARED_LIBS=ON -DCATCH_DEVELOPMENT_BUILD=ON -GNinja
cmake --build .
Platform information:
- OS: Windows NT
- Compiler+version: MSVC 19.30
- Catch version: v3.1.0
Additional context
CMake's WINDOWS_EXPORT_ALL_SYMBOLS
exports all symbols but link.exe
only auto links functions without the necessary __declspec(dllimport)
annotation. Selftest
relies on the above mentioned static
variables that are not automatically linked even though exported.
The cleanest solution at the moment is to:
- Create a new header file
----- catch2/export.hpp ------
#pragma once
#if defined(CATCH_STATIC) // Using static.
# define CATCH_SYMEXPORT
#elif defined(CATCH_STATIC_BUILD) // Building static.
# define CATCH_SYMEXPORT
#elif defined(CATCH_SHARED) // Using shared.
# ifdef _WIN32
# define CATCH_SYMEXPORT __declspec(dllimport)
# else
# define CATCH_SYMEXPORT
# endif
#elif defined(CATCH_SHARED_BUILD) // Building shared.
# ifdef _WIN32
# define CATCH_SYMEXPORT __declspec(dllexport)
# else
# define CATCH_SYMEXPORT
# endif
#else
# define CATCH_SYMEXPORT // Using static or shared.
//# error define CATCH_STATIC or CATCH_SHARED preprocessor macro to signal test library type being linked
#endif
- Include
catch2/export.hpp
in necessary header files (Minimumcatch2/internal/catch_context.hpp
andcatch2/catch_tostring.hpp
) as well as tosrc/CMakeLists.txt
- Annotate
catch2/internal/catch_context.hpp:31
withCATCH_SYMEXPORT
- Annotate
catch2/catch_tostring.hpp:299 and 305
withCATCH_SYMEXPORT
- Add necessary compile definitions to CMake
target_compile_definitions(Catch2 PRIVATE CATCH_SHARED_BUILD INTERFACE CATCH_SHARED)
This header can be generated by cmake of https://cmake.org/cmake/help/latest/module/GenerateExportHeader.html The good thing about the generated header is that if you build static library all definitions are empty so downstream users don't need to manually define CATCH_STATIC.