用 Makefile 构建 go 程序
基本命令
基本规则
Makefile 基本规则如下:
<target>: <prerequisites>
[tab] <commands>
在 :
前面的部分成为目标,我们通常会指定特定的目标进行特定的任务。如:build:
、clean:
、install:
等,通过 make build
、make clean
、make install
命令执行指定目标。
直接运行 make
命令且不带目标,Makefile 默认执行第一个目标。所以我们通常将 all:
目标作为约定放在第一位,用于编译整个程序。
第二行的命令部分前面必须用 tab 开头,否则会出错。
而 commands 中可以直接执行 shell 命令。
所以可以简单执行以下命令:
hello:
echo hello world
执行 make hello
就会看到:
echo hello world
hello world
会发现执行的命令也会输出,如果不想看到命令本身,只要在前面加一个 @
符号就可以了。
hello:
@echo hello world
伪目标
编写 C语言程序时我们经常会清理 .o
目标文件、或者其他临时文件。这时我们通常会写:
clean:
rm *.o temp
但是,如果当前目录下已经存在一个叫 clean
的文件,那么这个目标不会执行。因为 Makefile 会认为 clean 这个文件已经存在路,不需要重新构建。
为了避免这种情况发生,可以声明 clean
为"伪命令。
.PHONY: clean
clean:
rm *.o temp
命令
每行命令之前必须有一个tab键。如果想用其他键,可以用内置变量.RECIPEPREFIX声明。
.RECIPEPREFIX = >
all:
> echo Hello, world
需要注意的是,每行命令在一个单独的shell中执行。这些Shell之间没有继承关系。
所以为了继承上一个命令可以将:
- 两个命令卸载一行并用
;
隔开。 - 在换行符前面加一个反斜杠
\
。 - 用
.ONESHELL:
命令。
变量与控制
Makefile 还支持变量、条件判断和循环。
txt = Hello World
test:
@echo $(txt)
LIST = one two three
all:
for i in $(LIST); do \
echo $$i; \
done
# 等同于
all:
for i in one two three; do \
echo $i; \
done
ifeq ($(CC),gcc)
libs=$(libs_for_gcc)
else
libs=$(normal_libs)
endif
还有一些自动变量:
$* 不包含扩展名的目标文件名称。
$+ 所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件。
$< 第一个依赖文件的名称。
$? 所有的依赖文件,以空格分开,这些依赖文件的修改日期比目标的创建日期晚。
$@ 目标的完整名称。
$^ 所有的依赖文件,以空格分开,不包含重复的依赖文件。
$% 如果目标是归档成员,则该变量表示目标的归档成员名称。
用 Makefile 构建 go 程序
以下是一个 go 程序的 Makefile 可以作为参考
.PHONY: all clean test docker latest init
export CGO_ENABLED=0
export GOOS=linux
export GOARCH=amd64
BINARY=go-realworld-clean
VERSION=$(shell git describe --abbrev=0 --tags 2> /dev/null || echo "0.1.0")
BUILD=$(shell git rev-parse HEAD 2> /dev/null || echo "undefined")
LDFLAGS=-ldflags "-X main.Version=$(VERSION) -X main.Build=$(BUILD)"
all:
go build -o $(BINARY) $(LDFLAGS)
init:
git config core.hooksPath .githooks
docker:
docker build \
-t $(BINARY):latest \
-t $(BINARY):$(VERSION) \
--build-arg build=$(BUILD) --build-arg version=$(VERSION) \
-f Dockerfile --no-cache .
latest:
docker build \
-t $(BINARY):latest \
--build-arg build=$(BUILD) --build-arg version=$(VERSION) \
-f Dockerfile --no-cache .
test:
go test ./...
clean:
if [ -f $(BINARY) ] ; then rm $(BINARY) ; fi