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

Posted

技术标签:

【中文标题】如何从 Rake 任务中运行 Rake 任务?【英文标题】:How to run Rake tasks from within Rake tasks? 【发布时间】:2010-10-09 08:12:02 【问题描述】:

我有一个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

有没有办法像调用方法一样调用任务?或者我怎样才能达到类似的效果?

【问题讨论】:

我会选择社区投票并选择 221 次投票的答案(在撰写本文时)。原来的海报已经离开了SO 正确答案是***.com/a/1290119/1536309 仅供参考,使用 Rake::Task["build"].invoke 之类的东西可能比使用 system rake build 更高效,因为它不必创建新线程并加载 Rails 环境,system rake build 确实有去做。 【参考方案1】:

如果您需要将任务作为方法来运行,那么使用实际方法怎么样?

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

这首先重置任务的 already_invoked 状态,允许任务 然后再次执行,依赖和所有:

Rake::Task["build"].reenable
Rake::Task["build"].invoke

请注意,除非重新启用,否则已调用的依赖项不会自动重新执行。在 Rake >= 10.3.2 中,您也可以使用以下方法重新启用它们:

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

【讨论】:

请注意,如果您的任务位于命名空间中,则在调用任务时必须包含命名空间。例如。 Rake::Task['db:reset'].invoke 如果问题中的任务需要参数,您可以将它们作为参数传递给#invoke。例如。 Rake::Task['with:args'].invoke("pizza") 如果您需要设置环境变量,请在调用调用之前执行此操作。例如:ENV['VERSION'] = '20110408170816'; Rake::Task['db:migrate'].invoke 更多解释见here。 我最近发现 #reenable() 没有重新启用 pre-req,并且需要它。 This addition 到 Rake (>= 10.3.2),#all_prerequisite_tasks() 将迭代所有任务,包括 pre-req 的 pre-req 的。所以,Rake::Task[task].all_prerequisite_tasks.each &:reenable @kch,你能把这些串起来吗(比如在命令行rake db:reset db:migrate 上)。你能做类似的事情吗:Rake::Task["db:reset", "db:migrate"].invoke【参考方案2】:
task :invoke_another_task do
  # some code
  Rake::Task["another:task"].invoke
end

【讨论】:

我需要这样的解决方案的原因之一是因为 rake 任务加载需要很多时间。通过实施上述解决方案,是否会节省加载时间?【参考方案3】:

如果项目确实需要编译并生成文件,我建议不要创建常规调试和发布任务。正如您所说,您应该使用在您的示例中非常可行的文件任务,您的输出进入不同的目录。 假设您的项目只是使用 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)

这比要求的多一点,但显示了我的观点:

    根据需要创建输出目录。 仅在需要时重新编译文件(此示例仅适用于最简单的 test.c 文件)。 如果您想触发发布构建或调试构建,您可以轻松完成所有任务。 此示例还包括一种定义调试版本和发布版本之间细微差别的方法。 无需重新启用使用全局变量参数化的构建任务,因为现在不同的构建具有不同的任务。构建任务的代码重用是通过重用代码来定义构建任务来完成的。看看循环如何不执行两次相同的任务,而是创建任务,稍后可以触发(通过 all-task 或在 rake 命令行中选择其中一个)。

【讨论】:

【参考方案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

【讨论】:

【参考方案5】:

例如:

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

【讨论】:

这仅在任务未被调用时才调用。但是我需要使用它依赖的所有其他任务调用这些任务两次。【参考方案6】:
task :build_all do
  [ :debug, :release ].each do |t|
    $build_type = t
    Rake::Task["build"].reenable
    Rake::Task["build"].invoke
  end
end

这应该可以解决您的问题,我自己也需要同样的东西。

【讨论】:

这很实用,但是太冗长了。确定没有更好的了?【参考方案7】:
task :build_all do
  [ :debug, :release ].each do |t|
    $build_type = t
    Rake::Task["build"].execute
  end
end

【讨论】:

它不起作用,因为它只是执行 :build 任务的主体,而不调用依赖它的任务。

以上是关于如何从 Rake 任务中运行 Rake 任务?的主要内容,如果未能解决你的问题,请参考以下文章

如何从控制台运行 rake 任务?

如何在 Ruby 脚本中运行 Rake 任务?

强制 Rake 任务在特定的 Rails 环境中运行

如何运行 rake 任务,未使用 cron [重复]

如何使 rake 任务在 dev 以外的环境中运行?

如何运行 db:migrate 从另一个带参数的 rake 任务?