build-graph 0.3.7 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9e451eb353fae615423fa48f9f20a2890fcc6157
4
- data.tar.gz: 78c36724ea7dc989d38c4b908fc9b9d5a36db7da
3
+ metadata.gz: 0d8904d4ee2547485c75c63f61acf7e23dfd2415
4
+ data.tar.gz: 32d6fc93037e6436a9ed00864b1be734dbbf2485
5
5
  SHA512:
6
- metadata.gz: 80e50b90964b646412353ad187c787a4d40e47e4b11498bfe9f3c2df3e4c4f338b8f1b1b8909dc855204f3e8f26652735e3db9e6d6d8b706852dbeb3fbb7b5d4
7
- data.tar.gz: 67c5ce3bdf104256332a94ddd12becb2516d95941a3d697bdf6d753d6b4262d64e33fab17487be0b570ebb8f6d51650ffc967fd2af9b451b46dcb3d105121015
6
+ metadata.gz: 88acc9b5ad90951fb4b565fb0b0b6b306d4f9727fb4232919fab7f611d33c75215806f3f180adc25c54d6a7738f8481b7f04ae2b170411c865a81c1836dac377
7
+ data.tar.gz: 63666bd52493dbae90fa0dd302339340ffcc7f24f81075185f8d8c847984b0b538f7c9192a33b604f5927b315a900b37c9205197e29e11b699308d1b4ec89b60
data/Gemfile CHANGED
@@ -6,4 +6,9 @@ gemspec
6
6
  group :test do
7
7
  gem 'simplecov'
8
8
  gem 'coveralls', require: false
9
+
10
+ gem 'rb-fsevent'
11
+ gem 'rb-inotify'
12
+
13
+ gem 'graphviz'
9
14
  end
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
23
23
  spec.required_ruby_version = '>= 2.0'
24
24
 
25
25
  spec.add_dependency "process-group", "~> 0.2.1"
26
- spec.add_dependency "build-files", "~> 0.3.3"
26
+ spec.add_dependency "build-files", "~> 1.0.0"
27
27
 
28
28
  spec.add_dependency "system"
29
29
  spec.add_dependency "rainbow", "~> 2.0.0"
@@ -18,7 +18,10 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
- require_relative 'graph/controller'
21
+ require_relative 'graph/error'
22
+ require_relative 'graph/node'
23
+ require_relative 'graph/walker'
24
+ require_relative 'graph/edge'
22
25
 
23
26
  module Build
24
27
  module Graph
@@ -54,15 +54,23 @@ module Build
54
54
  def dirty?
55
55
  if inherit_outputs?
56
56
  return true
57
+ elsif @inputs.count == 0 or @outputs.count == 0
58
+ # If there are no inputs or no outputs we are always dirty:
59
+ return true
60
+
61
+ # I'm not entirely sure this is the correct approach. If input is a glob that matched zero items, but might match items that are older than outputs, what is the correct output from this function?
57
62
  else
58
63
  # Dirty if any outputs don't exist:
59
64
  return true if @outputs.any?{|path| !path.exist?}
60
65
 
61
66
  # Dirty if input modified after any output:
62
- input_modified_time = self.modified_time
63
-
64
- # Outputs should always be more recent than their inputs:
65
- return true if @outputs.any?{|output_path| output_path.modified_time < input_modified_time}
67
+ if input_modified_time = self.modified_time
68
+ # Outputs should always be more recent than their inputs:
69
+ return true if @outputs.any?{|output_path| output_path.modified_time < input_modified_time}
70
+ else
71
+ # None of the inputs exist:
72
+ true
73
+ end
66
74
  end
67
75
 
68
76
  return false
@@ -80,8 +88,8 @@ module Build
80
88
  "<#{self.class.name} #{@inputs.inspect} => #{@outputs.inspect} by #{@process.inspect}>"
81
89
  end
82
90
 
83
- def self.top(inputs = Files::Paths::NONE, outputs = :inherit, &block)
84
- self.new(inputs, outputs, block)
91
+ def self.top(inputs = Files::Paths::NONE, outputs = :inherit, **options, &block)
92
+ self.new(inputs, outputs, block, **options)
85
93
  end
86
94
  end
87
95
  end
@@ -111,11 +111,11 @@ module Build
111
111
  end
112
112
 
113
113
  def changed!
114
- @walker.delete(@node)
114
+ @walker.delete(@node) if (@inputs.update! or @outputs.update!)
115
115
  end
116
116
 
117
117
  def directories
118
- @inputs.roots + @outputs.roots
118
+ (@inputs.roots + @outputs.roots).collect{|path| path.to_s}
119
119
  end
120
120
 
121
121
  def inspect
@@ -20,6 +20,6 @@
20
20
 
21
21
  module Build
22
22
  module Graph
23
- VERSION = "0.3.7"
23
+ VERSION = "1.0.0"
24
24
  end
25
25
  end
@@ -19,6 +19,7 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  require 'set'
22
+ require 'logger'
22
23
 
23
24
  require_relative 'task'
24
25
  require_relative 'node'
@@ -29,7 +30,17 @@ module Build
29
30
  module Graph
30
31
  # A walker walks over a graph and applies a task to each node.
31
32
  class Walker
32
- def initialize(&block)
33
+ def self.for(task_class, *args, **options)
34
+ self.new(**options) do |walker, node|
35
+ task = task_class.new(walker, node, *args)
36
+
37
+ task.visit do
38
+ task.update
39
+ end
40
+ end
41
+ end
42
+
43
+ def initialize(logger: nil, &block)
33
44
  # Node -> Task mapping.
34
45
  @tasks = {}
35
46
 
@@ -43,7 +54,8 @@ module Build
43
54
  @failed_tasks = []
44
55
  @failed_outputs = Set.new
45
56
 
46
- @monitor = Files::Monitor.new
57
+ @logger = logger || Logger.new(nil)
58
+ @monitor = Files::Monitor.new(logger: @logger)
47
59
  end
48
60
 
49
61
  attr :tasks # {Node => Task}
@@ -62,13 +74,15 @@ module Build
62
74
 
63
75
  def update(nodes)
64
76
  Array(nodes).each do |node|
65
- @update.call(self, node)
77
+ self.call(node)
66
78
  end
67
79
  end
68
80
 
69
81
  def call(node)
70
82
  # We try to fetch the task if it has already been invoked, otherwise we create a new task.
71
83
  @tasks.fetch(node) do
84
+ @logger.debug{"Update: #{node}"}
85
+
72
86
  @update.call(self, node)
73
87
 
74
88
  # This should now be defined:
@@ -130,7 +144,8 @@ module Build
130
144
  end
131
145
 
132
146
  def enter(task)
133
- # puts "--> #{task.node.process}"
147
+ @logger.debug{"--> #{task.node.process}"}
148
+
134
149
  @tasks[task.node] = task
135
150
 
136
151
  # In order to wait on outputs, they must be known before entering the task. This might seem odd, but unless we know outputs are being generated, waiting for them to complete is impossible - unless this was somehow specified ahead of time. The implications of this logic is that all tasks must be sequential in terms of output -> input chaning. This is not a problem in practice.
@@ -142,7 +157,7 @@ module Build
142
157
  end
143
158
 
144
159
  def exit(task)
145
- # puts "<-- #{task.node.process}"
160
+ @logger.debug{"<-- #{task.node.process}"}
146
161
 
147
162
  # Fail outputs if the node failed:
148
163
  if task.failed?
@@ -171,6 +186,8 @@ module Build
171
186
  end
172
187
 
173
188
  def delete(node)
189
+ @logger.debug{">-< #{node.process}"}
190
+
174
191
  if task = @tasks.delete(node)
175
192
  @monitor.delete(task)
176
193
  end
@@ -184,6 +201,14 @@ module Build
184
201
  @failed_tasks = []
185
202
  @failed_outputs = Set.new
186
203
  end
204
+
205
+ def run(**options)
206
+ yield
207
+
208
+ monitor.run(**options) do
209
+ yield
210
+ end
211
+ end
187
212
  end
188
213
  end
189
214
  end
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.expand_path('../../../lib', __dir__)
4
+
5
+ require 'graphviz'
6
+ require_relative 'process_graph'
7
+
8
+ include ProcessGraph
9
+
10
+ program_root = Path.join(__dir__, "program")
11
+ code_glob = Glob.new(program_root, "*.cpp")
12
+ program_path = Path.join(program_root, "dictionary-sort")
13
+
14
+ group = Process::Group.new
15
+ logger = Logger.new($stderr)
16
+ walker = Walker.for(ProcessTask, group)
17
+
18
+ top = ProcessNode.top code_glob, title: 'top' do
19
+ process code_glob, program_path, title: 'build' do
20
+ object_files = inputs.with(extension: ".o") do |input_path, output_path|
21
+ depfile_path = input_path + ".d"
22
+
23
+ dependencies = Paths.new(input_path)
24
+
25
+ if File.exist? depfile_path
26
+ depfile = Build::Makefile.load_file(depfile_path)
27
+
28
+ dependencies = depfile[output_path] || dependencies
29
+ end
30
+
31
+ process dependencies, output_path, title: 'compile' do
32
+ run("clang++", "-MMD", "-O3",
33
+ "-o", output_path.shortest_path(input_path.root),
34
+ "-c", input_path.relative_path, "-std=c++11",
35
+ chdir: input_path.root
36
+ )
37
+ end
38
+ end
39
+
40
+ process object_files, program_path, title: 'link' do
41
+ run("clang++", "-O3", "-o", program_path, *object_files.to_a, "-lm", "-pthread")
42
+ end
43
+ end
44
+
45
+ process program_path, title: 'run' do
46
+ run("./" + program_path.relative_path, chdir: program_path.root)
47
+ end
48
+ end
49
+
50
+ interrupted = false
51
+
52
+ trap(:INT) do
53
+ exit(0)
54
+ end
55
+
56
+ viz = Graphviz::Graph.new
57
+ viz.attributes[:rankdir] = 'LR'
58
+
59
+ walker.run do
60
+ walker.update(top)
61
+ group.wait
62
+
63
+ walker.tasks.each do |node, task|
64
+ input_nodes = []
65
+ output_nodes = []
66
+
67
+ task.inputs.each do |path|
68
+ input_nodes << viz.add_node(path.basename)
69
+ end
70
+
71
+ task.outputs.each do |path|
72
+ output_nodes << viz.add_node(path.basename)
73
+ end
74
+
75
+ if output_nodes.size == 1
76
+ input_nodes.each do |input_node|
77
+ edge = input_node.connect(output_nodes.first)
78
+ edge.attributes[:label] = node.title
79
+ end
80
+ end
81
+ end
82
+
83
+ File.write('graph.dot', viz.to_dot)
84
+ `dot -Tpdf graph.dot > graph.pdf && open graph.pdf`
85
+ end
@@ -19,63 +19,10 @@
19
19
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
20
  # THE SOFTWARE.
21
21
 
22
- require 'build/graph/walker'
23
- require 'build/makefile'
24
-
25
- require 'process/group'
26
- require 'fileutils'
22
+ require_relative 'process_graph'
27
23
 
28
24
  module Build::Graph::GraphSpec
29
- include Build::Graph
30
- include Build::Files
31
-
32
- class ProcessNode < Node
33
- def initialize(inputs, outputs, block)
34
- super(inputs, outputs, block.source_location)
35
-
36
- @block = block
37
- end
38
-
39
- def evaluate(context)
40
- context.instance_eval(&@block)
41
- end
42
- end
43
-
44
- class ProcessTask < Task
45
- def process(inputs, outputs = :inherit, &block)
46
- inputs = Build::Files::List.coerce(inputs)
47
- outputs = Build::Files::List.coerce(outputs) unless outputs.kind_of? Symbol
48
-
49
- node = ProcessNode.new(inputs, outputs, block)
50
-
51
- self.invoke(node)
52
- end
53
-
54
- def wet?
55
- @group != nil
56
- end
57
-
58
- def run(*arguments)
59
- if wet?
60
- puts "\t[run] #{arguments.join(' ')}"
61
- status = @group.spawn(*arguments)
62
-
63
- if status != 0
64
- raise CommandError.new(status)
65
- end
66
- end
67
- end
68
-
69
- # This function is called to finish the invocation of the task within the graph.
70
- # There are two possible ways this function can generally proceed.
71
- # 1/ The node this task is running for is clean, and thus no actual processing needs to take place, but children should probably be executed.
72
- # 2/ The node this task is running for is dirty, and the execution of commands should work as expected.
73
- def update(group = nil)
74
- @group = group if @node.dirty?
75
-
76
- @node.evaluate(self)
77
- end
78
- end
25
+ include ProcessGraph
79
26
 
80
27
  describe Build::Graph do
81
28
  it "shouldn't update mtime" do
@@ -85,14 +32,7 @@ module Build::Graph::GraphSpec
85
32
  FileUtils.rm_f listing_output.to_a
86
33
 
87
34
  group = Process::Group.new
88
-
89
- walker = Walker.new do |walker, node|
90
- task = ProcessTask.new(walker, node)
91
-
92
- task.visit do
93
- task.update(group)
94
- end
95
- end
35
+ walker = Walker.for(ProcessTask, group)
96
36
 
97
37
  top = ProcessNode.top do
98
38
  process test_glob, listing_output do
@@ -134,14 +74,7 @@ module Build::Graph::GraphSpec
134
74
  program_path = Path.join(program_root, "dictionary-sort")
135
75
 
136
76
  group = Process::Group.new
137
-
138
- walker = Walker.new do |walker, node|
139
- task = ProcessTask.new(walker, node)
140
-
141
- task.visit do
142
- task.update(group)
143
- end
144
- end
77
+ walker = Walker.for(ProcessTask, group)
145
78
 
146
79
  #FileUtils.touch(code_glob.first)
147
80
 
@@ -183,5 +116,52 @@ module Build::Graph::GraphSpec
183
116
  expect(program_path).to be_exist
184
117
  expect(code_glob.first.modified_time).to be <= program_path.modified_time
185
118
  end
119
+
120
+ it "should copy files incrementally" do
121
+ program_root = Path.join(__dir__, "program")
122
+ files = Glob.new(program_root, "*.cpp")
123
+ destination = Path.new(__dir__) + "tmp"
124
+
125
+ group = Process::Group.new
126
+ walker = Walker.for(ProcessTask, group)
127
+
128
+ #FileUtils.touch(code_glob.first)
129
+
130
+ top = ProcessNode.top files do
131
+ fs.mkpath destination
132
+
133
+ inputs.each do |source_path|
134
+ destination_path = source_path.rebase(destination)
135
+
136
+ process source_path, destination_path do
137
+ fs.install inputs.first, outputs.first
138
+ end
139
+ end
140
+ end
141
+
142
+ trashed_files = false
143
+
144
+ thread = Thread.new do
145
+ sleep 0.1
146
+
147
+ destination.glob("*.cpp").each{|path| path.delete}
148
+
149
+ trashed_files = true
150
+ end
151
+
152
+ walker.run do
153
+ walker.update(top)
154
+ group.wait
155
+
156
+ break if trashed_files
157
+ end
158
+
159
+ thread.join
160
+
161
+ expect(destination).to be_exist
162
+ expect(destination.glob("*.cpp").count).to be == 2
163
+
164
+ destination.delete
165
+ end
186
166
  end
187
167
  end
@@ -21,7 +21,7 @@
21
21
 
22
22
  require 'build/graph/node'
23
23
  require 'build/files/glob'
24
- require 'build/files/path/filesystem'
24
+ require 'build/files/system'
25
25
 
26
26
  module Build::Graph::NodeSpec
27
27
  include Build::Graph
@@ -0,0 +1,77 @@
1
+
2
+ require 'process/group'
3
+ require 'build/files'
4
+ require 'build/graph'
5
+
6
+ module ProcessGraph
7
+ include Build::Graph
8
+ include Build::Files
9
+
10
+ class ProcessNode < Node
11
+ def initialize(inputs, outputs, block, title: nil)
12
+ super(inputs, outputs, block.source_location)
13
+
14
+ if title
15
+ @title = title
16
+ else
17
+ @title = self.process
18
+ end
19
+
20
+ @block = block
21
+ end
22
+
23
+ def evaluate(context)
24
+ context.instance_eval(&@block)
25
+ end
26
+
27
+ attr :title
28
+ end
29
+
30
+ class ProcessTask < Task
31
+ def initialize(walker, node, group)
32
+ super(walker, node)
33
+
34
+ @group = group
35
+ end
36
+
37
+ def process(inputs, outputs = :inherit, **options, &block)
38
+ inputs = Build::Files::List.coerce(inputs)
39
+ outputs = Build::Files::List.coerce(outputs) unless outputs.kind_of? Symbol
40
+
41
+ node = ProcessNode.new(inputs, outputs, block, **options)
42
+
43
+ self.invoke(node)
44
+ end
45
+
46
+ def wet?
47
+ @node.dirty?
48
+ end
49
+
50
+ def run(*arguments)
51
+ if wet?
52
+ puts "\t[run] #{arguments.join(' ')}"
53
+ status = @group.spawn(*arguments)
54
+
55
+ if status != 0
56
+ raise CommandError.new(status)
57
+ end
58
+ end
59
+ end
60
+
61
+ def fs
62
+ if wet?
63
+ FileUtils::Verbose
64
+ else
65
+ FileUtils::Verbose::Dry
66
+ end
67
+ end
68
+
69
+ # This function is called to finish the invocation of the task within the graph.
70
+ # There are two possible ways this function can generally proceed.
71
+ # 1/ The node this task is running for is clean, and thus no actual processing needs to take place, but children should probably be executed.
72
+ # 2/ The node this task is running for is dirty, and the execution of commands should work as expected.
73
+ def update
74
+ @node.evaluate(self)
75
+ end
76
+ end
77
+ end
@@ -118,8 +118,6 @@ static void test_dictionary ()
118
118
 
119
119
  std::cerr << "Checksum: " << checksum << " ? " << (checksum == 479465310674138860) << std::endl;
120
120
  std::cerr << "Total Time: " << elapsed_time << std::endl;
121
-
122
- std::cerr << "Finished." << std::endl;
123
121
  }
124
122
 
125
123
  int main (int argc, const char * argv[])
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: build-graph
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.7
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-21 00:00:00.000000000 Z
11
+ date: 2015-06-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: process-group
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.3.3
33
+ version: 1.0.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.3.3
40
+ version: 1.0.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: system
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -139,16 +139,18 @@ files:
139
139
  - Rakefile
140
140
  - build-graph.gemspec
141
141
  - lib/build/graph.rb
142
- - lib/build/graph/controller.rb
143
142
  - lib/build/graph/edge.rb
144
143
  - lib/build/graph/error.rb
145
144
  - lib/build/graph/node.rb
146
145
  - lib/build/graph/task.rb
147
146
  - lib/build/graph/version.rb
148
147
  - lib/build/graph/walker.rb
148
+ - spec/build/graph/build_test.rb
149
+ - spec/build/graph/graph.pdf
149
150
  - spec/build/graph/graph_spec.rb
150
151
  - spec/build/graph/inherit_spec.rb
151
152
  - spec/build/graph/node_spec.rb
153
+ - spec/build/graph/process_graph.rb
152
154
  - spec/build/graph/program/Benchmark.cpp
153
155
  - spec/build/graph/program/Benchmark.h
154
156
  - spec/build/graph/program/DictionarySort.h
@@ -182,9 +184,12 @@ specification_version: 4
182
184
  summary: Build::Graph is a framework for build systems, with specific functionality
183
185
  for dealing with file based processes.
184
186
  test_files:
187
+ - spec/build/graph/build_test.rb
188
+ - spec/build/graph/graph.pdf
185
189
  - spec/build/graph/graph_spec.rb
186
190
  - spec/build/graph/inherit_spec.rb
187
191
  - spec/build/graph/node_spec.rb
192
+ - spec/build/graph/process_graph.rb
188
193
  - spec/build/graph/program/Benchmark.cpp
189
194
  - spec/build/graph/program/Benchmark.h
190
195
  - spec/build/graph/program/DictionarySort.h
@@ -192,3 +197,4 @@ test_files:
192
197
  - spec/build/graph/program/main.cpp
193
198
  - spec/build/graph/task_spec.rb
194
199
  - spec/build/graph/walker_spec.rb
200
+ has_rdoc:
@@ -1,107 +0,0 @@
1
- # Copyright, 2014, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
3
- # Permission is hereby granted, free of charge, to any person obtaining a copy
4
- # of this software and associated documentation files (the "Software"), to deal
5
- # in the Software without restriction, including without limitation the rights
6
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- # copies of the Software, and to permit persons to whom the Software is
8
- # furnished to do so, subject to the following conditions:
9
- #
10
- # The above copyright notice and this permission notice shall be included in
11
- # all copies or substantial portions of the Software.
12
- #
13
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
- # THE SOFTWARE.
20
-
21
- require 'build/files/monitor'
22
-
23
- require_relative 'error'
24
- require_relative 'node'
25
- require_relative 'walker'
26
- require_relative 'edge'
27
-
28
- module Build
29
- module Graph
30
- # The top level graph controller is responsible for managing build graph state.
31
- class Controller
32
- def initialize
33
- super
34
-
35
- @nodes = {}
36
-
37
- build_graph!
38
- end
39
-
40
- attr :nodes
41
-
42
- # Override this to traverse the top nodes as required.
43
- def traverse!(walker)
44
- #Array(top).each do |node|
45
- # node.update!(walker)
46
- #end
47
- end
48
-
49
- # Walk the graph with the given callback.
50
- def walk(&block)
51
- Walker.new(self, &block)
52
- end
53
-
54
- # Build the initial graph structure.
55
- def build_graph!
56
- # We build the graph without doing any actual execution:
57
- nodes = []
58
-
59
- walker = walk do |walker, node|
60
- nodes << node
61
-
62
- yield walker, node
63
- end
64
-
65
- traverse! walker
66
-
67
- # We should update the status of all nodes in the graph once we've traversed the graph.
68
- nodes.each do |node|
69
- node.update_status!
70
- end
71
- end
72
-
73
- # Update the graph and print out timing information.
74
- def update_with_log
75
- puts Rainbow("*** Graph update traversal ***").green
76
-
77
- start_time = Time.now
78
-
79
- walker = update!
80
-
81
- return walker
82
- ensure
83
- end_time = Time.now
84
- elapsed_time = end_time - start_time
85
-
86
- $stdout.flush
87
- $stderr.puts Rainbow("Graph Update Time: %0.3fs" % elapsed_time).magenta
88
- end
89
-
90
- # Update the graph.
91
- def update!
92
- walker = walk do |walker, node|
93
- yield walker, node
94
- end
95
-
96
- traverse! walker
97
-
98
- return walker
99
- end
100
-
101
- # What to do when a task has a trasient failure:
102
- def task_failure(error, task)
103
- $stderr.puts Rainbow("Error: #{error.inspect}").red
104
- end
105
- end
106
- end
107
- end