2017-01-26 86 views
0

我在Windows 10時,Visual Studio 2015年。假設我建庫A和CMakeLists看上去就像如何覆蓋宏定義中的CMake

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

我定義宏WINDOWS_DLL_APIdllexport時,它的建館一,併爲被鏈接庫A.問題是,當我有另一個庫B中還鏈接一個外部應用程序定義WINDOWS_DLL_API作爲dllimport,我不知道如何改寫WINDOWS_DLL_APIdllexport。以下是我對庫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}) 

什麼是正確的方法來做到這一點?

+0

'...定義WINDOWS_DLL_API爲dllimport對於被鏈接庫A.'外部應用程序 - 'B'是 「外部」 庫,它與鏈接'A',所以它得到了這個定義。如果'B'不需要此定義,則不要將'A'定義爲* INTERFACE *。 – Tsyvarev

+0

@Tsyvarev如果我不把它定義,那麼如果我有鏈接到包括應用程序('add_executable')'TestA.h',''WINDOWS_DLL_API將是不確定的。 – user3667089

+0

這可能是有用的[GenerateExportHeader](https://cmake.org/cmake/help/v3.0/module/GenerateExportHeader.html)。它與'_dllexport/__ dllimport',並且還處理GCC構建與'-fvisibility = hidden' – Nazar554

回答

1

INTERFACE選項命令target_compile_definitions(以及其他target_* CMake的命令)是執行東西所有用戶庫,既可執行

意向明確執法爲至少單庫的用戶意味着該構想是在一個錯誤的方式使用。而應該使用其他方法。

在特定情況下,你需要使用不同的宏觀名字庫AB。並且最好完全刪除INTERFACE選項,因此即使您的圖書館的非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,它提供了更詳細的標題,它太在Windows平臺上工作。


。注意,當庫B包括從A頭,它把作爲foo()進口,這是正確的:所述函數是在A定義,而不是在B。使用你的方法(即使你設法爲B重新定義WINDOWS_DLL_API),庫B將錯誤地將foo()視爲導出

這是對概念一個優勢:打算克服概念信號,你做了錯事

+0

哇,這個工程,但它更加混亂,然後我期待,更不用說,如果我想我的代碼在Linux上編譯我不得不添加一些條件,使WINDOWS_DLL_API等於沒有。 – user3667089

+0

是的,如果需要支持多個平臺(OS),頭文件變得更加複雜。見例如[這個答案](http://stackoverflow.com/a/39231971/3440745)(我也添加了這個參考到我的文章中)。 – Tsyvarev

0

只想添加我的一塊我使用的代碼(通過CMake之前的版本2.8.12兼容)。

在我的根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檢查是在我的情況下,有必要因爲我也是交叉編譯的。

參考

+0

只是想指出的是,新的'target_compile_definitions'是首選的原因是因爲出口目標的時候,老'add_definitions'辦法是行不通的 – user3667089