Element-UI 中 Make 自動化構建分析

閒不住的李先森發表於2019-02-13

在習慣了使用 package.json 中的 src 來書寫構建命令之後,看到了 Element-ui 的程式碼倉庫中有 Makefile 這樣一個檔案。於是 Google 一下,發現了這個東西的強大。

Makefile 是一個適用於 C/C++ 的工具,較早作為工程化工具出現在 UNIX 系統中, 通過 make 命令來執行一系列的編譯和連線操作。在擁有 make 環境的目錄下, 如果存在一個 Makefile 檔案。 那麼輸入 make 命令將會執行 Makefile 檔案中的某個目標命令。

初識 Makefile

直接用 Element-UIMakefile 檔案作為例子來做演示。

.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 做了什麼。

  1. 執行 make 命令, 在該目錄下找到 Makefile 檔案。
  2. 找到 Makefile 檔案中對應命令列引數的 install 的目標。這裡的目標就是 install:
  3. 執行 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] 來拿到命令列的引數,來進一步編寫指令碼。

相關文章