RakeFileUtils の :noop, :verbose オプション
!注意!現在のRake(0.8.1.1)には、この記事のとおりにやっても一部うまく行かない問題があります。修正は後ほど公開します。id:ke-k:20080212:rakefileutils_fix で公開しています。
前にも書きましたが、Rakeではモジュール FileUtils(正確にはRakeFileUtils) がincludeされています。 FileUtils には、cp, mkdir, mv 等々のメソッドが定義されており、
file 'some-directory' do |t| mkdir t.name end
のようにシェルのコマンドのように各メソッドを使うことができます。FileUtils について詳しくは以下のページを参照してください。
- http://www.ruby-lang.org/ja/man/html/fileutils.html : fileutils - Rubyリファレンスマニュアル
また、Rakeではメソッド sh, ruby も定義されており、シェルコマンドの実行やrubyコマンドの呼び出しも簡単にできるようになっています。
task :hoge do |t| sh 'echo hoge' ruby '-e', 'puts "hoge"' end
これらのメソッドには、オプションを指定することができます。
mkdir t.name, :verbose => true, :noop => true
ほとんどのメソッドは、:noop, :verbose を指定できるようになっています。
- :noop
- コマンドを実際には実行しない
- :verbose
- コマンドを(デフォルトでは標準エラー出力に)出力する
cd 等の実行されないとその後のコマンドに影響を与えるコマンドには :noop オプションは無いようです。オプションについて詳しくはマニュアルに書いてあります。あと、コマンドごとに受け付けるオプションの一覧は、 FileUtils::OPT_TABLE にハッシュで入っているようです。
ただ、 :noop, :verbose などを実行するのは通常コマンドの実行内容を確認したいときであり、全体もしくは部分で指定できるとうれしいと思います。これを実現するのが、モジュール RakeFileUtils です。 FileUtils をincludeしており、以下の部分でラッパを生成しています。
module RakeFileUtils # ... FileUtils::OPT_TABLE.each do |name, opts| default_options = [] if opts.include?('verbose') default_options << ':verbose => RakeFileUtils.verbose_flag' end if opts.include?('noop') default_options << ':noop => RakeFileUtils.nowrite_flag' end next if default_options.empty? module_eval(<<-EOS, __FILE__, __LINE__ + 1) def #{name}( *args, &block ) super( *rake_merge_option(args, #{default_options.join(', ')} ), &block) end EOS end
前述のとおり、 FileUtils::OPT_TABLE には、各メソッドとそのメソッドが受け付けるオプションが入っています。これを見て、 :noop と :verbose を受け付けるメソッドには、デフォルト値を簡単に変更できるように、module_eval で同名のラッパメソッドを生成しているのが上記の部分です。デフォルト値は、 verbose, nowrite の各メソッドで設定します。
noop = nowrite # 現在の :noop デフォルト値を取得 v = verbose # 現在の :verbose デフォルト値を取得
noop = nowrite(true) # :noop デフォルト値を設定、noop には設定前のデフォルト値が入る v = verbose(true) # :verbose デフォルト値を設定、v には設定前のデフォルト値が入る sh "gcc hoge.c -o hoge" # sh "gcc hoge.c -o hoge", :noop => true, :verbose => true と同じ。コマンドが出力され、実際には実行されない verbose(v) # 元に戻す nowrite(noop) # 元に戻す
また、範囲で実行する場合には、ブロックで指定することもできます。
nowrite(true) do verbose(true) do sh "gcc hoge.c -o hoge" end end
ブロックを抜けると、もとの値に戻ります。例外が起きた場合もちゃんと戻してくれます。
rake に --dry-run を付けて実行したときには、
module Rake # ... class Application # ... # Do the option defined by +opt+ and +value+. def do_option(opt, value) case opt # ... when '--dry-run' verbose(true) nowrite(true) options.dryrun = true options.trace = true
verbose(true), nowrite(true) が実行されています。 ただ、 options.dryrun = true もセットされているので、
module Rake # ... class Task # ... # Execute the actions associated with this task. def execute(args) if application.options.dryrun puts "** Execute (dry run) #{name}" return end ...
タスクの実行の部分も "** Execute (dry run) #{name}" と表示されるだけで、タスクのブロックそのものが実行はされないのですが。実行コマンドの確認もしたいので、verbose(true), nowrite(true) はするけど、options.dryrun = true はしないオプションが欲しいです。現在は、確認するときだけRakefileの先頭で nowrite(true) を入れるのが現実的な解でしょうか。
--verbose オプションでは verbose(true) が、 --quiet オプションでは verbose(false) が実行されてます。
さて、ここまで書いてきておいてなんですが、現在のバージョンには nowrite, verbose によって設定されるデフォルト値が sh, ruby 以外のメソッドに適用されない問題があります。対策について後ほど書きます。id:ke-k:20080212:rakefileutils_fix で書いています。