如何在CMake中使用动态链接库?

时间:2019-10-28 20:31:42

标签: c++ windows cmake dynamic-linking

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

CMakeLists.txt:

shellComponent

main.cpp:

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)

power.h:

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

using namespace std;

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

#ifndef POWER_H #define POWER_H double power(double number) noexcept; #endif // POWER_H 的实现位于名为power.h的.dll中。 如果我使用MinGW 7.3.0 X64编译该项目,则说:

power.dll

如果我使用MSVC 2017 X64进行编译说:

error: undefined reference to `power(double)'

两个错误均表明链接器无法检测到error: LNK1104: cannot open file 'power.lib' 。 我进行了很多搜索,但是没有一种解决方案适合我! 有人可以帮忙吗? 预先感谢!

2 个答案:

答案 0 :(得分: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,然后您的{{1 }}项目。请注意,如果test库本身不是使用CMake构建的,则这种方法不可行,因此在这种情况下,您将不得不坚持第一种选择。

答案 1 :(得分:0)

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 symbol BUILD_DLL:

add_definitions(-DBUILD_DLL)

然后,当MSVC编译器和使用MINGW时,它应该生成power.lib文件。不要在使用DLL的项目中定义BUILD_DLL,它应该可以工作。