19、安装规则:install()命令,安装目标、文件、目录、脚本,DESTDIR与CMAKE_INSTALL_PREFIX
说到CMake的安装规则,我刚开始接触时也觉得挺绕的。说白了,install()就是告诉构建系统:这些东西编译完了,该放到哪个目录去。你想想看,一个项目编译出来那么多东西——可执行文件、动态库、头文件、配置文件,总得有个规矩把它们归置好。
我个人习惯把安装规则写在每个子目录的CMakeLists.txt末尾,这样模块清晰,维护起来也方便。嗯,咱们一步步来看。
install()命令的基本用法
install()是CMake里最核心的安装命令。它有好几种变体,分别对应不同的安装对象。我把它分成四类:
- 安装目标:可执行文件、库文件
- 安装文件:单个文件,比如配置文件
- 安装目录:整个目录,比如资源文件夹
- 安装脚本:在安装前后执行的自定义脚本
先看一个最简单的例子:
# 安装可执行文件到 bin 目录
install(TARGETS myapp RUNTIME DESTINATION bin)
# 安装动态库到 lib 目录
install(TARGETS mylib LIBRARY DESTINATION lib)
# 安装静态库到 lib 目录
install(TARGETS mylib_static ARCHIVE DESTINATION lib)
这里有个细节要注意:TARGETS后面跟的是CMake里定义的target名字,不是文件名。我曾经见过新手直接把源文件路径写进去,结果CMake报错半天找不到原因。
安装目标:TARGETS 的三种类型
CMake把目标文件分成三类,每一类对应不同的安装位置:
| 参数 | 适用对象 | 典型安装目录 |
|---|---|---|
RUNTIME |
可执行文件、DLL(Windows) | bin/ |
LIBRARY |
共享库(.so, .dylib) | lib/ |
ARCHIVE |
静态库(.a, .lib)、DLL导入库 | lib/ |
为什么会这样区分?因为不同平台对文件类型有不同约定。比如Linux下共享库放在lib/,而Windows下DLL放在bin/。CMake帮你处理了这些平台差异,你只需要按逻辑分类就行。
安装文件和目录
除了目标文件,我们经常还需要安装头文件、配置文件、资源文件等。这时候用FILES和DIRECTORY:
# 安装单个文件
install(FILES config.ini DESTINATION etc)
# 安装整个目录(递归)
install(DIRECTORY include/ DESTINATION include
FILES_MATCHING PATTERN "*.h")
# 安装目录时排除某些文件
install(DIRECTORY resources/ DESTINATION share/myapp
PATTERN ".git" EXCLUDE)
这里有个坑:DIRECTORY后面的路径如果带斜杠,行为会不一样。比如include/表示把目录内容安装过去,而include表示把目录本身安装过去。我曾经因为多写了一个斜杠,导致头文件路径多了一层嵌套,排查了半天。
安装脚本:在安装前后做点事
有时候我们需要在安装过程中执行一些额外操作,比如生成配置文件、设置权限、创建符号链接。这时候可以用SCRIPT或CODE:
# 安装前执行脚本
install(SCRIPT pre_install.cmake)
# 安装后执行内联代码
install(CODE [[
message(STATUS "安装完成!")
file(WRITE ${CMAKE_INSTALL_PREFIX}/README.txt "感谢使用")
]])
我个人更推荐用CODE写内联脚本,因为这样所有逻辑都在CMakeLists.txt里,不用额外维护一个文件。但如果脚本逻辑很复杂,还是单独放一个.cmake文件更清晰。
DESTDIR 与 CMAKE_INSTALL_PREFIX
这两个概念经常让人混淆。我简单解释一下:
CMAKE_INSTALL_PREFIX:编译时指定的安装根目录,默认是/usr/local。你在install()里写的DESTINATION都是相对于这个路径的。DESTDIR:安装时临时加的前缀,通常用于打包(比如deb、rpm)。它是一个环境变量,不是CMake变量。
举个例子:
# 设置安装前缀
cmake -DCMAKE_INSTALL_PREFIX=/opt/myapp ..
# 安装时使用 DESTDIR
make install DESTDIR=/tmp/staging
最终文件会被安装到/tmp/staging/opt/myapp/bin/。说白了,DESTDIR就是在CMAKE_INSTALL_PREFIX前面再加一层。
CMAKE_INSTALL_PREFIX是CMake变量,在配置阶段确定DESTDIR是环境变量,在安装阶段临时指定- 最终路径 =
$DESTDIR+$CMAKE_INSTALL_PREFIX+DESTINATION
避坑指南:我踩过的几个坑
讲几个我实际项目中遇到的教训:
- 路径硬编码:不要在
DESTINATION里写绝对路径,除非你明确知道自己在做什么。否则换台机器就炸了。 - 忘记安装头文件:库编译成功了,但别人引用时找不到头文件。我建议把所有公共头文件单独列一个
install(DIRECTORY ...)规则。 - 权限问题:默认安装的文件权限是644(可读不可执行)。如果你的脚本需要执行权限,记得加
PERMISSIONS参数。 - DESTDIR 不生效:如果你在CMake里用
set(CMAKE_INSTALL_PREFIX ...)硬编码了路径,DESTDIR可能不会按预期工作。最好让用户通过命令行传入。
知识体系总览
下面这张图概括了本章的核心逻辑:
总结
安装规则看似简单,但用好了能让你的项目专业度提升一个档次。我个人建议:
- 每个target都配上明确的install规则,不要偷懒
- 头文件和资源文件单独用
DIRECTORY安装,方便用户引用 - 打包时记得用
DESTDIR,别直接装到系统目录 - 多写几个
install(CODE ...)做安装后的校验,比如检查文件是否存在
嗯,安装这块就讲到这里。记住一点:安装规则写得好,别人用你的库时就少骂两句。