build-graph 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 93ccee936d5902138b3cd45b565d8dfa20a477d6
4
+ data.tar.gz: 221ef37fa2440af4b5b12be9cc209402c741096d
5
+ SHA512:
6
+ metadata.gz: 3bebcf37fa8a68d1f602317d39af3fbe778687baea8cd9fc1fdbe93df20ea1964402183aa052b166557293fea0eeefb183c27735e46dd7a5261a2b8fb7483b16
7
+ data.tar.gz: cb91ae793eaa9ae002060d3bc407255376a4a5a3a7592fb0912d306a4a972ef872c77b9ac9a4ca0f192503ee926e1f1c7a0e868cf3a31514ae73e39af669b6fe
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in build.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # Build::Graph
2
+
3
+ Build::Graph is a framework for build systems, with specific functionality for dealing with file based processes.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'build-graph'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install build-graph
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
30
+
31
+ ## License
32
+
33
+ Released under the MIT license.
34
+
35
+ Copyright, 2012, 2014, by [Samuel G. D. Williams](http://www.codeotaku.com/samuel-williams).
36
+
37
+ Permission is hereby granted, free of charge, to any person obtaining a copy
38
+ of this software and associated documentation files (the "Software"), to deal
39
+ in the Software without restriction, including without limitation the rights
40
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
41
+ copies of the Software, and to permit persons to whom the Software is
42
+ furnished to do so, subject to the following conditions:
43
+
44
+ The above copyright notice and this permission notice shall be included in
45
+ all copies or substantial portions of the Software.
46
+
47
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
48
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
49
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
50
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
51
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
52
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
53
+ THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ end
7
+
8
+ desc "Run tests"
9
+ task :default => :test
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'build/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "build-graph"
8
+ spec.version = Build::VERSION
9
+ spec.authors = ["Samuel Williams"]
10
+ spec.email = ["samuel.williams@oriontransfer.co.nz"]
11
+ spec.summary = %q{Build::Graph is a framework for build systems, with specific functionality for dealing with file based processes.}
12
+ spec.description = <<-EOF
13
+ Build::Graph is a framework for managing file-system based build processes. It provides graph based build functionality which monitors the file-system for changes. Because of this, it can efficiently manage large and complex process based builds.
14
+ EOF
15
+ spec.homepage = ""
16
+ spec.license = "MIT"
17
+
18
+ spec.files = `git ls-files`.split($/)
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_dependency "system"
24
+ spec.add_dependency "rainbow"
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.3"
27
+ spec.add_development_dependency "rake"
28
+ end
data/lib/build.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "build/version"
2
+
3
+ module Build
4
+
5
+ end
data/lib/build/edge.rb ADDED
@@ -0,0 +1,49 @@
1
+
2
+ require 'build/error'
3
+
4
+ module Build
5
+ # Represents an input to a graph node, with count inputs.
6
+ class Edge
7
+ def initialize(count = 0)
8
+ @fiber = Fiber.current
9
+ @count = count
10
+
11
+ @failed = []
12
+ end
13
+
14
+ attr :failed
15
+
16
+ attr :fiber
17
+ attr :count
18
+
19
+ def wait
20
+ if @count > 0
21
+ Fiber.yield
22
+ end
23
+
24
+ failed?
25
+ end
26
+
27
+ attr :failed
28
+
29
+ def failed?
30
+ @failed.size != 0
31
+ end
32
+
33
+ def traverse(node)
34
+ @count -= 1
35
+
36
+ if node.failed?
37
+ @failed << node
38
+ end
39
+
40
+ if @count == 0
41
+ @fiber.resume
42
+ end
43
+ end
44
+
45
+ def increment!
46
+ @count += 1
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,18 @@
1
+
2
+
3
+ module Build
4
+ class TransientError < StandardError
5
+ end
6
+
7
+ class CommandFailure < TransientError
8
+ def initialize(command, status)
9
+ super "Command #{command.inspect} failed with exit status #{status}!"
10
+
11
+ @command = command
12
+ @status = status
13
+ end
14
+
15
+ attr :command
16
+ attr :status
17
+ end
18
+ end
@@ -0,0 +1,273 @@
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 'set'
22
+ require 'pathname'
23
+
24
+ module Build
25
+ module Files
26
+ class List
27
+ include Enumerable
28
+
29
+ def +(list)
30
+ Composite.new([self, list])
31
+ end
32
+
33
+ def intersects? other
34
+ other.any?{|path| include?(path)}
35
+ end
36
+
37
+ def rebase(root)
38
+ raise NotImplementedError
39
+ end
40
+
41
+ def to_paths
42
+ relative_paths = self.each do |path|
43
+ path.relative_path
44
+ end
45
+
46
+ return Paths.new(@root, relative_paths)
47
+ end
48
+
49
+ def match(pattern)
50
+ all? {|path| path.match(pattern)}
51
+ end
52
+ end
53
+
54
+ class RelativePath < String
55
+ # Both paths must be full absolute paths, and path must have root as an prefix.
56
+ def initialize(path, root)
57
+ raise ArgumentError.new("#{root} is not a prefix of #{path}") unless path.start_with?(root)
58
+
59
+ super path
60
+
61
+ @root = root
62
+ end
63
+
64
+ attr :root
65
+
66
+ def relative_path
67
+ self.slice(@root.length..-1)
68
+ end
69
+ end
70
+
71
+ class Directory < List
72
+ def initialize(root, path = "")
73
+ @root = root.to_s
74
+ @path = path
75
+ end
76
+
77
+ attr :root
78
+ attr :path
79
+
80
+ def full_path
81
+ File.join(@root, @path)
82
+ end
83
+
84
+ def each(&block)
85
+ Dir.glob(full_path + "**/*").each do |path|
86
+ yield RelativePath.new(path, @root)
87
+ end
88
+ end
89
+
90
+ def roots
91
+ [full_path]
92
+ end
93
+
94
+ def eql?(other)
95
+ other.kind_of?(self.class) and @root.eql?(other.root) and @path.eql?(other.path)
96
+ end
97
+
98
+ def hash
99
+ [@root, @path].hash
100
+ end
101
+
102
+ def include?(path)
103
+ # Would be true if path is a descendant of full_path.
104
+ path.start_with?(full_path)
105
+ end
106
+
107
+ def rebase(root)
108
+ self.class.new(root, @path)
109
+ end
110
+ end
111
+
112
+ class Glob < List
113
+ def initialize(root, pattern)
114
+ @root = root.to_s
115
+ @pattern = pattern
116
+ end
117
+
118
+ attr :root
119
+ attr :pattern
120
+
121
+ def full_pattern
122
+ File.join(@root, @pattern)
123
+ end
124
+
125
+ # Enumerate all paths matching the pattern.
126
+ def each(&block)
127
+ Dir.glob(full_pattern).each do |path|
128
+ yield RelativePath.new(path, @root)
129
+ end
130
+ end
131
+
132
+ def roots
133
+ [@root]
134
+ end
135
+
136
+ def eql?(other)
137
+ other.kind_of?(self.class) and @root.eql?(other.root) and @pattern.eql?(other.pattern)
138
+ end
139
+
140
+ def hash
141
+ [@root, @pattern].hash
142
+ end
143
+
144
+ def include?(path)
145
+ File.fnmatch(full_pattern, path)
146
+ end
147
+
148
+ def rebase(root)
149
+ self.class.new(root, @pattern)
150
+ end
151
+ end
152
+
153
+ class Paths < List
154
+ def initialize(root, paths)
155
+ @root = root.to_s
156
+ @paths = Array(paths)
157
+ end
158
+
159
+ attr :paths
160
+
161
+ def each(&block)
162
+ @paths.each do |path|
163
+ full_path = File.join(@root, path)
164
+ yield RelativePath.new(full_path, @root)
165
+ end
166
+ end
167
+
168
+ def roots
169
+ [@root]
170
+ end
171
+
172
+ def eql? other
173
+ other.kind_of?(self.class) and @paths.eql?(other.paths)
174
+ end
175
+
176
+ def hash
177
+ @paths.hash
178
+ end
179
+
180
+ def include?(path)
181
+ # Compute a full relative path:
182
+ full_path = File.absolute_path(path, @root)
183
+
184
+ # If the full path starts with @root, test it for inclusion:
185
+ if full_path.start_with? @root
186
+ # Compute the relative component:
187
+ relative_path = full_path[@root.length..-1]
188
+
189
+ # Does this list of paths include it?
190
+ return @paths.include?(relative_path)
191
+ else
192
+ return false
193
+ end
194
+ end
195
+
196
+ def rebase(root)
197
+ self.class.new(root, @paths)
198
+ end
199
+
200
+ def to_paths
201
+ return self
202
+ end
203
+ end
204
+
205
+ class Composite < List
206
+ def initialize(files = Set.new)
207
+ @files = files
208
+ end
209
+
210
+ attr :files
211
+
212
+ def each(&block)
213
+ @files.each do |files|
214
+ files.each &block
215
+ end
216
+ end
217
+
218
+ def roots
219
+ @files.collect(&:roots).flatten.uniq
220
+ end
221
+
222
+ def eql?(other)
223
+ other.kind_of?(self.class) and @files.eql?(other.files)
224
+ end
225
+
226
+ def hash
227
+ @files.hash
228
+ end
229
+
230
+ def merge(list)
231
+ if list.kind_of? Composite
232
+ @files += list.files
233
+ elsif list.kind_of? List
234
+ @files << list
235
+ else
236
+ raise ArgumentError.new("Cannot merge non-list of file paths.")
237
+ end
238
+ end
239
+
240
+ def +(list)
241
+ if list.kind_of? Composite
242
+ Composite.new(@files + list.files)
243
+ else
244
+ Composite.new(@files + [list])
245
+ end
246
+ end
247
+
248
+ def include?(path)
249
+ @files.any? {|list| list.include?(path)}
250
+ end
251
+
252
+ def rebase(root)
253
+ self.class.new(@files.collect{|list| list.rebase(root)})
254
+ end
255
+
256
+ def to_paths
257
+ Composite.new(@files.collect(&:to_paths))
258
+ end
259
+
260
+ def self.[](files)
261
+ if files.size == 0
262
+ return None
263
+ elsif files.size == 1
264
+ files.first
265
+ else
266
+ self.class.new(files)
267
+ end
268
+ end
269
+ end
270
+
271
+ NONE = Composite.new
272
+ end
273
+ end