'How to have CMake export symbols automatically on Windows?

According to my experience, though it may be incorrect, on Windows if no translation unit exports any symbol then all symbols are implicitly exported when building a DLL with GNU C++ compiler. In contrast, if there is any translation unit exports any symbol, then all others symbols in the DLL are NOT implicitly exported.

In other words, if there is a header we #include declares a symbol with __declspec(dllexport), then we have to manually insert __declspec(dllexport) to every single declarations of other symbols. Otherwise, those symbols will not get exported.

Is there a convenient way in CMake world that we don't have to add __declspec(dllexport) ourselves? I've tried

set_target_properties(foo PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)

but it doesn't work. Any suggestion? Thanks.

Reedit: (2021-09-03 8:34:40 UTC)

Below is an example to recreate the problem.

example/CMakeLists.txt:

cmake_minimum_required(VERSION 3.21)
project(example VERSION 1.0)
add_executable(myapp main.cpp)
add_subdirectory(foo)
add_subdirectory(bar)
target_link_libraries(myapp PRIVATE foo)

example/main.cpp:

#include "foo.hpp" // for function foo_func

int main(int argc, char *argv[])
{
    foo_func();
    return 0;
}

example/foo/CMakeLists.txt:

add_library(foo SHARED foo.cpp)
target_include_directories(foo INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}")
target_link_libraries(foo PRIVATE bar)

example/foo/foo.hpp:

#ifndef FOO_HPP
#define FOO_HPP

void foo_func(void);

#endif

example/foo/foo.cpp:

#include "bar.hpp" // for function bar_funcA

void foo_func(void)
{
    bar_funcA();
}

example/bar/CMakeLists.txt

add_library(bar SHARED bar.cpp)
target_include_directories(bar INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
set_target_properties(bar PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)

example/bar/bar.hpp:

#ifndef BAR_HPP
#define BAR_HPP

void bar_funcA(void);
void bar_funcB(void);

#endif

example/bar/bar.cpp:

#include <cstdio> // for function printf

void bar_funcA(void)
{
    std::printf("%s\n", __func__);
}
__declspec(dllexport) void bar_funcB(void)
{
    std::printf("%s\n", __func__);
}

My environment:

Windows 10
MSYS2
CMake 3.21.2

Result:

$ cmake -G 'Unix Makefiles' -S example/ -B build/
$ cmake --build build/
[ 16%] Building CXX object bar/CMakeFiles/bar.dir/bar.cpp.obj
[ 33%] Linking CXX shared library libbar.dll
[ 33%] Built target bar
Consolidate compiler generated dependencies of target foo
[ 50%] Building CXX object foo/CMakeFiles/foo.dir/foo.cpp.obj
[ 66%] Linking CXX shared library libfoo.dll
C:/msys64/mingw64/x86_64-w64-mingw32/bin/ld.exe: CMakeFiles/foo.dir/objects.a(foo.cpp.obj):foo.cpp:(.text+0x9): undefined reference to `bar_funcA()'
collect2.exe: error: ld returned 1 exit status
make[2]: *** [foo/CMakeFiles/foo.dir/build.make:102: foo/libfoo.dll] Error 1
make[1]: *** [CMakeFiles/Makefile2:144: foo/CMakeFiles/foo.dir/all] Error 2
make: *** [Makefile:91: all] Error 2

According to Dependency Walker, the only symbol that gets exported from libbar.dll is bar_funcB.



Solution 1:[1]

According to WIN32 (LD):

… the default auto-export behavior will be disabled if either of the following are true:

  • A DEF file is used.

  • Any symbol in any object file was marked with the __declspec(dllexport) attribute.

In this case, the --export-all-symbols linker option forces the auto-export functionality and exports all symbols, except some special ones. Therefore, to also export bar_funcA without modifying C/C++ codes, add this line in src/bar/CMakeLists.txt:

target_link_options(bar PRIVATE "-Wl,--export-all-symbols")

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Community