我使用 CMake 编译生成 DLL(动态链接库)详解

2025-09-26 14:32:15

使用 CMake 编译生成 DLL(动态链接库)详解

在现代软件开发中,动态链接库(DLL)是实现模块化和代码复用的重要手段。尤其在 Windows 平台上,DLL 的使用尤为普遍。本文将详细介绍如何使用 CMake 编译生成 DLL,包括项目配置、代码编写、构建步骤以及跨平台考虑等内容。

基本概念

在 CMake 中,创建动态链接库(DLL)主要通过 add_library 命令实现,并指定库类型为 SHARED。为了确保在 DLL 中正确导出符号,需要适当设置导出宏。以下是实现这一过程的详细步骤。

项目结构

假设你的项目名称为 MyProject,其目录结构如下:

MyProject/

├── CMakeLists.txt

├── include/

│ └── mylib.h

└── src/

└── mylib.cpp

编写源代码

头文件 mylib.h

首先,创建一个头文件 mylib.h,用于声明 DLL 中导出的函数。

#ifndef MYLIB_H

#define MYLIB_H

#ifdef MYLIB_EXPORTS

#define MYLIB_API __declspec(dllexport)

#else

#define MYLIB_API __declspec(dllimport)

#endif

extern "C" {

MYLIB_API void hello();

}

#endif // MYLIB_H

说明:

MYLIB_EXPORTS 宏用于在编译 DLL 时导出符号,而在使用 DLL 时导入符号。

__declspec(dllexport) 和 __declspec(dllimport) 是 Windows 平台下用于导出和导入符号的关键字。

extern "C" 用于防止 C++ 的名称修饰,确保函数名在 DLL 中以 C 风格导出,便于其他语言或工具使用。

源文件 mylib.cpp

接下来,编写实现上述函数的源文件 mylib.cpp。

#include "mylib.h"

#include

void hello() {

std::cout << "Hello from DLL!" << std::endl;

}

配置 CMakeLists.txt

在项目根目录下创建 CMakeLists.txt 文件,配置项目的构建过程。

cmake_minimum_required(VERSION 3.10)

project(MyLibrary VERSION 1.0.0 LANGUAGES CXX)

# 设置 C++ 标准

set(CMAKE_CXX_STANDARD 11)

set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 定义库

add_library(MyLibrary SHARED src/mylib.cpp)

# 指定头文件的位置

target_include_directories(MyLibrary

PUBLIC

${PROJECT_SOURCE_DIR}/include

)

# 定义 MYLIB_EXPORTS 宏,用于导出符号

target_compile_definitions(MyLibrary

PRIVATE MYLIB_EXPORTS

)

# 设置库的输出目录(可选)

set_target_properties(MyLibrary PROPERTIES

RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"

LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"

ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"

)

说明:

add_library(MyLibrary SHARED src/mylib.cpp) 创建一个名为 MyLibrary 的动态链接库。

target_include_directories 指定了头文件的位置,使用 PUBLIC 表示这些头文件对使用该库的项目可见。

target_compile_definitions 定义了 MYLIB_EXPORTS 宏,仅在编译该库时有效,用于导出符号。

set_target_properties 设置了输出目录,可以根据需要调整。

构建 DLL

步骤 1:创建构建目录

打开终端(例如,Visual Studio 开发者命令提示符),然后执行以下命令创建并进入构建目录:

mkdir build

cd build

步骤 2:运行 CMake 配置项目

根据你的 Visual Studio 版本和目标架构,运行以下命令:

cmake .. -G "Visual Studio 16 2019" -A x64

说明:

-G "Visual Studio 16 2019" 指定使用的生成器,根据你的 Visual Studio 版本进行调整。

-A x64 指定生成 64 位库。你也可以选择 Win32 等其他架构。

步骤 3:编译项目

执行以下命令编译项目:

cmake --build . --config Release

说明:

--config Release 指定生成配置为 Release,你也可以选择 Debug。

完成后,生成的 DLL 将位于 build/bin/Release/ 目录下。

使用生成的 DLL

假设你有另一个项目需要使用 MyLibrary.dll,以下是如何配置和使用该 DLL 的示例。

创建使用 DLL 的项目

假设新项目名称为 MyApp,其目录结构如下:

MyApp/

├── CMakeLists.txt

├── main.cpp

源文件 main.cpp

编写一个简单的应用程序,调用 DLL 中的函数。

#include "mylib.h"

int main() {

hello();

return 0;

}

配置 CMakeLists.txt

在 MyApp 项目根目录下创建 CMakeLists.txt 文件:

cmake_minimum_required(VERSION 3.10)

project(MyApp LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 11)

set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 添加可执行文件

add_executable(MyApp main.cpp)

# 指定 MyLibrary 的路径

add_subdirectory(path/to/MyProject)

# 链接 MyLibrary

target_link_libraries(MyApp PRIVATE MyLibrary)

# 设置可执行文件的运行目录,以便找到 DLL

set_target_properties(MyApp PROPERTIES

RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"

)

说明:

add_subdirectory 将 MyProject 项目添加到 MyApp 项目中,需要根据实际路径调整。

target_link_libraries 将 MyLibrary 链接到 MyApp。

确保 MyLibrary.dll 位于可执行文件所在的目录,或者在系统的 PATH 中,以便在运行时找到 DLL。

构建和运行

在 MyApp 项目目录下执行以下命令:

mkdir build

cd build

cmake .. -G "Visual Studio 16 2019" -A x64

cmake --build . --config Release

# 运行可执行文件

./bin/Release/MyApp.exe

运行后,应该会在控制台看到输出:

Hello from DLL!

进一步优化

自动复制 DLL

为了方便运行,可以在 MyLibrary 的 CMakeLists.txt 中添加自定义命令,自动将 DLL 复制到 MyApp 的可执行文件目录中。

# 在 MyLibrary 的 CMakeLists.txt 中添加

add_custom_command(TARGET MyLibrary POST_BUILD

COMMAND ${CMAKE_COMMAND} -E copy $

$

)

说明:

这条命令将在 MyLibrary 构建后执行,将生成的 DLL 复制到 MyApp 的可执行文件目录中。

使用 GenerateExportHeader

为了简化导出符号的管理,可以使用 GenerateExportHeader 模块。

修改 CMakeLists.txt

# 在项目中启用 GenerateExportHeader

include(GenerateExportHeader)

add_library(MyLibrary SHARED src/mylib.cpp)

# 生成导出头文件

generate_export_header(MyLibrary

EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/mylib_export.h"

)

# 包含生成的导出头文件

target_include_directories(MyLibrary

PUBLIC

${PROJECT_SOURCE_DIR}/include

${CMAKE_CURRENT_BINARY_DIR}

)

修改头文件 mylib.h

#ifndef MYLIB_H

#define MYLIB_H

#include "mylib_export.h"

extern "C" {

MYLIB_EXPORT void hello();

}

#endif // MYLIB_H

说明:

GenerateExportHeader 自动生成导出头文件,简化跨平台导出符号的过程。

这样可以更方便地管理不同平台下的符号导出,无需手动定义 __declspec(dllexport) 和 __declspec(dllimport)。

跨平台考虑

虽然 DLL 是 Windows 平台的专用格式,但在其他平台上,CMake 也支持生成共享库(如 .so 文件在 Linux 上,.dylib 文件在 macOS 上)。在跨平台项目中,可以使用条件判断来设置导出宏:

示例:

// mylib.h

#ifndef MYLIB_H

#define MYLIB_H

#if defined(_WIN32) || defined(_WIN64)

#ifdef MYLIB_EXPORTS

#define MYLIB_API __declspec(dllexport)

#else

#define MYLIB_API __declspec(dllimport)

#endif

#else

#define MYLIB_API

#endif

extern "C" {

MYLIB_API void hello();

}

#endif // MYLIB_H

说明:

在非 Windows 平台上,MYLIB_API 不进行任何特殊处理,适用于 Unix-like 系统。

总结

使用 CMake 编译生成 DLL 涉及多个步骤,包括项目配置、代码编写、构建过程以及符号导出管理。通过本文的详细介绍,你应该能够:

定义库类型为 SHARED:使用 add_library 命令并指定为 SHARED。

管理符号导出:使用导出宏(如 __declspec(dllexport) 和 __declspec(dllimport))确保正确导出和导入符号。

配置 CMakeLists.txt:设置包含目录、编译选项和输出目录。

构建项目:使用 CMake 配置和生成构建文件,然后编译生成 DLL。

使用 DLL:在其他项目中链接生成的 DLL,并确保在运行时能够找到 DLL 文件。

优化和跨平台支持:通过自动复制 DLL 和使用 GenerateExportHeader 模块,进一步优化构建过程,并支持跨平台开发。