如何在CMake中覆盖宏定义

时间:2017-01-26 03:50:41

标签: c++ dll cmake dllimport dllexport

我在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})

这样做的正确方法是什么?

2 个答案:

答案 0 :(得分:2)

命令target_compile_definitions(以及其他target_* CMake命令)的 INTERFACE 选项的概念是为所有用户强制执行某些操作库,可执行文件

至少单个库用户的清除强制执行意味着该概念以错误的方式使用。而应该使用其他方法。

在给定的情况下,您需要为库AB使用不同的宏名称。并且最好完全删除 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检查是必要的,因为我也是交叉编译的。

<强>参考