我使用的是Windows 10 Visual Studio 2015。假设我正在用看起来像的CMakeLists构建库A
cmake_minimum_required(VERSION 3.7)
project(A)
set(DLLIMPORT "__declspec(dllimport)")
set(DLLEXPORT "__declspec(dllexport)")
set(PROJECT_SRCS
${PROJECT_SOURCE_DIR}/src/TestA.cpp)
set(PROJECT_INCS
${PROJECT_SOURCE_DIR}/include/TestA.h)
add_library(${PROJECT_NAME} SHARED ${PROJECT_SRCS} ${PROJECT_INCS})
target_compile_definitions(${PROJECT_NAME} INTERFACE
WINDOWS_DLL_API=${DLLIMPORT})
target_compile_definitions(${PROJECT_NAME} PRIVATE
WINDOWS_DLL_API=${DLLEXPORT})
target_include_directories(${PROJECT_NAME} PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include>)
在构建库A时,我将宏WINDOWS_DLL_API
定义为dllexport
,并将链接库A的外部应用程序的WINDOWS_DLL_API
定义为dllimport
。问题是,当我有另一个库B也在链接A时,不知道如何将WINDOWS_DLL_API
重写回dllexport
。以下是我对库B、的CMakeLists的尝试
cmake_minimum_required(VERSION 3.7)
project(B)
set(DLLEXPORT "__declspec(dllexport)")
set(PROJECT_SRCS
${PROJECT_SOURCE_DIR}/src/TestB.cpp)
set(PROJECT_INCS
${PROJECT_SOURCE_DIR}/include/TestB.h)
add_library(${PROJECT_NAME} SHARED ${PROJECT_SRCS} ${PROJECT_INCS})
target_include_directories(${PROJECT_NAME} PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/include>)
target_link_libraries(${PROJECT_NAME} A)
# does not work
target_compile_definitions(${PROJECT_NAME} PRIVATE
WINDOWS_DLL_API=${DLLEXPORT})
做这件事的正确方法是什么?
target_compile_definitions
(以及其他target_*
CMake命令)的INTERFACE选项的概念是为库的所有用户强制执行某些东西,包括可执行文件和库。
有意至少为一个库的用户清除强制执行意味着以错误的方式使用了该概念。相反,应该使用其他方法。
在给定的情况下,需要为库A
和B
使用不同的宏名称。最好完全删除INTERFACE选项,这样即使是库的非CMake用户也会很高兴。
测试A.h:
#ifdef BUILD_A
#define WINDOWS_DLL_API_A __declspec(dllexport)
#else
#define WINDOWS_DLL_API_A __declspec(dllimport)
#endif
...
WINDOWS_DLL_API_A void foo(void);
...
测试B.h:
#ifdef BUILD_B
#define WINDOWS_DLL_API_B __declspec(dllexport)
#else
#define WINDOWS_DLL_API_B __declspec(dllimport)
#endif
// Assume usage of A is here.
#include <TestA.h>
...
WINDOWS_DLL_API_B void bar(void);
A/CMakeLists.txt:
cmake_minimum_required(VERSION 3.7)
project(A)
...
add_library(${PROJECT_NAME} SHARED ...)
target_compile_definitions(${PROJECT_NAME} PRIVATE "BUILD_${PROJECT_NAME}=1")
B/CMakeLists.txt:
cmake_minimum_required(VERSION 3.7)
project(B)
...
add_library(${PROJECT_NAME} SHARED ...)
target_compile_definitions(${PROJECT_NAME} PRIVATE "BUILD_${PROJECT_NAME}=1")
target_link_libraries(${PROJECT_NAME} A)
另请参阅此答案,它提供了更详细的标题,也适用于Windows平台。
注意,当库B
包含来自A
的标头时,它将foo()
视为导入,这是正确的:函数在A
中定义,而不是在B
中定义。使用您的方法(即使您设法为B
重新定义WINDOWS_DLL_API
),库B
也会错误地将foo()
视为导出。
这是概念的优点:意图克服的概念表明你做错了什么。
我只是想添加我正在使用的代码(与2.8.12之前的CMake版本兼容)。
在我的根CMakeLists.txt
文件中,我有:
if (MSVC)
add_definitions(-DWINDOWS_DLL_API=__declspec(dllexport))
else()
add_definitions(-DWINDOWS_DLL_API=)
endif()
在(子)项目的CMakeLists.txt
中,使用我放入的DLL:
if (MSVC)
remove_definitions(-DWINDOWS_DLL_API=__declspec(dllexport))
add_definitions(-DWINDOWS_DLL_API=__declspec(dllimport))
endif()
MSVC
检查在我的情况下是必要的,因为我也交叉编译。
参考
- CMake-覆盖单个文件的编译标志