设Rakefile如下

desc 'default task'
task default: [:print]

desc 'print task'
task :print do
  puts 'hello'
end

require 'trace_tree'

binding.trace_tree do
  import 'dep.rb'
end


dep.rb文件如下

puts caller


运行一下

$ rake
Object#block in <top (required)=""> /home/z/test_rails/rk/Rakefile:10
└─Object#import $GemPath0/gems/rake-12.0.0/lib/rake/dsl_definition.rb:182
  └─Array#each $GemPath0/gems/rake-12.0.0/lib/rake/dsl_definition.rb:183
    └─Object#block in import $GemPath0/gems/rake-12.0.0/lib/rake/dsl_definition.rb:183
      ├─Rake.application $GemPath0/gems/rake-12.0.0/lib/rake/rake_module.rb:7
      └─Rake::Application#add_import $GemPath0/gems/rake-12.0.0/lib/rake/application.rb:754
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib/rake/rake_module.rb:28:in `load'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib/rake/rake_module.rb:28:in `load_rakefile'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib/rake/default_loader.rb:10:in `load'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib/rake/application.rb:765:in `load_imports'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib/rake/application.rb:695:in `raw_load_rakefile'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib/rake/application.rb:96:in `block in load_rakefile'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib/rake/application.rb:178:in `standard_exception_handling'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib/rake/application.rb:95:in `load_rakefile'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib/rake/application.rb:79:in `block in run'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib/rake/application.rb:178:in `standard_exception_handling'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib/rake/application.rb:77:in `run'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/exe/rake:27:in `<top (required)="">'
/home/z/.rbenv/versions/2.4.0/bin/rake:22:in `load'
/home/z/.rbenv/versions/2.4.0/bin/rake:22:in `</top></top>
' hello


用源码来看,import只是将文件名暂存起来

def add_import(fn)
  @pending_imports << fn
end


然后,在读完整个rakefile(即做完task、import等等)之后,运行任务之前,才加载这些要import的文件,所以即使importee依赖importor中的某些东西(例如importor中定义的方法),import语句也不必写在被依赖之物的后面

def load_imports
  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


而Rake Task Management Essentials一书中的例子

task 'dep.rb' do
  sh %Q{echo "puts 'Hello, from the dep.rb'" > dep.rb}
end

task :hello => 'dep.rb'

import 'dep.rb'


运行时输出

$ rake hello
echo "puts 'Hello, from the dep.rb'" > dep.rb
Hello, from the dep.rb
echo "puts 'Hello, from the dep.rb'" > dep.rb
Hello, from the dep.rb


与书中只输出一次echo...Hello不符,是因为有两次fn_task.invoke。这里的机制主要是lookup然后invoke,如果import的参数恰好是之前定义过的task,则会先调用一下这个task

# rake-12.0.0/lib/rake/task_manager.rb
def lookup(task_name, initial_scope=nil)
  initial_scope ||= @scope
  task_name = task_name.to_s
  if task_name =~ /^rake:/
    scopes = Scope.make
    task_name = task_name.sub(/^rake:/, "")
  elsif task_name =~ /^(\^+)/
    scopes = initial_scope.trim($1.size)
    task_name = task_name.sub(/^(\^+)/, "")
  else
    scopes = initial_scope
  end
  lookup_in_scope(task_name, scopes)
end

def lookup_in_scope(name, scope)
  loop do
    tn = scope.path_with_task_name(name)
    task = @tasks[tn]
    return task if task
    break if scope.empty?
    scope = scope.tail
  end
  nil
end


不过还是没明白书中举这例子有何意义