【问题标题】:How to use dynamic link library with CMake?如何在 CMake 中使用动态链接库?
【发布时间】:2019-10-28 20:31:42
【问题描述】:

我有一个简单的程序如下:

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 的实现位于名为 power.dll 的 .dll 中。 如果我用 MinGW 7.3.0 X64 编译这个项目说:

error: undefined reference to `power(double)'

如果我用 MSVC 2017 X64 编译它说:

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

这两个错误都表明链接器无法检测到power.dll。 我做了很多搜索,但没有一个解决方案对我有用! 有人可以帮忙吗? 提前致谢!

【问题讨论】:

  • power.dll 在您的系统上位于何处?你试过target_link_libraries(test PRIVATE power)吗?
  • 它位于 test.exe 目录和源目录中。是的,我也尝试了 target_link_libraries(test PRIVATE power),但 CMake 默认查找 .lib 版本而不是 .dll 版本...
  • 在 Windows 上链接是使用.lib 文件执行的。 .dll 仅在 运行时 使用。此外,请确保 power 函数已从库中导出
  • @Tsyvarev 我知道这一点,但我没有 .lib 版本,我只有 .dll 文件!所以真的没有办法链接到 .dll 文件吗?
  • 您可以从.dll 创建.lib。例如。 stackoverflow.com/questions/9360280/…stackoverflow.com/questions/9946322/… 或者只是谷歌“从 dll 获取库”。

标签: c++ windows cmake dynamic-linking


【解决方案1】:

您对动态库的建模不正确,无论是在 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.hpower_export.h 通过导入的目标

您可以在查找脚本中手动构建此类导入的目标,也可以让 CMake 为您完成。在第一种情况下,创建一个FindPower.cmake 脚本文件,确保它的位置是CMAKE_MODULE_PATH 的一部分,并编写用于查找库和头文件并在其中构建导入目标的代码。请注意,以可移植的方式做到这一点可能非常棘手,并且远远超出了 StackOverflow 问题的范围。在第二种情况下,让构建 power 库的 CMake 脚本执行安装步骤,在此期间将生成 a config file package,然后您的 test 项目可以使用它。请注意,如果 power 库本身不是使用 CMake 构建的,则此方法不可行,因此在这种情况下,您必须坚持使用第一个选项。

【讨论】:

  • 亲爱的@ComicSansMS 非常感谢您的出色回答。 OpenCV、Boost 和...是如何工作的,以便我们可以将我们的程序直接链接到这些库的 dll 中?他们如何处理这个问题?
  • @NimaGhorab 他们做的和我在答案中描述的一样。查找Boost ship as part of CMake 的脚本(方法#1),但您也可以自己轻松编写这样的脚本。 OpenCV provides a config file script 作为其安装的一部分(方法 #2)。
  • 非常感谢!作为我的最后一个问题,为什么在以下示例中使用 opencv(而不是 .lib)时我们应该链接到 .dll 文件?因为我在许多帖子中读到以这种方式链接 dll 文件是错误的! Linking to dynamic library using QMake
  • @NimaGhorab 简短回答:MinGW 以一种奇怪的方式(即非 Windows-y)处理动态库,主要是因为它的共享库模型很大程度上受 Linux .so 的影响文件。 Windows 上的共享库与 Linux 上的共享库有很大的不同。关于如何将共享库与 CMake 等可移植构建系统集成的大部分困惑通常是由于人们只了解他们如何在其中一个系统上工作,而不了解另一个。
【解决方案2】:

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

在使用CMake构建DLL power.dll的项目中,你应该define the symbolBUILD_DLL:

add_definitions(-DBUILD_DLL)

那么它应该在 MSVC 编译器时生成一个 power.lib 文件,在使用 MINGW 时生成一个 power.a 文件。不要在使用 DLL 的项目中定义 BUILD_DLL,它应该可以工作。

【讨论】:

  • 亲爱的@Pedro,它没有用!它现在给出这个错误:错误:BUILD_DLL:没有这样的文件或目录
  • 抱歉,小错误。 add_definitions() 宏必须添加到构建 DLL 的项目中,而不是使用 DLL 的项目中。
  • 如果您无法重建 DLL,那么您应该探索other options
  • 亲爱的@Pedro 它仍然无法正常工作!如果我使用 MinGW 说:错误:未定义对 `power(double)' 的引用,MSVC 说:power.h:12: error: C2144: syntax error: 'double' 前面应该有 ';'还有:power.h:12:错误:C4430:缺少类型说明符 - 假定为 int。注意:C++ 不支持 default-int
  • 亲爱的@Pedro,如果我只有 .lib 文件而没有 power.cpp 和 power.dll,我可以将 power.lib 转换为 power.dll 吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多