0%

良好的Makefile实践

1. 背景

这篇文章的主要目的是记录一些可以在Makefile中使用的技巧,以便可以为Makefile本身中的每个目标添加文档,并且可以将其作为make目标查看(例如make help)。

在项目中拥有可靠的文档是一件很了不起的事情,而且如果它们不会过时,那就更好了。通常在顶级Readme.md或类似文件中记录每个Make目标。尽管这是迈出的重要第一步,但更新Makefile而不是更新文档是很常见的,因此使它们变得毫无用处。

2. 目标

最终目标是能够根据Makefile中的注释运行以下内容。

1
2
3
4
5
6
7
8
9
10
11
# make

Usage:
make

Targets:
help Display this help
deps Check dependencies
clean Cleanup the project folders
build Build the project
watch Watch file changes and build

对于具有很多目标的复杂Makefile,我们也可以将它们组合在一起。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
> make

Usage:
make

Dependencies
deps Check dependencies

Cleanup
clean Cleanup the project folders

Building
build Build the project
watch Watch file changes and build

Helpers
help Display this help

让我们继续看看它是如何实现的

要求:

makeawk是唯一的要求。在macOS(BSD)和Linux(GNU)版本上均能工作。

实现

如上面的示例所示,我们可以针对两种不同的情况实现它们。

  • 简单的makefile
    对于目标文件很少的Makefile,我们可以列出所有目标文件而没有任何分组。

首先,添加一个帮助目标。

1
2
help:  ## Display this help
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n\nTargets:\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-10s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST)

然后,使用上述帮助目标中指定的语法添加注释。在此示例中,我们将使用##作为可打印注释的标签。

1
2
3
4
5
6
7
8
9
10
11
deps:  ## Check dependencies
$(info Checking and getting dependencies)

clean: ## Cleanup the project folders
$(info Cleaning up things)

build: clean deps ## Build the project
$(info Building the project)

watch: clean deps ## Watch file changes and build
$(info Watching and building the project)

(可选)将默认目标添加为help,最好在Makefile的顶部

1
.DEFAULT_GOAL:=help

(可选)调整目标和通过make help输出的注释之间的宽度。

在上述帮助目标中,将数字10替换为您喜欢的字符宽度。

完整的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
.DEFAULT_GOAL:=help
SHELL:=/bin/bash

.PHONY: help deps clean build watch

help: ## Display this help
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n\nTargets:\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-10s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST)

deps: ## Check dependencies
$(info Checking and getting dependencies)

clean: ## Cleanup the project folders
$(info Cleaning up things)

build: clean deps ## Build the project
$(info Building the project)

watch: clean deps ## Watch file changes and build
$(info Watching and building the project)
  • 分组的Makefile

添加分组与上面的分组非常相似。我们再添加一种注释格式,以区分分组注释和目标注释,并稍微调整帮助目标以适应更改。

添加帮助目标(确保正确复制/转换制表符)

1
2
help:  ## Display this help
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)

使用上述帮助目标中指定的语法添加注释。在此示例中,我们将使用##作为可打印注释的标记,并使用## @作为分组注释 的标记

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
##@ Dependencies

deps: ## Check dependencies
$(info Checking and getting dependencies)

##@ Cleanup

clean: ## Cleanup the project folders
$(info Cleaning up things)

##@ Building

build: clean deps ## Build the project
$(info Building the project)

watch: clean deps ## Watch file changes and build
$(info Watching and building the project)

(可选)提供默认目标,并调整目标和打印输出的注释之间的字符宽度,如上面“简单Makefile”部分中所述

完整的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
.DEFAULT_GOAL:=help
SHELL:=/bin/bash

##@ Dependencies

.PHONY: deps

deps: ## Check dependencies
$(info Checking and getting dependencies)

##@ Cleanup

.PHONY: clean

clean: ## Cleanup the project folders
$(info Cleaning up things)

##@ Building

.PHONY: build watch

build: clean deps ## Build the project
$(info Building the project)

watch: clean deps ## Watch file changes and build
$(info Watching and building the project)

##@ Helpers

.PHONY: help

help: ## Display this help
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)

参考:
make help - Well documented Makefiles
Gist by prwhite
Client9 - Self documenting makefiles
Marmelab - Self documented makefiles

欢迎关注我的其它发布渠道