Bagaimana menjalankan tugas Rake dari dalam tugas Rake?

411

Saya memiliki Rakefile yang mengkompilasi proyek dalam dua cara, sesuai dengan variabel global $build_type, yang dapat :debugatau :release(hasilnya masuk dalam direktori terpisah):

task :build => [:some_other_tasks] do
end

Saya ingin membuat tugas yang mengkompilasi proyek dengan kedua konfigurasi pada gilirannya, sesuatu seperti ini:

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

Apakah ada cara untuk memanggil tugas seolah-olah itu adalah metode? Atau bagaimana saya bisa mencapai hal serupa?

Andrew Marshall
sumber
7
yang mana jawabannya?
nurettin
Saya akan memilih komunitas dan memilih jawaban yang dipilih 221 kali (pada saat penulisan). Poster asli telah meninggalkan SO
MPritchard
jawaban yang benar adalah stackoverflow.com/a/1290119/1536309
Blair Anderson
FYI, menggunakan sesuatu seperti Rake::Task["build"].invokebisa jauh lebih berkinerja daripada menggunakan system rake buildkarena tidak harus membuat utas baru dan memuat lingkungan Rails, yang system rake buildmemang harus dilakukan.
Joshua Pinter

Jawaban:

639

Jika Anda memerlukan tugas untuk berperilaku sebagai metode, bagaimana dengan menggunakan metode yang sebenarnya?

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

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

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

Jika Anda lebih suka berpegang pada rake idiom, berikut adalah kemungkinan Anda, disusun dari jawaban masa lalu:

  • Ini selalu mengeksekusi tugas, tetapi tidak mengeksekusi dependensinya:

    Rake::Task["build"].execute
  • Ini mengeksekusi dependensi, tetapi hanya mengeksekusi tugas jika belum dipanggil:

    Rake::Task["build"].invoke
  • Ini pertama-tama akan me-reset status sudah_invoked tugas, memungkinkan tugas untuk kemudian dieksekusi lagi, dependensi dan semua:

    Rake::Task["build"].reenable
    Rake::Task["build"].invoke
  • Perhatikan bahwa dependensi yang sudah dipanggil tidak dieksekusi ulang secara otomatis kecuali diaktifkan kembali. Di Rake> = 10.3.2, Anda dapat menggunakan yang berikut untuk mengaktifkannya kembali:

    Rake::Task["build"].all_prerequisite_tasks.each(&:reenable)
kch
sumber
96
Perhatikan bahwa jika tugas Anda berada dalam ruang nama, Anda harus menyertakan namespace saat Anda menjalankan tugas. Misalnya. Rake::Task['db:reset'].invoke
David Tuite
126
Jika tugas dalam pertanyaan membutuhkan argumen, Anda dapat meneruskannya sebagai argumen ke #invoke. Misalnya. Rake::Task['with:args'].invoke("pizza")
Trotter
27
Jika Anda perlu mengatur variabel lingkungan, lakukan itu sebelum memanggil invoke. Sebagai contoh: ENV['VERSION'] = '20110408170816'; Rake::Task['db:migrate'].invokeLihat di sini untuk penjelasan lebih lanjut.
Michael Stalker
13
Baru-baru ini saya temukan #reenable()tidak mengaktifkan kembali pre-req's, dan membutuhkannya. Tambahan ini untuk Rake (> = 10.3.2), #all_prerequisite_tasks()akan mengulangi semua tugas, termasuk pra-req tentang pra-req. Jadi,Rake::Task[task].all_prerequisite_tasks.each &:reenable
Richard Michael
4
@kch, dapatkah Anda merangkai ini bersama-sama (seperti pada commandline rake db:reset db:migratemisalnya). Bisakah Anda melakukan sesuatu seperti: Rake::Task["db:reset", "db:migrate"].invoke
Jeff
125

sebagai contoh:

Rake::Task["db:migrate"].invoke
Marcin Urbanski
sumber
6
Ini memanggil tugas hanya jika belum dipanggil. Tapi saya harus menjalankan tugas dengan semua tugas lain tergantung pada dua kali.
58
task :build_all do
  [ :debug, :release ].each do |t|
    $build_type = t
    Rake::Task["build"].reenable
    Rake::Task["build"].invoke
  end
end

Itu seharusnya memilah Anda, hanya membutuhkan hal yang sama sendiri.

darkliquid
sumber
Ini fungsional, tetapi terlalu bertele-tele. Tentu tidak ada yang lebih baik?
kch
13
task :invoke_another_task do
  # some code
  Rake::Task["another:task"].invoke
end
Neeraj Kumar
sumber
Salah satu alasan mengapa saya membutuhkan solusi seperti ini, adalah karena pemuatan tugas rake membutuhkan banyak waktu. Dengan menerapkan solusi seperti di atas, apakah akan menghemat waktu pemuatan?
Dipan Mehta
11
task :build_all do
  [ :debug, :release ].each do |t|
    $build_type = t
    Rake::Task["build"].execute
  end
end
pjb3
sumber
Itu tidak bekerja, karena hanya mengeksekusi tubuh: membangun tugas dan tidak menjalankan tugas yang bergantung padanya.
4

Jika Anda ingin setiap tugas dijalankan terlepas dari kegagalan, Anda dapat melakukan sesuatu seperti:

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

Saya menyarankan untuk tidak membuat tugas debug dan rilis umum jika proyek benar-benar sesuatu yang dikompilasi dan menghasilkan file. Anda harus pergi dengan tugas file yang cukup bisa dilakukan dalam contoh Anda, saat Anda menyatakan, bahwa output Anda masuk ke direktori yang berbeda. Katakan proyek Anda hanya mengkompilasi file test.c untuk keluar / debug / test.out dan keluar / lepaskan / test.out dengan gcc Anda dapat mengatur proyek Anda seperti ini:

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]

Pengaturan ini dapat digunakan seperti:

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

Ini sedikit lebih seperti yang diminta, tetapi menunjukkan poin saya:

  1. direktori keluaran dibuat, jika perlu.
  2. file hanya dikompilasi ulang jika diperlukan (contoh ini hanya benar untuk file test.c yang paling sederhana).
  3. Anda memiliki semua tugas siap jika Anda ingin memicu rilis atau build debug.
  4. contoh ini mencakup cara untuk juga mendefinisikan perbedaan kecil antara debug dan rilis-build.
  5. tidak perlu mengaktifkan kembali tugas build yang dipisah dengan variabel global, karena sekarang build yang berbeda memiliki tugas yang berbeda. codereuse dari build-task dilakukan dengan menggunakan kembali kode untuk mendefinisikan build-task. lihat bagaimana loop tidak menjalankan tugas yang sama dua kali, tetapi alih-alih membuat tugas, yang nantinya dapat dipicu (baik oleh semua-tugas atau memilih salah satu dari mereka di baris perintah rake).
Gizmomogwai
sumber