Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to overwrite macro definition in CMake

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?

like image 278
user3667089 Avatar asked Oct 28 '25 04:10

user3667089


2 Answers

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.

like image 185
Tsyvarev Avatar answered Oct 29 '25 18:10

Tsyvarev


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
like image 26
Florian Avatar answered Oct 29 '25 17:10

Florian



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!