build-dependency 1.1.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6cb795ff1a6fb70f25f646a916bbdb307bc56646
4
- data.tar.gz: 54665a26d16a62df4161e21d6a8ad32c31101a7a
3
+ metadata.gz: d6f603e98eff139ae1a2c9441230b28d3202441c
4
+ data.tar.gz: f86fea3553677fee83f22c35d6629cde9c43c23e
5
5
  SHA512:
6
- metadata.gz: a86ff842300e174fe0eb00696a0c8ffee6141e28b9777009901c088004fe92c668ba391c3f3166eb07451c108bc5090cbf8028beb12ea67d3a41aa8bf25b2090
7
- data.tar.gz: 9c0e120c21cd393dd9f647e6e7b98206ec76ea43f573a5db6d6965ae3bd206988058321a061fdf7c93a0170c12da95d000ee956502180832354ddab1dc5b1aab
6
+ metadata.gz: 68c4d6b209f8162094851ffb5f7ee605832ba1549941953357d971274950ae4d76b179a9b3925ced730a0a55307a3648522c9d537813cee946feb2a6832ce62d
7
+ data.tar.gz: 1dfb5cbc65105925cf37ee020619db04bed17e9649cc79f66c354cd325efb67227ae891ccdce52f0b6de84c90cd0237f3b538fa9f646867bd636aa44b1af55a5
data/.rspec CHANGED
@@ -1,4 +1,3 @@
1
- --color
2
1
  --format documentation
3
2
  --backtrace
4
3
  --warnings
data/README.md CHANGED
@@ -40,6 +40,147 @@ The orange box is the top level dependency, the grey box is an alias, the blue b
40
40
 
41
41
  To create your own dependency graph, you need to expose a model object which represents something that has dependencies and can be depended on.
42
42
 
43
+ Here is an example of a package model for Arch Linux `PKGBUILD` files:
44
+
45
+ ```ruby
46
+ # A specific package.
47
+ class Package
48
+ include Build::Dependency
49
+
50
+ def initialize(path, metadata)
51
+ @path = path
52
+ @metadata = metadata
53
+
54
+ metadata.each do |key, value|
55
+ case key
56
+ when 'pkgname'
57
+ @name = value
58
+ when 'depends'
59
+ self.depends(value)
60
+ when 'provides'
61
+ self.provides(value)
62
+ when 'pkgver'
63
+ @pkgver = value
64
+ when 'pkgrel'
65
+ @pkgrel = value
66
+ when 'arch'
67
+ @arch = value
68
+ end
69
+ end
70
+
71
+ @name ||= File.basename(path)
72
+ self.provides(@name)
73
+ end
74
+
75
+ attr :path
76
+ attr :name
77
+ attr :metadata
78
+
79
+ def package_path
80
+ File.join(@path, package_file)
81
+ end
82
+
83
+ def package_file
84
+ "#{@name}-#{@pkgver}-#{@pkgrel}-#{@arch}.pkg.tar.xz"
85
+ end
86
+ end
87
+
88
+ # A context represents a directory full of packages.
89
+ class Context
90
+ def initialize(path)
91
+ @path = path
92
+ @packages = {}
93
+
94
+ load_packages!
95
+ end
96
+
97
+ def packages_path
98
+ @path
99
+ end
100
+
101
+ attr :packages
102
+
103
+ def load_packages!
104
+ Dir.foreach(packages_path) do |package_name|
105
+ next if package_name.start_with?('.')
106
+
107
+ package_path = File.join(packages_path, package_name)
108
+ next unless File.directory?(package_path)
109
+
110
+ LOGGER.info "Loading #{package_path}..."
111
+ output, status = Open3.capture2("makepkg", "--printsrcinfo", chdir: package_path)
112
+
113
+ metadata = output.lines.collect(&:strip).delete_if(&:empty?).collect{|line| line.split(/\s*=\s*/, 2)}
114
+
115
+ package = Package.new(package_path, metadata)
116
+ @packages[package.name] = package
117
+
118
+ if package.name != package_name
119
+ LOGGER.warn "Package in directory #{package_name} has pkgname of #{package.name}!"
120
+ end
121
+ end
122
+ end
123
+
124
+ # Compute the dependency chain for the selection of packages.
125
+ def provision_chain(selection)
126
+ Build::Dependency::Chain.new(selection, @packages.values, selection)
127
+ end
128
+ end
129
+ ```
130
+
131
+ ### Chains
132
+
133
+ A chain represents a list of resolved packages. You generate a chain from a list of dependencies, a list of all available packages, and a selection of packages which help to resolve ambiguities (e.g. if two packages provide the same target, selection and then priority is used to resolve the ambiguity).
134
+
135
+ Here is a rake task for the above model which can build a directory of packages including both local PKGBUILDs and upstream packages:
136
+
137
+ ```ruby
138
+ desc "Build a deployment of packages, specify the root package using TARGET="
139
+ task :collect do
140
+ target = ENV['TARGET'] or fail("Please supply TARGET=")
141
+
142
+ LOGGER.info "Resolving packages for #{target}"
143
+
144
+ context = Servers::Context.new(__dir__)
145
+
146
+ chain = context.provision_chain([target])
147
+
148
+ deploy_root = File.join(__dir__, "../deploy", target)
149
+
150
+ FileUtils::Verbose.rm_rf deploy_root
151
+ FileUtils::Verbose.mkdir_p deploy_root
152
+
153
+ system_packages = Set.new
154
+
155
+ # Depdencies that could not be resolved by our local packages must be resolved the system:
156
+ chain.unresolved.each do |(depends, source)|
157
+ output, status = Open3.capture2("pactree", "-lsu", depends.name)
158
+
159
+ abort "Failed to resolve dependency tree for package #{depends.name}" unless status.success?
160
+
161
+ system_packages += output.split(/\s+/)
162
+ end
163
+
164
+ # Copy system packages from pacman repositories:
165
+ Dir.chdir(deploy_root) do
166
+ Open3.pipeline(
167
+ ["pacman", "-Sp", *system_packages.to_a],
168
+ ['wget', '-nv', '-i', '-'],
169
+ )
170
+ end
171
+
172
+ # Copy local packages:
173
+ chain.ordered.each do |resolution|
174
+ package = resolution.provider
175
+ FileUtils::Verbose.cp package.package_path, File.join(deploy_root, package.package_file)
176
+ end
177
+ end
178
+ ```
179
+
180
+ # Wildcards
181
+
182
+ It's possible to include wildcards in the dependency name. This is useful if you use scoped names, e.g. `Test/*` would depend on all test targets. The wildcard matching is done by `File.fnmatch?`.
183
+
43
184
  ## Contributing
44
185
 
45
186
  1. Fork it
@@ -18,6 +18,6 @@ Gem::Specification.new do |spec|
18
18
  spec.add_dependency "graphviz"
19
19
 
20
20
  spec.add_development_dependency "bundler", "~> 1.3"
21
- spec.add_development_dependency "rspec", "~> 3.4.0"
21
+ spec.add_development_dependency "rspec", "~> 3.6"
22
22
  spec.add_development_dependency "rake"
23
23
  end
@@ -18,112 +18,10 @@
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 'set'
21
+ require_relative 'resolver'
22
22
 
23
23
  module Build
24
24
  module Dependency
25
- class UnresolvedDependencyError < StandardError
26
- def initialize(chain)
27
- super "Unresolved dependency chain: #{chain.unresolved.inspect}!"
28
-
29
- @chain = chain
30
- end
31
-
32
- attr :chain
33
- end
34
-
35
- TOP = Depends.new("<top>").freeze
36
-
37
- class Resolver
38
- def initialize
39
- @resolved = {}
40
- @ordered = []
41
- @provisions = []
42
- @unresolved = []
43
- @conflicts = {}
44
- end
45
-
46
- attr :resolved
47
- attr :ordered
48
- attr :provisions
49
- attr :unresolved
50
- attr :conflicts
51
-
52
- def freeze
53
- return unless frozen?
54
-
55
- @resolved.freeze
56
- @ordered.freeze
57
- @provisions.freeze
58
- @unresolved.freeze
59
- @conflicts.freeze
60
-
61
- super
62
- end
63
-
64
- protected
65
-
66
- def expand_nested(dependencies, provider)
67
- dependencies.each do |dependency|
68
- expand(Depends[dependency], provider)
69
- end
70
- end
71
-
72
- def expand(dependency, parent)
73
- # puts "** Expanding #{dependency.inspect} from #{parent.inspect} (private: #{dependency.private?})"
74
-
75
- if @resolved.include?(dependency)
76
- # puts "** Already resolved dependency!"
77
-
78
- return
79
- end
80
-
81
- provider = find_provider(dependency, parent)
82
-
83
- if provider == nil
84
- # puts "** Couldn't find provider -> unresolved"
85
- @unresolved << [dependency, parent]
86
- return nil
87
- end
88
-
89
- provision = provision_for(provider, dependency)
90
-
91
- # We will now satisfy this dependency by satisfying any dependent dependencies, but we no longer need to revisit this one.
92
- # puts "** Resolved #{dependency} (#{provision.inspect})"
93
- @resolved[dependency] = provider
94
-
95
- # If the provision was an Alias, make sure to resolve the alias first:
96
- if provision.alias?
97
- # puts "** Resolving alias #{provision} (#{provision.dependencies.inspect})"
98
- expand_nested(provision.dependencies, provider)
99
- end
100
-
101
- # puts "** Checking for #{provider.inspect} in #{resolved.inspect}"
102
- unless @resolved.include?(provider)
103
- # We are now satisfying the provider by expanding all its own dependencies:
104
- @resolved[provider] = provision
105
-
106
- # Make sure we satisfy the provider's dependencies first:
107
- expand_nested(provider.dependencies, provider)
108
-
109
- # puts "** Appending #{dependency} -> ordered"
110
-
111
- # Add the provider to the ordered list.
112
- @ordered << Resolution.new(provider, dependency)
113
- end
114
-
115
- # This goes here because we want to ensure 1/ that if
116
- unless provision == nil or provision.alias?
117
- # puts "** Appending #{dependency} -> provisions"
118
-
119
- # Add the provision to the set of required provisions.
120
- @provisions << provision
121
- end
122
-
123
- # For both @ordered and @provisions, we ensure that for [...xs..., x, ...], x is satisfied by ...xs....
124
- end
125
- end
126
-
127
25
  class Chain < Resolver
128
26
  # An `UnresolvedDependencyError` will be thrown if there are any unresolved dependencies.
129
27
  def self.expand(*args)
@@ -190,13 +88,32 @@ module Build
190
88
  return viable_providers.select{|provider| @selection.include? provider.name}
191
89
  end
192
90
 
193
- def find_provider(dependency, parent)
91
+ def expand_wildcard(dependency, parent)
92
+ @providers.flat_map do |provider|
93
+ provider.filter(dependency).flat_map do |name, provision|
94
+ expand_dependency(Depends[name], parent)
95
+ end
96
+ end
97
+ end
98
+
99
+ # Resolve a dependency into one or more provisions:
100
+ def expand_dependency(dependency, parent)
101
+ if dependency.wildcard?
102
+ return expand_wildcard(dependency, parent)
103
+ end
104
+
194
105
  # Mostly, only one package will satisfy the dependency...
195
106
  viable_providers = @providers.select{|provider| provider.provides? dependency}
196
107
 
197
108
  # puts "** Found #{viable_providers.collect(&:name).join(', ')} viable providers."
198
109
 
199
- if viable_providers.size > 1
110
+ if viable_providers.size == 1
111
+ provider = viable_providers.first
112
+ provision = provision_for(provider, dependency)
113
+
114
+ # The best outcome, a specific provider was named:
115
+ return [provision]
116
+ elsif viable_providers.size > 1
200
117
  # ... however in some cases (typically where aliases are being used) an explicit selection must be made for the build to work correctly.
201
118
  explicit_providers = filter_by_selection(viable_providers)
202
119
 
@@ -212,18 +129,19 @@ module Build
212
129
  if explicit_providers.size == 0
213
130
  # No provider was explicitly specified, thus we require explicit conflict resolution:
214
131
  @conflicts[dependency] = viable_providers
215
- return nil
216
132
  elsif explicit_providers.size == 1
133
+ provider = explicit_providers.first
134
+ provision = provision_for(provider, dependency)
135
+
217
136
  # The best outcome, a specific provider was named:
218
- return explicit_providers.first
137
+ return [provision]
219
138
  else
220
139
  # Multiple providers were explicitly mentioned that satisfy the dependency.
221
140
  @conflicts[dependency] = explicit_providers
222
- return nil
223
141
  end
224
- else
225
- return viable_providers.first
226
142
  end
143
+
144
+ return []
227
145
  end
228
146
 
229
147
  def provision_for(provider, dependency)
@@ -82,7 +82,7 @@ module Build
82
82
  super(dependency, parent)
83
83
  end
84
84
 
85
- def find_provider(dependency, parent)
85
+ def expand_dependency(dependency, parent)
86
86
  @chain.resolved[dependency]
87
87
  end
88
88
 
@@ -47,11 +47,15 @@ module Build
47
47
  end
48
48
  end
49
49
 
50
- Resolution = Struct.new(:provider, :dependency) do
50
+ Resolution = Struct.new(:provision, :dependency) do
51
51
  def name
52
52
  dependency.name
53
53
  end
54
54
 
55
+ def provider
56
+ provision.provider
57
+ end
58
+
55
59
  def to_s
56
60
  "resolution #{provider.name.inspect} -> #{dependency.name.inspect}"
57
61
  end
@@ -64,6 +68,18 @@ module Build
64
68
  @options = options
65
69
  end
66
70
 
71
+ def wildcard?
72
+ self.name.is_a?(String) and self.name.include?('*')
73
+ end
74
+
75
+ def match?(name)
76
+ if wildcard? and name.is_a?(String)
77
+ File.fnmatch?(self.name, name)
78
+ else
79
+ self.name == name
80
+ end
81
+ end
82
+
67
83
  attr :options
68
84
 
69
85
  def to_s
@@ -82,8 +98,12 @@ module Build
82
98
  name.is_a?(Symbol)
83
99
  end
84
100
 
85
- def self.[](name_or_dependency)
86
- name_or_dependency.is_a?(self) ? name_or_dependency : self.new(name_or_dependency)
101
+ class << self
102
+ undef []
103
+
104
+ def [](name_or_dependency)
105
+ name_or_dependency.is_a?(self) ? name_or_dependency : self.new(name_or_dependency)
106
+ end
87
107
  end
88
108
  end
89
109
 
@@ -97,12 +117,12 @@ module Build
97
117
  super
98
118
  end
99
119
 
100
- # Assign a priority to this unit.
120
+ # Assign a priority.
101
121
  def priority= value
102
122
  @priority = value
103
123
  end
104
124
 
105
- # The units default priority
125
+ # The default priority.
106
126
  def priority
107
127
  @priority ||= 0
108
128
  end
@@ -117,30 +137,48 @@ module Build
117
137
  @dependencies ||= Set.new
118
138
  end
119
139
 
140
+ def filter(dependency)
141
+ provisions.select{|name, provision| dependency.match?(name)}
142
+ end
143
+
120
144
  # Does this unit provide the named thing?
121
145
  def provides?(dependency)
122
146
  provisions.key?(dependency.name)
123
147
  end
124
148
 
125
149
  def provision_for(dependency)
126
- provisions[dependency.name]
150
+ return provisions[dependency.name]
151
+ end
152
+
153
+ def resolution_for(dependency)
154
+ return Resolution.new(provision_for(dependency), dependency)
127
155
  end
128
156
 
129
- # Mark this unit as providing the named thing, with an optional block.
130
- def provides(name_or_aliases, &block)
131
- if String === name_or_aliases || Symbol === name_or_aliases
132
- name = name_or_aliases
133
-
157
+ # Add one or more provisions to the provider.
158
+ # @param [Array<String>] names the named provisions to add.
159
+ # @param [Hash<Symbol, Array>] aliases the aliases to add.
160
+ # @example A named provision.
161
+ # target.provides "Compiler/clang" do
162
+ # cxx "clang"
163
+ # end
164
+ # @example A symbolic provision.
165
+ # target.provides compiler: "Compiler/clang"
166
+ def provides(*names, **aliases, &block)
167
+ names.each do |name|
134
168
  provisions[name] = Provision.new(name, self, block)
135
- else
136
- aliases = name_or_aliases
137
-
138
- aliases.each do |name, dependencies|
139
- provisions[name] = Alias.new(name, self, Array(dependencies))
140
- end
169
+ end
170
+
171
+ aliases.each do |name, dependencies|
172
+ provisions[name] = Alias.new(name, self, Array(dependencies))
141
173
  end
142
174
  end
143
175
 
176
+ # Add one or more dependencies to the provider.
177
+ # @param [Array<String>] names the dependency names to add.
178
+ # @example A named dependency.
179
+ # target.depends "Compiler/clang"
180
+ # @example A symbolic dependency.
181
+ # target.depends :compiler
144
182
  def depends(*names, **options)
145
183
  names.each do |name|
146
184
  dependencies << Depends.new(name, **options)
@@ -0,0 +1,133 @@
1
+ # Copyright, 2017, 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
+
23
+ module Build
24
+ module Dependency
25
+ class UnresolvedDependencyError < StandardError
26
+ def initialize(chain)
27
+ super "Unresolved dependency chain: #{chain.unresolved.inspect}!"
28
+
29
+ @chain = chain
30
+ end
31
+
32
+ attr :chain
33
+ end
34
+
35
+ TOP = Depends.new("<top>").freeze
36
+
37
+ class Resolver
38
+ def initialize
39
+ @resolved = {}
40
+ @ordered = []
41
+ @provisions = []
42
+ @unresolved = []
43
+ @conflicts = {}
44
+ end
45
+
46
+ attr :resolved
47
+ attr :ordered
48
+ attr :provisions
49
+ attr :unresolved
50
+ attr :conflicts
51
+
52
+ def freeze
53
+ return unless frozen?
54
+
55
+ @resolved.freeze
56
+ @ordered.freeze
57
+ @provisions.freeze
58
+ @unresolved.freeze
59
+ @conflicts.freeze
60
+
61
+ super
62
+ end
63
+
64
+ protected
65
+
66
+ def expand_nested(dependencies, provider)
67
+ dependencies.each do |dependency|
68
+ expand(Depends[dependency], provider)
69
+ end
70
+ end
71
+
72
+ def expand_provision(provision, dependency)
73
+ provider = provision.provider
74
+
75
+ # If the provision was an Alias, make sure to resolve the alias first:
76
+ if provision.alias?
77
+ # puts "** Resolving alias #{provision} (#{provision.dependencies.inspect})"
78
+ expand_nested(provision.dependencies, provider)
79
+ end
80
+
81
+ # puts "** Checking for #{provider.inspect} in #{resolved.inspect}"
82
+ unless @resolved.include?(provider)
83
+ # We are now satisfying the provider by expanding all its own dependencies:
84
+ @resolved[provider] = provision
85
+
86
+ # Make sure we satisfy the provider's dependencies first:
87
+ expand_nested(provider.dependencies, provider)
88
+
89
+ # puts "** Appending #{dependency} -> ordered"
90
+
91
+ # Add the provider to the ordered list.
92
+ @ordered << Resolution.new(provision, dependency)
93
+ end
94
+
95
+ # This goes here because we want to ensure 1/ that if
96
+ unless provision == nil or provision.alias?
97
+ # puts "** Appending #{dependency} -> provisions"
98
+
99
+ # Add the provision to the set of required provisions.
100
+ @provisions << provision
101
+ end
102
+
103
+ # For both @ordered and @provisions, we ensure that for [...xs..., x, ...], x is satisfied by ...xs....
104
+ end
105
+
106
+ def expand(dependency, parent)
107
+ # puts "** Expanding #{dependency.inspect} from #{parent.inspect} (private: #{dependency.private?})"
108
+
109
+ if @resolved.include?(dependency)
110
+ # puts "** Already resolved dependency!"
111
+
112
+ return nil
113
+ end
114
+
115
+ # The find_provider method is abstract in this base class.
116
+ provisions = expand_dependency(dependency, parent)
117
+
118
+ if provisions.empty?
119
+ # puts "** Couldn't resolve #{dependency}"
120
+ @unresolved << [dependency, parent]
121
+ else
122
+ # We will now satisfy this dependency by satisfying any dependent dependencies, but we no longer need to revisit this one.
123
+ # puts "** Resolved #{dependency}"
124
+ @resolved[dependency] = provisions
125
+
126
+ provisions.each do |provision|
127
+ expand_provision(provision, dependency)
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
@@ -20,6 +20,6 @@
20
20
 
21
21
  module Build
22
22
  module Dependency
23
- VERSION = "1.1.0"
23
+ VERSION = "1.2.1"
24
24
  end
25
25
  end
@@ -49,7 +49,13 @@ RSpec.describe Build::Dependency do
49
49
 
50
50
  it "should resolve direct dependency chain" do
51
51
  chain = Build::Dependency::Chain.expand(['fruit-juice'], [a, b, c])
52
- expect(chain.ordered.collect(&:first)).to be == [a, b, c]
52
+ expect(chain.ordered.collect(&:provider)).to be == [a, b, c]
53
+ expect(chain.unresolved).to be == []
54
+ end
55
+
56
+ it "should resolve wildcard dependency chain" do
57
+ chain = Build::Dependency::Chain.expand(['fruit-*'], [a, b, c])
58
+ expect(chain.ordered.collect(&:provider)).to be == [a, b, c]
53
59
  expect(chain.unresolved).to be == []
54
60
  end
55
61
 
@@ -64,7 +70,7 @@ RSpec.describe Build::Dependency do
64
70
  chain = Build::Dependency::Chain.expand(['pie'], [a, b, c, d])
65
71
 
66
72
  expect(chain.unresolved).to be == []
67
- expect(chain.ordered.collect(&:first)).to be == [a, d]
73
+ expect(chain.ordered.collect(&:provider)).to be == [a, d]
68
74
  end
69
75
 
70
76
  it "should format nicely" do
@@ -156,8 +162,8 @@ RSpec.describe Build::Dependency do
156
162
  expect(chain.conflicts).to be == {}
157
163
 
158
164
  expect(chain.ordered.size).to be == 2
159
- expect(chain.ordered[0]).to be == Build::Dependency::Resolution.new(apple, Build::Dependency::Depends.new("apple"))
160
- expect(chain.ordered[1]).to be == Build::Dependency::Resolution.new(salad, Build::Dependency::Depends.new("salad"))
165
+ expect(chain.ordered[0].provider).to be == apple
166
+ expect(chain.ordered[1].provider).to be == salad
161
167
  end
162
168
 
163
169
  it "should select dependencies with high priority" do
@@ -175,7 +181,9 @@ RSpec.describe Build::Dependency do
175
181
  expect(chain.conflicts).to be == {}
176
182
 
177
183
  # Should select higher priority package by default:
178
- expect(chain.ordered).to be == [Build::Dependency::Resolution.new(good_apple, Build::Dependency::Depends.new('apple'))]
184
+ expect(chain.ordered).to be == [good_apple.resolution_for(
185
+ Build::Dependency::Depends['apple']
186
+ )]
179
187
  end
180
188
 
181
189
  it "should expose direct dependencies" do
@@ -200,9 +208,9 @@ RSpec.describe Build::Dependency do
200
208
  expect(chain.unresolved).to be == []
201
209
  expect(chain.conflicts).to be == {}
202
210
  expect(chain.ordered).to be == [
203
- Build::Dependency::Resolution.new(system, Build::Dependency::Depends.new('clang')),
204
- Build::Dependency::Resolution.new(library, Build::Dependency::Depends.new('library')),
205
- Build::Dependency::Resolution.new(application, Build::Dependency::Depends.new('application')),
211
+ system.resolution_for(Build::Dependency::Depends.new('clang')),
212
+ library.resolution_for(Build::Dependency::Depends.new('library')),
213
+ application.resolution_for(Build::Dependency::Depends.new('application')),
206
214
  ]
207
215
  end
208
216
  end
@@ -26,11 +26,11 @@ RSpec.describe Build::Dependency::PartialChain do
26
26
 
27
27
  it "should generate full list of ordered providers" do
28
28
  expect(chain.ordered).to be == [
29
- Build::Dependency::Resolution.new(variant, Build::Dependency::Depends.new('Variant/debug')),
30
- Build::Dependency::Resolution.new(platform, Build::Dependency::Depends.new('Platform/linux')),
31
- Build::Dependency::Resolution.new(compiler, Build::Dependency::Depends.new("Language/C++17")),
32
- Build::Dependency::Resolution.new(lib, Build::Dependency::Depends.new('lib')),
33
- Build::Dependency::Resolution.new(app, Build::Dependency::Depends.new('app')),
29
+ variant.resolution_for(Build::Dependency::Depends.new('Variant/debug')),
30
+ platform.resolution_for(Build::Dependency::Depends.new('Platform/linux')),
31
+ compiler.resolution_for(Build::Dependency::Depends.new("Language/C++17")),
32
+ lib.resolution_for(Build::Dependency::Depends.new('lib')),
33
+ app.resolution_for(Build::Dependency::Depends.new('app')),
34
34
  ]
35
35
  end
36
36
 
@@ -53,10 +53,10 @@ RSpec.describe Build::Dependency::PartialChain do
53
53
 
54
54
  it "should select app packages" do
55
55
  expect(subject.ordered).to be == [
56
- Build::Dependency::Resolution.new(variant, Build::Dependency::Depends.new('Variant/debug')),
57
- Build::Dependency::Resolution.new(platform, Build::Dependency::Depends.new('Platform/linux')),
58
- Build::Dependency::Resolution.new(lib, Build::Dependency::Depends.new('lib')),
59
- Build::Dependency::Resolution.new(compiler, Build::Dependency::Depends.new("Language/C++14")),
56
+ variant.resolution_for(Build::Dependency::Depends.new('Variant/debug')),
57
+ platform.resolution_for(Build::Dependency::Depends.new('Platform/linux')),
58
+ lib.resolution_for(Build::Dependency::Depends.new('lib')),
59
+ compiler.resolution_for(Build::Dependency::Depends.new("Language/C++14")),
60
60
  ]
61
61
 
62
62
  graph = visualization.generate(subject)
@@ -98,19 +98,19 @@ RSpec.describe Build::Dependency::PartialChain do
98
98
  it "should include direct private dependencies" do
99
99
  partial_chain = chain.partial(b)
100
100
 
101
- expect(partial_chain.ordered.collect(&:first)).to be == [a]
101
+ expect(partial_chain.ordered.collect(&:provider)).to be == [a]
102
102
  end
103
103
 
104
104
  it "shouldn't include nested private dependencies" do
105
105
  partial_chain = chain.partial(c)
106
106
 
107
- expect(partial_chain.ordered.collect(&:first)).to be == [b]
107
+ expect(partial_chain.ordered.collect(&:provider)).to be == [b]
108
108
  end
109
109
 
110
110
  it "should follow non-private dependencies" do
111
111
  partial_chain = chain.partial(d)
112
112
 
113
- expect(partial_chain.ordered.collect(&:first)).to be == [b, c]
113
+ expect(partial_chain.ordered.collect(&:provider)).to be == [b, c]
114
114
  end
115
115
  end
116
116
  end
@@ -55,7 +55,7 @@ RSpec.describe Build::Dependency::Provider do
55
55
  end
56
56
 
57
57
  it "should format nicely" do
58
- expect(subject.to_s).to be == 'provision "c"'
58
+ expect(subject.to_s).to be == 'provides "c"'
59
59
  end
60
60
  end
61
61
 
@@ -83,7 +83,7 @@ RSpec.describe Build::Dependency::Provider do
83
83
  end
84
84
 
85
85
  it "should format nicely" do
86
- expect(subject.to_s).to be == 'alias :platform -> "linux"'
86
+ expect(subject.to_s).to be == 'provides :platform -> "linux"'
87
87
  end
88
88
  end
89
89
  end
@@ -0,0 +1,43 @@
1
+ # Copyright, 2017, 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
+ RSpec.describe Build::Dependency do
22
+ describe "test packages" do
23
+ let(:a) do
24
+ Package.new('Library/Frobulate').tap do |package|
25
+ package.provides 'Test/Frobulate' do
26
+ end
27
+ end
28
+ end
29
+
30
+ let(:b) do
31
+ Package.new('Library/Barbulate').tap do |package|
32
+ package.provides 'Test/Barbulate' do
33
+ end
34
+ end
35
+ end
36
+
37
+ it "should resolve all tests" do
38
+ chain = Build::Dependency::Chain.expand(['Test/*'], [a, b])
39
+ expect(chain.ordered.collect(&:provider)).to be == [a, b]
40
+ expect(chain.unresolved).to be == []
41
+ end
42
+ end
43
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: build-dependency
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-04-27 00:00:00.000000000 Z
11
+ date: 2017-09-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: graphviz
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 3.4.0
47
+ version: '3.6'
48
48
  type: :development
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: 3.4.0
54
+ version: '3.6'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -81,11 +81,13 @@ files:
81
81
  - README.md
82
82
  - Rakefile
83
83
  - build-dependency.gemspec
84
+ - build_system_rules_and_algorithms.pdf
84
85
  - full.svg
85
86
  - lib/build/dependency.rb
86
87
  - lib/build/dependency/chain.rb
87
88
  - lib/build/dependency/partial_chain.rb
88
89
  - lib/build/dependency/provider.rb
90
+ - lib/build/dependency/resolver.rb
89
91
  - lib/build/dependency/version.rb
90
92
  - lib/build/dependency/visualization.rb
91
93
  - partial.svg
@@ -93,6 +95,7 @@ files:
93
95
  - spec/build/dependency/partial_chain_spec.rb
94
96
  - spec/build/dependency/provider_spec.rb
95
97
  - spec/build/dependency/visualization_spec.rb
98
+ - spec/build/dependency/wildcard_spec.rb
96
99
  - spec/spec_helper.rb
97
100
  - visualization.svg
98
101
  homepage: ''
@@ -115,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
115
118
  version: '0'
116
119
  requirements: []
117
120
  rubyforge_project:
118
- rubygems_version: 2.6.10
121
+ rubygems_version: 2.6.12
119
122
  signing_key:
120
123
  specification_version: 4
121
124
  summary: A set of data structures and algorithms for dependency resolution.
@@ -124,4 +127,5 @@ test_files:
124
127
  - spec/build/dependency/partial_chain_spec.rb
125
128
  - spec/build/dependency/provider_spec.rb
126
129
  - spec/build/dependency/visualization_spec.rb
130
+ - spec/build/dependency/wildcard_spec.rb
127
131
  - spec/spec_helper.rb