teapot 1.2.6 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +1 -0
- data/Gemfile +1 -0
- data/PLANNING.md +20 -0
- data/README.md +1 -1
- data/lib/teapot/command.rb +19 -0
- data/lib/teapot/configuration.rb +0 -1
- data/lib/teapot/context.rb +1 -1
- data/lib/teapot/controller.rb +1 -0
- data/lib/teapot/controller/build.rb +1 -1
- data/lib/teapot/controller/list.rb +5 -15
- data/lib/teapot/controller/visualize.rb +12 -70
- data/lib/teapot/definition.rb +1 -1
- data/lib/teapot/dependency.rb +3 -219
- data/lib/teapot/loader.rb +16 -6
- data/lib/teapot/package.rb +20 -32
- data/lib/teapot/project.rb +4 -4
- data/lib/teapot/repository.rb +1 -1
- data/lib/teapot/target.rb +5 -16
- data/lib/teapot/version.rb +1 -1
- data/spec/teapot/target_spec.rb +1 -1
- data/spec/teapot/wait_spec.rb +1 -3
- data/teapot.gemspec +6 -5
- metadata +31 -18
- data/spec/teapot/dependency_spec.rb +0 -166
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eddf99e30dfd202f8c03b4b075c276eb4eccf6a6
|
4
|
+
data.tar.gz: b739803f53360546415b4f6aa91f51677b2b8dd5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a76805afdfba7756138fd61d50d9d8cbc63e05bd5e66857d23d94e45bef06206cd0a187f016cd0c4975f45eaf6c22e9a7226fc6f869e90b268a243d70bbcdaaa
|
7
|
+
data.tar.gz: 3df933c8591927c119010d54377a1679a59b40281d5793c9a3e68acafbf8a3c2a3660da7e91172940d1f014b9df8d91a56714e213d1d943247b106b05ede56e8
|
data/.rspec
CHANGED
data/Gemfile
CHANGED
data/PLANNING.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# Planning
|
2
|
+
|
3
|
+
Teapot 2.0 will feature isolated build directories and private dependencies.
|
4
|
+
|
5
|
+
## Isolated Build Directories
|
6
|
+
|
7
|
+
Individual packages will be built into discrete directories:
|
8
|
+
|
9
|
+
```
|
10
|
+
teapot/#{platform}/libpng/include/png.h
|
11
|
+
teapot/#{platform}/libpng/lib/libpng.a
|
12
|
+
```
|
13
|
+
|
14
|
+
## Private Dependencies
|
15
|
+
|
16
|
+
All dependencies by default are public.
|
17
|
+
|
18
|
+
Given a package, C, that depends on B, and B publicly depends on A, C also depends on A.
|
19
|
+
|
20
|
+
The problem is that some dependencies of B should not also be dependencies of A, for example internal build tools, etc. It's not necessary for a consumer of A to be aware of C in all cases.
|
data/README.md
CHANGED
@@ -188,7 +188,7 @@ You need to make sure any basic tools, e.g. compilers, system libraries, are ins
|
|
188
188
|
|
189
189
|
- 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`?
|
190
190
|
- Relative include paths might fail to work correctly if headers are not installed into same directory.
|
191
|
-
- 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?
|
191
|
+
- 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? *YES - Implemented*.
|
192
192
|
- Should packages have some way to expose system requirements, e.g. installed compiler, libraries, etc. Perhaps some kind of `Package#valid?` which allows custom logic?
|
193
193
|
|
194
194
|
## Contributing
|
data/lib/teapot/command.rb
CHANGED
@@ -29,6 +29,9 @@ require_relative 'controller/visualize'
|
|
29
29
|
|
30
30
|
require_relative 'repository'
|
31
31
|
|
32
|
+
# For IncompatibleTeapotError
|
33
|
+
require_relative 'loader'
|
34
|
+
|
32
35
|
require 'samovar'
|
33
36
|
|
34
37
|
module Teapot
|
@@ -126,6 +129,21 @@ module Teapot
|
|
126
129
|
end
|
127
130
|
end
|
128
131
|
|
132
|
+
class Visualize < Samovar::Command
|
133
|
+
self.description = "Generate a picture of the dependency graph."
|
134
|
+
|
135
|
+
options do
|
136
|
+
option '-o/--output-path <path>', "The output path for the visualization.", default: "dependency.svg"
|
137
|
+
option '-d/--dependency-name <name>', "Show the partial chain for the given named dependency."
|
138
|
+
end
|
139
|
+
|
140
|
+
many :targets, "Visualize these targets, or use them to help the dependency resolution process."
|
141
|
+
|
142
|
+
def invoke(parent)
|
143
|
+
parent.controller.visualize(@targets, **@options)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
129
147
|
class Clean < Samovar::Command
|
130
148
|
self.description = "Delete everything in the teapot directory."
|
131
149
|
|
@@ -151,6 +169,7 @@ module Teapot
|
|
151
169
|
'fetch' => Fetch,
|
152
170
|
'list' => List,
|
153
171
|
'build' => Build,
|
172
|
+
'visualize' => Visualize,
|
154
173
|
'clean' => Clean
|
155
174
|
|
156
175
|
def root
|
data/lib/teapot/configuration.rb
CHANGED
data/lib/teapot/context.rb
CHANGED
data/lib/teapot/controller.rb
CHANGED
@@ -71,7 +71,7 @@ module Teapot
|
|
71
71
|
ordered.each do |resolution|
|
72
72
|
target = resolution.provider
|
73
73
|
|
74
|
-
environment = target.environment(context.configuration)
|
74
|
+
environment = target.environment(context.configuration, chain)
|
75
75
|
|
76
76
|
if target.build
|
77
77
|
controller.add_target(target, environment.flatten)
|
@@ -52,28 +52,18 @@ module Teapot
|
|
52
52
|
log "\t\t- Author: #{author.name}" + contact_text
|
53
53
|
end
|
54
54
|
when Target
|
55
|
-
definition.dependencies.each do |
|
56
|
-
log "\t\t-
|
55
|
+
definition.dependencies.each do |dependency|
|
56
|
+
log "\t\t- #{dependency}".color(:red)
|
57
57
|
end
|
58
58
|
|
59
|
-
definition.provisions.each do |
|
60
|
-
|
61
|
-
log "\t\t- provides #{name.inspect} => #{provision.dependencies.inspect}".color(:green)
|
62
|
-
else
|
63
|
-
log "\t\t- provides #{name.inspect}".color(:green)
|
64
|
-
end
|
59
|
+
definition.provisions.each do |name, provision|
|
60
|
+
log "\t\t- #{provision}".color(:green)
|
65
61
|
end
|
66
62
|
when Configuration
|
67
63
|
definition.materialize
|
68
64
|
|
69
65
|
definition.packages.each do |package|
|
70
|
-
|
71
|
-
log "\t\t- links #{package.name} from #{package.options[:local]}".color(:green)
|
72
|
-
elsif package.external?
|
73
|
-
log "\t\t- clones #{package.name} from #{package.external_url(context.root)}".color(:green)
|
74
|
-
else
|
75
|
-
log "\t\t- references #{package.name} from #{package.path}".color(:green)
|
76
|
-
end
|
66
|
+
log "\t\t- #{package}".color(:green)
|
77
67
|
end
|
78
68
|
|
79
69
|
definition.imports.select(&:explicit).each do |import|
|
@@ -21,88 +21,30 @@
|
|
21
21
|
require_relative '../controller'
|
22
22
|
|
23
23
|
require 'graphviz'
|
24
|
-
require 'yaml'
|
25
24
|
|
26
25
|
module Teapot
|
27
26
|
class Controller
|
28
|
-
def visualize(dependency_names = [])
|
27
|
+
def visualize(dependency_names = [], output_path: nil, dependency_name: nil)
|
29
28
|
configuration = context.configuration
|
30
29
|
|
31
30
|
chain = context.dependency_chain(dependency_names, context.configuration)
|
32
31
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
base_attributes = {
|
37
|
-
:shape => 'box',
|
38
|
-
}
|
39
|
-
|
40
|
-
provision_attributes = base_attributes.dup
|
41
|
-
|
42
|
-
alias_attributes = {
|
43
|
-
:shape => 'box',
|
44
|
-
:color => 'grey',
|
45
|
-
}
|
46
|
-
|
47
|
-
chain.ordered.each do |resolution|
|
48
|
-
provider = resolution.provider
|
49
|
-
name = resolution.name
|
50
|
-
|
51
|
-
# Provider is the target that provides the dependency referred to by name.
|
52
|
-
node = g.add_node(name.to_s, base_attributes.dup)
|
53
|
-
|
54
|
-
if chain.dependencies.include?(name)
|
55
|
-
node.attributes[:color] = 'blue'
|
56
|
-
node.attributes[:penwidth] = 2.0
|
57
|
-
elsif chain.selection.include?(provider.name)
|
58
|
-
node.attributes[:color] = 'brown'
|
59
|
-
end
|
32
|
+
if dependency_name
|
33
|
+
provider = context.dependencies[dependency_name]
|
60
34
|
|
61
|
-
#
|
62
|
-
|
63
|
-
dependency_node = g.nodes[dependency.to_s]
|
64
|
-
|
65
|
-
node.connect(dependency_node) if dependency_node
|
66
|
-
end
|
67
|
-
|
68
|
-
# A provision provides other provisions...
|
69
|
-
provider.provisions.each do |(provision_name, provision)|
|
70
|
-
next if name == provision_name
|
71
|
-
|
72
|
-
provides_node = g.nodes[provision_name.to_s] || g.add_node(provision_name.to_s, provision_attributes)
|
73
|
-
|
74
|
-
if Dependency::Alias === provision
|
75
|
-
provides_node.attributes = alias_attributes
|
76
|
-
end
|
77
|
-
|
78
|
-
unless provides_node.connected?(node)
|
79
|
-
edge = provides_node.connect(node)
|
80
|
-
end
|
81
|
-
end
|
35
|
+
# TODO The visualisation generated isn't quite right. It's introspecting too much from the packages and not reflecting #ordered and #provisions.
|
36
|
+
chain = chain.partial(provider)
|
82
37
|
end
|
83
38
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
provider = resolution.provider
|
88
|
-
name = resolution.name
|
89
|
-
|
90
|
-
p = g.graphs[provider.name] || g.add_subgraph(provider.name, :rank => :same)
|
91
|
-
|
92
|
-
provider.dependencies.each do |dependency|
|
93
|
-
next if done.include? dependency
|
94
|
-
|
95
|
-
done << dependency
|
96
|
-
|
97
|
-
dependency_node = g.nodes[dependency.to_s]
|
98
|
-
|
99
|
-
p.add_node(dependency_node.name)
|
100
|
-
end
|
101
|
-
end
|
39
|
+
visualization = Build::Dependency::Visualization.new
|
40
|
+
|
41
|
+
graph = visualization.generate(chain)
|
102
42
|
|
103
|
-
|
43
|
+
if output
|
44
|
+
Graphviz::output(graph, :path => output)
|
45
|
+
end
|
104
46
|
|
105
|
-
|
47
|
+
return graph
|
106
48
|
end
|
107
49
|
end
|
108
50
|
end
|
data/lib/teapot/definition.rb
CHANGED
data/lib/teapot/dependency.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright,
|
1
|
+
# Copyright, 2017, 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
|
@@ -18,224 +18,8 @@
|
|
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 '
|
21
|
+
require 'build/dependency'
|
22
22
|
|
23
23
|
module Teapot
|
24
|
-
|
25
|
-
class UnresolvedDependencyError < StandardError
|
26
|
-
def initialize(chain)
|
27
|
-
super "Unresolved dependency chain!"
|
28
|
-
|
29
|
-
@chain = chain
|
30
|
-
end
|
31
|
-
|
32
|
-
attr :chain
|
33
|
-
end
|
34
|
-
|
35
|
-
Provision = Struct.new(:value)
|
36
|
-
Alias = Struct.new(:dependencies)
|
37
|
-
Resolution = Struct.new(:provider, :name)
|
38
|
-
|
39
|
-
def priority= value
|
40
|
-
@priority = value
|
41
|
-
end
|
42
|
-
|
43
|
-
def priority
|
44
|
-
@priority || 0
|
45
|
-
end
|
46
|
-
|
47
|
-
def provides?(name)
|
48
|
-
provisions.key? name
|
49
|
-
end
|
50
|
-
|
51
|
-
def provides(name_or_aliases, &block)
|
52
|
-
if String === name_or_aliases || Symbol === name_or_aliases
|
53
|
-
name = name_or_aliases
|
54
|
-
|
55
|
-
provisions[name] = Provision.new(block)
|
56
|
-
else
|
57
|
-
aliases = name_or_aliases
|
58
|
-
|
59
|
-
aliases.each do |name, dependencies|
|
60
|
-
provisions[name] = Alias.new(Array(dependencies))
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def provisions
|
66
|
-
@provisions ||= {}
|
67
|
-
end
|
68
|
-
|
69
|
-
def depends(name)
|
70
|
-
dependencies << name
|
71
|
-
end
|
72
|
-
|
73
|
-
def depends?(name)
|
74
|
-
dependencies.include? name
|
75
|
-
end
|
76
|
-
|
77
|
-
def dependencies
|
78
|
-
@dependencies ||= Set.new
|
79
|
-
end
|
80
|
-
|
81
|
-
class Chain
|
82
|
-
def initialize(selection, dependencies, providers, options = {})
|
83
|
-
# Explicitly selected targets which will be used when resolving ambiguity:
|
84
|
-
@selection = Set.new(selection)
|
85
|
-
|
86
|
-
# The list of dependencies that needs to be satisfied:
|
87
|
-
@dependencies = dependencies
|
88
|
-
|
89
|
-
# The available providers which match up to required dependencies:
|
90
|
-
@providers = providers
|
91
|
-
|
92
|
-
@resolved = Set.new
|
93
|
-
@ordered = []
|
94
|
-
@provisions = []
|
95
|
-
@unresolved = []
|
96
|
-
@conflicts = {}
|
97
|
-
|
98
|
-
@options = options
|
99
|
-
|
100
|
-
@dependencies.each do |dependency|
|
101
|
-
expand(dependency, "<top>")
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
attr :selection
|
106
|
-
attr :dependencies
|
107
|
-
attr :providers
|
108
|
-
|
109
|
-
attr :resolved
|
110
|
-
attr :ordered
|
111
|
-
attr :provisions
|
112
|
-
attr :unresolved
|
113
|
-
attr :conflicts
|
114
|
-
|
115
|
-
private
|
116
|
-
|
117
|
-
def ignore_priority?
|
118
|
-
@options[:ignore_priority]
|
119
|
-
end
|
120
|
-
|
121
|
-
def filter_by_priority(viable_providers)
|
122
|
-
# Sort from highest priority to lowest priority:
|
123
|
-
viable_providers = viable_providers.sort{|a,b| b.priority <=> a.priority}
|
124
|
-
|
125
|
-
# The first item has the highest priority:
|
126
|
-
highest_priority = viable_providers.first.priority
|
127
|
-
|
128
|
-
# We compute all providers with the same highest priority (may be zero):
|
129
|
-
return viable_providers.take_while{|provider| provider.priority == highest_priority}
|
130
|
-
end
|
131
|
-
|
132
|
-
def filter_by_selection(viable_providers)
|
133
|
-
return viable_providers.select{|provider| @selection.include? provider.name}
|
134
|
-
end
|
135
|
-
|
136
|
-
def find_provider(dependency, parent)
|
137
|
-
# Mostly, only one package will satisfy the dependency...
|
138
|
-
viable_providers = @providers.select{|provider| provider.provides? dependency}
|
139
|
-
|
140
|
-
# puts "** Found #{viable_providers.collect(&:name).join(', ')} viable providers.".color(:magenta)
|
141
|
-
|
142
|
-
if viable_providers.size > 1
|
143
|
-
# ... however in some cases (typically where aliases are being used) an explicit selection must be made for the build to work correctly.
|
144
|
-
explicit_providers = filter_by_selection(viable_providers)
|
145
|
-
|
146
|
-
# puts "** Filtering to #{explicit_providers.collect(&:name).join(', ')} explicit providers.".color(:magenta)
|
147
|
-
|
148
|
-
if explicit_providers.size != 1 and !ignore_priority?
|
149
|
-
# If we were unable to select a single package, we may use the priority to limit the number of possible options:
|
150
|
-
explicit_providers = viable_providers if explicit_providers.empty?
|
151
|
-
|
152
|
-
explicit_providers = filter_by_priority(explicit_providers)
|
153
|
-
end
|
154
|
-
|
155
|
-
if explicit_providers.size == 0
|
156
|
-
# No provider was explicitly specified, thus we require explicit conflict resolution:
|
157
|
-
@conflicts[dependency] = viable_providers
|
158
|
-
return nil
|
159
|
-
elsif explicit_providers.size == 1
|
160
|
-
# The best outcome, a specific provider was named:
|
161
|
-
return explicit_providers.first
|
162
|
-
else
|
163
|
-
# Multiple providers were explicitly mentioned that satisfy the dependency.
|
164
|
-
@conflicts[dependency] = explicit_providers
|
165
|
-
return nil
|
166
|
-
end
|
167
|
-
else
|
168
|
-
return viable_providers.first
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
def expand(dependency, parent)
|
173
|
-
# puts "** Expanding #{dependency} from #{parent}".color(:magenta)
|
174
|
-
|
175
|
-
if @resolved.include? dependency
|
176
|
-
# puts "** Already resolved dependency!".color(:magenta)
|
177
|
-
|
178
|
-
return
|
179
|
-
end
|
180
|
-
|
181
|
-
provider = find_provider(dependency, parent)
|
182
|
-
|
183
|
-
if provider == nil
|
184
|
-
# puts "** Couldn't find provider -> unresolved".color(:magenta)
|
185
|
-
@unresolved << [dependency, parent]
|
186
|
-
return nil
|
187
|
-
end
|
188
|
-
|
189
|
-
provision = provider.provisions[dependency]
|
190
|
-
|
191
|
-
# We will now satisfy this dependency by satisfying any dependent dependencies, but we no longer need to revisit this one.
|
192
|
-
@resolved << dependency
|
193
|
-
|
194
|
-
# If the provision was an Alias, make sure to resolve the alias first:
|
195
|
-
if Alias === provision
|
196
|
-
# puts "** Resolving alias #{provision}".color(:magenta)
|
197
|
-
|
198
|
-
provision.dependencies.each do |dependency|
|
199
|
-
expand(dependency, provider)
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
unless @resolved.include?(provider)
|
204
|
-
# We are now satisfying the provider by expanding all its own dependencies:
|
205
|
-
@resolved << provider
|
206
|
-
|
207
|
-
# Make sure we satisfy the provider's dependencies first:
|
208
|
-
provider.dependencies.each do |dependency|
|
209
|
-
expand(dependency, provider)
|
210
|
-
end
|
211
|
-
|
212
|
-
# puts "** Appending #{dependency} -> ordered".color(:magenta)
|
213
|
-
|
214
|
-
# Add the provider to the ordered list.
|
215
|
-
@ordered << Resolution.new(provider, dependency)
|
216
|
-
end
|
217
|
-
|
218
|
-
# This goes here because we want to ensure 1/ that if
|
219
|
-
unless provision == nil or Alias === provision
|
220
|
-
# puts "** Appending #{dependency} -> provisions".color(:magenta)
|
221
|
-
|
222
|
-
# Add the provision to the set of required provisions.
|
223
|
-
@provisions << provision
|
224
|
-
end
|
225
|
-
|
226
|
-
# For both @ordered and @provisions, we ensure that for [...xs..., x, ...], x is satisfied by ...xs....
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
# An `UnresolvedDependencyError` will be thrown if there are any unresolved dependencies.
|
231
|
-
def self.chain(selection, dependencies, providers)
|
232
|
-
chain = Chain.new(selection, dependencies, providers)
|
233
|
-
|
234
|
-
if chain.unresolved.size > 0
|
235
|
-
raise UnresolvedDependencyError.new(chain)
|
236
|
-
end
|
237
|
-
|
238
|
-
return chain
|
239
|
-
end
|
240
|
-
end
|
24
|
+
Dependency = ::Build::Dependency
|
241
25
|
end
|
data/lib/teapot/loader.rb
CHANGED
@@ -27,14 +27,12 @@ require 'build/rule'
|
|
27
27
|
require 'build/name'
|
28
28
|
require 'build/files'
|
29
29
|
|
30
|
-
# Required for CPU count, etc.
|
31
|
-
require 'system'
|
32
|
-
|
33
30
|
module Teapot
|
34
|
-
# Cannot load packages newer than this
|
35
|
-
|
31
|
+
# Cannot load packages newer than this.
|
32
|
+
# Version 1.3: Added support for build-dependency library which allows options for `#depends`. The primary use case is private dependencies.
|
33
|
+
LOADER_VERSION = "1.3"
|
36
34
|
|
37
|
-
# Cannot load packages older than this
|
35
|
+
# Cannot load packages older than this.
|
38
36
|
MINIMUM_LOADER_VERSION = "1.0"
|
39
37
|
|
40
38
|
class IncompatibleTeapotError < StandardError
|
@@ -53,6 +51,18 @@ module Teapot
|
|
53
51
|
attr :path
|
54
52
|
end
|
55
53
|
|
54
|
+
# This is a quick hack so that we can avoid using the system gem.. it's used in build-make package.
|
55
|
+
# TODO Remove this and also the dependency on facets, or fix facets.
|
56
|
+
module System
|
57
|
+
module CPU
|
58
|
+
def self.count
|
59
|
+
require 'etc'
|
60
|
+
|
61
|
+
Etc.nprocessors rescue 4
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
56
66
|
class Loader
|
57
67
|
Files = Build::Files
|
58
68
|
Rule = Build::Rule
|
data/lib/teapot/package.rb
CHANGED
@@ -19,8 +19,8 @@
|
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
21
|
require 'build/files'
|
22
|
+
require 'build/uri'
|
22
23
|
|
23
|
-
require_relative 'context'
|
24
24
|
require_relative 'definition'
|
25
25
|
|
26
26
|
module Teapot
|
@@ -63,32 +63,37 @@ module Teapot
|
|
63
63
|
attr :path
|
64
64
|
|
65
65
|
attr :uri
|
66
|
-
|
66
|
+
attr_accessor :options
|
67
|
+
|
68
|
+
def local
|
69
|
+
@options[:local].to_s
|
70
|
+
end
|
67
71
|
|
68
72
|
def local?
|
69
|
-
@options.
|
73
|
+
@options.include?(:local)
|
70
74
|
end
|
71
75
|
|
72
76
|
def external?
|
73
|
-
@options.
|
77
|
+
@options.include?(:source)
|
74
78
|
end
|
75
79
|
|
76
|
-
|
77
|
-
|
80
|
+
# The source uri from which this package would be cloned. Might be relative, in which case it's relative to the root of the context.
|
81
|
+
def source_uri
|
82
|
+
Build::URI[@options[:source]]
|
78
83
|
end
|
79
84
|
|
80
|
-
def external_url(
|
81
|
-
|
82
|
-
|
83
|
-
if base_uri.scheme == nil || base_uri.scheme == 'file'
|
84
|
-
base_uri = URI "file://" + File.expand_path(base_uri.path, relative_root) + "/"
|
85
|
-
end
|
86
|
-
|
87
|
-
return relative_url(base_uri)
|
85
|
+
def external_url(root_path = nil)
|
86
|
+
Build::URI[root_path] + source_uri + Build::URI[@uri]
|
88
87
|
end
|
89
88
|
|
90
89
|
def to_s
|
91
|
-
|
90
|
+
if self.local?
|
91
|
+
"links #{@name} from #{self.local}"
|
92
|
+
elsif self.external?
|
93
|
+
"clones #{@name} from #{self.external_url}"
|
94
|
+
else
|
95
|
+
"references #{@name} from #{@path}"
|
96
|
+
end
|
92
97
|
end
|
93
98
|
|
94
99
|
# Package may be used as hash key / in a set:
|
@@ -100,22 +105,5 @@ module Teapot
|
|
100
105
|
def eql?(other)
|
101
106
|
@path.eql?(other.path)
|
102
107
|
end
|
103
|
-
|
104
|
-
private
|
105
|
-
|
106
|
-
def relative_url(base_uri)
|
107
|
-
source_uri = URI(@uri)
|
108
|
-
|
109
|
-
unless source_uri.absolute?
|
110
|
-
source_uri = base_uri + source_uri
|
111
|
-
end
|
112
|
-
|
113
|
-
# Git can't handle the default formatting that Ruby uses for file URIs.
|
114
|
-
if source_uri.scheme == "file"
|
115
|
-
source_uri = "file://" + source_uri.path
|
116
|
-
end
|
117
|
-
|
118
|
-
return source_uri
|
119
|
-
end
|
120
108
|
end
|
121
109
|
end
|
data/lib/teapot/project.rb
CHANGED
@@ -42,10 +42,10 @@ module Teapot
|
|
42
42
|
super
|
43
43
|
end
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
45
|
+
attr_accessor :summary
|
46
|
+
attr_accessor :license
|
47
|
+
attr_accessor :website
|
48
|
+
attr_accessor :version
|
49
49
|
|
50
50
|
attr :authors
|
51
51
|
|
data/lib/teapot/repository.rb
CHANGED
data/lib/teapot/target.rb
CHANGED
@@ -25,6 +25,8 @@ require_relative 'definition'
|
|
25
25
|
require 'build/environment'
|
26
26
|
require 'build/rulebook'
|
27
27
|
|
28
|
+
require 'pry'
|
29
|
+
|
28
30
|
module Teapot
|
29
31
|
class BuildError < StandardError
|
30
32
|
end
|
@@ -49,22 +51,12 @@ module Teapot
|
|
49
51
|
super
|
50
52
|
end
|
51
53
|
|
52
|
-
# Given a configuration, compute the dependency chain for this target.
|
53
|
-
def provision_chain(configuration)
|
54
|
-
# Reduce the number of keystrokes for good health:
|
55
|
-
context = configuration.context
|
56
|
-
|
57
|
-
chain = Dependency::chain(context.selection, self.dependencies, context.targets.values)
|
58
|
-
end
|
59
|
-
|
60
54
|
# Given a specific configuration, generate the build environment based on this target and it's provision chain.
|
61
|
-
def environment(configuration)
|
62
|
-
chain =
|
63
|
-
|
64
|
-
environments = []
|
55
|
+
def environment(configuration, chain)
|
56
|
+
chain = chain.partial(self)
|
65
57
|
|
66
58
|
# Calculate the dependency chain's ordered environments:
|
67
|
-
environments
|
59
|
+
environments = chain.provisions.collect do |provision|
|
68
60
|
Build::Environment.new(&provision.value)
|
69
61
|
end
|
70
62
|
|
@@ -79,9 +71,6 @@ module Teapot
|
|
79
71
|
end
|
80
72
|
end
|
81
73
|
|
82
|
-
# Legacy method name.
|
83
|
-
alias environment_for_configuration environment
|
84
|
-
|
85
74
|
def build(&block)
|
86
75
|
if block_given?
|
87
76
|
@build = block
|
data/lib/teapot/version.rb
CHANGED
data/spec/teapot/target_spec.rb
CHANGED
@@ -40,7 +40,7 @@ module Teapot::TargetSpec
|
|
40
40
|
expect(chain.ordered[2].name).to be == 'Test/TargetSpec'
|
41
41
|
expect(chain.ordered[2].provider).to be == target
|
42
42
|
|
43
|
-
environment = target.environment(context.configuration)
|
43
|
+
environment = target.environment(context.configuration, chain)
|
44
44
|
# Environment#to_hash flattens the environment and evaluates all values:
|
45
45
|
hash = environment.to_hash
|
46
46
|
|
data/spec/teapot/wait_spec.rb
CHANGED
@@ -39,7 +39,7 @@ module Teapot::WaitSpec
|
|
39
39
|
ordered.each do |resolution|
|
40
40
|
target = resolution.provider
|
41
41
|
|
42
|
-
environment = target.environment(context.configuration)
|
42
|
+
environment = target.environment(context.configuration, chain)
|
43
43
|
|
44
44
|
if target.build
|
45
45
|
controller.add_target(target, environment.flatten)
|
@@ -48,8 +48,6 @@ module Teapot::WaitSpec
|
|
48
48
|
end
|
49
49
|
|
50
50
|
controller.update
|
51
|
-
|
52
|
-
puts $log
|
53
51
|
end
|
54
52
|
end
|
55
53
|
end
|
data/teapot.gemspec
CHANGED
@@ -24,15 +24,16 @@ Gem::Specification.new do |spec|
|
|
24
24
|
|
25
25
|
spec.has_rdoc = 'yard'
|
26
26
|
|
27
|
-
spec.required_ruby_version = '>= 2.1'
|
27
|
+
spec.required_ruby_version = '>= 2.1.0'
|
28
28
|
|
29
29
|
spec.add_dependency "rainbow", "~> 2.0"
|
30
|
-
spec.add_dependency "system", "~> 0.1.3"
|
31
30
|
|
32
|
-
spec.add_dependency "graphviz", "~> 0.
|
31
|
+
spec.add_dependency "graphviz", "~> 0.4"
|
33
32
|
|
34
|
-
spec.add_dependency "build", "~> 1.0
|
35
|
-
spec.add_dependency "build-files", "~> 1.0
|
33
|
+
spec.add_dependency "build", "~> 1.0"
|
34
|
+
spec.add_dependency "build-files", "~> 1.0"
|
35
|
+
spec.add_dependency "build-dependency", "~> 1.1"
|
36
|
+
spec.add_dependency "build-uri", "~> 1.0"
|
36
37
|
|
37
38
|
spec.add_dependency "facets", "~> 3.1"
|
38
39
|
spec.add_dependency "samovar", "~> 1.2"
|
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.
|
4
|
+
version: 1.3.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:
|
11
|
+
date: 2017-04-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rainbow
|
@@ -25,61 +25,75 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '2.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: graphviz
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.
|
33
|
+
version: '0.4'
|
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: '0.4'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: build
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '0
|
47
|
+
version: '1.0'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '0
|
54
|
+
version: '1.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name: build
|
56
|
+
name: build-files
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 1.0
|
61
|
+
version: '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: 1.0
|
68
|
+
version: '1.0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name: build-
|
70
|
+
name: build-dependency
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.1'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.1'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: build-uri
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
72
86
|
requirements:
|
73
87
|
- - "~>"
|
74
88
|
- !ruby/object:Gem::Version
|
75
|
-
version: 1.0
|
89
|
+
version: '1.0'
|
76
90
|
type: :runtime
|
77
91
|
prerelease: false
|
78
92
|
version_requirements: !ruby/object:Gem::Requirement
|
79
93
|
requirements:
|
80
94
|
- - "~>"
|
81
95
|
- !ruby/object:Gem::Version
|
82
|
-
version: 1.0
|
96
|
+
version: '1.0'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: facets
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -167,6 +181,7 @@ files:
|
|
167
181
|
- ".travis.yml"
|
168
182
|
- Gemfile
|
169
183
|
- Gemfile.local
|
184
|
+
- PLANNING.md
|
170
185
|
- README.md
|
171
186
|
- Rakefile
|
172
187
|
- bin/teapot
|
@@ -202,7 +217,6 @@ files:
|
|
202
217
|
- spec/teapot/command_spec.rb
|
203
218
|
- spec/teapot/context_spec.rb
|
204
219
|
- spec/teapot/context_spec/teapot.rb
|
205
|
-
- spec/teapot/dependency_spec.rb
|
206
220
|
- spec/teapot/generator_spec.rb
|
207
221
|
- spec/teapot/generator_spec/teapot.rb
|
208
222
|
- spec/teapot/generator_spec/template/$NAME.txt
|
@@ -227,7 +241,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
227
241
|
requirements:
|
228
242
|
- - ">="
|
229
243
|
- !ruby/object:Gem::Version
|
230
|
-
version:
|
244
|
+
version: 2.1.0
|
231
245
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
232
246
|
requirements:
|
233
247
|
- - ">="
|
@@ -235,7 +249,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
235
249
|
version: '0'
|
236
250
|
requirements: []
|
237
251
|
rubyforge_project:
|
238
|
-
rubygems_version: 2.
|
252
|
+
rubygems_version: 2.6.10
|
239
253
|
signing_key:
|
240
254
|
specification_version: 4
|
241
255
|
summary: Teapot is a tool for managing complex cross-platform builds.
|
@@ -243,7 +257,6 @@ test_files:
|
|
243
257
|
- spec/teapot/command_spec.rb
|
244
258
|
- spec/teapot/context_spec.rb
|
245
259
|
- spec/teapot/context_spec/teapot.rb
|
246
|
-
- spec/teapot/dependency_spec.rb
|
247
260
|
- spec/teapot/generator_spec.rb
|
248
261
|
- spec/teapot/generator_spec/teapot.rb
|
249
262
|
- spec/teapot/generator_spec/template/$NAME.txt
|
@@ -1,166 +0,0 @@
|
|
1
|
-
# Copyright, 2012, 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 'teapot/dependency'
|
22
|
-
|
23
|
-
module Teapot::DependencySpec
|
24
|
-
class BasicDependency
|
25
|
-
include Teapot::Dependency
|
26
|
-
|
27
|
-
def initialize(name = nil)
|
28
|
-
@name = name
|
29
|
-
end
|
30
|
-
|
31
|
-
attr :name
|
32
|
-
|
33
|
-
def inspect
|
34
|
-
"<BasicDependency:#{@name}>"
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
describe Teapot::Dependency do
|
39
|
-
it "Should resolve dependency chain" do
|
40
|
-
a = BasicDependency.new
|
41
|
-
|
42
|
-
a.provides 'apple' do
|
43
|
-
fruit ['apple']
|
44
|
-
end
|
45
|
-
|
46
|
-
b = BasicDependency.new
|
47
|
-
|
48
|
-
b.provides 'orange' do
|
49
|
-
fruit ['orange']
|
50
|
-
end
|
51
|
-
|
52
|
-
c = BasicDependency.new
|
53
|
-
|
54
|
-
c.provides 'fruit-juice' do
|
55
|
-
juice ['ice', 'cold']
|
56
|
-
end
|
57
|
-
|
58
|
-
c.depends 'apple'
|
59
|
-
c.depends 'orange'
|
60
|
-
|
61
|
-
chain = Teapot::Dependency::chain([], ['fruit-juice'], [a, b, c])
|
62
|
-
expect(chain.ordered.collect(&:first)).to be == [a, b, c]
|
63
|
-
|
64
|
-
d = BasicDependency.new
|
65
|
-
|
66
|
-
d.provides 'pie' do
|
67
|
-
end
|
68
|
-
|
69
|
-
d.depends 'apple'
|
70
|
-
|
71
|
-
chain = Teapot::Dependency::chain([], ['pie'], [a, b, c, d])
|
72
|
-
|
73
|
-
expect(chain.unresolved).to be == []
|
74
|
-
expect(chain.ordered.collect(&:first)).to be == [a, d]
|
75
|
-
end
|
76
|
-
|
77
|
-
it "should report conflicts" do
|
78
|
-
apple = BasicDependency.new('apple')
|
79
|
-
apple.provides 'apple'
|
80
|
-
apple.provides 'fruit'
|
81
|
-
|
82
|
-
bananna = BasicDependency.new('bananna')
|
83
|
-
bananna.provides 'fruit'
|
84
|
-
|
85
|
-
salad = BasicDependency.new('salad')
|
86
|
-
salad.depends 'fruit'
|
87
|
-
salad.provides 'salad'
|
88
|
-
|
89
|
-
chain = Teapot::Dependency::Chain.new([], ['salad'], [apple, bananna, salad])
|
90
|
-
expect(chain.unresolved.first).to be == ["fruit", salad]
|
91
|
-
expect(chain.conflicts).to be == {"fruit" => [apple, bananna]}
|
92
|
-
|
93
|
-
chain = Teapot::Dependency::Chain.new(['apple'], ['salad'], [apple, bananna, salad])
|
94
|
-
expect(chain.unresolved).to be == []
|
95
|
-
expect(chain.conflicts).to be == {}
|
96
|
-
end
|
97
|
-
|
98
|
-
it "should resolve aliases" do
|
99
|
-
apple = BasicDependency.new('apple')
|
100
|
-
apple.provides 'apple'
|
101
|
-
apple.provides :fruit => 'apple'
|
102
|
-
|
103
|
-
bananna = BasicDependency.new('bananna')
|
104
|
-
bananna.provides 'bananna'
|
105
|
-
bananna.provides :fruit => 'bananna'
|
106
|
-
|
107
|
-
salad = BasicDependency.new('salad')
|
108
|
-
salad.depends :fruit
|
109
|
-
salad.provides 'salad'
|
110
|
-
|
111
|
-
chain = Teapot::Dependency::chain(['apple'], ['salad'], [apple, bananna, salad])
|
112
|
-
expect(chain.unresolved).to be == []
|
113
|
-
expect(chain.conflicts).to be == {}
|
114
|
-
|
115
|
-
expect(chain.ordered.size).to be == 2
|
116
|
-
expect(chain.ordered[0]).to be == Teapot::Dependency::Resolution.new(apple, "apple")
|
117
|
-
expect(chain.ordered[1]).to be == Teapot::Dependency::Resolution.new(salad, "salad")
|
118
|
-
end
|
119
|
-
|
120
|
-
it "should select dependencies with high priority" do
|
121
|
-
bad_apple = BasicDependency.new('bad_apple')
|
122
|
-
bad_apple.provides 'apple'
|
123
|
-
bad_apple.priority = 20
|
124
|
-
|
125
|
-
good_apple = BasicDependency.new('good_apple')
|
126
|
-
good_apple.provides 'apple'
|
127
|
-
good_apple.priority = 40
|
128
|
-
|
129
|
-
chain = Teapot::Dependency::chain([], ['apple'], [bad_apple, good_apple])
|
130
|
-
|
131
|
-
expect(chain.unresolved).to be == []
|
132
|
-
expect(chain.conflicts).to be == {}
|
133
|
-
|
134
|
-
# Should select higher priority package by default:
|
135
|
-
expect(chain.ordered).to be == [Teapot::Dependency::Resolution.new(good_apple, 'apple')]
|
136
|
-
end
|
137
|
-
|
138
|
-
it "should expose direct dependencies" do
|
139
|
-
system = BasicDependency.new('linux')
|
140
|
-
system.provides 'linux'
|
141
|
-
system.provides 'clang'
|
142
|
-
system.provides system: 'linux'
|
143
|
-
system.provides compiler: 'clang'
|
144
|
-
|
145
|
-
library = BasicDependency.new('library')
|
146
|
-
library.provides 'library'
|
147
|
-
library.depends :system
|
148
|
-
library.depends :compiler
|
149
|
-
|
150
|
-
application = BasicDependency.new('application')
|
151
|
-
application.provides 'application'
|
152
|
-
application.depends :compiler
|
153
|
-
application.depends 'library'
|
154
|
-
|
155
|
-
chain = Teapot::Dependency::chain([], ['application'], [system, library, application])
|
156
|
-
|
157
|
-
expect(chain.unresolved).to be == []
|
158
|
-
expect(chain.conflicts).to be == {}
|
159
|
-
expect(chain.ordered).to be == [
|
160
|
-
Teapot::Dependency::Resolution.new(system, 'clang'),
|
161
|
-
Teapot::Dependency::Resolution.new(library, 'library'),
|
162
|
-
Teapot::Dependency::Resolution.new(application, 'application'),
|
163
|
-
]
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end
|