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
まとめ
今回は、タスクの読み込みについて調べてみた。
前回の初期化に比べると、なかなか複雑な感じがでてきた。
細かなオプションによって細かな挙動が変わるようで、いままで知らない使い方が発見できそうだ。