使用 Makefile 构建指令集
make
是一个历史悠久的构建工具,通过配置 Makefile
文件就可以很方便的使用你自己自定义的各种指令集,且与具体的编程语言无关。
例如配置如下的 Makefile
:
1
2
| run dev:
NODE_ENV=development nodemon server.js
|
这样当你在命令行执行 make run dev
时其实就会执行 NODE_ENV=development nodemon server.js
指令。
使用 Makefile
构建指令集可以很大的提升工作效率。
Makefile 基本语法
1
2
| <target>: <prerequisites>
<commands>
|
target
其实就是执行的目标,prerequisites
是执行这条指令的前置条件,commands
就是具体的指令内容。
示例:
1
2
3
4
5
| build: clean
go build -o myapp main.go
clean:
rm -rf myapp
|
这里的 build
有一个前置条件 clean
,意思就是当你执行 make build
时,会先执行 clean
的指令内容 rm -rf myapp
,然后再执行 build
的内容 go build -o myapp main.go
。
变量
自定义变量,示例:
1
2
3
4
5
6
7
| APP=myapp
build: clean
go build -o ${APP} main.go
clean:
rm -rf ${APP}
|
PHONY
上例中的定义了 target
目标有 build
和 clean
,如果当前目录中正好有一个文件叫做 build
或 clean
,那么其指令内容不会执行,这是因为 make 会把 target
视为文件,只有当文件不存在或发生改变时才会去执行命令。
为了解决这个问题,我们需要使用 PHONY
声明 target
其实是伪目标:
1
2
3
4
5
6
7
8
9
| APP=myapp
.PHONY: build
build: clean
go build -o ${APP} main.go
.PHONY: clean
clean:
rm -rf ${APP}
|
多个 PHONY 也可以统一声明在一行中:
递归的目标
假设我们的工程目录结构如下:
1
2
3
4
5
6
7
| ~/project
├── main.go
├── Makefile
└── mymodule/
├── main.go
└── Makefile
|
文件根目录下还有一个文件夹 mymodule
,它可能是一个单独的模块,也需要打包构建,并且定义有自己的 Makefile
:
1
2
3
4
5
6
| # ~/project/mymodule/Makefile
APP=module
build:
go build -o ${APP} main.go
|
现在当你处于项目的根目录时,如何去执行 mymodule 子目录下定义的 Makefile
呢?
使用 cd
命令也可以,不过我们有其它的方式去解决这个问题:使用 -C
标志和特定的 ${MAKE}
变量。
修改项目根目录中的 Makefile
为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| APP=myapp
.PHONY: build
build: clean
go build -o ${APP} main.go
.PHONY: clean
clean:
rm -rf ${APP}
.PHONY: build-mymodule
build-mymodule:
${MAKE} -C mymodule build
|
这样,当你执行 make build-mymodule
时,其将会自动切换到 mymodule
目录,并且执行 mymodule
目录下的 Makefile
中定义的 build
指令。
shell 输出作为变量
我们可以把 shell 中执行的指令的输出作为变量:
1
2
3
4
| V=$(shell go version)
gv:
echo ${V}
|
这里执行 make gv
就会先执行 go version
指令然后把输出的内容赋值给变量 V 。
判断语句
假设我们的指令依赖于环境变量 ENV
,我们可以使用一个前置条件去检查是否忘了输入 ENV
:
1
2
3
4
5
6
7
8
| .PHONY: run
run: check-env
echo ${ENV}
check-env:
ifndef ENV
$(error ENV not set, allowed values - `staging` or `production`)
endif
|
这里当我们执行 make run
时,因为有前置条件 check-env
会先执行前置条件中的内容,指令内容是一个判断语句,判断 ENV
是否未定义,如果未定义,则会抛出一个错误,错误提示就是 error
后面的内容。
帮助提示
添加 help
帮助提示:
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
| .PHONY: build
## build: build the application
build: clean
@echo "Building..."
@go build -o ${APP} main.go
.PHONY: run
## run: runs go run main.go
run:
go run -race main.go
.PHONY: clean
## clean: cleans the binary
clean:
@echo "Cleaning"
@rm -rf ${APP}
.PHONY: setup
## setup: setup go modules
setup:
@go mod init \
&& go mod tidy \
&& go mod vendor
.PHONY: help
## help: prints this help message
help:
@echo "Usage: \n"
@sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' | sed -e 's/^/ /'
|
这样当你执行 make help
时,就是打印如下的提示内容:
1
2
3
4
5
6
7
| Usage:
build build the application
run runs go run main.go
clean cleans the binary
setup setup go modules
help prints this help message
|
参考资料