build-dependency 1.1.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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