3、第一个CPack项目:创建最小CMake项目、添加CPack配置、运行CPack生成安装包、查看输出结果
3.1 从零开始:一个最小CMake项目
说实话,很多初学者一上来就搞复杂项目,结果被CMakeLists.txt绕晕了。我个人习惯是——先跑通最简单的,再慢慢加料。
咱们先创建一个最干净的C++项目。就一个源文件,一个头文件,啥花活都不整。
项目结构长这样:
myapp/
├── CMakeLists.txt
├── src/
│ └── main.cpp
└── include/
└── hello.h
hello.h 里就声明一个函数:
#ifndef HELLO_H
#define HELLO_H
void say_hello();
#endif
main.cpp 里实现它:
#include <iostream>
#include "hello.h"
void say_hello() {
std::cout << "Hello from CPack!" << std::endl;
}
int main() {
say_hello();
return 0;
}
然后是最关键的 CMakeLists.txt。我尽量写得精简:
cmake_minimum_required(VERSION 3.15)
project(MyApp VERSION 1.0.0 LANGUAGES CXX)
add_executable(myapp src/main.cpp)
target_include_directories(myapp PRIVATE include)
到这里,一个能编译运行的CMake项目就齐活了。你可以在终端里试试:
mkdir build && cd build
cmake ..
make
./myapp
看到 "Hello from CPack!" 输出,说明基础没问题。
3.2 添加CPack配置:三行代码搞定
好,现在咱们给这个项目加上打包能力。你猜怎么着?只需要在 CMakeLists.txt 末尾加几行配置。
修改后的 CMakeLists.txt 长这样:
cmake_minimum_required(VERSION 3.15)
project(MyApp VERSION 1.0.0 LANGUAGES CXX)
add_executable(myapp src/main.cpp)
target_include_directories(myapp PRIVATE include)
# ---------- CPack 配置开始 ----------
include(CPack)
set(CPACK_PACKAGE_NAME "MyApp")
set(CPACK_PACKAGE_VERSION "1.0.0")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "My first CPack project")
# ---------- CPack 配置结束 ----------
嗯,就这么简单。三行 set 命令,加上一个 include(CPack),CPack就集成进来了。
为什么会这样?因为 include(CPack) 会自动读取你设的这些变量,然后生成对应的打包规则。说白了,CPack就是个「配置驱动」的工具——你告诉它项目叫什么、版本多少,它就知道怎么打包。
make 编译通过,再运行CPack。
3.3 运行CPack:见证奇迹的时刻
配置写好了,怎么用?两步走:
- 先编译项目 —— 生成可执行文件
- 再运行CPack —— 生成安装包
在 build 目录下执行:
cmake ..
make
cpack
就这么简单?对,就这么简单。默认情况下,CPack会生成一个 .tar.gz 压缩包(Linux/macOS)或者 .zip 文件(Windows)。
你会在 build 目录下看到新生成的文件:
MyApp-1.0.0-Linux.tar.gz # 或者 MyApp-1.0.0-Darwin.tar.gz
解压看看里面有什么:
tar -xzf MyApp-1.0.0-Linux.tar.gz
cd MyApp-1.0.0-Linux
./bin/myapp
输出 "Hello from CPack!" —— 完美!
.deb 或 .rpm,可以用 cpack -G DEB 或 cpack -G RPM。不过那需要系统安装对应的打包工具。
3.4 查看输出结果:安装包里到底装了啥?
我第一次用CPack时,特别好奇它到底往包里塞了什么东西。咱们来解剖一下这个安装包。
默认情况下,CPack会把 make install 会安装的内容打包进去。但咱们还没配置安装规则呢!所以它只打包了可执行文件 myapp。
用 tar -tvf 查看包内容:
tar -tvf MyApp-1.0.0-Linux.tar.gz
输出类似:
drwxr-xr-x ... MyApp-1.0.0-Linux/
drwxr-xr-x ... MyApp-1.0.0-Linux/bin/
-rwxr-xr-x ... MyApp-1.0.0-Linux/bin/myapp
看到了吧?就一个 bin/myapp。没有头文件,没有文档,没有配置文件——因为我们没告诉CPack要装这些。
CMakeLists.txt 中用 install() 命令指定。否则CPack只会打包 make install 的目标。
咱们可以加个安装规则试试。在 CMakeLists.txt 里加上:
install(TARGETS myapp DESTINATION bin)
install(FILES include/hello.h DESTINATION include)
重新编译打包:
make
cpack
再查看包内容:
tar -tvf MyApp-1.0.0-Linux.tar.gz
输出:
drwxr-xr-x ... MyApp-1.0.0-Linux/
drwxr-xr-x ... MyApp-1.0.0-Linux/bin/
-rwxr-xr-x ... MyApp-1.0.0-Linux/bin/myapp
drwxr-xr-x ... MyApp-1.0.0-Linux/include/
-rw-r--r-- ... MyApp-1.0.0-Linux/include/hello.h
现在包里既有可执行文件,也有头文件了。这才像个正经的安装包嘛!
3.5 避坑指南:我踩过的三个坑
讲真,CPack虽然简单,但有几个地方容易翻车。我把自己踩过的坑列出来,你注意避开:
| 坑 | 现象 | 原因 | 解决办法 |
|---|---|---|---|
| 忘记编译 | CPack报错找不到文件 | 没先运行 make |
先 make 再 cpack |
| 版本号不匹配 | 包名显示 0.0.0 |
project() 里没写版本 |
加上 VERSION 1.0.0 |
| 安装路径混乱 | 解压后找不到可执行文件 | 没设 DESTINATION |
用 install() 指定路径 |
make,直接 cpack。结果打包的还是旧版本的可执行文件。排查了半天才发现问题。所以我现在养成了习惯——打包前一定先 make clean && make。
3.6 本章小结
咱们走完了第一个CPack项目的完整流程。你想想看,从创建最小CMake项目,到添加三行CPack配置,再到运行 cpack 生成安装包,整个过程其实不到十分钟。
核心就三点:
- 配置 —— 用
set()告诉CPack项目信息 - 编译 —— 先
make生成产物 - 打包 —— 运行
cpack生成安装包
下一章咱们会深入CPack的生成器配置,看看怎么打出 .deb、.rpm、.dmg 这些不同平台的安装包。到时候你会觉得——原来跨平台打包也可以这么顺手。