もしあなたがRubyプログラマーなら、Rake(故Jim Weirichによって作られたビルドユーティリティー)をほぼ確実に使ったことがあるだろう。しかし、Rakeがいかにパワフルでフレキシブルなツールになりうるかということは理解していないかもしれない。実際、私は、Quarto(自作のe-book製作ツールチェーン)の土台としてRakeを使うと決めるまで理解していなかった。
この投稿はRakeについてのシリーズの一部で、基本から始めて高度な使用法に進む。2013年の8月から9月に購読者に向け公開されたRubyTapasビデオのシリーズのひとつをもとにしている。各投稿はビデオから始まり、ビデオより文字を好む人のためにスクリプトが続く。
これらのエピソードを無料で公開するにあたり、より多くの人々が、この広く行き渡っているが過小評価されているツールのすべての能力を知り愛するようになることを願う。もしあなたがRakeに感謝するのであれば、Jimへの追悼としてthe Weirich Fundへの寄付を検討して欲しい。
これが前回までのいくつかのエピソードで使用したRakefileです。プロジェクトの“sources”サブディレクトリの中にMarkdownソースファイルがあり、“outputs”サブディレクトリの中に、並列の階層構造をなすHTMLファイルを生成します。
SOURCE_FILES = Rake::FileList.new("sources/**/*.md", "sources/**/*.markdown") do |fl|
fl.exclude("**/~*")
fl.exclude(/^scratch\//)
fl.exclude do |f|
`git ls-files #{f}`.empty?
end
end
task :default => :html
task :html => SOURCE_FILES.pathmap("%{^sources/,outputs/}X.html")
rule ".html" => ->(f){source_for_html(f)} do |t|
sh "pandoc -o #{t.name} #{t.source}"
end
def source_for_html(html_file)
SOURCE_FILES.detect{|f| f.ext('') == html_file.ext('')}
end
“outputs”ディレクトリの中に入力ファイルの階層構造を再生成しているので、HTMLファイルを生成する前に、目的のディレクトリが存在することを保証する必要があります。Rakeでこれを実現する簡単な方法は、directoryタスクを使用することです。これはディレクトリを対象としていることを除けばfileタスクと似ていますが、fileタスクとは異なり、存在しない場合にディレクトリを作成する方法をコードとして与える必要がありません。単にタスクを定義することにより、必要ならばディレクトリを作成するようRakeに暗黙的に指示します。
“.html”ルールに対して、このディレクトリを依存関係のリストに追加します。
rule ".html" => [->(f){source_for_html(f)}, "outputs"] do |t|
sh "pandoc -o #{t.name} #{t.source}"
end
rake
を実行すると、HTMLファイルを生成する前にディレクトリが作成されることが確認できます。しかし、残念ながら、appendix.html
ファイルをビルドしようとする時に問題に遭遇します。このファイルはsourcesディレクトリのサブディレクトリの中にあるので、HTMLの出力ファイルは、outputs
ディレクトリの対応するサブディレクトリの中に出力して欲しいのです。しかし、このサブディレクトリはまだ存在しません。
t$ rake
mkdir -p outputs
pandoc -o outputs/backmatter/appendix.html sources/backmatter/appendix.md
pandoc: outputs/backmatter/appendix.html: openFile: does not exist (No such file or directory)
rake aborted!
Command failed with status (1): [pandoc -o outputs/backmatter/appendix.html...]
/home/avdi/Dropbox/rubytapas/133-rake-file-operations/project/Rakefile:16:in `block in <top (required)>'
Tasks: TOP => default => html => outputs/backmatter/appendix.html
(See full trace by running task with --trace)
(訳注:1行目の先頭のtは原文のタイポと思われる。)
HTMLファイルの出力前に、このディレクトリや他の中間ディレクトリが存在することを保証するには、対象ファイルのディレクトリ部だけを渡すために
#pathmap
使って、mkdir -p
シェルコマンドを実行することも可能です。
sh "mkdir -p #{t.name.pathmap('%d')}"
しかし、Rakeにはこのためのショートカットがあります。シェルコマンドを実行する代わりに、
mkdir_p
メソッドをタスクの中で使用することができます。
SOURCE_FILES = Rake::FileList.new("sources/**/*.md", "sources/**/*.markdown") do |fl|
fl.exclude("**/~*")
fl.exclude(/^sources\/scratch\//)
fl.exclude do |f|
`git ls-files #{f}`.empty?
end
end
task :default => :html
task :html => SOURCE_FILES.pathmap("%{^sources/,outputs/}X.html")
directory "outputs"
rule ".html" => [->(f){source_for_html(f)}, "outputs"] do |t|
mkdir_p t.name.pathmap("%d")
sh "pandoc -o #{t.name} #{t.source}"
end
def source_for_html(html_file)
SOURCE_FILES.detect{|f|
f.ext('') == html_file.pathmap("%{^outputs/,sources/}X")
}
end
ここでrakeを実行すると、markdownからHTMLへの変換されるたびに、前もって対象ディレクトリが存在することが保証されます。
$ rake
mkdir -p outputs/backmatter
pandoc -o outputs/backmatter/appendix.html sources/backmatter/appendix.md
mkdir -p outputs
pandoc -o outputs/ch1.html sources/ch1.md
mkdir -p outputs
pandoc -o outputs/ch2.html sources/ch2.md
mkdir -p outputs
pandoc -o outputs/ch3.html sources/ch3.md
mkdir -p outputs
pandoc -o outputs/ch4.html sources/ch4.markdown
ビルドスクリプトを書いているときに、素早くすべての生成されたファイルを消す簡単な方法があれば便利なことはよくあります。これに対処するタスクを追加してみましょう。ここでも、シェルコマンドを実行する代わりに、Rakeのヘルパーメソッドを使用します。これはシェルの
rm -rf
コマンドに対応し、いかなる警告や確認もせずに再帰的にファイルとディレクトリを削除します。
task :clean do
rm_rf "outputs"
end
Rakeにはこのようなフィル操作を行うヘルパーメソッドがたくさんあり、そのすべてがUNIXシェルの同等なコマンドにちなんで名づけられています。これは、いくつかの点で便利です。一例をあげると、それらはRuby自身のメソッドなので、文字列展開などをすることなく、ファイルやfile listを直接渡せます。
また、Rakeの“quiet”フラグも感知します。Rakeコマンドは
-q
フラグつきで実行すると、必要な動作はすべて行いますが、STDOUT
への出力は行いません。
$ rake -q
$
これらのヘルパーほとんどすべては、Rubyの標準ライブライである
FileUtils
から直接受け継がれています。ですから、使用可能なヘルパーすべてのリストを確認したければ、単にthe FileUtils documentationを確認してください。今日は以上です。ハッピーハッキング!
Rakeについてのエピソード・文章を楽しんでいただけたことを願っている。もし今日、何かしら学んだのであれば、Jimの遺産である教育プログラムの継続を支援するため、the Weirich Fundに寄付することにより「恩送りすること(訳注:原文は"paying it forward")」を検討して欲しい。もし、今回のようなビデオをもっと見たければ、RubyTapasをじっくり見てほしい。シリーズが完結するまで、ほぼ毎日公開していくつもりなので、ほどなくご確認を!
P.S. 特に興味深い方法でRakeを使っているなら、連絡して欲しい。
関連する投稿:
0 件のコメント:
コメントを投稿