もしあなたがRubyプログラマーなら、Rake(故Jim Weirichによって作られたビルドユーティリティー)をほぼ確実に使ったことがあるだろう。しかし、Rakeがいかにパワフルでフレキシブルなツールになりうるかということは理解していないかもしれない。実際、私は、Quarto(自作のe-book製作ツールチェーン)の土台としてRakeを使うと決めるまで理解していなかった。
この投稿はRakeについてのシリーズの一部で、基本から始めて高度な使用法に進む。2013年の8月から9月に購読者に向け公開されたRubyTapasビデオのシリーズのひとつをもとにしている。各投稿はビデオから始まり、ビデオより文字を好む人のためにスクリプトが続く。
これらのエピソードを無料で公開するにあたり、より多くの人々が、この広く行き渡っているが過小評価されているツールのすべての能力を知り愛するようになることを願う。もしあなたがRakeに感謝するのであれば、Jimへの追悼としてthe Weirich Fundへの寄付を検討して欲しい。
このRakeの連続シリーズは終わりに向かって進んでいます。前回までのいくつかのビデオを通して、私がそうであったように、Rakeのパワーのすごさが分かってくるようになってきたと思います。しかしおそらく、素のRubyやシェルスクリプトを利用することに対するRakeを使用することの利点については、まだ懐疑的でしょう。もしそうであれば、今日がその印象を変える日だと思います。Rakeが、ただ同然で与えてくれる驚くほどパワフルな能力をお見せしたいと思います。
電子書籍を作るとします。
pygmentize
ユーティリティを使ってシンタックスハイライトされたHTMLに変換するために準備した、テキストを取り除いた数百のコード目録が含まれたディレクトリがあります。これは、このタスクを行う小さなRakefileです。目録のリスト、“highlights”のリスト(これは、HTMLの最終成果物です)、そして
highlight
と呼ばれるすべてを生成するタスクを定義しています。最後に、pygmentize
を実行して目録ファイルから.html
ファイルを生成するルールを定義しています。また、highlight
タスクに依存するデフォルトタスクも定義しています。
require "rake/clean"
task :default => :highlight
LISTINGS = FileList["listings/*"]
HIGHLIGHTS = LISTINGS.ext(".html")
CLEAN.include(HIGHLIGHTS)
task :highlight => HIGHLIGHTS
rule ".html" => ->(f){ FileList[f.ext(".*")].first } do |t|
sh "pygmentize -o #{t.name} #{t.source}"
end
pygmentize
タスクを使ったソースコードのハイライトは時間が掛かります。たくさんのソースファイルがある場合、多くの時間が掛かります。もしrakeをtime
コマンドの下で実行すると、そのプロセスに48秒かかることがわかります。
$ time rake
...
pygmentize -o listings/fd673484d50a66ea67fcd20e0c55f038a729e4d7.html listings/fd673484d50a66ea67fcd20e0c55f038a729e4d7.rb
pygmentize -o listings/ff6e24090e794c4db847b10ca993c872ca804101.html listings/ff6e24090e794c4db847b10ca993c872ca804101.rb
real 0m47.961s
user 0m41.912s
sys 0m4.852s
今のところ、これらのハイライトされたファイルは1つずつビルドされています。しかし、今は2013年で、2つの物理コアが載ったコンピュータを私は持っていて、ハイパースレッディングにより4つの仮想コアがあることになります。どうして2つ以上のファイルを同時にビルドできないのでしょうか?
それは実際に可能です。我々がすべきことは、rakefileの1行を
task
からmultitask
に変えるだけです。
multitask :highlight => HIGHLIGHTS
これは、
:highlight
タスクの事前条件を並行に処理できることをRakeに告げます。並列化したいタスクそのものではなく、並列化したいタスクに依存するタスクにこの変更を行っている点に注意してください。Rakeをもう一度実行します。Rakeが数百のRakeサブプロセスを同時に並列で起動し、それらが同じ
STDOUT
に出力を行うため、かなりみだれた出力になります。25秒を少し超えてたところで、ビルドが完了します。この1つの変更により、処理時間をおよそ半分にカットしました!
$ time rake
...
real 0m25.701s
user 1m13.492s
sys 0m8.272s
何個のタスクを並列で実行するかを微調整したい場合は、同時に実行できるプロセスの最大数をRakeに告げるために
-j
オプションを使用できます。各仮想コアにたいして1つ、すなわち4を指定してみます。興味深いことに、今度はほんの少し時間がかかります。どうしてかはよく分かりません。
$ time rake -j 4
real 0m26.752s
user 1m10.300s
sys 0m7.208s
先程述べたように、並列に実行するにはコードに1行変更を加えればよいといいましたが、それは少し嘘です。本当は、コードにまったく変更を加えずにタスクを並列に実行するよう、Rakeに命令することができます。
multitask
をtask
に戻して、-m
オプションつきでrake
を実行します。これは、すべてのタスクをmultitask
として扱うよう、Rakeに告げます。再び、歪んだ出力が確認できます。そして一段落すると、合計時間が25秒超であることが再び確認できます。
$ time rake -m
real 0m25.606s
user 1m13.060s
sys 0m7.856s
Rakeの並列化は賢くもあります。もし他のタスクが
:highlight
タスクに依存していれば、次のフェーズに進む前に、すべてのpygmentize
プロセスが完了するまで待機します。では、Rakeを使ったビルドの自動化から得たものは何でしょう?タスクを達成するための複雑な依存関係やルールを宣言するための簡単な方法だけではありません。ファイル操作のための便利なメソッドのセットだけではありません。扱いやすいコマンドラインのフロントエンドだけではありません。それらすべてに加え、繰り返し行われるタスクの並列化がただで手に入ります。そして、それが私の言うハッピーハッキングです!
Rakeについてのエピソード・文章を楽しんでいただけたことを願っている。もし今日、何かしら学んだのであれば、Jimの遺産である教育プログラムの継続を支援するため、the Weirich Fundに寄付することにより「恩送りすること(訳注:原文は"paying it forward")」を検討して欲しい。もし、今回のようなビデオをもっと見たければ、RubyTapasをじっくり見てほしい。シリーズが完結するまで、ほぼ毎日公開していくつもりなので、ほどなくご確認を!
P.S. 特に興味深い方法でRakeを使っているなら、連絡して欲しい。
関連する投稿:
0 件のコメント:
コメントを投稿