teapot 1.2.6 → 1.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 +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
|