如何在 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
调用中。该调用现在负责提供以前由构建库的行处理的所有信息:
- 提供导入目标
power
供 target_link_libraries
调用使用
- 通过该导入目标关联导入库(
power.lib
文件)的库名称
- 通过导入的目标
power.h
和 power_export.h
的 public 包含目录
您可以在查找脚本中手动构建这样一个导入的目标,也可以让 CMake 为您完成。在第一种情况下,创建一个 FindPower.cmake
脚本文件,确保它的位置是 CMAKE_MODULE_PATH
的一部分,并编写用于查找库和 header 文件并在其中构建导入目标的代码.请注意,以可移植的方式做到这一点可能非常棘手,并且远远超出了 Whosebug 问题的范围。在第二种情况下,让构建 power
库 的 CMake 脚本 执行安装步骤,在此期间将生成 a config file package,然后您可以使用它test
项目。请注意,如果 power
库本身不是使用 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
调用中。该调用现在负责提供以前由构建库的行处理的所有信息:
- 提供导入目标
power
供target_link_libraries
调用使用 - 通过该导入目标关联导入库(
power.lib
文件)的库名称 - 通过导入的目标
power.h
和 power_export.h
的 public 包含目录
您可以在查找脚本中手动构建这样一个导入的目标,也可以让 CMake 为您完成。在第一种情况下,创建一个 FindPower.cmake
脚本文件,确保它的位置是 CMAKE_MODULE_PATH
的一部分,并编写用于查找库和 header 文件并在其中构建导入目标的代码.请注意,以可移植的方式做到这一点可能非常棘手,并且远远超出了 Whosebug 问题的范围。在第二种情况下,让构建 power
库 的 CMake 脚本 执行安装步骤,在此期间将生成 a config file package,然后您可以使用它test
项目。请注意,如果 power
库本身不是使用 CMake 构建的,则此方法不可行,因此在这种情况下,您将不得不坚持使用第一个选项。