要点: Make 是一个已经有 48 年历史的构建自动化工具,至今仍然非常有用。这篇文章解释了 Makefile 是如何工作的,并说明了为什么它们虽然语法可能看起来有点复杂,却依然很棒。文中还展示了适用于现代开发流程的实际示例——从 Terraform 自动化到本地开发环境等。你甚至可以使用 Amazon Q 开发者 CLI 生成 Makefile,而无需成为 Make 专家级别的用户!别再不停地按向上箭头键,开始让你的开发任务自动化吧。🚀
此处略去若干文字
目录- Makefile 为什么
-
Makefile 分解
-
常见用法
-
Terraform 自动化
-
本地开发环境的自动化
-
Makefile 48 年后的使用
-
我的链接缩短器 CDK 项目
-
处理静态网站
- 结语
好的,我相信大家肯定都听说过make
,这是一个在软件开发领域经常用于构建自动化的关键工具。你真的用过它吗?你见过它的实际用法吗?本文将带你回顾make
的历史、工作原理以及一些超实用的用例(不仅仅是构建C语言程序),以及它在未来(如2025年)能为你带来哪些好处。
不论你信不信,make
已经有 48 年的历史(截至撰写时)了,这就比 DNS 、 BSD Unix 甚至 TCP/IP 还要早。它是由 Stuart Feldman 编写的,为了解决当时一个常见的问题:软件开发者在修改源代码后会忘记哪些文件需要重新编译。而在当时,你通常只有有限的时间来调试和编译程序,这导致了一个糟糕的开发体验。
虽然我不常忘记编译文件这点我没什么问题,但我经常忘记哪个命令对应我的命令行历史,结果只能不停地按向上箭头键寻找,在Bash历史记录中寻找相同的命令,希望它还在那里。遗憾的是,通常它不在那里。那么,让我们来看看make
究竟是怎么回事。
但是,Darko,为什么不直接写一个deploy.sh
或build.sh
脚本就完事了呢?
好眼力,亲爱的读者,确实,即便是斯图亚特时代,确实是这样做的,但常常涉及非常复杂的脚本编写,有时对于你的项目来说可能会有些过度。此外,简单脚本经常只是直接重建应用程序,重新编译所有东西,这会花费太多时间。当时,编译既费钱又费时间,所以你得小心。make
实际上解决了这个问题,它只会编译更改过的源代码文件,并构建了一个复杂的依赖关系网络,以更好地管理你的构建流程。
让我们来看一个简单的C项目中的Makefile
。哦对了,Makefile
就是一个用来描述如何构建、部署或执行其他任务的文件。Makefile
是用来告诉make
如何构建和部署项目的文件。当你运行make
时,它会在文件中查找这些指令。
# 定义变量
CC = gcc
CFLAGS = -Wall -g
TARGET = myprogram
# 源文件
SRCS = main.c utils.c
OBJS = $(SRCS:.c=.o)
# 默认目标(构建)
all: $(TARGET)
# 链接目标文件以生成可执行文件
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $@ $^
# 将源文件编译成目标文件
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
# 清理生成的文件
clean:
rm -f $(TARGET) $(OBJS)
# 声明虚拟目标(这些目标并不对应实际文件)
.PHONY: all clean
全屏模式,退出全屏
... 哇哦,这看起来好吓人哦!那是什么东西啊! 🤯
Makefile 解析
嗯嗯,我知道!这确实看起来挺复杂的。它使用的声明式编程语言确实是那个时代的特色,需要花点时间适应。但我可以给你快速入门一下这段代码是干嘛的:
# 定义变量
CC = gcc
CFLAGS = -Wall -g
TARGET = myprogram
# 源文件
SRCS = main.c utils.c
OBJS = $(SRCS:.c=.o)
切换到全屏。切换回正常。
首先,我们定义所有变量!实际上,为了增加混乱程度,make
将这些称为宏,但无所谓。它们基本上就是变量。首先,我们提前定义了一些东西,这样我们就无需手动在文件的后面进行更改了。在这里,我们告诉它我们要用哪个编译器,我们需要设置哪些编译器选项,以及我们的程序叫什么名字。另外,我们还指明了需要编译哪些源文件。
最后它有一个很酷的小方法来定义 OBJS
变量(宏,无论是啥):$(SRCS:.c=.o)
,简单来说,它会把 SRCS
宏里的 .c
文件扩展名改为 .o
,从而生成一个类似于 OBJS = main.o utils.o
的宏。这样的宏。
# 默认构建目标
all: $(TARGET)
全屏 退出全屏
这就是我们所说的 目标 ,基本上就是一个会执行的代码块及其依赖项。如果运行 make all
或者仅仅运行 make
(没有带任何参数),all
目标将会被执行。因为它的依赖项是 $(TARGET)
,所以它会先执行 $(TARGET)
。稍后我会解释依赖项是如何工作的。说到 $(TARGET)
...
# 将对象文件链接起来来生成可执行文件
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $@ $^
切换到全屏模式;退出全屏模式
这里就是链接发生的地方。你可以看到,这里不仅有各种宏,还有一些奇怪的新宏。让我把这段代码展开,如果用实际值替换所有的宏,这段代码会是什么样子。
# 编译myprogram程序
下面是一个简单的Makefile片段,用于编译C程序
myprogram: main.o utils.o
gcc -Wall -g -o myprogram main.o utils.o
切换到全屏模式,退出全屏
这就好理解多了!这里的关键在于这两个内置宏:$@
和 $^
,分别指向目标文件 $(TARGET)
和所有依赖项。这使得它非常灵活,能够自动包含 $(OBJS)
宏中的所有元素,使其更加动态。
# 将.c文件编译为.o文件
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
进入全屏模式:退出全屏模式
这里实际上是编译的地方。这里我们还能看到一个非常强大的make
特性,叫做模式规则。这个规则的意思是,通过%.o: %.c
说明:要构建任何以_.o结尾的文件,你需要一个具有相同名字但以.c**结尾的源文件。这真是太神奇了! 🤩
最后,实际的编译命令只是进行了一些宏展开,并加上了必需的前置步骤和最终目标。比如我们现在就有这种情况,结果会是这样:
gcc -Wall -g -c main.c -o main.o
进入全屏 退出全屏
而且,由于有两个 *.c
文件,我们也得到了如下内容:
gcc -Wall -g -c utils.c -o utils.o
点击这里进入/退出全屏
# 清理生成的文件
清理:
rm -f $(TARGET) $(OBJS)
切换到全屏模式 恢复到正常模式
这是我最喜欢的Makefile
部分,就是clean
目标!我非常喜欢这一点,不管编译了多少不同的对象,只要运行make clean
,它就会删除编译生成的二进制文件和其他对象文件。在这里,你可以真正定义什么是对你的clean
。
在我们的例子中,该命令将如下所示:
rm -f myprogram main.o utils.o
# 删除myprogram和编译对象main.o、utils.o
点击进入全屏 点击退出全屏
# 定义伪目标(这些目标不代表实际文件)
.PHONY: all clean
进入全屏;退出全屏
这一点也很重要。.PHONY
目标是必要的,来避免与实际文件发生冲突。简单来说,它告诉 make
,all
和 clean
并不是需要创建的文件,而是要执行的命令或任务。
例如,如果我们没有定义 .PHONY
。如果在你的项目目录中有一个叫做 clean
的文件。运行 make clean
会检查 clean
文件是否需要重新生成,确认文件存在且没有依赖项,最终什么也不做 ...。
记得孩子们,记得要定义你们的 .PHONY
目标! 👏
Makefile中的.PHONY
目标是用来指定不是文件名的目标,要记得定义它们哦! 👏
好的,那么这个东西只是 C
语言软件开发者用的吗?我需要拿出我的 UNIX System V 许可证吗?完全不是这样,朋友们,make
真是软件开发的好帮手。(双关玩得飞起)。让我来给你们讲几个我见过的例子。
Terraform 自动部署
我的好朋友Cobus,一位长期的Terraform用户,使用了make
和Makefile
来进一步简化他管理的堆栈(stacks)的工作流程。你可以在这里查看完整的Makefile
这里,下面这里有几个我觉得很酷的部分:
检查一下对应的操作系统,并根据它设置相应的宏。
UNAME:= $(shell uname)
ifeq ($(UNAME),Darwin)
OS_X := 真
SHELL := /bin/bash
# OS_X 为真,表示该操作系统为 macOS
else
OS_DEB := 真
SHELL := /bin/bash
# OS_DEB 为真,表示该操作系统为基于 Debian 的系统
endif
全屏显示;退出全屏
在执行 terraform destroy
之前给用户发出恰当的警告:
destroy: check
@echo "切换到 [$(value ENV)] 环境..."
@terraform workspace select $(value ENV)
@echo "## 💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥 ##"
@echo "你真的确定要彻底删除 [$(value ENV)] 环境吗?"
@echo "## 💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥💥 ##"
@read -p "按 Enter 键继续"
@terraform destroy \ -var-file="env_vars/$(value ENV).tfvars"
全屏 退出全屏
本地开发环境自动处理
(例如自动配置工具)
借助Docker和make
,你可以轻松地为你的软件开发项目自动化本地开发环境的创建。这使你可以自动化某些开发流程中的元素,并将许多构建任务向左移。虽然不使用make
也可以在本地Docker容器中设置数据库和服务——make
让这变得简单得多。(对于这些双关语我一点也不感到抱歉)
这里有一个我用于一些Rust开发的Makefile
示例,让我展示给你看。在这个Makefile
中,我需要在本地环境中启动Postgres和Redis服务。你可以在这里查看这个完整的Makefile
here
但让我们来看看我认为非常有用的几个要点。例如,这里我们使用简单的赋值方式来定义一个宏PG_PASSWORD
,这意味着该表达式只会被计算一次。使用:=
作为赋值操作符来实现这一点,因此每次引用$(PG_PASSWORD)
时,它不会再去执行openssl
命令,而是直接获取其值。
我们在代码里用 PG_PASSWORD
宏动态设置 Postgres 密码。
生成一个随机密码(12个字符),使用openssl命令生成12个字符的base64编码字符串。
PG_PASSWORD := $(shell openssl rand -base64 12 | tr -d '\n')
全屏模式,退出全屏
在我们实际运行应用程序之前,我们会检查 .env
文件是否已创建。如果没有找到该文件,我们会提示用户执行 make setup
命令。如果该文件存在,我们就加载该文件并运行 cargo run
🚀
# 使用 .env 文件中的环境变量来运行
run:
@if [ ! -f .env ]; then \
echo "未找到环境文件。请先执行 'make setup' 操作。"; \
exit 1; \
fi
@echo "加载环境变量并启动应用程序..."
@source .env && cargo run
全屏进入,全屏退出
make
这个工具在很多地方都能派上用场,比如视频游戏开发、自动调整图像大小和数据处理等等。
好了,现在你差不多明白了 make
的基本用法,你可能会想:"这玩意儿到底是干什么用的?为什么这么复杂?我花时间学它还值得吗?”。
好吧,朋友,的确如此!一旦你习惯了 make
,就不会再回头了。但我完全理解从未使用过 make
的人会觉得从头开始使用它可能很复杂。因此,在今天,借助现代技术,让我们在48年后开始使用 make
。让我来展示一下如何使用 Amazon Q 开发者 CLI 为你的项目生成 Makefile
们。😀
首先,Amazon Q 开发者 CLI 工具是一个基于命令行的 生成式AI助手。但真的,它是一个助手!它不仅帮助你解答编码问题,还能为你处理实际任务。从 生成代码 到 编写部署脚本,甚至 在你的本地或远程系统上运行命令。我超喜欢这个工具!😍
要开始使用 Amazon Q 开发者,你可以免费做到这一点,只需先为自己申请一个 AWS Builder ID 并 安装 Q CLI。我等你哦…… ⌚
哦,你回来了!太棒了!让我给你演示一下如何用这个工具为我的项目生成各种Makefiles。
我的CDK链接缩短工具项目
我这里有一个相当简单的AWS CDK项目,用于搭建我自己的网址缩短器。该项目使用了TypeScript、Rust和HTML作为编程语言,其中部署了AWS Lambda函数、DynamoDB表和CloudFront分发服务。还有,它包含多个堆栈(Stack),因此,使用一个Makefile
来管理这些组件非常有用。
我的项目结构大概就是这样
.
├── bin
│ └── krtk-rs.ts # CDK应用程序入口点
├── lambda
│ ├── create_link # 创建短链接的Lambda
│ ├── get_links # 获取链接的Lambda
│ ├── visit_link # 处理链接访问的Lambda
│ └── process_analytics # 处理分析的Lambda函数
├── lib
│ ├── certificate-stack.ts # SSL证书管理堆栈
│ └── krtk-rs-stack.ts # 主要基础设施组件
├── shared # 共享Rust代码
├── website # 前端资产
│ ├── assets
│ │ └── main.js # 前端JavaScript
│ └── index.html # 主页HTML
└── test
└── krtk-rs.test.ts # CDK堆栈测试(尚未实现)
全屏模式: 进入 全屏模式: 退出
在这个项目的根目录中,我通过运行q chat
来启动AWS Q Developer CLI。这会打开命令行聊天界面。
,这是 Q 聊天窗口的截图。
现在,我总是喜欢了解一下我当前项目的具体情况,这样我就能把这种结构化的思考加入到上下文中(尽管它已经可以访问我的整个仓库了)。
跟我讲讲我的项目是做什么的,它是怎么搭建起来的
全屏模式 退出全屏
然后我用下面的提示让它生成一个 Makefile
帮我创建一个Makefile,以便我可以自动化开发此堆栈时所需的某些操作。
确保包括个人和所有堆栈的部署命令。
帮我为cargo lambda设置本地开发环境,并配置所有必需的环境变量以确保它们正常运行。
切换到全屏模式。退出全屏。
过了一会儿,它给我以下结果,
# krtk.rs Makefile
# 自动化常见的开发和部署任务
.PHONY: help install build bootstrap deploy deploy-cert deploy-main local-dev clean test lint env-setup
# 默认目标
help:
@echo "krtk.rs - 无服务器短链接服务"
@echo ""
@echo "用法:"
@echo "make install 安装所有依赖"
@echo "make build 构建项目"
@echo "make bootstrap 启动 AWS CDK 环境"
@echo "make deploy 部署所有堆栈"
@echo "make deploy-cert 部署证书堆栈"
@echo "make deploy-main 部署主要堆栈"
@echo "make local-dev 启动本地开发环境"
@echo "make clean 清理构建文件"
@echo "make test 运行测试"
@echo "make lint 运行代码检查"
@echo "make env-setup 设置本地环境变量"
# 安装目标
install:
@echo "运行 npm install 安装依赖..."
npm install
@echo "运行 cargo install cargo-lambda 安装 cargo-lambda..."
cargo install cargo-lambda
@echo "依赖安装成功。"
# 构建目标
build:
@echo "构建项目..."
npm run build
@echo "构建 Rust Lambda 函数..."
进入 lambda/create_link 并且 cargo lambda build --release
进入 lambda/get_links 并且 cargo lambda build --release
进入 lambda/visit_link 并且 cargo lambda build --release
进入 lambda/process_analytics 并且 cargo lambda build --release
@echo "构建完成。"
# AWS CDK 目标
bootstrap:
@echo "启动 AWS CDK 环境..."
cdk bootstrap
@echo "启动 AWS CDK 环境完成。"
deploy:
@echo "部署所有堆栈..."
cdk deploy --all --require-approval never
@echo "部署完成。"
deploy-cert:
@echo "部署证书堆栈..."
cdk deploy CertificateStack --require-approval never
@echo "证书堆栈部署完成。"
deploy-main:
@echo "部署主要应用堆栈..."
cdk deploy KrtkRsStack --require-approval never
@echo "主堆栈部署完成。"
# 本地开发
local-dev:
@echo "启动本地开发环境..."
@echo "设置本地 DynamoDB 实例..."
docker run -d -p 8000:8000 --name dynamodb-local amazon/dynamodb-local
@echo "创建本地 DynamoDB 表..."
aws dynamodb create-table \
--table-name local-links \
--attribute-definitions AttributeName=id,AttributeType=S \
--key-schema AttributeName=id,KeyType=HASH \
--provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
--endpoint-url http://localhost:8000 || true
@echo "启动本地 API Gateway..."
@echo "本地开发环境已准备好。"
@echo "本地运行 Lambda 函数,可以使用 cargo lambda watch。"
# 清理
clean:
@echo "清理构建文件..."
rm -rf cdk.out
find lambda -name target -type d -exec rm -rf {} +
@echo "停止本地 DynamoDB 实例..."
docker stop dynamodb-local || true
docker rm dynamodb-local || true
@echo "清理完成。"
# 测试
test:
@echo "运行 npm test 运行测试..."
运行 npm test
进入 lambda/create_link 并且 cargo test
进入 lambda/get_links 并且 cargo test
进入 lambda/visit_link 并且 cargo test
进入 lambda/process_analytics 并且 cargo test
@echo "测试完成。"
# 代码检查
lint:
@echo "运行 npm lint 运行代码检查..."
运行 npm lint
进入 lambda/create_link 并且 cargo clippy -- -D warnings
进入 lambda/get_links 并且 cargo clippy -- -D warnings
进入 lambda/visit_link 并且 cargo clippy -- -D warnings
进入 lambda/process_analytics 并且 cargo clippy -- -D warnings
@echo "代码检查完成。"
# 环境设置
env-setup:
@echo "设置本地开发环境的环境变量如下:"
@echo "export DYNAMODB_TABLE=local-links" > .env.local
@echo "export DYNAMODB_ENDPOINT=http://localhost:8000" >> .env.local
@echo "export TABLE_NAME=local-links" > .env
@echo "export AWS_REGION=us-east-1" >> .env.local
@echo "export DOMAIN_NAME=localhost" >> .env.local
@echo "环境变量已设置在 .env.local 文件中"
@echo "运行 'source .env.local' 加载这些变量。"
全屏模式 退出全屏
哇,这可真多!更牛的是,它竟然运行了 dynamodb-local
来模拟我的生产环境。还将这些信息存储在了 .env
文件中,以便在执行时用到。但是好像有点不对劲……它并没有通过 cargo lambda watch
命令把环境变量传递给我的 Lambda 函数。所以我礼貌地问了它一句:
确保 cargo lambda watch 也包含一个环境变量,用于设置我的表名,因为我的函数需要这个环境变量。
更好的做法是通过使用 `--env-file` 参数来传递给 cargo lambda watch 命令,并将 `.env` 文件路径作为参数传递。
全屏模式,退出全屏
现在我已经修改了Makefile
的一部分,也加入了用于测试我的Lambda函数的命令。
[...]
# 环境设置(必须在本地开发或监视命令之前运行)
env-setup:
@echo "为本地开发设置环境变量..."
@echo "DYNAMODB_TABLE=local-links" > .env
@echo "TABLE_NAME=local-links" > .env
@echo "DYNAMODB_ENDPOINT=http://localhost:8000" >> .env
@echo "AWS_REGION=us-east-1" >> .env
@echo "DOMAIN_NAME=localhost" >> .env
@echo "环境变量已设置好,并保存在 .env 文件中"
# 本地开发
local-dev: env-setup
@echo "开始本地开发环境..."
@echo "设置本地 DynamoDB..."
docker run -d -p 8000:8000 --name dynamodb-local amazon/dynamodb-local || true
@echo "使用 docker 来启动并运行亚马逊的本地 DynamoDB 容器,端口映射为 8000,并以 'dynamodb-local' 命名这个容器。"
@echo "创建本地 DynamoDB 数据表..."
@echo "接下来,我们将使用 AWS CLI 创建名为 'local-links' 的 DynamoDB 表。"
aws dynamodb create-table \
--table-name local-links \
--attribute-definitions AttributeName=id,AttributeType=S \
--key-schema AttributeName=id,KeyType=HASH \
--provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
--endpoint-url http://localhost:8000 || true
@echo "本地开发环境现已准备就绪。"
@echo "使用 'make watch-<function-name>' 运行本地 Lambda 功能。"
# 使用 .env 文件中的环境变量的 Lambda 监视命令
watch-create-link: env-setup
@echo "监控 create_link lambda..."
cd lambda/create_link && cargo lambda watch --env-file ../../.env
watch-get-links: env-setup
@echo "监控 get_links lambda..."
cd lambda/get_links && cargo lambda watch --env-file ../../.env
watch-visit-link: env-setup
@echo "监控 visit_link lambda..."
cd lambda/visit_link && cargo lambda watch --env-file ../../.env
watch-process-analytics: env-setup
@echo "监控 process_analytics lambda..."
cd lambda/process_analytics && cargo lambda watch --env-file ../../.env
[...]
全屏模式 退出全屏
我只是在q chat
里回复了一个大大的y
,准备好了!👏 我甚至还得到了一个关于如何使用我新拿到的Makefile
的小指南:
我为你在 krtk.rs 项目中创建了全面的 Makefile,这将有助于自动化开发任务。我还添加了一个 .gitignore 文件,以确保环境变量和构建工件不会被误提交到你的仓库。
## Makefile 功能
Makefile 包含以下命令:
1. 安装与设置:
• make install - 安装 npm 依赖和 cargo-lambda
• make env-setup - 创建一个包含本地开发变量的 .env 文件
2. 构建和部署:
• make build - 构建 CDK 项目和 Rust Lambda 函数
• make bootstrap - 初始化你的 AWS CDK 环境
• make deploy - 部署所有堆栈
• make deploy-cert - 仅部署证书堆栈
• make deploy-main - 仅部署主应用程序堆栈
3. 本地开发:
• make local-dev - 设置本地 DynamoDB 实例并创建所需表
• 开发 Lambda 函数时使用环境变量:
• make watch-create-link
• make watch-get-links
• make watch-visit-link
• make watch-process-analytics
4. 测试和维护:
• make test - 运行 CDK 和 Lambda 函数的测试
• make lint - 运行 TypeScript 和 Rust 代码的 linters
• make clean - 清理构建工件并停止本地服务
## 环境变量
Makefile 创建一个包含以下变量的 .env 文件:
• DYNAMODB_TABLE=local-links
• DYNAMODB_ENDPOINT=http://localhost:8000
• AWS_REGION=us-east-1
• DOMAIN_NAME=localhost
这些环境变量通过 cargo lambda watch 命令的 --env-file 参数传递给 Lambda 函数。
## 使用
开始本地开发流程:
bash
# 安装依赖
make install
# 设置本地环境并启动 DynamoDB
make local-dev
# 本地运行特定的 Lambda 函数
make watch-create-link
部署:
bash
# 部署所有堆栈
make deploy
# 或者单独部署堆栈
make deploy-cert
make deploy-main
使用帮助命令(make 或 make help)可以查看所有可用命令及其描述。
全屏模式 退出全屏
最后,让我们通过运行几个 make
命令来看看实际效果!比如,看看本地开发是怎么样的!
首先: make install
- 这只是安装运行我的栈所需的所有包。
然后: make local-dev
- 这会配置我的本地开发环境,启动本地版本的DynamoDB。
现在,我可以通过运行 make watch-create-link
来本地执行我的 create-link
Lambda 函数。这会创建一个 HTTP 端点,我可以像用户一样通过这个端点与我的 Lambda 函数进行交互。要调用它,我只需运行以下 curl
命令即可:
# 下面是一个例子命令
# 此命令用于生成短链接
curl -X POST http://10.0.1.11:9000/lambda-url/create_link \
-H "Content-Type: application/json" \
-d '{"url_to_shorten": "https://rup12.net"}'
# JSON参数示例:{"url_to_shorten": "https://rup12.net"}
# -d 用于传递JSON数据
# 此命令将返回一个短链接
进入全屏模式,退出全屏
看看吧...
缩短URL失败 💥 :“链接创建失败 - 服务错误:资源未找到异常(ResourceNotFoundException { message: Some(\"请求的资源未找到\"), meta: ErrorMetadata { code: Some(\"ResourceNotFoundException\"), message: Some(\"请求的资源未找到\")}
请单击全屏以进入全屏模式,单击退出全屏以退出全屏模式
嗯,那看来这是未来的Darko的问题,我得去调试那些Rusty Lambda函数。🤔
我执行了 make clean
,最后清理了我的开发环境里的东西。
使用静态网站
接下来是一个我为开源应用程序构建的简单着陆页。它运行在静态网站上,使用的是AWS Amplify,但通过AWS S3集成部署到上面。因此,我不必通过AWS控制台将文件上传到S3,然后前往Amplify并触发_部署_操作。为什么不使用一个Makefile
来完成这一切呢?
咱们去问问这位Q吧:
需要为我的网站在这里编写一个Makefile,并将其上传到我的AWS账户中的名为shuk.rs的bucket中。
我希望使用这个Makefile来部署更改,以及使用aws CLI的start-deployment命令更新AWS Amplify网站。
为了使这一切运作,我需要一个app-id。因此,请同时获取这个值。请使用名为 personal
的AWS CLI配置文件。
这是用于amplify部署的AWS CLI命令的示例:
aws amplify start-deployment --app-id <app_id> \
--branch-name <branch_name> \
--source-url s3://amzn-s3-demo-website-bucket/prefix \
--source-url-type BUCKET_PREFIX
进入全屏 退出全屏
我确保给了它我想要在部署时运行的命令示例。这可以确保一切正常,尤其是如果这是一个新功能。这个命令是从文档页面得到的。
看看这个,它竟然实际上本地用 AWS CLI 帮我找到了 app-id
。它首先在 us-east-1
中查找,没找到,然后又试了 us-west-2
,这次成功了。
你看它这建了什么呢?
# Makefile for shuk.rs 网站部署
# 此 Makefile 负责将网站部署到 S3 存储桶和 AWS Amplify
# 配置
BUCKET_NAME = shuk.rs
REGION = us-west-2
AMPLIFY_APP_ID = d32qis9bnvy6r1
AMPLIFY_BRANCH = staging
AWS_PROFILE = personal
# 要部署的文件列表
WEBSITE_FILES = index.html install.sh shuk.gif favicon.png
.PHONY: deploy deploy-s3 deploy-amplify clean help
# 默认目标
help:
@echo "可选操作:"
@echo "deploy - 将网站部署到 S3 存储桶并更新 Amplify 应用"
@echo "deploy-s3 - 只将网站部署到 S3 存储桶"
@echo "deploy-amplify - 只更新 Amplify 应用"
@echo "clean - 删除任何临时文件"
@echo "help - 显示此帮助信息"
# 部署到 S3 和 Amplify
deploy: deploy-s3 deploy-amplify
# 部署到 S3 存储桶
deploy-s3:
@echo "将网站文件部署到 S3 存储桶 $(BUCKET_NAME)... "
@for file in $(WEBSITE_FILES); do \
content_type=$(case $file in \
*.html) echo "text/html";; \
*.sh) echo "text/plain";; \
*.gif) echo "image/gif";; \
*.png) echo "image/png";; \
*) echo "application/octet-stream";; \
esac); \
echo "上传 $file,类型为:$content_type"; \
aws s3 cp $file s3://$(BUCKET_NAME)/ --content-type "$content_type" --profile $(AWS_PROFILE) --region $(REGION); \
done
@echo "S3 部署完成"
# 更新 Amplify 应用
deploy-amplify:
@echo "开始为 Amplify 应用 $(AMPLIFY_APP_ID) 的 $(AMPLIFY_BRANCH) 分支部署... "
@aws amplify start-deployment \
--app-id $(AMPLIFY_APP_ID) \
--branch-name $(AMPLIFY_BRANCH) \
--source-url s3://$(BUCKET_NAME)/ \
--source-url-type BUCKET_PREFIX \
--profile $(AWS_PROFILE) \
--region $(REGION)
@echo "Amplify 部署开始"
# 清理临时文件
clean:
@echo "开始清理临时文件... "
@find . -name "*.tmp" -delete
@echo "清理完成"
切换到全屏,退出全屏
好的,我不能在那里硬性写死 app-id
。还记得我之前提到的那个小小的 :=
操作符吗?没错!它实际上可以把一个表达式变成一个命令!太好了!让我们让它在执行时 动态 获取 app-id
。
你可以让 app-id 能够动态更新,而不是将其硬编码进 make 文件里吗?其他部分仍然可以硬编码。
点击进入全屏模式,点击退出全屏模式
就这样!我们已经得到了正确的 AMPLIFY_APP_ID
,它会自动帮我们拿到 app-id
。
# Makefile 用于 shuk.rs 网站部署
# 此 Makefile 用于将部署到 S3 存储桶和 AWS Amplify
# 配置信息
BUCKET_NAME = shuk.rs
REGION = us-west-2
AMPLIFY_BRANCH = staging
AWS_PROFILE = personal
# 要部署的文件列表
WEBSITE_FILES = index.html install.sh shuk.gif favicon.png
# 动态获取 shuk.rs 的 Amplify 应用 ID
AMPLIFY_APP_ID := $(shell aws amplify list-apps --region $(REGION) --profile $(AWS_PROFILE) --query "apps[?name=='shuk.rs'].appId" --output text)
.PHONY: deploy deploy-s3 deploy-amplify clean help check-app-id
...
切换到全屏模式 退出全屏
最后一步,我将对 index.html
做一些改动,然后用 make deploy
部署。
砰 💥 部署成功!谢谢 make
❤️
好了朋友们,我认为我们已经充分探讨了 make
以及它如何帮助我们在日常开发生活中。我们看到,尽管 make
已经有 48年历史之久,它在我们现代的技术栈中仍然非常相关且有用。从贝尔实验室最初解决编译问题开始,到帮助我们简化 Terraform 部署和网站更新,这说明好工具如 make
能够伴随时间的推移依然保持其价值。
是的,一开始语法可能有点繁琐(那些奇怪的宏和模式规则让人晕头转向 😯),但是一旦你过了这最初的难关,你就会发现自动化重复任务并规范化工作流程的强大工具。再也不用拼命地猛按向上箭头键希望能找到三天前你运行的那个命令了!
正如我们在使用 AWS Q Developer CLI 的示例中所见,你甚至不需要精通 make
就可以开始使用它。现代 AI 工具可以帮助生成适合你的项目的复杂 Makefiles
,无论是复杂的 CDK 部署还是简单的静态网站更新,AI 工具都能应对自如。
动手吧,为你的项目创建一个 Makefile
文件!将来你可以直接输入 make deploy
,而不是记住那个需要 15 个参数的 AWS CLI 命令。经过近五十年的发展,make
依然让我们的生活变得更轻松(这个双关真的很有趣,不用道歉 😆)。
记得孩子们,要定义你的 .PHONY
目标哦!棒棒哒!👏
共同學習,寫下你的評論
評論加載中...
作者其他優(yōu)質(zhì)文章