How to overwrite macro definition in CMake

杀马特。学长 韩版系。学妹 提交于 2021-01-27 21:40:51

问题


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?


回答1:


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.




回答2:


Just wanted to add my piece of code I'm using (compatible with CMake versions prior to 2.8.12).

In my root CMakeLists.txt file I have:

if (MSVC)
    add_definitions(-DWINDOWS_DLL_API=__declspec\(dllexport\))
else()
    add_definitions(-DWINDOWS_DLL_API=)
endif()

In the (sub-)project's CMakeLists.txt using the DLL I've put:

if (MSVC)
    remove_definitions(-DWINDOWS_DLL_API=__declspec\(dllexport\))
    add_definitions(-DWINDOWS_DLL_API=__declspec\(dllimport\))
endif()

The MSVC checks are necessary in my case because I also cross-compile.

Reference

  • CMake - override compile flags for single files


来源:https://stackoverflow.com/questions/41866614/how-to-overwrite-macro-definition-in-cmake

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!