如何在CMake中覆盖宏定义 [英] How to overwrite macro definition in CMake

查看:441
本文介绍了如何在CMake中覆盖宏定义的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用的是Windows 10,Visual Studio2015.假设我使用的CMakeLists构建库A

  cmake_minimum_required(版本3.7)项目(A)设置(DLLIMPORT"__declspec(dllimport)")设置(DLLEXPORT"__declspec(dllexport)")设置(PROJECT_SRCS$ {PROJECT_SOURCE_DIR}/src/TestA.cpp)设置(PROJECT_INCS$ {PROJECT_SOURCE_DIR}/include/TestA.h)add_library($ {PROJECT_NAME} SHARED $ {PROJECT_SRCS} $ {PROJECT_INCS})target_compile_definitions($ {PROJECT_NAME}接口WINDOWS_DLL_API = $ {DLLIMPORT})target_compile_definitions($ {PROJECT_NAME} PRIVATEWINDOWS_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 ,并将 WINDOWS_DLL_API 定义为 dllimport 对于链接库A的外部应用程序,问题是当我还有另一个也链接A的库B时,我不知道如何将 WINDOWS_DLL_API 覆盖回 dllexport .以下是我对库B的CMakeList的尝试,

  cmake_minimum_required(版本3.7)项目(B)设置(DLLEXPORT"__declspec(dllexport)")设置(PROJECT_SRCS$ {PROJECT_SOURCE_DIR}/src/TestB.cpp)设置(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)#不起作用target_compile_definitions($ {PROJECT_NAME} PRIVATEWINDOWS_DLL_API = $ {DLLEXPORT}) 

正确的方法是什么?

INTERFACE 选项的

解决方案

概念(用于命令 target_compile_definitions (以及其他 target _ * CMake命令)为库的所有用户(包括可执行文件)强制执行某些操作.

意图清除强制执行至少一个库用户,这意味着该概念使用了错误的方式.而应该使用其他方法.

在给定的情况下,您需要为库 A B 使用不同的宏名称.最好完全删除 INTERFACE 选项,这样,即使您的库的非CMake用户也会很满意.

TestA.h :

  #ifdef BUILD_A#定义WINDOWS_DLL_API_A __declspec(dllexport)#别的#定义WINDOWS_DLL_API_A __declspec(dllimport)#万一...WINDOWS_DLL_API_A void foo(void);... 

TestB.h :

  #ifdef BUILD_B#定义WINDOWS_DLL_API_B __declspec(dllexport)#别的#定义WINDOWS_DLL_API_B __declspec(dllimport)#万一//假设这里是A的用法.#include< TestA.h>...WINDOWS_DLL_API_B void bar(void); 

A/CMakeLists.txt :

  cmake_minimum_required(版本3.7)项目(A)...add_library($ {PROJECT_NAME} SHARED ...)target_compile_definitions($ {PROJECT_NAME} PRIVATE"BUILD _ $ {PROJECT_NAME} = 1") 

B/CMakeLists.txt :

  cmake_minimum_required(版本3.7)项目(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()导出为 .

这是构思的一个优点:意图克服构思表示您做错了事.

I am on Windows 10, Visual Studio 2015. Suppose I am building library A with CMakeLists looking like

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>)

I am defining the macro WINDOWS_DLL_API as dllexport when it's building library A, and defining WINDOWS_DLL_API as dllimport for external applications that is linking library A. The problem is when I have another library B that is also linking A, I don't know how to overwrite WINDOWS_DLL_API back to dllexport. Below is my attempt of my CMakeLists for library B,

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

What is the right way to do it?

解决方案

Concept of INTERFACE option for command target_compile_definitions (and for other target_* CMake commands) is to enforce something for all users of the library, both executables and libraries.

Intention to clear enforcement for at least single library's user means that the conception is used in a wrong way. And other approaches should be used instead.

In given case, you need to use different macro names for libraries A and B. And it is better to remove INTERFACE option completely, so even non-CMake users of your library will be happy.

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)

See also this answer, which provides more detailed header, which works on Windows platforms too.


Note, that when the library B includes header from A, it treats foo() as imported, and this is correct: the function is defined in A, not in B. With your approach (even if you would manage to redefine WINDOWS_DLL_API for B), library B would incorrectly treat foo() as exported.

This is an advantage of the conception: intention to overcome a conception signals that you do something wrong.

这篇关于如何在CMake中覆盖宏定义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆