Rakeを徹底解剖 - その2 "タスクの読みこみ"
前回に引き続き、rakeの仕組みを知るべく、ソースリーディングをして処理を解読していく。
前回では、init
の中身を調べて行ったが今回は、タスクの読み込みおよびタスクの定義に焦点を当てて、読み込んでいく。
## rake/application.rb def run standard_exception_handling do init # 初期化 load_rakefile # taskの読み込み <= ココ top_level # コマンドの実行 end end
まだ、その1を読んでいない方はこちらから。
タスクの読み込み
まずは、load_rakefile
を見てみる。
## rake/application.rb def load_rakefile standard_exception_handling do raw_load_rakefile end end
前回もでてきたExceptionをcatchするブロックが登場。
読み込み処理の肝心な部分は、raw_load_rakefile
のようだ。
## rake/application.rb def raw_load_rakefile # :nodoc: rakefile, location = find_rakefile_location if (! options.ignore_system) && (options.load_system || rakefile.nil?) && system_dir && File.directory?(system_dir) print_rakefile_directory(location) glob("#{system_dir}/*.rake") do |name| add_import name end else fail "No Rakefile found (looking for: #{@rakefiles.join(', ')})" if rakefile.nil? @rakefile = rakefile Dir.chdir(location) print_rakefile_directory(location) Rake.load_rakefile(File.expand_path(@rakefile)) if @rakefile && @rakefile != '' options.rakelib.each do |rlib| glob("#{rlib}/*.rake") do |name| add_import name end end end load_imports end
まずは、この中のfind_rakefile_location
から。
def find_rakefile_location # :nodoc: here = Dir.pwd until (fn = have_rakefile) Dir.chdir("..") return nil if Dir.pwd == here || options.nosearch here = Dir.pwd end [fn, here] ensure Dir.chdir(Rake.original_dir) end
この中では、Rakefileを探してる模様。
実行したときのカレントディレクトリから始まり、見つかるまでは親のディレクトリをひたすら登っていくようになっている。
そのため、rakeはそのプロジェクト内のディレクトリであれば、どこからでも呼べるようになっているようだ。
さて、再びraw_load_rakefile
へ戻る。
if (! options.ignore_system) && (options.load_system || rakefile.nil?) && system_dir && File.directory?(system_dir) print_rakefile_directory(location) glob("#{system_dir}/*.rake") do |name| add_import name end else
options.ignore_system
に関しては、--no-system
を設定したときの動作である。今回は通常のrakeの挙動を追っていきたいの、ここはパスする。
よって、else
の部分だけを切り出して深掘っていく。
rakefile, location = find_rakefile_location fail "No Rakefile found (looking for: #{@rakefiles.join(', ')})" if rakefile.nil? @rakefile = rakefile # カレントディレクトリの移動 Dir.chdir(location) # 現在のディレクトリの位置を表示 (Rakefileが実行時のカレントディクトリにない場合のみ) print_rakefile_directory(location) # Rakefileをロード Rake.load_rakefile(File.expand_path(@rakefile)) if @rakefile && @rakefile != '' # rakelibに指定されたディレクトリがrake読み込み対象になる # のちの load_imports で 読み込まれる options.rakelib.each do |rlib| glob("#{rlib}/*.rake") do |name| add_import name end end load_imports
ここで気になったのは、load_rakefile
。
ただ、中を探してみるととくに特別なことはしておらず、ただ単にload
を呼び出している。
## rake/rake_module.rb def load_rakefile(path) load(path) end
タスクの追加はRake::Application
自身が行っていると思ったが、そうではないようだ。
このときにRakefile
を読み込んでいるのは、main
である。
気になっていたDSLの解釈のmoduleであるRake::DSL
をなぜmain
にextend
しているのだろうというところだが、
その理由はload
によってタスクの読み込みを行っているためであることが判明した。
さて、読み込みとしては最後となるload_imports
。
rakelib
ディレクトリや、引数として指定されたディレクトリのrakeタスクを読み込むようだ。
ここで使っているlookup
だが、まだ詳しく処理の内容はわからない。
返り値としては、nilになるため、ここの処理の内容が詳しくはわからない。
どうやら、すでにタスクが定義されているとタスクが返却されるようだ。
そしてそれを実行しているようだ。
現時点でこの役割はわからないが、あまり一般的な利用法でもないと思うので、とりあえずここも深堀しないことにする。(のちにわかるかもしれない)
def load_imports # :nodoc: while fn = @pending_imports.shift next if @imported.member?(fn) fn_task = lookup(fn) and fn_task.invoke ext = File.extname(fn) loader = @loaders[ext] || @default_loader loader.load(fn) if fn_task = lookup(fn) and fn_task.needed? fn_task.reenable fn_task.invoke loader.load(fn) end @imported << fn end end
まとめ
今回は、タスクの読み込みについて調べてみた。
前回の初期化に比べると、なかなか複雑な感じがでてきた。
細かなオプションによって細かな挙動が変わるようで、いままで知らない使い方が発見できそうだ。