在習慣了使用 package.json
中的 src
來書寫構建命令之後,看到了 Element-ui
的程式碼倉庫中有 Makefile
這樣一個檔案。於是
Google 一下,發現了這個東西的強大。
Makefile
是一個適用於 C/C++
的工具,較早作為工程化工具出現在 UNIX
系統中, 通過 make
命令來執行一系列的編譯和連線操作。在擁有 make
環境的目錄下, 如果存在一個 Makefile
檔案。 那麼輸入 make
命令將會執行 Makefile
檔案中的某個目標命令。
初識 Makefile
直接用 Element-UI
的 Makefile
檔案作為例子來做演示。
.PHONY: dist test
default: help
install:
npm install
new:
node build/bin/new.js $(filter-out $@,$(MAKECMDGOALS))
deploy:
@npm run deploy
help:
@echo " \033[35mmake\033[0m \033[1m命令使用說明\033[0m"
複製程式碼
mac 中,是直接可以執行 make 命令的。 Windows下載 make 的 GUN 工具
如果電腦中已經在全域性安裝了 make
命令,那麼當我們 clone element
的程式碼之後,在根目錄執行 make install
這句命令將會起到和 npm install
一樣的效果。
來看一下 make install
做了什麼。
- 執行
make
命令, 在該目錄下找到Makefile
檔案。 - 找到
Makefile
檔案中對應命令列引數的install
的目標。這裡的目標就是install:
- 執行
npm install
下面的語句。npm run dev
Makefile
檔案格式
make
命令本身並不難, 它只是執行其後跟的 target
。而 target
具體的內容體現在 Makefile
檔案當中。
Makefile
檔案由一系列規則構成,每一條的規則需要明確兩件事情:構建目標是什麼,以及如何構建。 每一條的規則都遵循一下格式:
<target> : <prerequisites>
[tab] <commands>
複製程式碼
第一行被 :
分為了兩部分。
- 前面部分為目標
target
, 是執行make target
命令時可以匹配到的目標。 - 後一部分為前置條件(依賴目標), 如果第一行寫為
target1: target2 target3
, 那麼在執行make target1
的時候,需要先執行make target2
make target3
最後才會執行target1
下面的commonds
語句。
第二行必須由一個 tab
鍵開始,後面跟著需要執行的語句
每一個規則的
target
都是必須的,prerequisites
和 第二行都是可選的, 但是兩者之間必須至少存在一個。
只是這一點 Element-UI
專案中的 Makefile
程式碼就已經可以明白大部分了。但是看到程式碼中的第一行 .PHONY: dist test
這又是什麼呢?
目標(target)與偽目標(phony target)
每一個規則都會包含一個 target
, 在 make
中, target
通常是檔名, 指明這條規則需要構建的物件。目標可以是一個或者多個檔名, 之間用空格隔開。
當然,除了檔名, target
的名字還可以是具體的操作名稱,這類 target
又被稱為 偽目標(phony target)
。例如下面的 remove
就是一個偽目標。他的作用是刪除檔案。
remove:
rm *.js
複製程式碼
但是再執行 make remove
的時候,這個目錄下剛好也有一個檔案叫做remove,那麼這條命令是不會被執行的,因為 Make 發現該檔案已經存在,就沒有必要重新構建了,就會忽略掉這條命令。
為了避免這種情況的發生,可以主動宣告 remove 是偽目標。
.PHONY: remove
remove:
rm *.js
複製程式碼
當使用 .PHONY:
主動宣告 remove 是偽目標之後,make 就不會檢查是否存在有 remove 的檔案,而是每次都執行 remove 命令。
同樣可以看到在 Element 專案中的 Makefile 檔案中的第一行就宣告瞭 .PHONY: dist test
, 也是為了避免目錄中已經存在的檔名導致該命令無法正常執行。
回聲 @
在 Makefile 檔案中 #
表示註釋。
test:
# 這是一條註釋
touch a.js
複製程式碼
當執行 make test
之後,會列印一條註釋,然後再建立 a.js 這個檔案。
當在命令的前面加上 @
, 就可以關閉回聲。(由於 echo
也相當於輸入回聲,所以通常在註釋和 echo
前面都加上 @
)
test:
@# 這是測試
@echo some
複製程式碼
在 Element 中, 如果執行
make new some
專案中就會生成一個新的元件, 讓我們分析node build/bin/new.js $(filter-out $@,$(MAKECMDGOALS))
這條命令,看一下涉及到的 make 知識。
變數
Makefile 允許使用等號來自定義變數, 使用 $()
來呼叫變數。
txt = hello world
test:
@echo $(txt)
複製程式碼
自動變數 $@
Make 命令還提供了一些自動變數,他們的值與當前的規則有關。
$@
指代的是當前的目標,就是執行 make 命令所匹配的目標。比如執行 make foo
時, $@
就是指 foo
利用這個變數可以完成對當前目標的簡寫:
a.js b.js c.js:
touch $@
複製程式碼
特殊變數 MAKECMDGOALS
make 在執行時, 會設定一個特殊的變數 MAKECMDGOALS
, 該變數記錄了命令列引數指定的目標列表。也就是說使用這個變數,我們可以得到命令列的引數。
create:
@echo $(MAKECMDGOALS)
複製程式碼
執行命令 make create newCom
將會列印出 create newCom
這兩個欄位。
函式
make 還可以使用函式, 格式如下:
$(function arguments)
# 或者
${function arguments}
複製程式碼
filter-out
反過濾函式
過濾掉 objs 中所有含有 pattert 的內容, 格式:
$(filter-out pattert, objs)
objs = aa bb cc dd ee
pattert = bb cc ee
out:
@echo $(filter-out $(pattert), $(objs))
複製程式碼
執行 make out
輸出 aa dd
node build/bin/new.js $(filter-out $@,$(MAKECMDGOALS))
在 Element 中 無論是執行 make new some
還是執行 make new-lang some
邏輯都是向某一個指令碼檔案傳遞一個引數。
這條命令首先使用 node
來執行 build/bin/new.js
這個檔案, 並且使用 filter-out
函式在命令列的引數中把當前目標去掉。
我們可以新建一個 build/bin/new.js
檔案, 寫入下面的程式碼,看一下輸入內容。
<!-- build/bin/new.js -->
console.log(process.argv)
複製程式碼
Makefile
new:
node build/bin/new.js $(filter-out $@, $(MAKECMDGOALS))
複製程式碼
執行命令: make new testCom
輸出。
[ 'C:\\Program Files\\nodejs\\node.exe',
'C:\\Users\\***\\Desktop\\***\\a.js',
'testCom' ]
複製程式碼
在 new.js 中我們就可以通過 process.argv[2]
來拿到命令列的引數,來進一步編寫指令碼。