Como executar tarefas Rake de dentro das tarefas Rake?

411

Eu tenho um Rakefile que compila o projeto de duas maneiras, de acordo com a variável global $build_type, que pode ser :debugou :release(os resultados vão em diretórios separados):

task :build => [:some_other_tasks] do
end

Desejo criar uma tarefa que compila o projeto com as duas configurações, algo como isto:

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

Existe uma maneira de chamar uma tarefa como se fosse um método? Ou como posso conseguir algo semelhante?

Andrew Marshall
fonte
7
qual é a resposta?
precisa
Eu iria com o voto da comunidade e escolheria a resposta com o voto 221 vezes (no momento da redação). O pôster original deixou SO
MPritchard 7/13/13
A resposta correta é stackoverflow.com/a/1290119/1536309
Blair Anderson
Para sua informação, usar algo como Rake::Task["build"].invokepode ter muito mais desempenho do que usar system rake buildporque não precisa criar um novo thread e carregar o ambiente Rails, o que system rake buildprecisa ser feito.
Joshua Pinter

Respostas:

639

Se você precisa que a tarefa se comporte como um método, que tal usar um método real?

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

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

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

Se você preferir manter rakeo idioma, aqui estão suas possibilidades, compiladas a partir de respostas anteriores:

  • Isso sempre executa a tarefa, mas não executa suas dependências:

    Rake::Task["build"].execute
  • Este executa as dependências, mas somente executa a tarefa se ainda não tiver sido chamado:

    Rake::Task["build"].invoke
  • Isso primeiro redefine o estado já_invocado da tarefa, permitindo que a tarefa seja executada novamente, dependências e tudo:

    Rake::Task["build"].reenable
    Rake::Task["build"].invoke
  • Observe que as dependências já invocadas não são reexecutadas automaticamente, a menos que sejam reativadas. No Rake> = 10.3.2, você pode usar o seguinte para reativá-los também:

    Rake::Task["build"].all_prerequisite_tasks.each(&:reenable)
kch
fonte
96
Observe que, se suas tarefas estiverem em espaços para nome, você deverá incluir o espaço para nome ao invocar a tarefa. Por exemplo. Rake::Task['db:reset'].invoke
David Tuite
126
Se a tarefa nas perguntas receber argumentos, você poderá passá-los como argumentos para #invoke. Por exemplo. Rake::Task['with:args'].invoke("pizza")
Trotter
27
Se você precisar definir uma variável de ambiente, faça isso antes de chamar invoke. Por exemplo: ENV['VERSION'] = '20110408170816'; Rake::Task['db:migrate'].invokeVeja aqui para mais explicações.
Michael Stalker
13
Descobri recentemente #reenable()que não reativa o pré-requisito e precisava dele. Esta adição ao Rake (> = 10.3.2), #all_prerequisite_tasks()iterará todas as tarefas, incluindo pré-requisições de pré-requisições. Então,Rake::Task[task].all_prerequisite_tasks.each &:reenable
Richard Michael
4
@ kch, você pode agrupá-los (como na linha rake db:reset db:migratede comando, por exemplo). Você pode fazer algo parecido com: Rake::Task["db:reset", "db:migrate"].invoke
Jeff
125

por exemplo:

Rake::Task["db:migrate"].invoke
Marcin Urbanski
fonte
6
Isso chama a tarefa apenas se ainda não foi chamada. Mas preciso invocar as tarefas com todas as outras tarefas das quais depende duas vezes.
58
task :build_all do
  [ :debug, :release ].each do |t|
    $build_type = t
    Rake::Task["build"].reenable
    Rake::Task["build"].invoke
  end
end

Isso deveria resolver você, só precisava da mesma coisa.

líquido escuro
fonte
Isso é funcional, mas muito detalhado. Claro que não há nada melhor?
kch
13
task :invoke_another_task do
  # some code
  Rake::Task["another:task"].invoke
end
Neeraj Kumar
fonte
Uma das razões pelas quais eu precisava de uma solução como essa é porque o carregamento de tarefas de rake leva muito tempo. Ao implementar uma solução como a anterior, economizará tempo de carregamento?
Dipan Mehta
11
task :build_all do
  [ :debug, :release ].each do |t|
    $build_type = t
    Rake::Task["build"].execute
  end
end
pjb3
fonte
Não funciona, porque apenas executa o corpo da tarefa: build e não invoca as tarefas que dependem dela.
4

Se você deseja que cada tarefa seja executada independentemente de qualquer falha, você pode fazer algo como:

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
fonte
-1

Eu sugeriria não criar tarefas gerais de depuração e lançamento, se o projeto for realmente algo compilado e resultar em arquivos. Você deve executar tarefas de arquivo, o que é bastante factível no seu exemplo, como você afirma, que sua saída entra em diretórios diferentes. Digamos que seu projeto compila apenas um arquivo test.c para out / debug / test.out e out / release / test.out com o gcc, você pode configurar seu projeto assim:

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]

Essa configuração pode ser usada como:

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

Isso faz um pouco mais, conforme solicitado, mas mostra meus pontos:

  1. os diretórios de saída são criados, conforme necessário.
  2. os arquivos são recompilados apenas se necessário (este exemplo está correto apenas para os arquivos test.c mais simples).
  3. você terá todas as tarefas prontamente em mãos se desejar acionar a versão ou depuração.
  4. este exemplo inclui uma maneira de definir também pequenas diferenças entre depuração e compilações de versão.
  5. não é necessário reativar uma tarefa de construção parametrizada com uma variável global, porque agora as diferentes construções têm tarefas diferentes. o uso de código da tarefa de construção é feito reutilizando o código para definir as tarefas de construção. veja como o loop não executa a mesma tarefa duas vezes, mas, em vez disso, cria tarefas que podem ser acionadas posteriormente (pela tarefa completa ou pela escolha de uma delas na linha de comando rake).
Gizmomogwai
fonte