teapot 1.0.0.pre.rc7 → 1.0.0.pre.rc9

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: 89ec99bb9d6d04670ccda7c53e838c9ac266372e
4
- data.tar.gz: b6ee7d9d431b290ec697a19a76674a5a9e82bd98
3
+ metadata.gz: c85e14574b10f98c7393860c30732ba0ce291322
4
+ data.tar.gz: 7800be07e0609957751fcdd79bdd3f454b2e9042
5
5
  SHA512:
6
- metadata.gz: fce6cf29a849f8fc5047dc580b69a46b8153c611a0a89f1374e01407175f30f521e47d4aceb0060a78296586df60ee687f7118869207277b56234a4b64bc20d5
7
- data.tar.gz: 5e499452699b8f1ed0f5b92409cbc78e862dbf108971dca638254a00fa4d9779a8f653ac17debc1f89512ea89d4091329fe9b9c22191e6e1e74146a50463ae3e
6
+ metadata.gz: dc81b95ae0929cc0a7f6c1b387e8b71276b1db78b3f9e8948b7ec72053be173f9279a56c2bb9a6ef6b1f9a6a7de3fa300edd44400e14e64146b828333bcac9a6
7
+ data.tar.gz: d097232b756dc542314069a3f464da5ed8ad7d456db959a6fe5c6c93b9bb8dc25056b0d0a8b3903ced6e33fcd308e480336735e5f6f898f25c13b0b8e738cad0
@@ -1,8 +1,4 @@
1
1
  language: ruby
2
2
  rvm:
3
- - "1.9"
4
3
  - "2.0"
5
4
  - "2.1"
6
- matrix:
7
- allow_failures:
8
- - rvm: "1.9"
data/README.md CHANGED
@@ -14,7 +14,7 @@ Teapot is a decentralised build tool for managing complex cross-platform project
14
14
 
15
15
  Ensure that you already have a working install of Ruby 1.9.3+
16
16
 
17
- $ gem install teapot
17
+ $ gem install teapot
18
18
 
19
19
  ## Usage
20
20
 
@@ -58,9 +58,10 @@ For Mac OS X (requires Xcode Command Line Tools):
58
58
 
59
59
  You need to make sure any basic tools, e.g. compilers, system libraries, are installed correctly before building. Consult the platform and library documentation for any dependencies.
60
60
 
61
- ## Dependency Graph
61
+ ## Open Issues
62
62
 
63
63
  - Should packages be built into a shared prefix or should they be built into unique prefixes and joined together either via install or `-L` and `-I`?
64
+ - Relative include paths might fail to work correctly if headers are not installed into same directory.
64
65
  - Should packages expose the tools required to build themselves as dependencies? e.g. should `build-cmake` as required by, say, `OpenCV`, be exposed to all who depend on `OpenCV`? Should there be a mechanism for non-public dependencies, i.e. dependencies which are not exposed to dependants?
65
66
 
66
67
  ## Contributing
@@ -1,15 +1,15 @@
1
1
  # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
2
+ #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the "Software"), to deal
5
5
  # in the Software without restriction, including without limitation the rights
6
6
  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
7
  # copies of the Software, and to permit persons to whom the Software is
8
8
  # furnished to do so, subject to the following conditions:
9
- #
9
+ #
10
10
  # The above copyright notice and this permission notice shall be included in
11
11
  # all copies or substantial portions of the Software.
12
- #
12
+ #
13
13
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
14
  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
15
  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -26,6 +26,7 @@ require 'build/makefile'
26
26
 
27
27
  require 'teapot/name'
28
28
 
29
+ require 'graphviz'
29
30
  require 'process/group'
30
31
  require 'system'
31
32
 
@@ -67,6 +68,10 @@ module Teapot
67
68
  scope.instance_exec(@arguments, &@callback)
68
69
  end
69
70
  end
71
+
72
+ def inspect
73
+ @rule.name.inspect
74
+ end
70
75
  end
71
76
 
72
77
  class Top < Graph::Node
@@ -87,6 +92,10 @@ module Teapot
87
92
  def requires_update?
88
93
  true
89
94
  end
95
+
96
+ def inspect
97
+ @task_class.name.inspect
98
+ end
90
99
  end
91
100
 
92
101
  class Task < Graph::Task
@@ -139,7 +148,9 @@ module Teapot
139
148
 
140
149
  def visit
141
150
  super do
151
+ @controller.enter(self, @node)
142
152
  @node.apply!(self)
153
+ @controller.exit(self, @node)
143
154
  end
144
155
  end
145
156
  end
@@ -161,6 +172,8 @@ module Teapot
161
172
 
162
173
  attr :top
163
174
 
175
+ attr :visualisation
176
+
164
177
  # Because we do a depth first traversal, we can capture global state per branch, such as `@task_class`.
165
178
  def traverse!(walker)
166
179
  @top.each do |node|
@@ -186,15 +199,54 @@ module Teapot
186
199
  end
187
200
  end
188
201
 
202
+ def enter(task, node)
203
+ return unless @g
204
+
205
+ parent_node = @hierarchy.last
206
+
207
+ task_node = @g.nodes[node] || @g.add_node(node, shape: 'box')
208
+
209
+ if parent_node
210
+ parent_node.connect(task_node)
211
+ end
212
+
213
+ node.inputs.map{|path| path.shortest_path(Dir.pwd)}.each do |path|
214
+ input_node = @g.nodes[path.to_s] || @g.add_node(path.to_s, shape: 'box')
215
+ input_node.connect(task_node)
216
+ end
217
+
218
+ @hierarchy << task_node
219
+ end
220
+
221
+ def exit(task, node)
222
+ return unless @g
223
+
224
+ @hierarchy.pop
225
+
226
+ task_node = @g.nodes[node] || @g.add_node(node, shape: 'box')
227
+
228
+ node.outputs.map{|path| path.shortest_path(Dir.pwd)}.each do |path|
229
+ output_node = @g.nodes[path.to_s] || @g.add_node(path.to_s, shape: 'box')
230
+ output_node.connect(task_node)
231
+ end
232
+ end
233
+
189
234
  def update!
190
235
  group = Process::Group.new
191
236
 
237
+ @g = Graphviz::Graph.new('G', rankdir: "LR")
238
+ @hierarchy = []
239
+
192
240
  walker = super do |walker, node|
193
241
  @task_class.new(self, walker, node, group)
194
242
  end
195
243
 
196
244
  group.wait
197
245
 
246
+ if ENV['BUILD_GRAPH_PDF']
247
+ Graphviz::output(@g, path: ENV['BUILD_GRAPH_PDF']) rescue nil
248
+ end
249
+
198
250
  return walker
199
251
  end
200
252
  end
@@ -1,15 +1,15 @@
1
1
  # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
2
+ #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the "Software"), to deal
5
5
  # in the Software without restriction, including without limitation the rights
6
6
  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
7
  # copies of the Software, and to permit persons to whom the Software is
8
8
  # furnished to do so, subject to the following conditions:
9
- #
9
+ #
10
10
  # The above copyright notice and this permission notice shall be included in
11
11
  # all copies or substantial portions of the Software.
12
- #
12
+ #
13
13
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
14
  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
15
  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -35,7 +35,7 @@ module Teapot
35
35
  def self.check(definition, definitions)
36
36
  previous = definitions[definition.name]
37
37
 
38
- raise new(definition, previous) if previous
38
+ raise self.new(definition, previous) if previous
39
39
  end
40
40
  end
41
41
 
@@ -21,6 +21,8 @@
21
21
  require 'teapot/controller'
22
22
  require 'teapot/build'
23
23
 
24
+ $TEAPOT_DEBUG_GRAPH = false
25
+
24
26
  module Teapot
25
27
  class Controller
26
28
  class BuildFailedError < StandardError
@@ -45,24 +47,33 @@ module Teapot
45
47
  end
46
48
  end
47
49
 
48
- controller.run do
49
- # The graph has been dirtied because files have changed, traverse and update it:
50
- walker = controller.update_with_log
51
-
52
- # Only run once is asked:
53
- unless @options[:continuous]
54
- if walker.failed?
55
- raise BuildFailedError.new("Failed to build all nodes successfully!")
50
+ walker = nil
51
+
52
+ # We need to catch interrupt here, and exit with the correct exit code:
53
+ begin
54
+ controller.run do
55
+ # The graph has been dirtied because files have changed, traverse and update it:
56
+ walker = controller.update_with_log
57
+
58
+ if $TEAPOT_DEBUG_GRAPH
59
+ controller.nodes.each do |key, node|
60
+ puts "#{node.status} #{node.inspect}" unless node.clean?
61
+ end
56
62
  end
57
63
 
58
- break
59
- end
60
-
61
- if $TEAPOT_DEBUG_GRAPH
62
- controller.nodes.each do |key, node|
63
- puts "#{node.status} #{node.inspect}"# unless node.clean?
64
+ # Only run once is asked:
65
+ unless @options[:continuous]
66
+ if walker.failed?
67
+ raise BuildFailedError.new("Failed to build all nodes successfully!")
68
+ end
69
+
70
+ break
64
71
  end
65
72
  end
73
+ rescue Interrupt
74
+ if walker && walker.failed?
75
+ raise BuildFailedError.new("Failed to build all nodes successfully!")
76
+ end
66
77
  end
67
78
 
68
79
  return chain, ordered
@@ -37,6 +37,14 @@ module Teapot
37
37
  Provision = Struct.new(:value)
38
38
  Alias = Struct.new(:dependencies)
39
39
 
40
+ def priority= value
41
+ @priority = value
42
+ end
43
+
44
+ def priority
45
+ @priority || 0
46
+ end
47
+
40
48
  def provides?(name)
41
49
  provisions.key? name
42
50
  end
@@ -72,7 +80,7 @@ module Teapot
72
80
  end
73
81
 
74
82
  class Chain
75
- def initialize(selection, dependencies, providers)
83
+ def initialize(selection, dependencies, providers, options = {})
76
84
  # Explicitly selected targets which will be used when resolving ambiguity:
77
85
  @selection = Set.new(selection)
78
86
 
@@ -88,6 +96,8 @@ module Teapot
88
96
  @unresolved = []
89
97
  @conflicts = {}
90
98
 
99
+ @options = options
100
+
91
101
  @dependencies.each do |dependency|
92
102
  expand(dependency, "<top>")
93
103
  end
@@ -105,6 +115,25 @@ module Teapot
105
115
 
106
116
  private
107
117
 
118
+ def ignore_priority?
119
+ @options[:ignore_priority]
120
+ end
121
+
122
+ def filter_by_priority(viable_providers)
123
+ # Sort from highest priority to lowest priority:
124
+ viable_providers = viable_providers.sort{|a,b| b.priority <=> a.priority}
125
+
126
+ # The first item has the highest priority:
127
+ highest_priority = viable_providers.first.priority
128
+
129
+ # We compute all providers with the same highest priority (may be zero):
130
+ return viable_providers.take_while{|provider| provider.priority == highest_priority}
131
+ end
132
+
133
+ def filter_by_selection(viable_providers)
134
+ return viable_providers.select{|provider| @selection.include? provider.name}
135
+ end
136
+
108
137
  def find_provider(dependency, parent)
109
138
  # Mostly, only one package will satisfy the dependency...
110
139
  viable_providers = @providers.select{|provider| provider.provides? dependency}
@@ -113,9 +142,17 @@ module Teapot
113
142
 
114
143
  if viable_providers.size > 1
115
144
  # ... however in some cases (typically where aliases are being used) an explicit selection must be made for the build to work correctly.
116
- explicit_providers = viable_providers.select{|provider| @selection.include? provider.name}
117
-
145
+ explicit_providers = filter_by_selection(viable_providers)
146
+
118
147
  # puts "** Filtering to #{explicit_providers.collect(&:name).join(', ')} explicit providers.".color(:magenta)
148
+
149
+ if explicit_providers.size != 1 and !ignore_priority?
150
+ # If we were unable to select a single package, we may use the priority to limit the number of possible options:
151
+ explicit_providers = viable_providers if explicit_providers.empty?
152
+
153
+ explicit_providers = filter_by_priority(explicit_providers)
154
+ end
155
+
119
156
 
120
157
  if explicit_providers.size == 0
121
158
  # No provider was explicitly specified, thus we require explicit conflict resolution:
@@ -1,15 +1,15 @@
1
1
  # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
- #
2
+ #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the "Software"), to deal
5
5
  # in the Software without restriction, including without limitation the rights
6
6
  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
7
  # copies of the Software, and to permit persons to whom the Software is
8
8
  # furnished to do so, subject to the following conditions:
9
- #
9
+ #
10
10
  # The above copyright notice and this permission notice shall be included in
11
11
  # all copies or substantial portions of the Software.
12
- #
12
+ #
13
13
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
14
  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
15
  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -18,8 +18,6 @@
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 'rexec/environment'
22
-
23
21
  require 'rainbow'
24
22
  require 'rainbow/ext/string'
25
23
 
@@ -44,26 +44,15 @@ module Teapot
44
44
  dynamic? and @options[:implicit]
45
45
  end
46
46
 
47
- def typed?
48
- @options[:typed]
47
+ # Optional parameters are those that are either defined as optional or implicit.
48
+ def optional?
49
+ @options[:optional] || implicit?
49
50
  end
50
51
 
51
52
  def applicable? arguments
52
- # The parameter is either optional, or is included in the argument list, otherwise we fail.
53
- unless @options[:optional] or arguments.include?(@name)
54
- return false
55
- end
56
-
57
- value = arguments[@name]
58
-
59
- # If the parameter is optional, and wasn't provided, we are okay.
60
- if @options[:optional]
61
- return true if value == nil
62
- end
63
-
64
- # If the parameter is typed, and we don't match the expected type, we fail.
65
- if type = @options[:typed]
66
- return false unless type === value
53
+ value = arguments.fetch(@name) do
54
+ # Value couldn't be found, if it wasn't optional, this parameter didn't apply:
55
+ return optional?
67
56
  end
68
57
 
69
58
  # If a pattern is provided, we must match it.
@@ -76,8 +65,10 @@ module Teapot
76
65
 
77
66
  def compute(arguments, scope)
78
67
  if implicit?
79
- scope.instance_exec(arguments, &@dynamic)
68
+ # Can be replaced if supplied:
69
+ arguments[@name] || scope.instance_exec(arguments, &@dynamic)
80
70
  elsif dynamic?
71
+ # Argument is optional:
81
72
  scope.instance_exec(arguments[@name], arguments, &@dynamic)
82
73
  else
83
74
  arguments[@name]
@@ -19,5 +19,5 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  module Teapot
22
- VERSION = "1.0.0-rc7"
22
+ VERSION = "1.0.0-rc9"
23
23
  end
@@ -29,6 +29,10 @@ module Teapot::DependencySpec
29
29
  end
30
30
 
31
31
  attr :name
32
+
33
+ def inspect
34
+ "<BasicDependency:#{@name}>"
35
+ end
32
36
  end
33
37
 
34
38
  describe Teapot::Dependency do
@@ -109,5 +113,51 @@ module Teapot::DependencySpec
109
113
  expect(chain.conflicts).to be == {}
110
114
  expect(chain.ordered).to be == [[apple, "apple"], [salad, "salad"]]
111
115
  end
116
+
117
+ it "should select dependencies with high priority" do
118
+ bad_apple = BasicDependency.new('bad_apple')
119
+ bad_apple.provides 'apple'
120
+ bad_apple.priority = 20
121
+
122
+ good_apple = BasicDependency.new('good_apple')
123
+ good_apple.provides 'apple'
124
+ good_apple.priority = 40
125
+
126
+ chain = Teapot::Dependency::chain([], ['apple'], [bad_apple, good_apple])
127
+
128
+ expect(chain.unresolved).to be == []
129
+ expect(chain.conflicts).to be == {}
130
+
131
+ # Should select higher priority package by default:
132
+ expect(chain.ordered).to be == [[good_apple, 'apple']]
133
+ end
134
+
135
+ it "should expose direct dependencies" do
136
+ system = BasicDependency.new('linux')
137
+ system.provides 'linux'
138
+ system.provides 'clang'
139
+ system.provides system: 'linux'
140
+ system.provides compiler: 'clang'
141
+
142
+ library = BasicDependency.new('library')
143
+ library.provides 'library'
144
+ library.depends :system
145
+ library.depends :compiler
146
+
147
+ application = BasicDependency.new('application')
148
+ application.provides 'application'
149
+ application.depends :compiler
150
+ application.depends 'library'
151
+
152
+ chain = Teapot::Dependency::chain([], ['application'], [system, library, application])
153
+
154
+ expect(chain.unresolved).to be == []
155
+ expect(chain.conflicts).to be == {}
156
+ expect(chain.ordered).to be == [
157
+ [system, 'clang'],
158
+ [library, 'library'],
159
+ [application, 'application']
160
+ ]
161
+ end
112
162
  end
113
163
  end
@@ -24,6 +24,8 @@ Gem::Specification.new do |spec|
24
24
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
25
25
  spec.require_paths = ["lib"]
26
26
 
27
+ spec.has_rdoc = 'yard'
28
+
27
29
  spec.required_ruby_version = '>= 2.0'
28
30
 
29
31
  spec.add_dependency "rainbow", "~> 2.0.0"
@@ -31,9 +33,9 @@ Gem::Specification.new do |spec|
31
33
 
32
34
  spec.add_dependency "system", "~> 0.1.3"
33
35
 
34
- spec.add_dependency "graphviz", "~> 0.0.2"
36
+ spec.add_dependency "graphviz", "~> 0.1.0"
35
37
 
36
- spec.add_dependency "build-files", "~> 0.2.8"
38
+ spec.add_dependency "build-files", "~> 0.2.9"
37
39
  spec.add_dependency "build-graph", "~> 0.3.5"
38
40
  spec.add_dependency "build-makefile", "~> 0.2.0"
39
41
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: teapot
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre.rc7
4
+ version: 1.0.0.pre.rc9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-19 00:00:00.000000000 Z
11
+ date: 2014-10-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rainbow
@@ -58,28 +58,28 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 0.0.2
61
+ version: 0.1.0
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 0.0.2
68
+ version: 0.1.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: build-files
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 0.2.8
75
+ version: 0.2.9
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 0.2.8
82
+ version: 0.2.9
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: build-graph
85
85
  requirement: !ruby/object:Gem::Requirement