如何从Rake任务中运行Rake任务?


410

我有一个Rakefile,它根据全局变量$build_type可以两种方式编译项目,可以是:debug:release(结果放在单独的目录中):

task :build => [:some_other_tasks] do
end

我希望创建一个任务,依次使用两种配置来编译项目,如下所示:

task :build_all do
  [ :debug, :release ].each do |t|
    $build_type = t
    # call task :build with all the tasks it depends on (?)
  end
end

有没有办法像调用方法一样调用任务?还是我可以实现类似的目标?


7
答案是哪一个?
nurettin 2012年

我会参加社区投票,并选择221次投票(在撰写本文时)。原始海报已于SO
MPritchard


仅供参考,使用类似的东西Rake::Task["build"].invoke可以比使用更多的性能,system rake build因为它不必创建新线程并加载Rails环境,而system rake build确实需要这样做。
约书亚·品特

Answers:


639

如果您需要任务表现为一种方法,那么使用实际方法呢?

task :build => [:some_other_tasks] do
  build
end

task :build_all do
  [:debug, :release].each { |t| build t }
end

def build(type = :debug)
  # ...
end

如果您愿意遵循rake的习惯用法,则可以根据过去的答案进行整理,这是您的可能:

  • 这总是执行任务,但不执行其依赖项:

    Rake::Task["build"].execute
  • 这将执行依赖关系,但仅在尚未被调用的情况下才执行任务:

    Rake::Task["build"].invoke
  • 这首先会重置任务的has_invoked状态,然后可以再次执行任务,依赖关系和所有其他任务:

    Rake::Task["build"].reenable
    Rake::Task["build"].invoke
  • 请注意,除非重新启用依赖关系,否则不会自动重新执行已经调用的依赖关系。在Rake> = 10.3.2中,您也可以使用以下命令重新启用它们:

    Rake::Task["build"].all_prerequisite_tasks.each(&:reenable)

96
请注意,如果您的任务在名称空间中,则在调用任务时必须包括名称空间。例如。Rake::Task['db:reset'].invoke
David Tuite 2011年

126
如果问题中的任务带有参数,则可以将它们作为参数传递给#invoke。例如。Rake::Task['with:args'].invoke("pizza")
猪蹄

26
如果需要设置环境变量,请在调用invoke之前执行此操作。例如:ENV['VERSION'] = '20110408170816'; Rake::Task['db:migrate'].invoke有关更多说明,请参见此处
Michael Stalker 2013年

13
我最近发现#reenable()并没有重新启用pre-req,而是需要它。 这除了耙(> = 10.3.2),#all_prerequisite_tasks()会遍历所有任务,包括预-REQ的预-REQ的公司。因此,Rake::Task[task].all_prerequisite_tasks.each &:reenable
理查德·迈克尔

4
@kch,您可以将它们串在一起吗(例如在命令行rake db:reset db:migrate上)。您能做类似的事情吗? Rake::Task["db:reset", "db:migrate"].invoke
杰夫

125

例如:

Rake::Task["db:migrate"].invoke

6
仅在尚未调用任务时才调用它。但是我需要将它与两次依赖的所有其他任务一起调用这些任务。

58
task :build_all do
  [ :debug, :release ].each do |t|
    $build_type = t
    Rake::Task["build"].reenable
    Rake::Task["build"].invoke
  end
end

那应该把你整理出来,我自己也需要同样的东西。


这是功能性的,但是太冗长了。确定没有更好的办法了吗?
kch

13
task :invoke_another_task do
  # some code
  Rake::Task["another:task"].invoke
end

我需要这样的解决方案的原因之一是因为rake任务加载需要大量时间。通过实施上述解决方案,可以节省加载时间吗?
Dipan Mehta

11
task :build_all do
  [ :debug, :release ].each do |t|
    $build_type = t
    Rake::Task["build"].execute
  end
end

它不起作用,因为它仅执行:build任务的主体,并且不调用依赖于此任务的任务。

4

如果您希望每个任务都能运行而不会失败,则可以执行以下操作:

task :build_all do
  [:debug, :release].each do |t| 
    ts = 0
    begin  
      Rake::Task["build"].invoke(t)
    rescue
      ts = 1
      next
    ensure
      Rake::Task["build"].reenable # If you need to reenable
    end
    return ts # Return exit code 1 if any failed, 0 if all success
  end
end

-1

如果项目确实是经过编译并因此生成文件的项目,我建议不要创建常规的调试和发布任务。您应该使用文件任务,在您的示例中这很可行,正如您指出的那样,您的输出将进入不同的目录。假设您的项目只是使用gcc将test.c文件编译为out / debug / test.out和out / release / test.out,则可以这样设置项目:

WAYS = ['debug', 'release']
FLAGS = {}
FLAGS['debug'] = '-g'
FLAGS['release'] = '-O'
def out_dir(way)
  File.join('out', way)
end
def out_file(way)
  File.join(out_dir(way), 'test.out')
end
WAYS.each do |way|
  desc "create output directory for #{way}"
  directory out_dir(way)

  desc "build in the #{way}-way"
  file out_file(way) => [out_dir(way), 'test.c'] do |t|
    sh "gcc #{FLAGS[way]} -c test.c -o #{t.name}"
  end
end
desc 'build all ways'
task :all => WAYS.map{|way|out_file(way)}

task :default => [:all]

此设置可以像以下方式使用:

rake all # (builds debug and release)
rake debug # (builds only debug)
rake release # (builds only release)

这可以按照要求做,但显示了我的观点:

  1. 根据需要创建输出目录。
  2. 仅在需要时才重新编译文件(此示例仅对最简单的test.c文件正确)。
  3. 如果要触发发布版本或调试版本,则所有任务都随时可用。
  4. 此示例包括一种还可以定义调试版本与发布版本之间的细微差别的方法。
  5. 无需重新启用使用全局变量参数化的构建任务,因为现在不同的构建具有不同的任务。通过重用代码定义构建任务来完成构建任务的代码重用。了解循环如何不执行两次相同的任务,而是执行创建的任务,这些任务随后可以被触发(通过全任务或在rake命令行中选择其中一个)。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.