在Linux环境下开发工业自动化应用程序,通常涉及实时调度、串口或以太网通信、PLC交互以及各类厂商提供的C/C++ SDK。CMake作为跨平台构建生成器,能够帮助团队以声明式方式管理这些复杂依赖,并支持交叉编译与多目标部署。下面从实际工程角度梳理关键配置技巧。

一、使用find_package与自定义模块定位工业SDK
工业设备厂商常提供非标准安装位置的库文件,例如将头文件放在/opt/xxx-sdk/include,库放在/opt/xxx-sdk/lib。如果直接在CMakeLists.txt里写绝对路径,既不灵活也不利于协作。更好的做法是利用find_package配合自定义的Find模块。
我们可以编写FindDeviceSDK.cmake并放入项目的cmake/目录,通过设置CMAKE_MODULE_PATH让CMake加载它。该模块内部使用find_path和find_library搜索关键文件,再导出目标变量。这样主构建文件只需一句find_package(DeviceSDK REQUIRED)即可,后续用target_link_libraries引用,避免硬编码。
# cmake/FindDeviceSDK.cmake
find_path(DEVICE_SDK_INCLUDE_DIR
NAMES device_api.h
PATHS /opt/xxx-sdk/include
)
find_library(DEVICE_SDK_LIBRARY
NAMES xxxdevice
PATHS /opt/xxx-sdk/lib
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(DeviceSDK
REQUIRED_VARS DEVICE_SDK_LIBRARY DEVICE_SDK_INCLUDE_DIR
)
if(DeviceSDK_FOUND)
set(DEVICE_SDK_LIBRARIES ${DEVICE_SDK_LIBRARY})
set(DEVICE_SDK_INCLUDE_DIRS ${DEVICE_SDK_INCLUDE_DIR})
endif()
这种方式的优势在于:当现场更换SDK版本或路径时,只需调整模块内的搜索路径,而不用改动业务代码的构建逻辑。同时,它让CI流水线可以通过环境变量注入路径,实现无人值守构建。
二、用option与条件编译管理调试与实时选项
工业现场对日志量和实时性要求差异很大。开发阶段需要详细日志,而产线运行则要求关闭冗余输出并开启实时调度。CMake的option命令可以定义用户可配置的布尔开关,在配置阶段决定编译宏。
例如我们定义ENABLE_DEBUG_LOG与ENABLE_RT_PREEMPT,在检测到开启时通过target_compile_definitions添加宏,并链接实时库libpthread与librt。这样同一份源码能构建出调试版与量产版,避免手工改代码带来的失误。
option(ENABLE_DEBUG_LOG "开启详细调试日志" OFF)
option(ENABLE_RT_PREEMPT "开启实时内核支持" ON)
if(ENABLE_DEBUG_LOG)
target_compile_definitions(app_main PRIVATE DEBUG_LOG=1)
endif()
if(ENABLE_RT_PREEMPT)
target_compile_definitions(app_main PRIVATE RT_PREEMPT=1)
target_link_libraries(app_main PRIVATE pthread rt)
endif()
在C++代码中,使用这些宏控制行为:
#include <iostream>
int main() {
#ifdef DEBUG_LOG
std::cout << "调试: 初始化工业通信" << std::endl;
#endif
#ifdef RT_PREEMPT
// 设置实时调度策略
struct sched_param param;
param.sched_priority = 80;
sched_setscheduler(0, SCHED_FIFO, ¶m);
#endif
return 0;
}
通过命令行cmake -DENABLE_DEBUG_LOG=ON即可切换,不需要触碰源文件。对于多型号控制器,还能将常用组合写成预设脚本,提升部署效率。
三、编写install规则打包现场运行目录
工业应用程序部署时往往不止一个可执行文件,还包括配置文件、启动脚本与设备描述文件。CMake的install指令能将构建产物按目录结构复制到目标机,配合CPack可生成tar包。
建议为运行目录建立清晰层级:bin/放可执行文件,conf/放YAML或JSON配置,lib/放依赖的动态库。使用install(TARGETS)、install(DIRECTORY)与install(FILES)分别处理,既方便维护,也降低现场运维出错概率。
install(TARGETS app_main
RUNTIME DESTINATION bin
)
install(DIRECTORY conf/
DESTINATION conf
)
install(FILES startup.sh
DESTINATION .
PERMISSIONS OWNER_EXECUTE OWNER_READ GROUP_READ WORLD_READ
)
执行make install DESTDIR=/tmp/app_image后,便得到完整目录树。若结合交叉编译工具链文件,还能直接产出ARM架构控制器的运行包。这种标准化打包方式,让工厂技术员只需解压即可运行,显著缩短停机调试时间。
四、工具链文件支持交叉编译
工业自动化终端可能是基于ARM或RISC-V的嵌入式板卡。CMake工具链文件(toolchain file)将编译器前缀、系统根目录等集中定义,使同一份CMakeLists.txt能生成x86调试版与ARM现场版。
工具链文件示例如下,指定CMAKE_SYSTEM_NAME与目标编译器,再通过-DCMAKE_TOOLCHAIN_FILE=arm.cmake引用。这样find_library会优先搜索目标系统的库路径,避免误链主机库。
# arm.cmake set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR arm) set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc) set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++) set(CMAKE_FIND_ROOT_PATH /opt/arm-rootfs) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
借助上述四项技巧,Linux工业自动化项目的构建配置可以做到结构清晰、切换灵活且易于交付。团队在新增设备型号或升级SDK时,改动范围被限制在少量CMake脚本内,从而降低长期维护成本。