Как запустить Rake-задачи из Rake-задач?

411

У меня есть 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
Я бы пошел с голосованием сообщества и выбрал бы ответ, проголосовавший 221 раз (на момент написания). Оригинальный постер вышел так
MPritchard
правильный ответ - stackoverflow.com/a/1290119/1536309
Блэр Андерсон,
К вашему сведению, использование чего-то подобного Rake::Task["build"].invokeможет быть намного более производительным, чем использование, system rake buildпотому что ему не нужно создавать новый поток и загружать среду Rails, что system rake buildи нужно делать.
Джошуа Пинтер

Ответы:

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
  • Это сначала сбрасывает состояние задачи yes_invoked, позволяя затем выполнить задачу снова, зависимости и все:

    Rake::Task["build"].reenable
    Rake::Task["build"].invoke
  • Обратите внимание, что уже вызванные зависимости не выполняются автоматически, если они не включены повторно. В Rake> = 10.3.2 вы можете использовать следующее, чтобы снова включить их:

    Rake::Task["build"].all_prerequisite_tasks.each(&:reenable)
KCH
источник
96
Обратите внимание, что если ваши задачи находятся в пространствах имен, вы должны включить пространство имен при вызове задачи. Например. Rake::Task['db:reset'].invoke
Дэвид Туит
127
Если задача в вопросах принимает аргументы, вы можете передать их в качестве аргументов в #invoke. Например. Rake::Task['with:args'].invoke("pizza")
Троттер
27
Если вам нужно установить переменную окружения, сделайте это перед вызовом invoke. Например: ENV['VERSION'] = '20110408170816'; Rake::Task['db:migrate'].invokeсмотрите здесь для более подробного объяснения.
Майкл Сталкер
13
Я недавно обнаружил, #reenable()что не повторно включить предварительные требования, и нуждался в этом. Это дополнение к Rake (> = 10.3.2) #all_prerequisite_tasks()будет повторять все задачи, включая предварительные требования предварительных требований. Итак,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

Это должно разобраться, просто нужно то же самое самому.

darkliquid
источник
Это функционально, но слишком многословно. Конечно, нет ничего лучше?
KCH
13
task :invoke_another_task do
  # some code
  Rake::Task["another:task"].invoke
end
Нирадж Кумар
источник
Одна из причин, по которой мне понадобилось такое решение, заключается в том, что загрузка rake-задач занимает много времени. Реализуя решение, подобное описанному выше, сэкономит ли оно время загрузки?
Дипан Мехта
11
task :build_all do
  [ :debug, :release ].each do |t|
    $build_type = t
    Rake::Task["build"].execute
  end
end
pjb3
источник
Он не работает, потому что он просто выполняет тело задачи: 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
bbbco
источник
-1

Я бы посоветовал не создавать общие задачи отладки и выпуска, если проект действительно является чем-то, что компилируется и приводит к файлам. Вы должны пойти с файловыми задачами, что вполне выполнимо в вашем примере, поскольку вы утверждаете, что ваш вывод идет в разные каталоги. Скажем, ваш проект просто компилирует файл test.c в out / debug / test.out и out / release / test.out с помощью gcc, вы можете настроить свой проект следующим образом:

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).
Gizmomogwai
источник