2017-01-26 31 views
0

Я на Windows 10, Visual Studio 2015. Предположим, я строю библиотека А с 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_API, как dllexport, когда это здание библиотеки A , и определяя WINDOWS_DLL_API как dllimport для внешних приложений, которые связывают библиотеку A. Проблема заключается в том, что у меня есть другая библиотека B, которая также связывает A, я не знаю, как перезаписать WINDOWS_DLL_API до dllexport. Ниже моя попытка моих CMakeLists для библиотеки 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}) 

Что такое правильный способ сделать это?

+0

'... определение WINDOWS_DLL_API в DllImport для внешних приложений, которые ссылающиеся библиотека A.' -' B' является "внешней" библиотекой, которая связывает с 'A', поэтому он получает это определение. Если это определение не требуется для 'B', просто не определяйте его для' A' как * INTERFACE *. – Tsyvarev

+0

@ Tssyvarev Если я не определяю его, то если у меня есть приложение ('add_executable'), которое ссылается на A и включает' 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

Концепция из ИНТЕРФЕЙС вариант для команды target_compile_definitions (и для других команд target_* CMake) является обеспечение кое-что для всех пользователей библиотеки, как исполняемые файлы и библиотеки.

Намерения ясного исполнения для по крайней мере, одного пользователей библиотеки означает, что концепция используется в неправильном. Вместо этого следует использовать другие подходы.

В данном случае вам необходимо использовать разные имена макросов для библиотек A и B. И лучше удалить опцию 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); 

А/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. С вашим подходом (даже если вам удастся переопределить WINDOWS_DLL_API для B), библиотека B будет неправильно обрабатывать foo() как экспортировано.

Это преимущество концепции: намерение преодолеть зачатия сигналы, которые вы делаете что-то неправильно.

+0

wow, это работает, но это более беспорядочно, чем я ожидал, не говоря уже о том, что если я хочу, чтобы мой код компилировался на linux, я должен добавить еще несколько условий, чтобы сделать WINDOWS_DLL_API равным нулю. – user3667089

+0

Да, файлы заголовков становятся более сложными, если необходимо поддерживать несколько платформ (ОС). См. [этот ответ] (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