Rakeとdry run

Rakeに-nオプションを指定するとdry runできる。ただ、dry runと言ってもタスクの実行予定が表示されるだけなので、いまいち使いどころがない。

$ cat Rakefile
task :example do
  sh 'ls', '-ld', '1'
end
$ rake -n example
** Invoke example (first_time)
** Execute (dry run) example

ところで、Rakeが提供するshメソッドや、Rakeが拡張しているmkdirなどのFileUtils由来のメソッドは、nowriteの値により動作を変える。

とはいうものの、nowriteの値はrakeコマンドの-nオプションに従って設定される。上述の通り-nオプションを指定するとタスクが実行されなくなるため、タスク定義の上ではnowriteそのものにはあまり意味がない。

$ cat Rakefile
task :example do
  p nowrite ? 'nowrite' : 'write'
end
$ rake
"write"
$ rake -n
** Invoke example (first_time)
** Execute (dry run) example

ただしnowriteメソッドに引数としてtrueまたはfalseを指定すると、nowriteの値を変更できる。また、その際にブロックが与えられると、そのブロック内でのみnowriteの値を変えることができる。

たとえば以下の内容のRakefileを作る。

task :example do
  sh 'ls', '-ld', '1'
  nowrite(true) do
    sh 'ls', '-ld', '2'
  end
  sh 'ls', '-ld', '3'
end

rakeコマンドを実行すると、次のようにls -ld 2だけが実行されないのが分かる。

$ rake example -f r 
ls -ld 1
-rw-r--r--  1 akira  501  0  1 14 13:44 1
ls -ld 2
ls -ld 3
-rw-r--r--  1 akira  501  0  1 14 13:44 3

これを利用して、次のようなタスク定義をすると、Rakeによるコマンド実行内容を知ることが可能となる。

task :example do
  sh 'ls', '-ld', '1'
end

task :dry_run do
  nowrite(true) { Rake::Task[:example].invoke }
end

タスクdry_runnowrite(true)の中でタスクexampleを実行しているので、タスクexample内での実行内容を表示するだけで終了する。

$ rake dry_run
ls -ld 1
$ rake example
ls -ld 1
-rw-r--r--  1 akira  501  0  1 14 13:44 1

同じようなことを他のタスクでも行いたければ、以下のようなdry_runメソッドを用意しておくとよいかもしれない。

def dry_run(task = nil)
  verbose(true) do
    nowrite(true) do
      return yield unless task
      Rake::Task[task.scope.path].invoke
    end
  end
end

namespace :example do
  task :dry_run do |t|
    dry_run(t)
  end
end

dry_runメソッドは、与えられたタスクの名前からネームスペース(scope)を取得し、その名前と同じ名前のタスクをnowrite(true)の中で実行する。

verbosenowriteとよくにたメソッドで、rakeコマンドの-vオプションまたは-qオプションに従った値を返す。nowriteと同じように引数にtrueまたはfalseをとり、その値を変更できる。ここでは、dry runのためのタスク実行で、実行内容を常に表示させるために使っている。

なお、verboseについては少し注意を要する。rakeコマンドに-vオプションも-qオプションも指定しないとき、verboseの値はObjectオブジェクトになる。このため、以下のコードは予想と違った結果になるかもしれない。

task :verbose do
  puts 'verbose' if verbose
end

実行結果は次の通りとなる。

$ rake verbose -v
verbose
$ rake verbose -q
$ rake verbose
verbose

verboseのデフォルトの値が、なぜObjectオブジェクトなのかは分からないが、-vオプションが指定されたときだけメッセージを表示する、といったときには次のようにする。

task :verbose do
  puts 'verbose' if verbose == true
end