Comment exécuter les tâches Rake à partir des tâches Rake?

411

J'ai un Rakefile qui compile le projet de deux manières, selon la variable globale $build_type, qui peut être :debugou :release(les résultats vont dans des répertoires séparés):

task :build => [:some_other_tasks] do
end

Je souhaite créer une tâche qui compile le projet à tour de rôle avec les deux configurations, quelque chose comme ceci:

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-t-il un moyen d'appeler une tâche comme s'il s'agissait d'une méthode? Ou comment puis-je obtenir quelque chose de similaire?

Andrew Marshall
la source
7
laquelle est la réponse?
nurettin
J'irais avec le vote de la communauté et choisirais la réponse votée 221 fois (au moment de la rédaction). L'affiche originale a quitté SO
MPritchard
la bonne réponse est stackoverflow.com/a/1290119/1536309
Blair Anderson
Pour info, utiliser quelque chose comme Rake::Task["build"].invokepeut être beaucoup plus performant que d'utiliser system rake buildcar il n'a pas besoin de créer un nouveau thread et de charger l'environnement Rails, ce qui system rake builddoit être fait.
Joshua Pinter

Réponses:

639

Si vous avez besoin que la tâche se comporte comme une méthode, pourquoi ne pas utiliser une méthode réelle?

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

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

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

Si vous préférez vous en tenir aux rakeidiomes de, voici vos possibilités, compilées à partir des réponses passées:

  • Cela exécute toujours la tâche, mais il n'exécute pas ses dépendances:

    Rake::Task["build"].execute
  • Celui-ci exécute les dépendances, mais il n'exécute la tâche que si elle n'a pas déjà été invoquée:

    Rake::Task["build"].invoke
  • Cela réinitialise d'abord l'état déjà invoqué de la tâche, permettant à la tâche d'être à nouveau exécutée, les dépendances et tout:

    Rake::Task["build"].reenable
    Rake::Task["build"].invoke
  • Notez que les dépendances déjà invoquées ne sont pas automatiquement réexécutées sauf si elles sont réactivées. Dans Rake> = 10.3.2, vous pouvez également utiliser les éléments suivants pour les réactiver:

    Rake::Task["build"].all_prerequisite_tasks.each(&:reenable)
kch
la source
96
Notez que si vos tâches sont dans des espaces de noms, vous devez inclure l'espace de noms lorsque vous appelez la tâche. Par exemple. Rake::Task['db:reset'].invoke
David Tuite
127
Si la tâche dans les questions prend des arguments, vous pouvez les passer comme arguments à #invoke. Par exemple. Rake::Task['with:args'].invoke("pizza")
Trotter
27
Si vous devez définir une variable d'environnement, faites-le avant d'appeler invoke. Par exemple: ENV['VERSION'] = '20110408170816'; Rake::Task['db:migrate'].invokeVoir ici pour plus d'explications.
Michael Stalker
13
J'ai récemment découvert #reenable()qu'il ne réactivait pas les pré-demandes et en avais besoin. Cet ajout à Rake (> = 10.3.2), #all_prerequisite_tasks()itérera toutes les tâches, y compris les pré-requis des pré-requis. Donc,Rake::Task[task].all_prerequisite_tasks.each &:reenable
Richard Michael
4
@kch, pouvez-vous les enchaîner (comme sur la ligne rake db:reset db:migratede commande par exemple). Pouvez-vous faire quelque chose comme: Rake::Task["db:reset", "db:migrate"].invoke
Jeff
125

par exemple:

Rake::Task["db:migrate"].invoke
Marcin Urbanski
la source
6
Cela n'invoque la tâche que si elle n'a pas déjà été invoquée. Mais je dois invoquer les tâches avec toutes les autres tâches dont cela dépend deux fois.
58
task :build_all do
  [ :debug, :release ].each do |t|
    $build_type = t
    Rake::Task["build"].reenable
    Rake::Task["build"].invoke
  end
end

Cela devrait vous trier, j'avais juste besoin de la même chose moi-même.

darkliquid
la source
C'est fonctionnel, mais beaucoup trop verbeux. Bien sûr, il n'y a rien de mieux?
kch
13
task :invoke_another_task do
  # some code
  Rake::Task["another:task"].invoke
end
Neeraj Kumar
la source
L'une des raisons pour lesquelles j'avais besoin d'une solution comme celle-ci, c'est que le chargement des tâches de râteau prend beaucoup de temps. En mettant en œuvre une solution comme ci-dessus, cela réduira-t-il le temps de chargement?
Dipan Mehta
11
task :build_all do
  [ :debug, :release ].each do |t|
    $build_type = t
    Rake::Task["build"].execute
  end
end
pjb3
la source
Cela ne fonctionne pas, car il exécute simplement le corps de la tâche: build et n'appelle pas les tâches qui en dépendent.
4

Si vous souhaitez que chaque tâche s'exécute indépendamment des échecs, vous pouvez faire quelque chose comme:

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
la source
-1

Je suggérerais de ne pas créer de tâches générales de débogage et de publication si le projet est vraiment quelque chose qui est compilé et qui se traduit donc par des fichiers. Vous devriez aller avec des tâches de fichier, ce qui est tout à fait faisable dans votre exemple, comme vous le dites, que votre sortie va dans différents répertoires. Supposons que votre projet compile simplement un fichier test.c vers out / debug / test.out et out / release / test.out avec gcc, vous pouvez configurer votre projet comme ceci:

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]

Cette configuration peut être utilisée comme:

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

Cela fait un peu plus comme demandé, mais montre mes points:

  1. des répertoires de sortie sont créés, si nécessaire.
  2. les fichiers ne sont recompilés qu'en cas de besoin (cet exemple n'est correct que pour le plus simple des fichiers test.c).
  3. vous avez toutes les tâches à portée de main si vous souhaitez déclencher la version de publication ou la version de débogage.
  4. cet exemple inclut un moyen de définir également de petites différences entre le débogage et les versions de version.
  5. pas besoin de réactiver une tâche de build paramétrée avec une variable globale, car maintenant les différentes builds ont des tâches différentes. la codeuse de la tâche de build se fait en réutilisant le code pour définir les tâches de build. voyez comment la boucle n'exécute pas deux fois la même tâche, mais des tâches créées à la place, qui peuvent ensuite être déclenchées (soit par toutes les tâches, soit en choisissant l'une d'entre elles sur la ligne de commande de râteau).
Gizmomogwai
la source