我在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>)
我将宏WINDOWS_DLL_API
定义为dllexport
,当它构建库A,并将WINDOWS_DLL_API
定义为dllimport
,用于链接库A的外部应用程序问题是,当我有另一个也连接A的库B时,我不知道如何将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})
这样做的正确方法是什么?
答案 0 :(得分:2)
target_compile_definitions
(以及其他target_*
CMake命令)的 INTERFACE 选项的概念是为所有用户强制执行某些操作库,可执行文件和库。
对至少单个库用户的清除强制执行意味着该概念以错误的方式使用。而应该使用其他方法。
在给定的情况下,您需要为库A
和B
使用不同的宏名称。并且最好完全删除 INTERFACE 选项,因此即使您图书馆的非CMake用户也会感到满意。
<强> TestA.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);
...
<强> TestB.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)
另请参阅this answer,它提供了更详细的标题,也适用于Windows平台。
注意,当库B
包含来自A
的标题时,它会将foo()
视为导入,这是正确的:该函数定义于A
,而不是B
。使用您的方法(即使您设法为WINDOWS_DLL_API
重新定义B
),库B
会错误地将foo()
视为导出。
这是概念的一个优点:意图克服概念信号,表明您做错了。
答案 1 :(得分:0)
只是想添加我正在使用的代码片段(与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
检查是必要的,因为我也是交叉编译的。
<强>参考强>