使用 Make 構建網站
原文 http://www.ruanyifeng.com/blog/2015/03/build-website-with-make.html
網站開發正變得越來越專業,涉及到各種各樣的工具和流程,迫切需要構建自動化。
所謂"構建自動化",就是指使用構建工具,自動實現"從源碼到網頁"的開發流程。這有利于提高開發效率、改善代碼質量。
本文介紹如何使用make命令,作為網站的構建工具。以下內容既是make語法的實例,也是網站構建的實戰教程。你完全可以將代碼略作修改,拷貝到自己的項目。
(題圖:國家考古博物館,西班牙,攝于2014年8月)
一、Make的優點
首先解釋一下,為什么要用Make。
目前,網站項目(尤其是Node.js項目)有三種構建方案。
- 方案一:基于Node.js的專用構建工具( Grunt 、 Gulp 、 Brunch 、 Broccoli 、 Mimosa )
- 方案二:npm run命令( 教程1 、 2 、 3 )
- 方案三:make命令
我覺得,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變量重新賦值為,優先在 node modules/.bin 目錄尋找命令。這是因為(當前項目的)node模塊,會在 node modules/.bin 目錄設置一個符號鏈接。PATH變量指向這個目錄以后,調用各種命令就不用寫路徑了。比如,調用JSHint,就不用寫 ~/node_modules/.bin/jshint ,只寫 jshint 就行了。
SHELL變量指定構建環境使用BASH。
四、檢查語法錯誤
第一個任務是,檢查源碼有沒有語法錯誤。
js_files = $(shell find ./lib -name '*.js')lint: $(js_files) jshint $?</pre>
上面代碼中,shell函數調用find命令,找出lib目錄下所有js文件,保存在變量js_files。然后,就可以用jshint檢查這些文件。
使用時調用下面的命令。
$ make lint
五、模板編譯
第二個任務是編譯模板。假定模板都在templates目錄,需要編譯為build目錄下的templates.js文件。
build/templates.js: templates/*.handlebars mkdir -p $(dir $@) handlebars templates/*.handlebars > $@ template: build/templates.js上面代碼查看build目錄是否存在,如果不存在就新建一個。dir函數用于取出構建目標的路徑名(build),內置變量$@代表構建目標(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 <span>$@</span>) $< coffee: $(build_files)</pre>
上面代碼中,首先獲取所有的Coffee腳本文件,存放在變量source files,函數wildcard用來擴展通配符。然后,將變量source files中的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) </span></pre>
使用時調用下面的命令。
$ make concat
八、壓縮JavaScript腳本
將所有JavaScript腳本,壓縮為build目錄下的app.js。
app_bundle := build/app.js $(app_bundle): $(build_files) $(template_js) uglifyjs -cmo $@ $^ min: $(app_bundle)使用時調用下面的命令。
$ make min
還有另一種寫法,可以另行指定壓縮工具。
UGLIFY ?= uglify $(app_bundle): $(build_files) $(template_js) $(UGLIFY) -cmo $@ $^上面代碼將壓縮工具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-cssbuild-js: browserify -t brfs src/app.js > site/app.js
build-css: stylus src/style.styl > site/style.css
.PHONY build-js build-css</pre>
十四、參考鏈接
- 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
(完)
</div> </div>