如何覆盖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 命令)是为了为库的所有用户强制执行某些操作,包括可执行文件和库.
有意 明确 对 至少一个 图书馆的用户强制执行意味着这个概念被 以错误的方式使用。应该改用其他方法。
在给定的情况下,您需要为库 A
和 B
使用 不同的 宏名称。最好完全删除 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
我在 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 命令)是为了为库的所有用户强制执行某些操作,包括可执行文件和库.
有意 明确 对 至少一个 图书馆的用户强制执行意味着这个概念被 以错误的方式使用。应该改用其他方法。
在给定的情况下,您需要为库 A
和 B
使用 不同的 宏名称。最好完全删除 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