Skip to content

Instantly share code, notes, and snippets.

@jca02266
Last active September 17, 2017 23:56
Show Gist options
  • Save jca02266/419dba35f9a96fb5508208f691039e0a to your computer and use it in GitHub Desktop.
Save jca02266/419dba35f9a96fb5508208f691039e0a to your computer and use it in GitHub Desktop.
sample code of tsort library
require 'tsort'
# usage
=begin
$ cat depends
t1: t2
t2: t3
t3:
t4: t1
$ ruby make.rb
t3:
#<Proc:0x0000060005acc0@make.rb:90>
t2: t3
#<Proc:0x0000060005acc0@make.rb:90>
t1: t2
#<Proc:0x0000060005acc0@make.rb:90>
t4: t1
#<Proc:0x0000060005acc0@make.rb:90>
$ date > t3
$ date > t2
$ date > t1
$ date > t4
$ ls
depends make.rb t1 t2 t3 t4
=end
class Make
include TSort
attr_accessor :ignore_case
def initialize
@ignore_case = true
@dep = {}
end
def rule(outputs, inputs=[], &block)
if @ignore_case
outputs = outputs.collect {|v|
if Symbol === v
v
else
v.downcase
end
}
inputs = inputs.collect(&:downcase)
end
inputs = inputs.flat_map {|v|
Dir.glob(v)
}
triple = [outputs, inputs, block]
outputs.each {|f|
dep = @dep[f]
if dep
old_outputs, old_inputs, old_block = old_triple = dep[0]
new_inputs = old_inputs + inputs
new_triple = [old_outputs, new_inputs, old_block]
@dep.delete(old_triple)
@dep[new_triple] = new_inputs
@dep[f] = [new_triple]
else
@dep[f] = [triple]
end
}
@dep[triple] = inputs
end
def each(target)
each_strongly_connected_component_from(target) {|ns|
if ns.length != 1
fs = ns.delete_if {|n| Array === n}
raise TSort::Cyclic.new("cyclic dependencies: #{fs.join ', '}")
end
n = ns.first
if Array === n
yield n
end
}
end
def list(target)
each(target) {|outputs, inputs, block|
puts "#{outputs.join(", ")}: #{inputs.join(", ")}"
puts " #{block}" if block
}
end
def build(target)
each(target) {|outputs, inputs, block|
outputs.each {|out|
next if Symbol === out
inputs_time = inputs.map {|f| File.mtime f}.max
begin
output_time = File.mtime out
rescue Errno::ENOENT
output_time = nil
end
if block
if output_time == nil ||
inputs_time != nil && output_time <= inputs_time
block.call(out, inputs)
end
end
}
}
end
def tsort_each_child(node, &block)
@dep.fetch(node).each(&block)
end
def load(file, &func)
File.foreach(file) do |line|
next if /^[ \t]*#/ =~ line # comment
m = /^(?<targets>.*?):(?<depends>.*)/.match(line)
if m
targets = m[:targets].strip.split(/,/).map(&:strip)
depends = m[:depends].strip.split(/,/).map(&:strip)
rule(targets, depends, &func)
rule([:all], targets)
end
end
end
end
def command(arg)
print "$ ", arg, "\n"
system arg
end
m = Make.new
m.load('depends') {|target, depends|
command "date > #{target}"
}
begin
m.list(:all)
m.build(:all)
rescue => e
puts "Error: " + e.message
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment