Reputation: 14881
I have a method that depends on two files for input, and that produces two or more files as output. There is no one-to-one mapping between each input file and each output file - the method performs operations that combines the input data to produce various output files.
Here's a (somewhat contrived) example:
class Util
def self.perform
`cat source1.txt source2.txt > target1.txt`
`cat source2.txt source1.txt > target2.txt`
end
end
Edit: The actual code in my case is much more complicated than this, and it is not viable to separate the work done to produce each target file into distinct parts. Hence setting up one file task per target file is not really an option in my case.
How do I set up a rake task for running this method once when needed and only then, ie. if one or more of the target files are missing, or if any of the source files are newer than any of the target files?
My current solution looks like this:
task :my_task => ['source1.txt', 'source2.txt'] do |t|
targets = ['target1.txt', 'target2.txt']
if targets.all? { |f| File.exist? f }
mtime_newest_source = t.prerequisites.map {|f| File.mtime(f) }.max
mtime_oldest_target = targets.map {|f| File.mtime(f) }.min
# Stop task early if all targets are up to date
next if mtime_newest_source < mtime_oldest_target
end
# Code that actually produces target files goes here
end
What I am looking for is a way to define the task so that none of the code within its block will be run unless the target files need to be rebuilt.
Upvotes: 3
Views: 1310
Reputation: 135
I don't believe the above answers are correct. The comment from Lars
['target1.txt', 'target2.txt'].each {
|target| file target => ['source1.txt', 'source2.txt'] { do_stuff_here }
}
Will run { do stuff here }
twice if rake 'target1.txt', 'target2.txt'
is called.
I believe a correct resolution of the dependencies is achieved by
file 'target1.txt' => ['source1.txt', 'source2.txt'] { do_stuff_here }
file 'target2.txt' => 'target1.txt'
Upvotes: 0
Reputation: 4296
FileTasks are designed for exactly this. Here's a good old long explanation, and here's a quick example:
file 'target1.txt' => ['source1.txt', 'source2.txt'] do
# do something to generate target1.txt
`cat source1.txt source2.txt > target1.txt`
end
file 'target2.txt' => ['source1.txt', 'source2.txt'] do
# again, generate the file
`cat source2.txt source1.txt > target2.txt`
end
File tasks can depend on other rake tasks or files on disk. If your tasks depends on a file, rake will compare the timestamps and only run your task if the dependent file is newer.
Here's a working example of generating all of targets from both tasks. Rake is smart enough to skip the second task when the first one creates both files.
def make_the_targets
`cat source1.txt source2.txt > target1.txt`
`cat source2.txt source1.txt > target2.txt`
end
file 'target1.txt' => ['source1.txt', 'source2.txt'] do
puts "making target1.txt"
make_the_targets
end
file 'target2.txt' => ['source1.txt', 'source2.txt'] do
puts "making target2.txt"
make_the_targets
end
task :make_targets => ['target1.txt', 'target2.txt']
Upvotes: 4