make
make
是一个根据指定Shell
命令进行构建的工具。
将代码变成可执行文件的操作叫做编译;先编译这个,还是先编译那个的顺序被称为构建。Make
是最常用的构建工具,诞生于 1977 年,主要用于C语言项目的构建,但只要某个文件由变化,需要重新构建,我们都可以用 Make 进行构建。
makefile
目标(Target)
makefile
是 make
构建时要执行的 shell
命令的集合,即构建规则写在这里面,每条规则形式如下:
<target>: <prerequisites>
[tab] <commands>
:
前面的 target
叫做目标,后面部分是由前置依赖项和命令部分构成;依赖项和命令部分都是可选项,但是必须存在一个。
注意
命令部分前面必须由 tab
建起首,然后后面紧跟着要执行的命令。
例如:
clean:
rm *.txt
简而言之 target
就是 make 要执行的操作名称,是一个 “伪目标”,而上面的示例的作用就是删除当前目录下的所有 txt
文件。执行命令为:
make clean
注意
当我们执行构建命令时,若此时当前目录中存在构建目标同名的文件,例如 clean
,那么 make 不会执行该目标后面的构建命令。
定义伪目标
为了避免上述情况我们可以声明对于的目标为 “伪目标”,声明语法如下:
.PHONY: clean
# 多个目标
.PHONY: cleanall cleanobj cleandiff
提示
我们执行 make
命令时,后面没有执行构建的目标,默认会构建第一个目标,所以通常我们将必须的放在第一个构建目标。
例如:
make && make install
# 第一个目标用来初始化,比如依赖项检查等前置操作
# install 目标则执行实际安装
前置依赖项
前置依赖项就是目标紧跟着的部分,通常是一组文件名,之间通过空格分隔。
构建触发条件
① 目标依赖项必须都存在,否则将无法构建
② 目标依赖项修改时间比目标新,则目标需要重新构建
③ 构建的目标已存在将不会重复构建(伪目标除外)
构建命令
构建命令用来表示如何构建目标,可以是一行或者多行,它是构建目标的具体指令。每一行命令必须是由 tab
符开头,可以通过内置变量 .RECIPREPREFIX
声明为其他符号作为起始符,例如:
.RECIPEPREFIX = >
all:
> echo Hello world
警告
每行指令相互独立,之间没有前后继承关系,即无法使用上一行指令的结果。
解决办法:
① 将多条命令写在同一行,用逗号分隔。
② 在命令行末尾换行符前加上 \
转义。
③ 指定 .ONESHELL:
命令。
提示
#
是 Makefile
中表示注释。
构建时 make
会打印每条构建的指令,然后再执行,即回声(echoing);若想要避免这种情况,可以在每条命令前面加上 @
,就可以关闭回声。
test:
# 这是测试
@echo Hello
通配符
*
匹配所有
?
匹配当个字符
[...]
匹配所有以括号中某个字符开头的
%
模式匹配
变量
自定义变量:
name = Stan
msg = Hello $(name)
test:
@echo $(msg)
=
在执行时扩展,允许递归扩展
:=
在定义时扩展
?=
变量值为空时才赋值
+=
将值追加到变量的尾端
内置变量:
Make命令提供一系列内置变量,比如,$(CC)
指向当前使用的编译器,$(MAKE)
指向当前使用的Make工具。
自动变量:
变量 | 说明 |
---|---|
$@ |
当前目标 |
$< |
第一个依赖项 |
$? |
比当前目标新的依赖项 |
$^ |
当前目标依赖项 |
$* |
通配符 % 所匹配的内容 |
$(@D) |
当前目标目录名 |
$(@F) |
当前目标文件名 |
$(<D) |
依赖项的目录名 |
$(<F) |
依赖项的文件名 |
条件和循环
条件判断:
ifeq ($(CC),gcc)
libs=$(libs_for_gcc)
else
libs=$(normal_libs)
endif
循环:
LIST = one two three
all:
for i in $(LIST); do \
echo $$i; \
done
# 等同于
all:
for i in one two three; do \
echo $i; \
done```
使用函数
用语法如下:
$(function arguments)
# 或者
${function arguments}
makefile 示例
SHELL := /bin/bash
BASEDIR = $(shell pwd)
.PHONY: help
help:
@grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
.PHONY: install
install: ## Install dependencies
@go mod download
@go mod vendor
.PHONY: dev
dev: ## Run with Dev
@test -f conf/config.local.yml || cp conf/config.local.yml.example conf/config.local.yml
@go run cmd/todomvc/todomvc.go
.PHONY: build
build: ## Build todomvc
@go build -o build/todomvc cmd/todomvc/todomvc.go
clean: ### Remove build dir
@rm -fr build