CMake ——库的安装与导出
如何成为 C++ 程序员
不知道是哪一天的哪个程序吸引了你,或者单纯是脑子里哪根弦搭错了,你决定要成为一名 C++ 程序员。
受到国内 C++ 领域大神谭浩强的影响,你入坑了 Visual C++ 6.0 ,去国内精英程序员论坛 CSDN 搜索了半天之后你终于在 Windows 11 上成功安装了 Visual C++ 6.0 ,身边人纷纷投来看到了原始人的目光,你发现了异样,又在对简体中文的支持处于世界领先地位的百度上找到了 Visual C++ 6.0 的上位替代 Visual Studio ,装完之后发现 C 盘急得红温了。
你心想:这下终于能写一写 C++ 了,对着 Visual Studio 疯狂输出,写了几坨 C with class 之后你发现你的源文件、头文件越来越多,还有了链接别的库的需求,对着 GUI 像猴子一样戳来戳去终于配置好了之后你发现你的项目结构一坨,你觉得 Visual Studio 虽然有 GUI ,但也不怎么好用。
你打算学一学命令行自己管理项目文件,离开 M$ 之后你首先看中了 GNU GCC ,你倒了八辈子血霉选择去
SourceForge
手动下载一个 MinGW-W64 ,废了九牛二虎之力你终于学会了
g++ test.cpp -o test
,你觉得自己很牛逼,但你还是不知道这句指令背后发生了什么,同时你发现你的编辑器没有代码补全之类的现代人应该用的东西,而且组织多个源文件时也不能一直手写
g++
编译,你又决定学习 Makefile ,恭喜你,你已经在学习 C++ 的道路上越走越远了。
你一边学习着 Neovim, Lua, Makefile, CMake, clangd, clang-tidy, clang-format 之类花里胡哨的东西,一边想着“我到底什么时候能开始学习 C++ ?”
1. Targets 文件
因为现在站在了库的开发者视角,提供
project-name-targets.cmake
才是正确的导出做法, CMake 提供了一个方便的生成 targets.cmake 的方式:
1 |
install(TARGETS ${PROJECT_NAME} |
1 |
include(GNUInstallDirs) # 引入 CMAKE_INSTALL_*DIR 变量 |
生成后需要配置安装,可以用
install(EXPORT)
:
1 |
install(EXPORT ${PROJECT_NAME}-targets |
1 |
find_package(project-name) |
2. Config 文件
Config 文件用来保证用户在
find_package
之后真的配置好了库的所有细节,官方推荐的做法是写一个
project-name-config.cmake
,这里用
*.in
文件生成:
1 |
@PACKAGE_INIT@ |
我的喜好是把这个文件放进
project_root/cmake/config.cmake.in
,然后在主 CMakeLists.txt 中这样输出实际的 Config 文件:
1 |
include(CMakePackageConfigHelpers) |
官方还推荐写一个 ConfigVersion 文件,用来配置库的版本号:
1 |
write_basic_package_version_file( |
这两个文件也需要配置安装,用
install(FILES)
即可:
1 |
install(FILES |
3. 头文件
下面的配置方式有一个前提,关于头文件的结构,按照我喜欢的方式的话,项目的头文件目录应该像这样:
1
2
3
4
5 project_root
└── include
└── project_name
├── ***.hpp
└── ***.hpp大概是把
project_root
当作 PREFIX 的感觉,项目内引入头文件时要这样写:
1
之前没有考虑配置安装或导出的项目可能会有这样的 CMake 代码:
1 |
target_include_directories(${PROJECT_NAME} PRIVATE ${${PROJECT_NAME}_INCLUDES}) |
这样写的话 CMake 会认为你的头文件不需要安装,所以要把
PRIVATE
改成
PUBLIC
:
1 |
target_include_directories(${PROJECT_NAME} PUBLIC ${${PROJECT_NAME}_INCLUDES}) |
1 |
target_include_directories(${PROJECT_NAME} PUBLIC |
1 |
install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) |
4. 完整示例
比如项目叫
foo
,是一个库,库名也叫
foo
,项目结构:
1 |
. |
Config 文件模板
./cmake/config.cmake.in
文件应该是这样的:
1 |
@PACKAGE_INIT@ |
./CMakeLists.txt
中安装和导出相关的语句是这样的:
1 |
# 引入要用到的 CMake 模块 |