Указание предварительных файлов для задачи Rake

У меня есть вспомогательный класс, который сканирует весь каталог моего проекта и собирает список исходных файлов и соответствующих (целевых) объектных файлов. Зависимости от задачи компиляции определяются после сканирования исходного каталога, как показано ниже.

CLEAN.include(FileList[obj_dir + '**/*.o'])
CLOBBER.include(FileList[exe_dir + '**/*.exe'])

$proj = DirectoryParser.new(src_dir)

$proj.source_files.each do |source_file|
  file source_file.obj_file do
    sh "gcc -c ..."
  end
end

$proj.obj_files.each do |obj_file|
  task :compile => obj_file
end

task :compile do
end

Поскольку $proj является глобальным, DirectoryParser.new() вызывается при вызове любой из задач, включая clean и clobber. Это делает задачи clean и clobber медленными, а это нежелательно.

Чтобы обойти эту проблему, я переместил все создание зависимостей файлов в задачу по умолчанию. Это делает мои задачи clean и clobber быстрыми, однако теперь я не могу вызывать свои задачи компиляции или компоновки независимо друг от друга.

CLEAN.include(FileList[obj_dir + '**/*.o'])
CLOBBER.include(FileList[exe_dir + '**/*.exe'])

task :compile => $proj.source_files do   # Throws error!
end

task :default => do
  $proj = DirectoryParser.new(src_dir)

  $proj.source_files.each do |source_file|
    file source_file.obj_file do
      sh "gcc -c ..."
    end
  end

  $proj.obj_files.each do |obj_file|
    task :compile => obj_file
  end

  ... compile
  ... link
  ... execute
end

Как обойти эту проблему? Я уверен, что кто-то уже сталкивался с подобной проблемой. Буду признателен за любую помощь.


person thegreendroid    schedule 27.11.2011    source источник
comment
Я решил сохранить то же решение, что и выше, за счет того, что задачи clean и clobber занимают немного больше времени, чем хотелось бы. Если кто-то не предложит лучшее решение?   -  person thegreendroid    schedule 03.12.2011


Ответы (2)


Вы можете попробовать двухэтапный подход.

Создайте новую задачу generate_dependencies. Эта задача создает (статический) файл rake с вашими зависимостями и действиями.

Этот сгенерированный rakefile может быть загружен в ваш rake файл.

Некоторый пример кода (непроверенный):

GENERATED = 'generated_dependencies.rb'

task :generate_dependencies do
  $proj = DirectoryParser.new(src_dir)

  File.open(GENERATED, 'w') do |f|
    $proj.source_files.each do |source_file|
      f << <<-code
      file #{source_file.obj_file} do
        sh "gcc -c " #etc.
      end
      code
    end

    $proj.obj_files.each do |obj_file|
      f << "task :compile => #{obj_file}"
    end

    #~ ... compile
    #~ ... link
    #~ ... execute
  end
end

require GENERATED

Теперь у вас есть два шага:

  1. создайте пустой 'generated_dependencies.rb' (чтобы вы не получили ошибку при первом вызове скрипта)
  2. позвони rake generate_dependencies
  3. Проверьте сгенерированный файл - если он не годится, замените генератор ;)
  4. вызовите rake compile или rake link (или rake, если вы хотите использовать задачу по умолчанию) ... - зависимости определены в сгенерированном файле.

    • When something changes (new files), continue from step 2.
    • Если структура осталась прежней (нет новых файлов, только изменения в коде), вам нужен только шаг 4.
person knut    schedule 27.11.2011
comment
Спасибо за ответ. Означает ли это, что «rake generate_dependencies» и «rake install» должны вызываться пользователем вручную? Это создает ненужную нагрузку на пользователя, и я действительно этого не хочу. Я хочу, чтобы это происходило прозрачно для пользователя. - person thegreendroid; 28.11.2011
comment
Думаю, нет. Я немного изменил свой ответ. Вы можете определить значение по умолчанию по своему усмотрению (например, последовательность компиляции, компоновки и выполнения). Перед запуском по умолчанию необходимо запустить один раз rake generate_dependencies. Единственная проблема, которая у вас есть: если структура изменится (новые исходные коды...), любой должен вызвать rake generate_dependencies для обновления зависимостей. Я пытался найти "настоящее" решение, но столкнулся с проблемами - в то же время у меня появилась идея с этим двухэтапным решением. Не совсем полное решение, но иногда лучше иметь половинчатое решение, чем никакого решения. - person knut; 28.11.2011
comment
Еще раз спасибо за ответ. Запуск generate_dependencies вручную - это не то, что мне нужно, так как это возлагает на пользователя бремя запоминания вещей и опыта, которые могут быть очень подвержены ошибкам. Обходной путь, который я использовал, работает, но единственным недостатком является то, что вы не можете вызывать задачи компиляции/связывания независимо (вы должны вызывать rake, который вызывает задачу по умолчанию). - person thegreendroid; 28.11.2011
comment
...э-э, есть еще пара других, которые я только что видел. Я предложил редактирование на ваше рассмотрение. Кроме того, чтобы убедиться, что файл существует, вы можете touch GENERATED (метод существует, и если вы не уверены, вы всегда можете sh "touch #{GENERATED}") - person Fund Monica's Lawsuit; 04.03.2016

Мне удалось элегантно обойти эту проблему, используя шаблон проектирования Singleton и полностью отказавшись от использования зависимостей между файлами и задачами Rake. DirectoryParser теперь является одноэлементным классом (путем смешивания встроенной библиотеки «singleton» Ruby)

CLEAN.include(FileList[obj_dir + '**/*.o'])
CLOBBER.include(FileList[exe_dir + '**/*.exe'])

task :compile do
  $proj = DirectoryParser.instance
  $proj.source_files.each do |source_file|
      sh "gcc -c ..." unless uptodate?(obj_file, source_file)
  end
end

task :link do
  $proj = DirectoryParser.instance
  ...
end

Теперь мои задачи очистки/удаления выполняются быстро, и я все еще могу независимо вызывать задачи компиляции/связывания.

person thegreendroid    schedule 25.08.2012