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 +4 -4
- data/Gemfile +5 -0
- data/build-graph.gemspec +1 -1
- data/lib/build/graph.rb +4 -1
- data/lib/build/graph/node.rb +14 -6
- data/lib/build/graph/task.rb +2 -2
- data/lib/build/graph/version.rb +1 -1
- data/lib/build/graph/walker.rb +30 -5
- data/spec/build/graph/build_test.rb +85 -0
- data/spec/build/graph/graph.pdf +0 -0
- data/spec/build/graph/graph_spec.rb +51 -71
- data/spec/build/graph/node_spec.rb +1 -1
- data/spec/build/graph/process_graph.rb +77 -0
- data/spec/build/graph/program/main.cpp +0 -2
- metadata +11 -5
- data/lib/build/graph/controller.rb +0 -107
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0d8904d4ee2547485c75c63f61acf7e23dfd2415
|
4
|
+
data.tar.gz: 32d6fc93037e6436a9ed00864b1be734dbbf2485
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 88acc9b5ad90951fb4b565fb0b0b6b306d4f9727fb4232919fab7f611d33c75215806f3f180adc25c54d6a7738f8481b7f04ae2b170411c865a81c1836dac377
|
7
|
+
data.tar.gz: 63666bd52493dbae90fa0dd302339340ffcc7f24f81075185f8d8c847984b0b538f7c9192a33b604f5927b315a900b37c9205197e29e11b699308d1b4ec89b60
|
data/Gemfile
CHANGED
data/build-graph.gemspec
CHANGED
@@ -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.
|
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"
|
data/lib/build/graph.rb
CHANGED
@@ -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/
|
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
|
data/lib/build/graph/node.rb
CHANGED
@@ -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
|
-
|
65
|
-
|
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
|
data/lib/build/graph/task.rb
CHANGED
@@ -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
|
data/lib/build/graph/version.rb
CHANGED
data/lib/build/graph/walker.rb
CHANGED
@@ -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
|
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
|
-
@
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
Binary file
|
@@ -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
|
-
|
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
|
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
|
@@ -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.
|
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-
|
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.
|
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.
|
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
|