1. 作用
通俗的來講:
其實和 shell 腳本差不多,只不過有一些自己的規則可以讓你使用起來更方便,比如你寫 shell 腳本的話,只能寫在一個文件裏然後執行這個文件,makefile 可以讓你執行這個裏面的某一條命令。
2. 格式
Makefile文件由一系列規則(rules)構成。每條規則的形式如下。
<target> : <prerequisites>
[tab] <commands>
上面第一行冒號前面的部分,叫做"目標"(target),冒號後面的部分叫做"前置條件"(prerequisites);第二行必須由一個tab鍵起首,後面跟着"命令"(commands)。
"目標"是必需的,不可省略;"前置條件"和"命令"都是可選的,但是兩者之中必須至少存在一個。
2.1 目標
當目標是文件名的時候,比如這時的目標是a.txt:
a.txt: b.txt c.txt
cat b.txt c.txt > a.txt
當目標爲一個操作的名字時,比如這個clean,稱爲‘僞目標’,但是當你執行 make clean
的時候,如果正好有一個文件叫做clean,那麼這個命令不會執行。因爲Make發現clean文件已經存在,就認爲沒有必要重新構建了,就不會執行指定的rm命令。
clean:
rm *.o
所以,一般我們會明確聲明clean是"僞目標":
.PHONY: clean
clean:
rm *.o temp
多個僞目標直接用空格隔開
像.PHONY這樣的內置目標名還有不少,可以查看手冊。
2.2 前置條件
2.3 命令
var-lost:
export foo=bar
echo "foo=[$$foo]"
上面代碼執行後(make var-lost),取不到foo的值。因爲兩行命令在兩個不同的進程執行。一個解決辦法是將兩行命令寫在一行,中間用分號分隔。
- 兩行命令寫在一行,中間用分號分隔。
- 換行符前加反斜槓轉義。
- 加上.ONESHELL:
var-kept:
export foo=bar; echo "foo=[$$foo]"
var-kept:
export foo=bar; \
echo "foo=[$$foo]"
.ONESHELL:
var-kept:
export foo=bar;
echo "foo=[$$foo]"
3. 語法
3.1 註釋
用 #
3.2 回聲
make會打印每條命令,然後再執行,這就叫做回聲(echoing)。
在命令的前面加上@,就可以關閉回聲。
注:通常只在註釋和純顯示的echo命令前面加上@。
test:
@# 這是測試
@echo TODO
3.3 通配符
*、? 、[…]
3.4 變量與賦值
txt = Hello World
test:
@echo $(txt)
用Shell變量,需要在美元符號前,再加一個美元符號,這是因爲Make命令會對美元符號轉義。
test:
@echo $$HOME
VARIABLE = value
# 在執行時擴展,允許遞歸擴展。
VARIABLE := value
# 在定義時擴展。
VARIABLE ?= value
# 只有在該變量爲空時才設置值。
VARIABLE += value
# 將值追加到變量的尾端。
3.5 自動變量
1、$@
$@指代當前目標,就是Make命令當前構建的那個目標。比如,make foo的 $@ 就指代foo。
2、$<
< 就指代p1。
等等…
3.6 判斷與循環
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
3.7 函數
$(function arguments)
# 或者
${function arguments}
Makefile提供了許多內置函數,可供調用。下面是幾個常用的內置函數。
1、shell 函數:
用來執行 shell 命令
srcfiles := $(shell echo src/{00..99}.txt)
2、call 函數:
$(call variable,param,param,…)
$(1),$(2)等等表示param,param,…
等等…還有好多函數
最後的實踐
我項目中的一些實踐:
SHELL := /bin/bash
.PHONY: prerequ-program client server prod_server prod_client
install:|prerequ-program
define require_install
if test "$(shell which $(1))" = ""; \
then \
brew install $(2); \
else \
echo $(1) is exists. skip install; \
fi
endef
prerequ-program:
@$(call require_install,mongod,mongo)
mkdir -p ./db/
if [ "${shell pgrep mongod}" = "" ]; then mongod --bind_ip 127.0.0.1 --fork --dbpath ./db/ --logpath ./db/mongod.log; fi
@echo "start mongod success!"
# 開發模式
server:
cd server && npm install && npm run dev
client:
cd client && npm install && npm run dev
# 生產模式
prod_server:
cd server && npm install && npm start
prod_client:
cd client && npm install && npm run build
比如我的項目要安裝 mongodb,那麼調用shell函數判斷一下有沒有 mongod,沒有的話就 brew install mongo
,否則在當前目錄下新建文件 mkdir -p ./db/