13、安装规则:install命令详解、安装目标、安装文件、安装目录、配置安装路径
说到 CMake 的安装规则,我刚开始接触时也觉得有点绕。说白了,install 命令就是告诉 CMake:「编译好的东西该放到哪里去」。但如果你只是随便写个 install(TARGETS ...) 就跑,那大概率会踩坑。
我个人习惯把安装规则分成三类来理解:安装目标(可执行文件、库)、安装文件(头文件、配置文件)、安装目录(整个目录结构)。咱们一个一个来拆解。
13.1 install 命令的基本语法
先看最核心的 install() 命令。它有好几种形式,但最常用的就三种:
# 安装目标(可执行文件、库)
install(TARGETS target1 target2
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/static)
# 安装文件
install(FILES file1.h file2.h
DESTINATION include/mylib)
# 安装目录
install(DIRECTORY config/
DESTINATION etc/mylib)
嗯,这里要注意:DESTINATION 是相对路径,它相对于 CMAKE_INSTALL_PREFIX。默认情况下,这个前缀在 Linux 上是 /usr/local,在 Windows 上是 C:/Program Files/${PROJECT_NAME}。
核心要点: CMAKE_INSTALL_PREFIX 是所有安装路径的根目录。你可以通过 cmake -DCMAKE_INSTALL_PREFIX=/my/custom/path .. 来修改它。
13.2 安装目标:RUNTIME、LIBRARY、ARCHIVE 的区别
我在项目中遇到过不少同事,写 install(TARGETS mylib DESTINATION lib) 就完事了。结果在 Windows 上编译动态库时,发现 .dll 被装到了 lib 目录,而不是 bin 目录。为什么会这样?
因为 CMake 把目标分成了三类:
| 类型 | 含义 | 典型文件 | 建议安装位置 |
|---|---|---|---|
RUNTIME |
运行时需要的文件 | .exe, .dll, .so(某些情况) | bin/ |
LIBRARY |
共享库(非运行时) | .so, .dylib | lib/ |
ARCHIVE |
静态库或导入库 | .a, .lib | lib/ 或 lib/static |
你想想看,一个动态库在 Linux 上是 .so(属于 LIBRARY),在 Windows 上是 .dll(属于 RUNTIME)和 .lib(属于 ARCHIVE)。所以正确的写法是:
add_library(mylib SHARED src/mylib.cpp)
install(TARGETS mylib
RUNTIME DESTINATION bin # Windows 上的 .dll
LIBRARY DESTINATION lib # Linux 上的 .so
ARCHIVE DESTINATION lib/static) # Windows 上的 .lib
我的习惯: 即使你只做 Linux 开发,也建议把 RUNTIME、LIBRARY、ARCHIVE 都写上。这样跨平台时不会出问题。我曾经因为只写了 LIBRARY,导致 Windows 上 .dll 没被安装,调试了半天才发现。
13.3 安装文件:头文件和配置文件
安装文件相对简单,但有个坑要注意——路径结构。
# 错误示范:直接把头文件拍平到 include 目录
install(FILES src/foo.h src/bar.h
DESTINATION include)
# 正确做法:保持目录结构
install(FILES include/mylib/foo.h include/mylib/bar.h
DESTINATION include/mylib)
我个人建议,头文件安装时一定要保留子目录结构。否则用户 #include <mylib/foo.h> 时会找不到文件。嗯,这个坑我踩过不止一次。
另外,如果你有多个文件要安装,可以用 FILE_SET(CMake 3.23+):
target_sources(mylib
PUBLIC FILE_SET HEADERS
BASE_DIRS include
FILES include/mylib/foo.h include/mylib/bar.h)
install(TARGETS mylib
FILE_SET HEADERS DESTINATION include)
这样更简洁,而且 CMake 会自动处理头文件的依赖关系。
13.4 安装目录:整个目录结构搬过去
install(DIRECTORY ...) 适合安装配置文件、资源文件、脚本等。它的语法是:
install(DIRECTORY config/ scripts/
DESTINATION share/mylib
FILES_MATCHING
PATTERN "*.json"
PATTERN "*.sh"
PATTERN ".git" EXCLUDE)
注意 config/ 后面的斜杠!有斜杠表示安装目录下的内容,没有斜杠表示安装目录本身。举个例子:
install(DIRECTORY config/ DESTINATION etc)→ 安装后是etc/file1.confinstall(DIRECTORY config DESTINATION etc)→ 安装后是etc/config/file1.conf
这个细节我刚开始也记不住,后来干脆统一加斜杠,省心。
13.5 配置安装路径:CMAKE_INSTALL_PREFIX
前面说了,CMAKE_INSTALL_PREFIX 是所有安装路径的根。但实际项目中,我们经常需要更灵活的路径控制。比如:
# 在 CMakeLists.txt 中设置默认值
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "/opt/${PROJECT_NAME}" CACHE PATH "..." FORCE)
endif()
# 或者用 GNUInstallDirs 模块
include(GNUInstallDirs)
install(TARGETS mylib
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
GNUInstallDirs 模块会根据平台自动设置 CMAKE_INSTALL_BINDIR、CMAKE_INSTALL_LIBDIR 等变量。我个人强烈推荐使用它,尤其是做跨平台项目时。
避坑指南: 我曾经在 macOS 上使用 Homebrew 安装的 CMake,默认 CMAKE_INSTALL_PREFIX 是 /usr/local。但 Homebrew 本身也管理 /usr/local,结果我的库和 Homebrew 的库混在一起,卸载时出了大问题。后来我养成了习惯:开发阶段一律用 -DCMAKE_INSTALL_PREFIX=$PWD/install 指定到本地目录。
13.6 知识体系总览
下面这张图总结了安装规则的核心逻辑:
13.7 实战建议
最后,分享几个我多年积累下来的经验:
- 开发阶段用本地目录:
cmake -DCMAKE_INSTALL_PREFIX=$PWD/install ..,避免污染系统目录。 - 用 GNUInstallDirs 模块:它帮你处理了不同平台的路径差异,省心。
- 安装头文件时保留子目录:用户期望
#include <mylib/foo.h>而不是#include <foo.h>。 - 动态库的 RUNTIME 和 LIBRARY 都要写:跨平台时不会漏掉 .dll 或 .so。
- 用
cmake --install . --prefix /path:这是 CMake 3.15+ 的安装方式,比make install更通用。
一个小技巧: 如果你不确定安装后的目录结构,可以用 cmake --install . --prefix /tmp/test-install --verbose 先试装到临时目录,看看文件都放到了哪里。确认无误后再正式安装。
嗯,关于安装规则,核心就是理解 DESTINATION 是相对于 CMAKE_INSTALL_PREFIX 的,以及不同目标类型对应不同的安装位置。把这些搞清楚了,安装这块就不会再出幺蛾子了。
公众号:蓝海资料掘金营,微信 deep3321