如何在 CMake 中使用动态 link 库?

How to use dynamic link library with CMake?

我有如下简单的程序:

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 无法被链接器检测到。 我进行了多次搜索,但 none 的解决方案对我有用! 任何人都可以帮忙吗? 提前致谢!

Windows 中的动态链接要求使用关键字 __declspec 声明外部可见符号。你的 header "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 symbol BUILD_DLL:

add_definitions(-DBUILD_DLL)

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

您的动态库建模不正确,无论是在 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 生成宏以可移植的方式在共享库接口上导出函数。由于生成的文件进入二进制目录树,我们必须相应地调整库的包含目录。

为确保函数正确导出,请按如下方式更改 header:

#ifndef POWER_H
#define POWER_H

#include <power_export.h>

POWER_EXPORT double power(double number) noexcept;

#endif // POWER_H

请注意 generare_export_header 允许您广泛自定义生成的导出 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 调用中。该调用现在负责提供以前由构建库的行处理的所有信息:

  • 提供导入目标 powertarget_link_libraries 调用使用
  • 通过该导入目标关联导入库(power.lib 文件)的库名称
  • 通过导入的目标
  • power.hpower_export.h 的 public 包含目录

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