build-graph 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 77b51e210994a3c93e59ddebf8b3aead06b84c07
4
- data.tar.gz: 5a9ece86c313dbbf9a0ddd462cd18b10eed49ff5
3
+ metadata.gz: f429cca0a6fccf4d050a393c0a89e319c6eaadea
4
+ data.tar.gz: 5c6430d2aca1fcb6ef21cef6b1cad7bfa8514342
5
5
  SHA512:
6
- metadata.gz: afb78262e92072da8e29dce01898447246e7f718a65b4c5f25a79f049fb9474a0fa21a1685758c11720d0f7cbc1b4c7ee328845da1adaf52a67b860da261c69d
7
- data.tar.gz: 4395c36481c93ada46363d602f34e136d9a8759e1022a3f669e97abe05f957ec7b2cab09fe81b06c38f7b796c5baf3a3261b5cb4ebca9e55d9e134f837479e4d
6
+ metadata.gz: 577cd4e1940d0a819ed599f2ed9950a52db4b7bfcc0031f411cc4f2c40bd9d7624294d33a1ab56f18c6eb73abf1ab59fc6a6ab7c264fcbaecc4c28cb49d77702
7
+ data.tar.gz: a1aba82f3d79dc93a0fe93720208005ece5f89c0a2b194000a225dd1c42536f2b11d4c65f04c63144d9929b3b0cbf2f9b418568f11d7bc2b2d734ccf2bc230a1
data/.travis.yml CHANGED
@@ -2,3 +2,10 @@ language: ruby
2
2
  rvm:
3
3
  - "2.0"
4
4
  - "2.1"
5
+ before_install:
6
+ # libstdc++-4.8-dev
7
+ - sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test
8
+ # clang++-3.2
9
+ - sudo add-apt-repository --yes ppa:h-rayflood/llvm
10
+ - sudo apt-get -qq update
11
+ - sudo apt-get -qq install libstdc++-4.8-dev clang-3.3
data/README.md CHANGED
@@ -19,10 +19,6 @@ Or install it yourself as:
19
19
 
20
20
  $ gem install build-graph
21
21
 
22
- ### Naming
23
-
24
- I'd like to call this gem, simply, `build`, but this name is not available.
25
-
26
22
  ## Usage
27
23
 
28
24
  TODO: Write usage instructions here
data/Rakefile CHANGED
@@ -1,9 +1,6 @@
1
1
  require "bundler/gem_tasks"
2
- require "rake/testtask"
2
+ require "rspec/core/rake_task"
3
3
 
4
- Rake::TestTask.new do |t|
5
- t.libs << 'test'
6
- end
4
+ RSpec::Core::RakeTask.new(:spec)
7
5
 
8
- desc "Run tests"
9
- task :default => :test
6
+ task :default => :spec
data/build-graph.gemspec CHANGED
@@ -1,11 +1,11 @@
1
1
  # coding: utf-8
2
2
  lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'build/version'
4
+ require 'build/graph/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "build-graph"
8
- spec.version = Build::VERSION
8
+ spec.version = Build::Graph::VERSION
9
9
  spec.authors = ["Samuel Williams"]
10
10
  spec.email = ["samuel.williams@oriontransfer.co.nz"]
11
11
  spec.summary = %q{Build::Graph is a framework for build systems, with specific functionality for dealing with file based processes.}
@@ -20,11 +20,14 @@ Gem::Specification.new do |spec|
20
20
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
21
  spec.require_paths = ["lib"]
22
22
 
23
- spec.add_dependency "process-group", "~> 0.1.0"
24
-
23
+ spec.add_dependency "process-group", "~> 0.2.1"
24
+ spec.add_dependency "build-files", "~> 0.1.0"
25
+ spec.add_dependency "build-makefile", "~> 0.1.0"
26
+
25
27
  spec.add_dependency "system"
26
- spec.add_dependency "rainbow"
28
+ spec.add_dependency "rainbow", "~> 2.0.0"
27
29
 
28
30
  spec.add_development_dependency "bundler", "~> 1.3"
31
+ spec.add_development_dependency "rspec", "~> 3.0.0.rc1"
29
32
  spec.add_development_dependency "rake"
30
33
  end
data/lib/build/graph.rb CHANGED
@@ -1,74 +1,26 @@
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.
1
20
 
2
- require 'build/files/monitor'
3
-
4
- require 'build/error'
5
- require 'build/node'
6
- require 'build/walker'
7
- require 'build/edge'
21
+ require_relative 'graph/controller'
8
22
 
9
23
  module Build
10
- class Graph < Files::Monitor
11
- def initialize
12
- super
13
-
14
- @nodes = {}
15
-
16
- build_graph!
17
- end
18
-
19
- attr :nodes
20
-
21
- # You need to override this to traverse the top nodes as required:
22
- def traverse!(walker)
23
- #Array(top).each do |node|
24
- # node.update!(walker)
25
- #end
26
- end
27
-
28
- def walk(&block)
29
- Walker.new(self, &block)
30
- end
31
-
32
- def build_graph!
33
- # We build the graph without doing any actual execution:
34
- nodes = []
35
-
36
- walker = walk do |walker, node|
37
- nodes << node
38
-
39
- yield walker, node
40
- end
41
-
42
- traverse! walker
43
-
44
- # We should update the status of all nodes in the graph once we've traversed the graph.
45
- nodes.each do |node|
46
- node.update_status!
47
- end
48
- end
49
-
50
- def update_with_log
51
- puts Rainbow("*** Graph update traversal ***").green
52
-
53
- start_time = Time.now
54
-
55
- walker = update!
56
- ensure
57
- end_time = Time.now
58
- elapsed_time = end_time - start_time
59
-
60
- $stdout.flush
61
- $stderr.puts Rainbow("Graph Update Time: %0.3fs" % elapsed_time).magenta
62
- end
63
-
64
- def update!
65
- walker = walk do |walker, node|
66
- yield walker, node
67
- end
68
-
69
- traverse! walker
70
-
71
- return walker
72
- end
24
+ module Graph
73
25
  end
74
26
  end
@@ -0,0 +1,95 @@
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
+ class Controller < Files::Monitor
31
+ def initialize
32
+ super
33
+
34
+ @nodes = {}
35
+
36
+ build_graph!
37
+ end
38
+
39
+ attr :nodes
40
+
41
+ # You need to override this to traverse the top nodes as required:
42
+ def traverse!(walker)
43
+ #Array(top).each do |node|
44
+ # node.update!(walker)
45
+ #end
46
+ end
47
+
48
+ def walk(&block)
49
+ Walker.new(self, &block)
50
+ end
51
+
52
+ def build_graph!
53
+ # We build the graph without doing any actual execution:
54
+ nodes = []
55
+
56
+ walker = walk do |walker, node|
57
+ nodes << node
58
+
59
+ yield walker, node
60
+ end
61
+
62
+ traverse! walker
63
+
64
+ # We should update the status of all nodes in the graph once we've traversed the graph.
65
+ nodes.each do |node|
66
+ node.update_status!
67
+ end
68
+ end
69
+
70
+ def update_with_log
71
+ puts Rainbow("*** Graph update traversal ***").green
72
+
73
+ start_time = Time.now
74
+
75
+ walker = update!
76
+ ensure
77
+ end_time = Time.now
78
+ elapsed_time = end_time - start_time
79
+
80
+ $stdout.flush
81
+ $stderr.puts Rainbow("Graph Update Time: %0.3fs" % elapsed_time).magenta
82
+ end
83
+
84
+ def update!
85
+ walker = walk do |walker, node|
86
+ yield walker, node
87
+ end
88
+
89
+ traverse! walker
90
+
91
+ return walker
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,70 @@
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_relative 'error'
22
+
23
+ module Build
24
+ module Graph
25
+ # Represents an input to a graph node, with count inputs.
26
+ class Edge
27
+ def initialize(count = 0)
28
+ @fiber = Fiber.current
29
+ @count = count
30
+
31
+ @failed = []
32
+ end
33
+
34
+ attr :failed
35
+
36
+ attr :fiber
37
+ attr :count
38
+
39
+ def wait
40
+ if @count > 0
41
+ Fiber.yield
42
+ end
43
+
44
+ failed?
45
+ end
46
+
47
+ attr :failed
48
+
49
+ def failed?
50
+ @failed.size != 0
51
+ end
52
+
53
+ def traverse(node)
54
+ @count -= 1
55
+
56
+ if node.failed?
57
+ @failed << node
58
+ end
59
+
60
+ if @count == 0
61
+ @fiber.resume
62
+ end
63
+ end
64
+
65
+ def increment!
66
+ @count += 1
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,36 @@
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
+ module Build
22
+ class TransientError < StandardError
23
+ end
24
+
25
+ class CommandFailure < TransientError
26
+ def initialize(command, status)
27
+ super "Command #{command.inspect} failed with exit status #{status}!"
28
+
29
+ @command = command
30
+ @status = status
31
+ end
32
+
33
+ attr :command
34
+ attr :status
35
+ end
36
+ end
@@ -0,0 +1,156 @@
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/state'
22
+
23
+ module Build
24
+ module Graph
25
+ class Node
26
+ def initialize(controller, inputs, outputs)
27
+ @controller = controller
28
+
29
+ @state = Files::IOState.new(inputs, outputs)
30
+
31
+ @status = :unknown
32
+ @fiber = nil
33
+
34
+ # These are immutable - rather than change them, create a new node:
35
+ @inputs = inputs
36
+ @outputs = outputs
37
+
38
+ @controller.add(self)
39
+ end
40
+
41
+ def eql?(other)
42
+ other.kind_of?(self.class) and @inputs.eql?(other.inputs) and @outputs.eql?(other.outputs)
43
+ end
44
+
45
+ def hash
46
+ [@inputs, @outputs].hash
47
+ end
48
+
49
+ def directories
50
+ @state.files.roots
51
+ end
52
+
53
+ def remove!
54
+ @controller.delete(self)
55
+ end
56
+
57
+ # It is possible this function is called unnecessarily. The state check confirms whether a change occurred or not.
58
+ def changed!(outputs = [])
59
+ # Don't do anything if we are already dirty.
60
+ return if dirty?
61
+
62
+ if @state.intersects?(outputs) || @state.update!
63
+ # puts "** Dirty: #{@inputs.to_a.inspect} -> #{@outputs.to_a.inspect}"
64
+
65
+ # Could possibly use unknown status here.
66
+ @status = :dirty
67
+
68
+ # If this node changes, we force all other nodes which depend on this node to be dirty.
69
+ @controller.update(directories, @outputs)
70
+ end
71
+ end
72
+
73
+ attr :inputs
74
+ attr :outputs
75
+
76
+ attr :state
77
+ attr :status
78
+
79
+ def unknown?
80
+ @status == :unknown
81
+ end
82
+
83
+ def dirty?
84
+ @status == :dirty
85
+ end
86
+
87
+ def clean?
88
+ @status == :clean
89
+ end
90
+
91
+ def clean!
92
+ @status = :clean
93
+ end
94
+
95
+ def fail!
96
+ @status = :failed
97
+ end
98
+
99
+ def failed?
100
+ @status == :failed
101
+ end
102
+
103
+ def updating?
104
+ @fiber != nil
105
+ end
106
+
107
+ # If we are in the initial state, we need to check if the outputs are fresh.
108
+ def update_status!
109
+ #puts "Update status: #{@inputs.inspect} -> #{@outputs.inspect} (status=#{@status} @fiber=#{@fiber.inspect}) @status=#{@status} @state.fresh?=#{@state.fresh?}"
110
+
111
+ if @status == :unknown
112
+ # This could be improved - only stale files should be reported, instead we report all.
113
+ unless @state.fresh?
114
+ changed!(self.inputs)
115
+ else
116
+ @status = :clean
117
+ end
118
+ end
119
+ end
120
+
121
+ def inspect
122
+ "<#{dirty? ? '*' : ''}inputs=#{inputs.inspect} outputs=#{outputs.inspect} fiber=#{@fiber.inspect} fresh=#{@state.fresh?}>"
123
+ end
124
+
125
+ def requires_update?
126
+ not clean?
127
+ end
128
+
129
+ # Perform some actions to update this node, returns when completed, and the node is no longer dirty.
130
+ def update!(walker)
131
+ #puts "Walking #{@inputs.to_a.inspect} -> #{@outputs.to_a.inspect} (dirty=#{dirty?} @fiber=#{@fiber.inspect})"
132
+
133
+ # If a fiber already exists, this node is in the process of updating.
134
+ if requires_update? and @fiber == nil
135
+ # puts "Beginning: #{@inputs.to_a.inspect} -> #{@outputs.to_a.inspect}"
136
+
137
+ @fiber = Fiber.new do
138
+ task = walker.task(self)
139
+
140
+ task.visit
141
+
142
+ # Commit changes:
143
+ # puts "** Committing: #{@inputs.to_a.inspect} -> #{@outputs.to_a.inspect}"
144
+
145
+ @state.update!
146
+ @fiber = nil
147
+
148
+ task.exit
149
+ end
150
+
151
+ @fiber.resume
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end