1

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.

Cody
  • 609
  • 4
  • 21
  • @Tsyvarev Thanks for reply. The question is updated. Thanks. – Cody Sep 03 '21 at 08:28
  • @Tsyvarev Sorry. Re-reedited. Thanks. :) – Cody Sep 03 '21 at 08:37
  • 2
    Well, it seems that `WINDOWS_EXPORT_ALL_SYMBOLS` doesn't work under MSys, because CMake doesn't treat "Unix Makefiles" generator as "MS-compatible tools on Windows". See also that question about MinGW: https://stackoverflow.com/questions/59838565/windos-export-import-symbols-under-mingw-vs-msvs-cmakes-windows-export-all-sym – Tsyvarev Sep 03 '21 at 08:44
  • @Tsyvarev Hi, I found the solution in https://sourceware.org/binutils/docs/ld/WIN32.html, if you are still interested in this problem, please see the above content. It's been reedit. Thanks. – Cody Sep 03 '21 at 12:38
  • 1
    Please, do not put *solutions* into the **question post**. Instead, write an **answer** and describe solution there. On Stack Overflow self-answers are perfectly allowed: https://stackoverflow.com/help/self-answer. – Tsyvarev Sep 03 '21 at 16:27

1 Answers1

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")
Community
  • 1
  • 1
Cody
  • 609
  • 4
  • 21