網站開發正變得越來越專業,涉及到各種各樣的工具和流程,迫切需要構建自動化。
所謂"構建自動化",就是指使用構建工具,自動實現"從原始碼到網頁"的開發流程。這有利於提高開發效率、改善程式碼質量。
本文介紹如何使用make命令,作為網站的構建工具。以下內容既是make語法的例項,也是網站構建的實戰教程。你完全可以將程式碼略作修改,複製到自己的專案。
(題圖:國家考古博物館,西班牙,攝於2014年8月)
一、Make的優點
首先解釋一下,為什麼要用Make。
目前,網站專案(尤其是Node.js專案)有三種構建方案。
我覺得,make是大型專案的首選方案。npm run可以認為是make的簡化形式,只適用於簡單專案,而Grunt、Gulp那樣的工具,有很多問題。
(1)外掛問題
Grunt和Gulp的操作,都由外掛完成。即使是檔案改名這樣簡單的任務,都要寫外掛,相當麻煩。而Make是直接呼叫命令列,根本不用擔心找不到外掛。
(2)相容性問題
外掛的版本,必須與Grunt和Gulp的版本匹配,還必須與對應的命令列程式匹配。比如,grunt-contrib-jshint外掛現在是0.11.0版,對應Grunt 0.4.5版和JSHint 2.6.0版。萬一Grunt和JSHint升級,而外掛沒有升級,就有可能出現相容性問題。Make是直接呼叫JSHint,不存在這個問題。
(3)語法問題
Grunt和Gulp都有自己的語法,並不容易學,尤其是Grunt,語法很羅嗦,很難一眼看出來程式碼的意圖。當然,make也不容易學,但它有複用性,學會了還可以用在其他場合。
(4)功能問題
make已經使用了幾十年,全世界無數的大專案都用它構建,早就證明非常可靠,各種情況都有辦法解決,前人累積的經驗和資料也非常豐富。相比之下,Grunt和Gulp的歷史都不長,使用範圍有限,目前還沒有出現它們能做、而make做不到的任務。
基於以上理由,我看好make。
二、常見的構建任務
下面是一些常見的網站構建任務。
- 檢查語法
- 編譯模板
- 轉碼
- 合併
- 壓縮
- 測試
- 刪除
這些任務用到 JSHint、handlebars、CoffeeScript、uglifyjs、mocha 等工具。對應的package.json檔案如下。
"devDependencies": { "coffee-script": "~1.9.1", "handlebars": "~3.0.0", "jshint": "^2.6.3", "mocha": "~2.2.1", "uglify-js": "~2.4.17" }
我們來看看,Make 命令怎麼完成這些構建任務。
三、Makefile的通用配置
開始構建之前,要編寫Makefile檔案。它是make命令的配置檔案。所有任務的構建規則,都寫在這個檔案(參見《Make 命令教程》)。
首先,寫入兩行通用配置。
PATH := node_modules/.bin:$(PATH) SHELL := /bin/bash
上面程式碼的PATH和SHELL都是BASH變數。它們被重新賦值。
PATH變數重新賦值為,優先在 nodemodules/.bin 目錄尋找命令。這是因為(當前專案的)node模組,會在 nodemodules/.bin 目錄設定一個符號連結。PATH變數指向這個目錄以後,呼叫各種命令就不用寫路徑了。比如,呼叫JSHint,就不用寫 ~/node_modules/.bin/jshint ,只寫 jshint 就行了。
SHELL變數指定構建環境使用BASH。
四、檢查語法錯誤
第一個任務是,檢查原始碼有沒有語法錯誤。
js_files = $(shell find ./lib -name '*.js') lint: $(js_files) jshint $?
上面程式碼中,shell函式呼叫find命令,找出lib目錄下所有js檔案,儲存在變數js_files。然後,就可以用jshint檢查這些檔案。
使用時呼叫下面的命令。
$ make lint
五、模板編譯
第二個任務是編譯模板。假定模板都在templates目錄,需要編譯為build目錄下的templates.js檔案。
build/templates.js: templates/*.handlebars mkdir -p $(dir [email protected]) handlebars templates/*.handlebars > [email protected] template: build/templates.js
上面程式碼檢視build目錄是否存在,如果不存在就新建一個。dir函式用於取出構建目標的路徑名(build),內建變數[email protected]代表構建目標(build/templates.js)。
使用時呼叫下面的命令。
$ make template
六、Coffee指令碼轉碼
第三個任務是,將CofferScript指令碼轉為JavaScript指令碼。
source_files := $(wildcard lib/*.coffee) build_files := $(source_files:lib/%.coffee=build/%.js) build/%.js: lib/%.coffee coffee -co $(dir [email protected]) $< coffee: $(build_files)
上面程式碼中,首先獲取所有的Coffee指令碼檔案,存放在變數sourcefiles,函式wildcard用來擴充套件萬用字元。然後,將變數sourcefiles中的coffee檔名,替換成js檔名,即 lib/x.coffee 替換成 build/x.js 。
使用時呼叫下面的命令。
$ make coffee
七、合併檔案
使用cat命令,合併多個檔案。
JS_FILES := $(wildcard build/*.js) OUTPUT := build/bundle.js concat: $(JS_FILES) cat $^ > $(OUTPUT)
使用時呼叫下面的命令。
$ make concat
八、壓縮JavaScript指令碼
將所有JavaScript指令碼,壓縮為build目錄下的app.js。
app_bundle := build/app.js $(app_bundle): $(build_files) $(template_js) uglifyjs -cmo [email protected] $^ min: $(app_bundle)
使用時呼叫下面的命令。
$ make min
還有另一種寫法,可以另行指定壓縮工具。
UGLIFY ?= uglify $(app_bundle): $(build_files) $(template_js) $(UGLIFY) -cmo [email protected] $^
上面程式碼將壓縮工具uglify放在變數UGLIFY。注意,變數的賦值符是 ?= ,表示這個變數可以被命令列引數覆蓋。
呼叫時這樣寫。
$ make UGLIFY=node_modules/.bin/jsmin min
上面程式碼,將jsmin命令給變數UGLIFY,壓縮時就會使用jsmin命令。
九、刪除臨時檔案
構建結束前,刪除所有臨時檔案。
clean: rm -rf build
使用時呼叫下面的命令。
$ make clean
十、測試
假定測試工具是mocha,所有測試用例放在test目錄下。
test: $(app_bundle) $(test_js) mocha
當指令碼和測試用例都存在,上面程式碼就會執行mocha。
使用時呼叫下面的命令。
$ make test
十一、多工執行
構建過程需要一次性執行多個任務,可以指定一個多工目標。
build: template concat min clean
上面程式碼將build指定為執行模板編譯、檔案合併、指令碼壓縮、刪除臨時檔案四個任務。
使用時呼叫下面的命令。
$ make build
如果這行規則在Makefile的最前面,執行時可以省略目標名。
$ make
通常情況下,make一次執行一個任務。如果任務都是獨立的,互相沒有依賴關係,可以用引數 -j 指定同時執行多個任務。
$ make -j build
十二、宣告偽檔案
最後,為了防止目標名與現有檔案衝突,顯式宣告哪些目標是偽檔案。
.PHONY: lint template coffee concat min test clean build
十三、Makefile檔案示例
下面是兩個簡單的Makefile檔案,用來補充make命令的其他構建任務。
例項一。
PROJECT = "My Fancy Node.js project" all: install test server test: ;@echo "Testing ${PROJECT}....."; \ export NODE_PATH=.; \ ./node_modules/mocha/bin/mocha; install: ;@echo "Installing ${PROJECT}....."; \ npm install update: ;@echo "Updating ${PROJECT}....."; \ git pull --rebase; \ npm install clean : ; rm -rf node_modules .PHONY: test server install clean update
例項二。
all: build-js build-css build-js: browserify -t brfs src/app.js > site/app.js build-css: stylus src/style.styl > site/style.css .PHONY build-js build-css
十四、參考連結
- Jess Telford, Example using Makefile for cloverfield
- Oskar Schöldström, How to use Makefiles in your web projects
- James Coglan, Building JavaScript projects with Make
- Rob Ashton, The joy of make
(完)