如何让 CMake 在 Windows 上自动导出符号?
How to have CMake export symbols automatically on Windows?
根据我的经验,尽管它可能不正确,但在 Windows 如果没有翻译单元导出任何符号,那么在使用 GNU C++ 编译器构建 DLL 时所有符号都将隐式导出。相反,如果有任何翻译单元导出任何符号,则 DLL 中的所有其他符号都不会隐式导出。
换句话说,如果有头文件我们#include
用__declspec(dllexport)
声明一个符号,那么我们必须手动插入__declspec(dllexport)
到其他符号的每一个声明。否则,将不会导出这些符号。
在 CMake 世界中有没有一种我们不必自己添加 __declspec(dllexport)
的便捷方法?我试过了
set_target_properties(foo PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
但它不起作用。有什么建议吗?谢谢。
重新编辑:(2021-09-03 8:34:40 UTC)
下面是重现问题的示例。
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__);
}
我的环境:
Windows 10
MSYS2
CMake 3.21.2
结果:
$ 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
根据 Dependency Walker,从 libbar.dll 导出的唯一符号是 bar_funcB
。
根据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.
在这种情况下,--export-all-symbols
链接器选项 强制执行 自动导出功能并导出所有符号,一些特殊符号除外。因此,要在不修改 C/C++ 代码的情况下也导出 bar_funcA
,请在 src/bar/CMakeLists.txt 中添加此行:
target_link_options(bar PRIVATE "-Wl,--export-all-symbols")
根据我的经验,尽管它可能不正确,但在 Windows 如果没有翻译单元导出任何符号,那么在使用 GNU C++ 编译器构建 DLL 时所有符号都将隐式导出。相反,如果有任何翻译单元导出任何符号,则 DLL 中的所有其他符号都不会隐式导出。
换句话说,如果有头文件我们#include
用__declspec(dllexport)
声明一个符号,那么我们必须手动插入__declspec(dllexport)
到其他符号的每一个声明。否则,将不会导出这些符号。
在 CMake 世界中有没有一种我们不必自己添加 __declspec(dllexport)
的便捷方法?我试过了
set_target_properties(foo PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
但它不起作用。有什么建议吗?谢谢。
重新编辑:(2021-09-03 8:34:40 UTC)
下面是重现问题的示例。
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__);
}
我的环境:
Windows 10
MSYS2
CMake 3.21.2
结果:
$ 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
根据 Dependency Walker,从 libbar.dll 导出的唯一符号是 bar_funcB
。
根据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.
在这种情况下,--export-all-symbols
链接器选项 强制执行 自动导出功能并导出所有符号,一些特殊符号除外。因此,要在不修改 C/C++ 代码的情况下也导出 bar_funcA
,请在 src/bar/CMakeLists.txt 中添加此行:
target_link_options(bar PRIVATE "-Wl,--export-all-symbols")