CI配置时使用Makefile有哪些好处?
CI配置时使用Makefile有哪些好处?
一、什么是 Makefile?
Makefile
本质上是一个包含了一系列规则的文件。这些规则定义了如何编译和链接程序,每一个规则都由以下三个部分组成:
- 目标(Target): 想要生成的文件或想要执行的操作的名称,比如
install
、test
、clean
等。 - 依赖(Prerequisites): 为了生成目标,所需要依赖的文件或其他目标。
- 命令(Commands): 为了通过依赖来生成目标所需要执行的 shell 命令。
一个 Makefile
的基本结构如下:
1 | target: prerequisite1 prerequisite2 |
关键特性:
- 自动化:
make
工具会自动读取Makefile
并执行指定的命令,从而自动化编译、测试、部署等一系列任务。 - 抽象化: 可以将复杂的、多步骤的 shell 命令封装在一个简单的
make
命令背后。例如,make test
可能包含了安装依赖、启动数据库、运行测试套件、生成报告等多个步骤。
二、为什么在 CI 配置中使用 Makefile?
将 Makefile
引入 CI 配置文件可以带来以下核心优势:
1. 本地与云端环境统一 (Dry-run Locally)
这是使用 Makefile
最显著的好处之一。开发者可以在自己的开发机上执行 make test
或 make build
,并期望得到与 CI服务器上完全一致的执行结果。这使得开发者在提交代码前就能快速地对 CI 流程进行测试和调试,而无需等待漫长的 CI pipeline 运行,从而极大地缩短了反馈周期。
-
没有
Makefile
的情况: 开发者需要阅读冗长的.gitlab-ci.yml
或其他 CI 配置文件,并手动在本地终端执行其中复杂的script
片段。这个过程不仅繁琐,而且容易出错。 -
使用
Makefile
的情况: CI 配置文件变得异常简洁。- .github-ci.yml 示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- ubuntu-latest
- macos-latest
- windows-latest
steps:
- uses: actions/checkout@v4
- name: Setup Rust toolchain
uses: ./.github/actions/setup-builder
- name: Cache Rust artifacts
uses: Swatinem/rust-cache@v2
- name: Build
run: make build
- .github-ci.yml 示例:
Makefile中的build:
1 | build: |
开发者在本地和 CI 服务器上都只需要执行相同的命令:make build
。
2. 简化与抽象化 CI 配置
现代 CI/CD 流程可能非常复杂,包含代码检查、单元测试、集成测试、构建 Docker 镜像、推送到制品库、部署到不同环境等众多步骤。如果将所有这些逻辑都直接写在 CI 平台的 YAML 配置文件中,会导致文件变得异常冗长、难以阅读和维护。
Makefile
通过将这些复杂的脚本逻辑封装在简单的目标(target)中,极大地简化了 CI 配置文件。CI 的配置文件只负责调用任务,而 Makefile
负责定义任务的具体执行方式。
3. 提高可重用性和可移植性
Makefile
是一个与具体 CI/CD 平台无关的通用工具。
- 跨平台重用: 如果团队决定从 Jenkins 迁移到 GitLab CI,或者需要同时支持多个 CI 平台,无需重写所有的构建和测试脚本。只需要在新的 CI 平台的配置文件中调用相同的
make
命令即可。核心的业务逻辑(Makefile
中的内容)可以被无缝迁移和重用。 - 项目间重用: 对于遵循相似构建流程的多个项目,它们可以共享或复用
Makefile
的大部分内容,只需针对特定项目进行微调。
4. 利用 make
的增量构建能力
make
的设计初衷就是为了处理依赖关系和增量构建。虽然在无状态的 CI 环境中(每次都从零开始),这个优势可能不那么明显。但在某些场景下,例如配置了缓存(caching)的 CI pipeline,make
可以智能地判断哪些部分无需重新构建,从而显著加快 CI 的运行速度。这个对于Rust项目没什么优势😄,cargo本身自带了增量构建的能力。