Rakeの初歩について書こうと思います。 1回のブログでは難しいので、4回シリーズで書きます。 その内容はそれぞれ、タスク、ファイルタスク(前半)、ファイルタスク(後半)、応用例です。
文中に[R]という記号で始まる段落は、「Ruby上級者向けの解説」です。 上級とは、ほぼ「クラスを記述できるレベル」を指します。 上級以外の方はこの部分を飛ばしてください。
(2022/7/30追記) Githubに「はじめてのRake」をアップロードしました。 このブログの内容を更に発展させたチュートリアルです。
Github Pagesもあります。
Rakeのインストール
RakeはRubyのアプリケーションです。 RubyのインストールについてはRubyの公式ホームページを参考にしてください。 Rakeは「Rubyの標準添付ライブラリ」なので、インストールされたRubyの中に含まれることが多いですが、もしそうでないときは、
- Linuxディストリビューションのrubyパッケージをインストールした場合=>パッケージのrakeをインストール
- 別の方法でインストールした場合=>コマンドから「gem install rake」でインストール
などの方法でインストールしてください。
Rakeとは?
Rakeは、Makeと同様の機能を、Rubyプログラムとして実装したアプリケーションです。
Makeは、Cでコンパイルするときに、コンパイル過程全体をコントロールするプログラムです。 しかし、MakeはC専用ではなく、いろいろなコンパイラやトランスレータ(ある形式から別の形式に変換するプログラム)を制御することができます。 便利なMakeですが、その文法はマニアックです。 初歩的な使い方をしているうちは、分かりやすいのですが、使えば使うほど分かりにくくなっていきます。 例えばこんな感じです。
application: $(OBJS) $(CC) -o $(@F) $(OBJS) $(LIBS) $(OBJS): %.o: %.c $(HEADER) $(CC) -fPIC -c -o $(@F) $(CFLAGS) $<
それに対してRakeは
- Rubyの文法をそのまま使うことができる
- したがって、分かりやすく柔軟な書き方ができる
という利点があります。
Rakeの基本
まず、コマンドのrake
とカレントディレクトリに置くRakefile
というファイルがポイントになります。
rakeコマンドは引数にタスク名(タスクは後で説明します)をとり、
$ rake hello
の形で使います。
この例では、引数hello
はタスク名です。
このときrake
は次のことを順に実行します。
「Rakefileにタスクの定義を書く」ことが、rakeを使う際のポイントになります。 当然、コマンドラインから呼び出されるタスクはRakefileの中で定義されていなければなりません。
[R] 「タスクの定義を書く」「タスクを定義する」などは、Rubyのドキュメントで使われている言い回しです。 Rubyに熟練している人は、これが具体的に何を意味するか気になるかもしれません。 Ruby的には「Taskクラスのインスタンスを生成する」ことを意味します。
Rakefileでのタスク定義
タスクはオブジェクトで、名前、事前タスク(前提条件)、アクションを持ってますが、事前タスクとアクションは無くても構いません。
それでは、まず名前だけを持っているタスクを作成してみましょう。 Rakefileに次のように書き込みます。
task :simple_task
task
はタスクを定義するためのコマンド(命令)だと考えてください。
一般に「コマンド」はプログラム言語において、コンピュータに何かをさせるためのものです。
例えば、Shellでは、「cd」はカレント・ディレクトリを移動する「コマンド」です。
「cd /var」によって、カレントディレクトリが「/var」に移動しますが、それは「/var
引数を与えてcd
コマンドを実行した」ことの結果なのです。
同様に「task」コマンドには引数「:simple_task」が与えられています。
そして「taskコマンドを実行することにより、simple_taskを名前とするタスクが作成される」のです。
なお、引数の:simple_task
はシンボルですが、文字列を使っても構いません。
task "simple_task"
両者に対してtaskコマンドが行う動作は全く同じです。
実は、taskコマンドは、Rubyの文法から見ると、taskメソッドの呼び出しで、:simple_task
はtaskメソッドへの引数です。
ですので、今後はtaskを「コマンド」あるいは「メソッド」ということがありますが、
- 「コマンド」は、taskの「タスク作成」機能に注目している場合
- 「メソッド」は、Rubyの文法上の機能に注目している場合
で使い分けをしています。 細かいことになるので、あまり気にしなくても構いません。
[R] Rubyの文法から見た場合、
task
コマンドは「メソッド呼び出し」で、:simple_task
はtaskメソッドの引数です。 Rubyではメソッド呼び出しの引数にカッコを付けても付けなくても良いのでこのように書けるのです。 もしカッコを付けるのならば、task("simple_task")
となります。 (taskとカッコの間にはスペースを入れない)。 どちらでも定義できますが、カッコ無しを用いるのが良いです。
「タスクを定義する」とは「Taskクラスのインスタンスを生成する」ことです。 インスタンスの生成には通常newメソッドが使われますが、Task.newよりもtaskメソッドの方が便利です。 なお、taskメソッドでは、その実行の中で「Task.new」が呼び出され、タスクのインスタンスが生成される仕組みになっています。
タスク「simple_task」には事前タスクとアクションは定義されていません。
コマンドラインからタスクを実行してみましょう。
$ rake simple_task
タスクは呼び出されているのですが、アクションが無いため、見た目には何も起こりません。 タスクが定義できているかどうかは、次のようにするとわかります。
$ rake -AT rake simple_task #
オプションATは登録されているすべてのタスクを表示します。 これで、simple_taskが定義されていることがわかりました。
アクション
アクションは、taskメソッド呼び出しのブロックで表します。
task :hello do print "Hello world!\n" end
このタスクはhello
という名前です。
helloには事前タスクはありません。
アクションは「Hello world!」と画面表示する、というものです。
では、このタスクを実行してみましょう。
$ rake hello Hello world!
タスクhelloが呼び出され、そのアクションが実行されて「Hello world!」の文字列が表示されました。
[R] Rubyにはブロックを(1)波カッコ(
{
と}
)で表す(2)do
とend
で表す、の2つの方法があります。 Rakefileではどちらも動作しますが、読みやすさの点からdo
とend
を使うのが良いでしょう。 また、波カッコを使う場合、次のように書くと動作しません。task :hello {print "Hello world!\n"}
これは、do-endより波カッコの方が強く結合するために起こるエラーです。 Rubyのドキュメントが参考になるので見てください。 これを解消するには、引数にカッコをつけます。
task(:hello) {print "Hello world!\n"}
Rakeでは、「taskがあたかもコマンドであるかのように表現したい」ということがあります。 波カッコを使うとそれができませんから、動作はするけれども推奨はできないのです。
RakeはRuby文法の自由さ(引数のカッコを省略できるなど)を使って、taskなどのコマンドを提供しています。 このように、特定の分野のために作られたコマンドをもつ言語を「DSL(Domain-Specific Language)」といいます。 do-end推奨の背景にはDSLの考え方があります。
事前タスク
あるタスクが事前タスクを持っている場合、そのタスクが呼び出され(実行され)る前に事前タスクを呼び出します。
タスクの定義は
task タスク名 => 事前タスク(の配列)do アクション end
のようになります。
「タスク名=>事前タスク(の配列)」のところは、Rubyのハッシュです。
メソッド呼出の末尾にハッシュを渡す場合は カッコ({
と}
) を省略することができます。
省略しなければ「{タスク名 => 事前タスク(の配列)}」となりますが、これでも動作します。
また、タスク名がシンボルの場合、例えば「:abc => "def"」と書くのを「abc: "def"」と書くことができます。 同様に、「:abc => :def」と「abc: :def」は同じです。
次の例では、firstとsecondという2つのタスクがあり、firstがsecondの事前タスクになっています。
task second: :first do print "Second.\n" end task :first do print "First.\n" end
タスクsecondを呼び出すと、事前タスクであるfirstがその前に呼び出されます。
firstを実行 => secondを実行
という順になります。
$ rake second First. Second.
Rakefileの例
歌川さんの味玉のレシピをMakefileで記述するが面白かったので、そのRake版を作ってみました。
# 味玉をつくる task :お湯を湧かす do print "お湯を湧かします\n" end task 卵を茹でる: :お湯を湧かす do print "卵を茹でます\n" end task :'8分待つ' => :卵を茹でる do print "8分待ちます\n" end task ボウルに氷を入れる: :'8分待つ' do print "ボウルに氷を入れます\n" end task ボウルに水を入れる: :ボウルに氷を入れる do print "ボウルに水を入れます\n" end task ボウルに卵を入れる: :ボウルに水を入れる do print "ボウルに卵を入れます\n" end task 卵の殻を剥く: :ボウルに卵を入れる do print "卵の殻を剥きます\n" end task :ジップロックに日付を書く do print "ジップロックに日付を書きます\n" end task ジップロックにめんつゆを入れる: [:ジップロックに日付を書く, :卵の殻を剥く] do print "ジップロックにめんつゆを入れます\n" end task ジップロックに卵を入れる: :ジップロックにめんつゆを入れる do print "ジップロックに卵を入れます\n" end task 一晩寝かせる: :ジップロックに卵を入れる do print "一晩寝かせます\n" end task 味玉: :一晩寝かせる do print "味玉ができました\n" end
実行してみます。
$ rake 味玉 ジップロックに日付を書きます お湯を湧かします 卵を茹でます 8分待ちます ボウルに氷を入れます ボウルに水を入れます ボウルに卵を入れます 卵の殻を剥きます ジップロックにめんつゆを入れます ジップロックに卵を入れます 一晩寝かせます 味玉ができました
タスクの呼び出しは一度だけ
すでに呼び出されたタスクは実行されません。 つまり「タスクの実行は1度だけ」です。
例えば、味玉のRakefileで
task ジップロックに卵を入れる: :ジップロックにめんつゆを入れる do
のところを
task ジップロックに卵を入れる: [:ジップロックにめんつゆを入れる, :卵の殻を剥く] do
とすると、「卵の殻を剥く」が2箇所で事前タスクになります。 呼び出しが2回ありますが、実行は1回だけなので、実行結果は同じになります。
[R] タスクのインスタンス・メソッドに「invoke」(呼び出し)と「execute」(実行)があります。 invokeはアクションを一度だけ実行しますが、executeはそのメソッドが呼ばれた回数だけ何度でも実行します。 それで、Rubyのドキュメントでは「呼び出し」と「実行」の2つの言葉を区別して使っているようです。 このブログでは使い分けが曖昧な箇所がありますが、大きな混乱はないと思っています。 なお、invokeは自身のタスクを呼び出す前に事前タスクを呼び出しますが、executeは事前タスクを呼び出しません。
タスク名には文字列も使える
今までタスク名にシンボルを使ってきましたが、文字列を使うこともできます。
task "simple_task" task "second" => "first"
このような書き方も可能です。 シンボルでハッシュを記述するには「{abc: :def}」のような書き方ができますが、シンボルの最初に数字がくるときにはこれが使えません。 「{0abc: :def}」や「{abc: :2def}」はシンタックス・エラーになります。 「{:'0abc' => :def}」「{abc: :'2def'}」のように書かなければなりません。 文字列ではこのような心配がなく、シングルあるいはダブルクォートで囲めばエラーにはなりません。
慣例としては
task abc: %w[def ghi]
の書き方が多く用いられるようです。
%wは空白で区切られた文字列の配列を返します。
%w[def ghi]
と["def", "ghi"]
は同じです。
Rubyのドキュメントの%記法を参考にしてください。
味玉の例で%記法を使うと
task ジップロックにめんつゆを入れる: %w[ジップロックに日付を書く 卵の殻を剥く] do
となります。