CMake构建第三方开源库
C/C++用户库可以通过 CMake 构建第三方开源库,并将开源库提供的源码、头文件或预编译库集成到 PLC 工程中。本文以 FFTW 这类第三方源码库为例,说明如何把第三方源码加入用户库构建,并通过 IDE 菜单完成 CMake 构建和编译。
本文重点说明第三方开源库的构建集成。用户库函数、功能块的接口创建和代码实现,请参考 C/C++ 用户库的其他说明文档。
操作流程
整体流程如下:
- 创建 C/C++ 用户库,并创建需要导出的函数或功能块接口。
- 将第三方开源库源码复制到用户库目录下。
- 配置第三方源码、头文件和编译选项。
- 执行 CMake构建,检查 CMake 配置是否正确。
- 执行 编译,生成用户库构建产物。
- 在 ST 程序中调用用户库导出的函数或功能块。
创建C/C++用户库
在工程中创建一个 C/C++ 用户库。例如创建名为 fftw 的用户库,并创建一个函数 FFT_TEST 作为 ST 侧调 用入口。
创建完成后,工程树中会出现用户库节点。WasomeCodeX_AI 会为该用户库自动生成 CMakeLists.txt、.MODULE 目录和接口框架代码。
典型目录结构如下:
MODULES/
└── fftw/
├── .MODULE/
│ └── cpp/
├── implements/
│ └── cpp/
├── CMakeLists.txt
└── wa_module.h
放置第三方源码
将第三方开源库源码复制到当前 C/C++ 用户库目录下。以 FFTW 为例,可将解压后的 fftw-3.3.10 放到 MODULES/fftw 目录中。
MODULES/
└── fftw/
├── .MODULE/
├── implements/
├── fftw-3.3.10/
│ ├── api/
│ ├── dft/
│ ├── kernel/
│ ├── rdft/
│ ├── reodft/
│ ├── simd-support/
│ └── config.h
├── CMakeLists.txt
└── module.cmake
建议把第三方源码放在用户库目录内部,不建议引用工程外部的绝对路径。这样工程迁移、打包和多人协作时不容易出现路径失效问题。
第三方库内容通常按以下方式接入:
| 类型 | 示例 | 处理方式 |
|---|---|---|
| 源码库 | .c、.cpp | 加入 MOD_EXTRA_SRC 或 MOD_FULL_SRC |
| 头文件 | .h、.hpp | 加入包含路径 |
| 静态库 | .a | 加入链接库 |
| 目标文件 | .o、.obj | 作为额外源文件或链接输入加入 |
配置第三方源码参与编译
用户库的 CMake 配置有两种方式:
| 方案 | 做法 | 适用场景 |
|---|---|---|
| 方案一 | 直接修改 CMakeLists.txt | 临时验证、快速试验 |
| 方案二 | 创建 module.cmake | 推荐用于正式工程和长期维护 |
方案一:直接修改CMakeLists.txt
用户可以打开用户库根目录下的 CMakeLists.txt,直接设置 MOD_EXTRA_SRC、MOD_FULL_SRC、MOD_EXTRA_INCLUDE_DIRS 等变量。
CMakeLists.txt 中通常会包含类似说明:
## --- 源文件 ---
## 自动生成的 wasome.cmake 会将自动扫描到的当前目录及子目录下的源文件填入私有列表,
## 可通过以下变量覆盖或补充。
#set(MOD_FULL_SRC src/a.c src/b.cpp) # 完全替换自动发现的源文件列表
#set(MOD_EXTRA_SRC vendor/x.c vendor/y.c) # 在自动列表之后追加源文件
## --- 包含路径与预处理 ---
#set(MOD_EXTRA_INCLUDE_DIRS third_party/include inc)
#set(MOD_COMPILE_DEFINITIONS DEBUG=1 USE_FOO) # 不带 -D 前缀
这种方式简单直接,适合快速确认第三方库是否能参与编译。但如果后续执行 重新生成CMakeLists文件,手动写入的内容可能被覆盖或需要重新整理。
方案二:创建module.cmake
推荐在用户库根目录创建 module.cmake,将第三方源码、头文件路径、编译选项和链接配置写在该文件中。用户库构建时会自动包含 module.cmake。
推荐原因:
| 原因 | 说明 |
|---|---|
| 避免生成文件被覆盖 | CMakeLists.txt 可能随接口或工程操作重新生成 |
| 配置更集中 | 第三方库相关 CMake 内容都放在一个文件中 |
| 便于迁移维护 | 拷贝用户库目录时,第三方源码和配置可以一起迁移 |
| 便于排查问题 | 构建失败时可以优先检查 module.cmake 中的路径、源码和选项 |
module.cmake 示例:
set(FFTW3_DIR ${CMAKE_CURRENT_LIST_DIR}/fftw-3.3.10)
enable_language(C)
file(GLOB fftw_api_SOURCE ${FFTW3_DIR}/api/*.c)
file(GLOB fftw_dft_SOURCE ${FFTW3_DIR}/dft/*.c)
file(GLOB fftw_kernel_SOURCE ${FFTW3_DIR}/kernel/*.c)
file(GLOB fftw_rdft_SOURCE ${FFTW3_DIR}/rdft/*.c)
file(GLOB fftw_reodft_SOURCE ${FFTW3_DIR}/reodft/*.c)
file(GLOB fftw_simd_SOURCE ${FFTW3_DIR}/simd-support/*.c)
set(SOURCEFILES
${fftw_api_SOURCE}
${fftw_dft_SOURCE}
${fftw_kernel_SOURCE}
${fftw_rdft_SOURCE}
${fftw_reodft_SOURCE}
${fftw_simd_SOURCE}
)
include_directories(
${FFTW3_DIR}
${FFTW3_DIR}/api
)
set(MOD_EXTRA_SRC
${SOURCEFILES}
${CMAKE_CURRENT_LIST_DIR}/implements/cpp/wa_func.cpp
)
MOD_EXTRA_SRC与MOD_FULL_SRC
配置第三方源码时,常用变量如下:
| 变量 | 说明 | 建议 |
|---|---|---|
MOD_EXTRA_SRC | 在 IDE 自动扫描到的源文件之后追加源码 | 推荐优先使用 |
MOD_FULL_SRC | 完全替换自动扫描结果,只使用指定源码 | 需要完全控制编译输入时使用 |
MOD_EXTRA_INCLUDE_DIRS | 追加头文件搜索路径 | 适合简单头文件目录 |
MOD_COMPILE_DEFINITIONS | 追加预处理宏定义 | 不需要写 -D 前缀 |
MOD_LINK_LIBRARIES | 追加链接库 | 引入 .a 或系统库时使用 |
MOD_FULL_SRC 示例:
set(MOD_FULL_SRC
${SOURCEFILES}
${CMAKE_CURRENT_LIST_DIR}/implements/cpp/wa_func.cpp
${CMAKE_CURRENT_LIST_DIR}/.MODULE/cpp/wa_interface.cpp
)
使用 MOD_FULL_SRC 时,需要把用户库接口桥接文件一并加入,例如 .MODULE/cpp/wa_interface.cpp。否则 PLC 侧可能无法调用用户库导出的函数或功能块。
排除不需要的第三方源码
第三方开源库经常包含测试程序、命令行工具、平台相关代码或可选特性。这些文件不一定适合 PLC 目标环境,需要从编译列表中排除。
list(REMOVE_ITEM SOURCEFILES
${FFTW3_DIR}/libbench2/useropt.c
${FFTW3_DIR}/tools/some_command_line_tool.c
)
常见需要关注的源码类型:
| 类型 | 处理建议 |
|---|---|
| 测试程序 | 通常不加入用户库编译 |
| 命令行工具 | 通常依赖 main、文件系统或终端输入,不加入编译 |
| 平台汇编/SIMD | 目标工具链不支持时关闭或排除 |
| 线程/OpenMP | 目标环境不支持时关闭相关选项 |
| 文件系统访问 | PLC 运行环境不支持时需要适配或禁用 |
执行CMake构建和编译
在工程树中右键点击 C/C++ 用户库,可以看到构建相关操作。
右键菜单中与第三方库构建相关的操作如下:
| 菜单项 | 作用 | 使用时机 |
|---|---|---|
| 编译 | 执行 CMake 配置、编译、链接和安装,生成用户库构建产物 | CMake 配置已经准备好,需要生成可被 PLC 工程使用的用户库时 |
| 清除编译 | 清理当前用户库的构建产物和中间文件 | 修改源码列表、编译选项或第三方库目录后,需要重新完整构建时 |
| CMake构建 | 只执行 CMake 配置,不执行实际编译 | 修改 CMakeLists.txt 或 module.cmake 后,先检查路径、源文件和选项是否正确 |
| 打开CMakeLists文件 | 打开用户库根目录下的 CMakeLists.txt | 查看 IDE 生成的 CMake 入口文件,或临时修改 MOD_* 配置 |
| 生成接口代码 | 根据用户库函数、功能块接口重新生成 C/C++ 框架代码 | 修改用户库 POU 接口后,需要同步生成 C/C++ 调用框架时 |
| 重新生成CMakeLists文件 | 重新生成用户库标准 CMakeLists.txt | CMakeLists.txt 被误改或需要恢复 IDE 默认结构时 |
| 日志 | 打开 CMake 构建日志和编译日志 | CMake 构建或编译失败,需要查看详细错误原因时 |
| 在文件浏览器中显示 | 打开用户库所在的磁盘目录 | 检查第三方源码、module.cmake 和用户实现文件是否放置正确 |
集成第三方开源库时,建议先执行 CMake构建。确认 CMake 配置通过后,再执行 编译。这样可以先排查路径、源码列表和头文件配置问题,避免直接进入大量源码编译后才发现配置错误。
建议按以下顺序执行:
- 执行 CMake构建,检查路径、源文件列表、头文件目录和编译选项是否正确。
- CMake 构建成功后,执行 编译,生成用户库构建产物。
构建成功时,终端会显示类似信息:
正在运行 CMake 构建配置 — fftw
cmake 配置 ... (日志: .generate/cpp_building/fftw/release/logs/cmake-configure.log)
-> cmake 配置 OK
用户库 'fftw' 已完成 CMake 构建配置
编译成功时,终端会显示类似信息:
正在编译用户库 — fftw
cmake 配置 ...
-> cmake 配置 OK
[编译与安装] ...
Linking CXX static library libfftw.a
用户库 'fftw' 编译成功
查看文件和日志
在右键菜单中选择 在文件浏览器中显示,可以确认第三方源码、module.cmake、用户实现文件是否都在用户库目录内。
在文件浏览器视图中,点击工具栏的项目视图按钮可以返回工程项目树。
构建失败时,可通过右键菜单中的 日志 查看 CMake 构建日志和编译日志。常见问题如下:
| 问题 | 常见原因 | 处理建议 |
|---|---|---|
| 找不到源码目录 | 第三方库目录名称或路径不一致 | 检查 ${CMAKE_CURRENT_LIST_DIR} 拼接路径 |
| 找不到头文件 | 未配置 include 路径 | 补充 include_directories 或 MOD_EXTRA_INCLUDE_DIRS |
| 源文件未参与编译 | file(GLOB ...) 路径不匹配 | 检查源码实际目录和变量内容 |
undefined reference | 源文件缺失或库未链接 | 补充源码,或加入 MOD_LINK_LIBRARIES |
multiple definition | 同一源文件被重复加入 | 检查自动扫描和 MOD_EXTRA_SRC 是否重复 |
出现 main 冲突 | 加入了测试程序或工具程序 | 排除测试目录、示例目录或命令行工具 |
在ST程序中调用验证
编译成功后,即可在 ST 程序中调用 C/C++ 用户库导出的函数或功能块。
PROGRAM DEMO_PLC_PRG1
VAR
END_VAR
FFT_TEST();
END_PROGRAM
验证建议:
| 验证点 | 说明 |
|---|---|
| ST 编译是否通过 | 确认用户库接口已正确生成 |
| C/C++ 编译是否通过 | 确认第三方源码和用户实现均可构建 |
| 在线运行是否正常 | 确认第三方库运行时没有访问不支持的系统能力 |
| 断点调试是否进入用户函数 | 确认 ST 调用链已经进入 C/C++ 用户库 |
构建大型开源库时,不建议一次性加入全部源码目录。更稳妥的方式是先加入核心源码和头文件,让一个最小调用编译通过,再逐步打开可选功能。