如何覆盖CMake中的宏定义

How to overwrite macro definition in CMake

我在 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。问题是当我有另一个也链接 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})

正确的做法是什么?

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

有意 明确 至少一个 图书馆的用户强制执行意味着这个概念被 以错误的方式使用。应该改用其他方法。

在给定的情况下,您需要为库 AB 使用 不同的 宏名称。最好完全删除 INTERFACE 选项,这样即使 non-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,它提供了更详细的 header,它也适用于 Windows 平台。


请注意,当库 B 包含来自 A 的 header 时,它将 foo() 视为 导入的 ,并且这是正确的:函数是在 A 中定义的,而不是在 B 中定义的。使用您的方法(即使您设法为 B 重新定义 WINDOWS_DLL_API),库 B 会错误地将 foo() 视为 exported .

这是观念的优势:想要克服观念的意图表明你做错了什么.

只是想添加我正在使用的一段代码(与 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 检查对我来说是必要的,因为我也 cross-compile。

参考

  • CMake - override compile flags for single files