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!" 输出,说明基础没问题。

小提示: 我建议项目名和可执行文件名保持一致,后面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就是个「配置驱动」的工具——你告诉它项目叫什么、版本多少,它就知道怎么打包。

核心要点: CPack本身不编译代码,它只负责把编译好的产物打包成安装包。所以一定要先 make 编译通过,再运行CPack。

3.3 运行CPack:见证奇迹的时刻

配置写好了,怎么用?两步走:

  1. 先编译项目 —— 生成可执行文件
  2. 再运行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 这些不同平台的安装包。到时候你会觉得——原来跨平台打包也可以这么顺手。