30、安装规则:install()命令安装目标、文件、目录到指定路径

说实话,很多初学者写CMakeLists.txt时,最头疼的就是最后一步——怎么把编译好的东西装到系统里?

我刚开始用CMake那会儿,也踩过这个坑。编译完一个库,发现头文件没装过去,结果别的项目引用时各种找不到路径。后来才明白,install()命令才是CMake构建流程的"最后一公里"

核心认知:install()不是编译的一部分,而是"部署"的一部分。它告诉CMake:编译完成后,把哪些东西复制到哪个目录。

30.1 安装目标:TARGETS模式

这是最常用的模式。说白了,就是把编译出来的库、可执行文件装到指定位置。

# 安装可执行文件到 bin 目录
install(TARGETS myapp
    RUNTIME DESTINATION bin
)

# 安装动态库到 lib 目录
install(TARGETS mylib
    LIBRARY DESTINATION lib
)

# 安装静态库到 lib 目录
install(TARGETS mylib_static
    ARCHIVE DESTINATION lib
)

这里有个细节我特别想强调:RUNTIME、LIBRARY、ARCHIVE这三个关键字不能乱用。

关键字 适用对象 典型安装路径
RUNTIME 可执行文件、DLL(Windows) bin/
LIBRARY 动态库(.so/.dylib) lib/
ARCHIVE 静态库(.a/.lib)、DLL导入库 lib/

我的习惯:如果一个目标同时生成动态库和静态库,我会分别写两个install(TARGETS ...)语句,各指定对应的DESTINATION。这样更清晰,不容易出错。

30.2 安装文件:FILES模式

有时候我们需要安装一些非编译产物,比如配置文件、脚本、文档等。这时候用FILES模式。

# 安装单个文件
install(FILES config.ini
    DESTINATION etc/myapp
)

# 安装多个文件
install(FILES 
    README.md
    LICENSE
    CHANGELOG.md
    DESTINATION share/doc/myapp
)

嗯,这里要注意:FILES模式不会自动创建子目录。如果你想把文件装到某个子目录下,得在DESTINATION里写完整路径。

30.3 安装目录:DIRECTORY模式

这个模式我用的最多。尤其是安装头文件时,整个include目录直接扔过去,省事。

# 安装整个目录
install(DIRECTORY include/
    DESTINATION include
)

# 安装目录,但排除某些文件
install(DIRECTORY include/
    DESTINATION include
    FILES_MATCHING
    PATTERN "*.h"
    PATTERN "*.hpp"
    PATTERN ".git" EXCLUDE
)

我曾经踩过的坑:DIRECTORY后面跟的路径,如果带斜杠(include/),表示复制目录内容;如果不带斜杠(include),表示复制目录本身。这个区别很容易搞混,我至少在这上面翻过两次车。

30.4 安装脚本与权限控制

有时候安装前后需要执行一些脚本,比如创建用户、设置环境变量等。CMake提供了SCRIPT和CODE参数。

# 安装前执行脚本
install(SCRIPT pre_install.cmake)

# 安装后执行CMake代码
install(CODE [[
    message(STATUS "安装完成!")
    file(WRITE ${CMAKE_INSTALL_PREFIX}/etc/installed.txt "ok")
]])

# 设置文件权限
install(FILES myscript.sh
    DESTINATION bin
    PERMISSIONS OWNER_EXECUTE OWNER_READ
                GROUP_EXECUTE GROUP_READ
                WORLD_EXECUTE WORLD_READ
)

30.5 安装配置与目标导出

如果你在写一个库,想让别人通过find_package找到它,那就得用EXPORT和CONFIG模式。

# 安装目标并导出
install(TARGETS mylib
    EXPORT MyLibTargets
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib
    RUNTIME DESTINATION bin
)

# 安装导出的目标文件
install(EXPORT MyLibTargets
    FILE MyLibTargets.cmake
    DESTINATION lib/cmake/MyLib
)

# 安装配置文件
install(FILES MyLibConfig.cmake
    DESTINATION lib/cmake/MyLib
)

说实话,这个配置导出功能我一开始觉得挺复杂的。但后来发现,一旦配好了,其他项目用find_package(MyLib)就能直接引用,省去了手动设置头文件路径和库路径的麻烦

30.6 安装路径的变量与默认值

CMake提供了一些内置变量来控制安装路径:

变量名 默认值 说明
CMAKE_INSTALL_PREFIX /usr/local(Unix)
C:/Program Files(Windows)
安装根目录
CMAKE_INSTALL_BINDIR bin 可执行文件目录
CMAKE_INSTALL_LIBDIR lib 或 lib64 库文件目录
CMAKE_INSTALL_INCLUDEDIR include 头文件目录
# 使用GNUInstallDirs模块获取标准路径
include(GNUInstallDirs)

install(TARGETS mylib
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)

install(DIRECTORY include/
    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

我建议:尽量用GNUInstallDirs模块。它自动处理了不同平台的路径差异,比如64位系统下lib和lib64的区别。省心不少。

30.7 知识体系总览

下面这张图把install()命令的几种模式串起来了,方便你快速回顾:

install() 命令知识体系 install() 命令 TARGETS 模式 FILES 模式 DIRECTORY 模式 EXPORT 模式 RUNTIME / LIBRARY / ARCHIVE DESTINATION 指定路径 安装配置文件、脚本等 支持 PERMISSIONS 权限 安装整个目录树 支持 PATTERN 过滤 生成 find_package 可用的 CMake 配置文件 配合 GNUInstallDirs 模块使用,自动适配平台路径

30.8 实战建议

最后,分享几个我实际项目中的经验:

  • 先本地测试:cmake --install . --prefix /tmp/test_install 先装到临时目录,确认路径和文件都对,再装到系统目录。
  • 区分Debug和Release:可以用 CONFIGURATIONS Debug;Release 参数,让不同配置装到不同目录。
  • 组件化安装:COMPONENT 参数把安装分成多个组件(如runtime、dev、doc),用户可以选择只装需要的部分。

一句话总结:install()命令就是CMake的"部署说明书"。写清楚它,你的项目才能被其他人(或者未来的你)顺利使用。


公众号:蓝海资料掘金营,微信deep3321