在Makefile中优雅添加帮助信息

·

1 min read

在软件开发的世界里,Makefile 是一个不可或缺的工具,它用于定义一系列规则来编译程序。然而,随着项目的增长,Makefile 也会变得复杂起来,对于新加入团队的成员或者偶尔访问项目的人来说,理解如何使用 Makefile 中提供的命令可能会成为一个挑战。因此,在 Makefile 中添加清晰的帮助信息不仅提高了可维护性,也增强了用户体验。

输出示例

Usage:

  make <target> [VARIABLE=value...]

Targets:

  build                          构建项目
  clean                          清理构建文件
  help                           显示此帮助信息
  test                           运行测试套件

Makefile示例与解释

下面是一个Makefile 示例,展示了如何为每个目标(target)添加描述,并通过 help 目标显示所有可用命令及其简短说明。此方法使得 Makefile 更具用户友好性和自解释性。

# 定义默认目标为 help,当用户只运行 'make' 时自动显示帮助信息。
.DEFAULT_GOAL := help

# 确保所有目标都有相应的描述,使用 ## 分隔符后跟描述文本。
.PHONY: build
build: ## 构建项目
    @echo "构建项目..."

.PHONY: clean
clean: ## 清理构建文件
    @echo "清理构建文件..."

.PHONY: test
test: ## 运行测试套件
    @echo "运行测试套件..."

# 添加 help 目标,用于显示帮助信息。
.PHONY: help
help: ## 显示此帮助信息
    @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
    sort | \
    awk ' \
    BEGIN { \
        FS = ":.*?## " ;\
        printf "\nUsage:\n\n" ;\
        printf "  make \033[36m<target>\033[0m [VARIABLE=value...]\n\n" ;\
        printf "Targets:\n\n" ;\
    } \
    { \
        printf "  \033[36m%-30s\033[0m %s\n", $$1, $$2 ;\
    }'

关键点解析:

  • 默认目标:设置 .DEFAULT_GOAL := help 确保了当用户仅输入 make 而不指定任何目标时,会自动调用 help 目标,从而显示帮助信息。

  • 注释规范:为了使 grepawk 正确识别并提取目标描述,我们遵循了一种特定的注释格式,即每个目标后跟随 ## 和描述文字。这种方式不仅易于阅读,而且确保了帮助信息的准确生成。

  • 帮助信息生成help 目标利用 grep 来查找所有符合模式的目标和其后的描述,然后使用 awk 对输出进行格式化处理。这里还加入了 ANSI 转义序列来给目标名称加上颜色,使得输出更加吸引眼球。

  • 伪目标声明:通过 .PHONY 声明伪目标(Makefile中help的命令行解释如 build, clean, test, help),可以避免与实际文件或目录同名的情况发生,保证这些命令总是被执行,即使存在相同名字的文件。

这种做法不仅提升了 Makefile 的易用性,还让开发者之间的交流变得更加顺畅。无论你是项目的新手还是经验丰富的老手,都能快速了解如何操作 Makefile,进而提高工作效率。希望这个例子能够为你提供一些灵感,帮助你在自己的项目中实现类似的改进。

Makefile中help的命令行解释

该命令的工作原理如下:

  1. grep -E '^[a-zA-Z_-]+:.*?## .*$$' Makefile:从Makefile文件中搜索符合正则表达式的行。这个正则表达式匹配以字母、下划线或连字符开头,后面跟着冒号和可能存在的其他字符,直到找到##符号,这通常是自定义的帮助文本标记。

  2. sort:对grep的结果进行排序,虽然在很多情况下Makefile中的目标已经按一定的逻辑顺序排列,但排序可以确保输出的一致性。

  3. awk:处理和格式化grepsort产生的输出。

    • BEGIN {FS = ":.*?# "; ...}:在处理任何输入之前设置字段分隔符FS:, 任意数量的非#字符,然后是空格和#。这样,awk就能正确地将每行分为两个字段:构建目标名称和其后的注释。

    • printf "Usage:\n\n make \033[36m<target>\033[0m [VARIABLE=value...]\n\nTargets:\n\n":打印出用法说明的头部。

    • {printf " \033[36m%-30s\033[0m %s\n", $1, $2}:对于每一行,格式化并打印构建目标和对应的注释。这里使用了ANSI转义序列\033[36m\033[0m来给目标名称添加颜色(青色),使其更易读。

参考链接

https://github.com/nginxinc/nginx-prometheus-exporter/blob/main/Makefile