Rake 我就不再介紹了,Ruby 的 Make ,許多方面都比 Make 要更好用一些。和 Makefile 不同的是,Rakefile 本身其實就是一段 Ruby 代碼,這樣的好處有很多,一方面在 Rake 里面就可以很直接地做任何 Ruby 能做的事了,另一方面由于 Ruby 對 DSL 支持良好,所以 Rakefile 通常看起來也并不那么“代碼”。
不過,代碼始終是代碼,Makefile 尚且可以寫得很亂,Rakefile 要寫亂就更容易了,幸運地是,Rake 提供了一些功能讓我們可以來對 Rakefile 做一些組織工作。
其中之一就是 import 功能,把不同功能的 task 寫到不同的文件中,例如,像這個樣子:
復制代碼 代碼如下:
Rakefile
task/
+-- doc.rake
+-- compile.rake
`-- deploy.rake
這樣,在 Rakefile 里寫上
復制代碼 代碼如下:
import("task/doc.rake")
這樣的語句導入各個子任務即可,不同的任務寫到不同的文件里面就不會一團糟了。而且,import 同 Ruby 自己的 require 不一樣,import 并不是立即進行導入的,而是在整個 Rakefile 執行結束之后才全部導入,因此,可以在任意的地方寫 import ,而不用擔心依賴關系,需要共享的變量之類的只要在主 Rakefile 中定義了即可。
import 是組織不同的功能模塊,除此之外,Rake 還允許我們對一些重復性的任務進行抽象,具體來說,就是自定義的 task 。通常情況下,我們使用 Rake 提供的通用 task 和文件 task 來構造我們需要完成的工作,除此之外,Rake 還自帶了一些針對特殊任務的 task 類型,例如構建 rdoc 或者運行 test 等。實際上,一種任務就是一個普通的 Ruby 類,我們可以繼承 Rake 里的 Task 類并重新定義相關的函數來實現自定義的 task 類型。不過,這樣多少有些麻煩,實際上,很多時候我們要定義的任務都可以分解為一些小任務用內置的通用 task 和 file task 來實現的,這個時候可以用 Tasklib 來更方便地定義自定義的任務。
具體地來說,就是寫一個類,繼承自 Tasklib (雖然實際上只是約定而并不是必須的),然后在這個類的初始化函數里用 task 或者 file 來定義實際完成任務的子 task 即可。用一個實際的例子來說,比如說,我們可以定義一個 ErlcTask ,可以用來把一些 Erlang 文件編譯到某個目錄下,并在 clean 的時候自動能把編譯出來的 .beam 文件清理掉:
復制代碼 代碼如下:
require 'rake'
require 'rake/clean'
require 'rake/tasklib'
class ErlcTask Rake::TaskLib
attr_accessor :name
attr_accessor :sources
attr_accessor :dest_dir
attr_accessor :include_path
attr_accessor :flags
attr_accessor :extra_dep
def initialize(name = :erlc)
# default values
if name.is_a? Hash
@name = name.keys.first
@extra_dep = name.values.first
else
@name = name
@extra_dep = []
end
@sources = FileList[]
@dest_dir = '.'
@include_path = []
@flags = "-W +warn_unused_vars +warn_unused_import"
yield self if block_given?
define
end
def define
beams = @sources.pathmap(File.join(@dest_dir, '%n.beam'))
include_path = Array(@include_path).map{|incl|"-I"+incl}.join(" ")
directory @dest_dir
beams.zip(@sources).each do |beam, source|
file beam => source do
sh "erlc -pa #{@dest_dir} #{@flags} #{include_path} -o #{@dest_dir} #{source}"
end
end
task @name => beams + Array(@extra_dep)
CLEAN.include(beams)
end
end
首先定義一些 Task 相關的屬性,在初始化函數里設置初值,然后調用 block 來填充實際的值,最后調用 define 函數,define 函數就使用 directory 、file 和 task 分別定義了建立目錄、編譯和清理的任務。如果了解 Ruby 和 Rake 的基本語法的話,應該很容易看明白了。
接下來把這個文件保存到某個 .rb 里,然后在 Rakefile 里 require 之,就可以這樣寫了:
復制代碼 代碼如下:
ErlcTask.new :compile do |t|
t.sources = FileList['src/*.erl']
t.dest_dir = '../ebin'
t.include_path = '../include'
t.extra_dep = :library
end
看起來就清爽多了!并且可以重復利用。 末了,順便再感嘆一下,雖然最近都是用 Python 用得多一些,但是每次再寫 Ruby 都能感覺到寫起來很舒服,這是基本不可能在 Python 里找到的感覺啊!