Как использовать динамическую библиотеку ссылок с CMake? - PullRequest
1 голос
/ 28 октября 2019

У меня есть простая программа следующим образом:

CMakeLists.txt:

cmake_minimum_required(VERSION 3.5)

project(test LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

LINK_DIRECTORIES(${PROJECT_SOURCE_DIR})

add_executable(test main.cpp)
target_include_directories(test PRIVATE ${PROJECT_SOURCE_DIR})    
target_link_libraries(test PRIVATE power.dll)

main.cpp:

#include <iostream>
#include "power.h"

using namespace std;

int main()
{
    cout << "Hello World!" << endl;
    power(4.);
    return 0;
}

power.h:

#ifndef POWER_H
#define POWER_H

double power(double number) noexcept;

#endif // POWER_H

Реализация power.h находится в .dll с именем power.dll. Если я компилирую этот проект с MinGW 7.3.0, X64 говорит:

error: undefined reference to `power(double)'

Если я компилирую его с MSVC 2017, X64 говорит:

error: LNK1104: cannot open file 'power.lib'

обе ошибки показывают, что power.dll может 'Обнаружить компоновщик. Я сделал много поисков, но ни одно из решений не сработало для меня! Кто-нибудь может помочь с этим? Заранее спасибо!

Ответы [ 2 ]

1 голос
/ 29 октября 2019

Ваше моделирование динамической библиотеки некорректно как на CMake, так и на уровне исходного кода.

В качестве отправной точки попробуйте собрать dll как часть того же проекта CMake, что и исполняемый файл-потребитель:

cmake_minimum_required(VERSION 3.5)

project(test LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

include(GenerateExportHeader)
add_library(power SHARED power_sources.cpp power.h)
generate_export_header(power)
target_include_directories(power PUBLIC ${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR})

add_executable(test main.cpp)
target_link_libraries(test PRIVATE power)

Обратите внимание на использование функции generate_export_header, которая инструктирует CMake генерировать макросы для экспорта функций в интерфейсах совместно используемых библиотек в переносимом виде. Поскольку сгенерированные файлы попадают в двоичное дерево каталогов, мы должны соответствующим образом настроить каталоги включения для библиотеки.

Чтобы убедиться, что функция экспортируется правильно, измените свой заголовок следующим образом:

#ifndef POWER_H
#define POWER_H

#include <power_export.h>

POWER_EXPORT double power(double number) noexcept;

#endif // POWER_H

Обратите внимание, что generare_export_header позволяет широко настраивать сгенерированный заголовок экспорта.

Убедитесь, что вы получаете проект для сборки и запуска с этой базовой линии.

Если вы хотите построить dllвнешне (что не является строго необходимым, но так как это то, о чем ваш вопрос ...), мы должны изменить файл CMake на что-то вроде:

cmake_minimum_required(VERSION 3.5)

project(test LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(power)

add_executable(test main.cpp)
target_link_libraries(test PRIVATE power)

Со всей магией, происходящей здесь в find_package звонок. Этот вызов теперь отвечает за предоставление всей информации, которая ранее была обработана строками библиотеки:

  • Предоставление импортированной цели power для потребления вызовом target_link_libraries
  • Ассоциация имени библиотеки библиотеки импорта (файл power.lib) через эту импортированную цель
  • Экспонирование общих включенных каталогов для обоих power.h и power_export.hчерез эту импортированную цель

Вы можете либо создать такую ​​импортированную цель вручную в скрипте поиска, либо CMake сделает это за вас. В первом случае создайте файл сценария FindPower.cmake, убедитесь, что его местоположение является частью CMAKE_MODULE_PATH, и напишите код для поиска библиотеки и заголовочных файлов и построения импортируемой цели там. Обратите внимание, что получить это правильно в портативном виде может быть очень сложно и выходит далеко за рамки вопроса StackOverflow. Во втором случае попросите сценарий CMake , который создает библиотеку power , выполнить шаг установки, во время которого будет сгенерирован пакет файла конфигурации , который затем может быть использован вашим test проект. Обратите внимание, что этот подход нежизнеспособен, если библиотека power сама не создается с помощью CMake, поэтому в этом случае вам придется придерживаться первого варианта.

0 голосов
/ 29 октября 2019

Динамическое связывание в Windows требует, чтобы внешне видимые символы объявлялись с ключевым словом __declspec. Ваш заголовок "power.h" должен быть изменен:

#ifndef POWER_H
#define POWER_H

#if defined(__WIN32__) && !defined(__CYGWIN__)
#  if (defined(_MSC_VER) || defined(__MINGW32__)) && defined(BUILD_DLL)
#    define POWERAPI __declspec(dllexport)
#  elif (defined(_MSC_VER) || defined(__MINGW32__))
#    define POWERAPI __declspec(dllimport)
#  endif
#endif

POWERAPI double power(double number) noexcept;

#endif // POWER_H

При создании проекта DLL power.dll с CMake, вы должны определить символ BUILD_DLL:

add_definitions(-DBUILD_DLL)

тогда он должен генерировать файл power.lib при компиляции MSVC и power.a при использовании MINGW. Не определяйте BUILD_DLL в проекте, использующем DLL, и он должен работать.

...