I noticed weird inconsistent behavior of CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS CMake variable. Considering small example project (Windows, MinGW):
cmake_minimum_required(VERSION 3.20 FATAL_ERROR)
project(Test VERSION 1.0.0)
option(BUILD_SHARED_LIBS "Build the project using shared libraries (Windows *.dll, or Linux *.so)" ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O3")
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(CMAKE_CXX_STANDARD 17)
add_library(foobar foobar.cpp)
target_compile_definitions(foobar PRIVATE "FOOBAR_LIBRARY")
add_library(baz baz.cpp)
target_link_libraries(baz PUBLIC foobar)
and the sources:
/****** foobar.h ******/
#pragma once
#if defined(FOOBAR_LIBRARY)
#define FOOBAR_EXPORT __declspec(dllexport)
#else
#define FOOBAR_EXPORT __declspec(dllimport)
#endif
void foo();
FOOBAR_EXPORT void bar();
// void bar(); // removing explicit FOOBAR_EXPORT makes it work
/****** foobar.cpp ******/
#include "foobar.h" // commenting-out the header include will make it work
void foo() {}
void bar() {}
/****** baz.cpp ******/
#include "foobar.h"
void baz() {
foo();
bar();
}
After configuring it with
cmake -G "MinGW Makefiles" ../
and trying to compile with
cmake --build .
the linker fails with error:
baz.cpp:5: undefined reference to `foo()'
I noticed that:
FOOBAR_EXPORT
from void bar
makes it link correctly#include "foobar.h"
from foobar.cpp
makes it link correctlyFOOBAR_EXPORT
to void foo
makes it link correctlyWhat can I change in my CMakeLists.txt
or definitions of FOOBAR_EXPORT
to always have all symbols exported, even if some of them have explicit __declspec
in front of declaration?
Applying any of the 3 solutions above is a maintenance hell for me, because the problem is in large autogenerated OpenAPI code.
When linking on windows, there is a default behavior to automatically export all symbols in a DLL unless some conditions are met. The primary condition is:
If
--export-all-symbols
is not given explicitly on the command line, then 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.
The overall set of symbols exported is described as:
When auto-export is in operation, ld will export all the non-local (global and common) symbols it finds in a DLL, with the exception of a few symbols known to belong to the system’s runtime and libraries. As it will often not be desirable to export all of a DLL’s symbols, which may include private functions that are not part of any public interface, the command-line options listed above may be used to filter symbols out from the list for exporting. The
--output-def
option can be used in order to see the final list of exported symbols with all exclusions taken into effect.
So, if you find yourself in the sitation where you want to re-enable the 'all symbol export' behavior; you can add the --export-all-symbols
option to the linker.
The command line way to get that to happen is -Wl,--export-all-symbols
.
The cmake way to get this to happen is to add this option to the CMakeLists.txt
for the linker; This can be accomplished by adding it to the CXX_FLAGS:
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--export-all-symbols")
However, cmake indicates that CMAKE_SHARED_LINKER_FLAGS
is a slightly better choice of variable in this case.