build-dependency 1.0.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2d91dfd64e537d8898ffdd3c1202e3f6a7a28ecd
4
+ data.tar.gz: 9da62e2d3c498ff5f3ddceb353bd4b988ffad174
5
+ SHA512:
6
+ metadata.gz: 871cc55a5997ab29148b52f2df6fa6f56c2ed1c495f5653a338bd62f0307790e54e8545969b3878f40f87e481910da7bce7f258713529d4c7c998edc51ab99dc
7
+ data.tar.gz: a5a62b09a6592e0f44017c53e0fb4560f68a06d2d4613e16657272fc8bb71a4cf80faba2612afc1ca2685a9b40a450fa195a5a3435e05f03ae5a7d59bc3519f0
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,5 @@
1
+ --color
2
+ --format documentation
3
+ --backtrace
4
+ --warnings
5
+ --require spec_helper
@@ -0,0 +1,19 @@
1
+ language: ruby
2
+ sudo: false
3
+ dist: trusty
4
+ cache: bundler
5
+ rvm:
6
+ - 2.1.8
7
+ - 2.2.4
8
+ - 2.3.0
9
+ - 2.4.0
10
+ - ruby-head
11
+ - jruby-head
12
+ matrix:
13
+ allow_failures:
14
+ - rvm: "ruby-head"
15
+ - rvm: "jruby-head"
16
+ addons:
17
+ apt:
18
+ packages:
19
+ - graphviz
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in build-dependency.gemspec
4
+ gemspec
5
+
6
+ group :development do
7
+ gem 'guard'
8
+ gem 'guard-rspec'
9
+ end
10
+
11
+ group :test do
12
+ gem 'simplecov'
13
+ gem 'coveralls', require: false
14
+ end
@@ -0,0 +1,9 @@
1
+
2
+ directories %w(lib spec)
3
+ clearing :on
4
+
5
+ guard :rspec, cmd: "bundle exec rspec" do
6
+ watch(%r{^spec/.+_spec\.rb$})
7
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
8
+ watch("spec/spec_helper.rb") { "spec" }
9
+ end
@@ -0,0 +1,73 @@
1
+ # Build::Dependency
2
+
3
+ Build::Dependency provides dependency resolution algorithms.
4
+
5
+ [![Build Status](https://secure.travis-ci.org/ioquatix/build-dependency.svg)](http://travis-ci.org/ioquatix/build-dependency)
6
+ [![Code Climate](https://codeclimate.com/github/ioquatix/build-dependency.svg)](https://codeclimate.com/github/ioquatix/build-dependency)
7
+ [![Coverage Status](https://coveralls.io/repos/ioquatix/build-dependency/badge.svg)](https://coveralls.io/r/ioquatix/build-dependency)
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'build-dependency'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install build-dependency
22
+
23
+ ## Usage
24
+
25
+ A dependency graph is a DAG (directed acyclic graph), such that if `A` depends on `B`, `A` has an edge pointing to `B`.
26
+
27
+ A dependency list is an ordered list of dependencies, such that if `A` depends on `B`, `B` will be listed earlier than `A`.
28
+
29
+ A dependency chain is the result of traversing the dependency graph from a given set of dependencies. It contains an ordered list of providers, a list of specific provisions.
30
+
31
+ ![Full Dependency Graph](full.svg)
32
+
33
+ A private dependency is not traversed when creating a partial chain. When building a partial chain for `app`, we don't follow `lib`'s private dependency on `Language/C++17`.
34
+
35
+ ![Partial Dependency Graph](partial.svg)
36
+
37
+ The orange box is the top level dependency, the grey box is an alias, the blue box is a specific provider, and the bold boxes are specific provisions which are being included.
38
+
39
+ ### Model
40
+
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
+
43
+ ## Contributing
44
+
45
+ 1. Fork it
46
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
47
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
48
+ 4. Push to the branch (`git push origin my-new-feature`)
49
+ 5. Create new Pull Request
50
+
51
+ ## License
52
+
53
+ Released under the MIT license.
54
+
55
+ Copyright, 2017, by [Samuel G. D. Williams](http://www.codeotaku.com/samuel-williams).
56
+
57
+ Permission is hereby granted, free of charge, to any person obtaining a copy
58
+ of this software and associated documentation files (the "Software"), to deal
59
+ in the Software without restriction, including without limitation the rights
60
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
61
+ copies of the Software, and to permit persons to whom the Software is
62
+ furnished to do so, subject to the following conditions:
63
+
64
+ The above copyright notice and this permission notice shall be included in
65
+ all copies or substantial portions of the Software.
66
+
67
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
68
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
69
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
70
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
71
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
72
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
73
+ THE SOFTWARE.
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec) do |task|
5
+ task.rspec_opts = ["--require", "simplecov"] if ENV['COVERAGE']
6
+ end
7
+
8
+ task :default => :spec
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ require_relative 'lib/build/dependency/version'
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = "build-dependency"
6
+ spec.version = Build::Dependency::VERSION
7
+ spec.authors = ["Samuel Williams"]
8
+ spec.email = ["samuel.williams@oriontransfer.co.nz"]
9
+ spec.summary = %q{A nested hash data structure for controlling build dependencys.}
10
+ spec.homepage = ""
11
+ spec.license = "MIT"
12
+
13
+ spec.files = `git ls-files -z`.split("\x0")
14
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
15
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
16
+ spec.require_paths = ["lib"]
17
+
18
+ spec.add_dependency "graphviz"
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.3"
21
+ spec.add_development_dependency "rspec", "~> 3.4.0"
22
+ spec.add_development_dependency "rake"
23
+ end
@@ -0,0 +1,123 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
3
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
4
+ <!-- Generated by graphviz version 2.40.1 (20161225.0304)
5
+ -->
6
+ <!-- Title: G Pages: 1 -->
7
+ <svg width="357pt" height="404pt"
8
+ viewBox="0.00 0.00 357.00 404.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
9
+ <g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 400)">
10
+ <title>G</title>
11
+ <polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-400 353,-400 353,4 -4,4"/>
12
+ <!-- Variant/debug -->
13
+ <g id="node1" class="node">
14
+ <title>Variant/debug</title>
15
+ <polygon fill="#add8e6" stroke="#000000" stroke-width="2" points="130,-36 4,-36 4,0 130,0 130,-36"/>
16
+ <text text-anchor="middle" x="67" y="-14.3" font-family="Monaco" font-size="14.00" fill="#000000">Variant/debug</text>
17
+ </g>
18
+ <!-- variant -->
19
+ <g id="node2" class="node">
20
+ <title>variant</title>
21
+ <polygon fill="#d3d3d3" stroke="#000000" points="104.5,-108 29.5,-108 29.5,-72 104.5,-72 104.5,-108"/>
22
+ <text text-anchor="middle" x="67" y="-86.3" font-family="Monaco" font-size="14.00" fill="#000000">variant</text>
23
+ </g>
24
+ <!-- variant&#45;&gt;Variant/debug -->
25
+ <g id="edge1" class="edge">
26
+ <title>variant&#45;&gt;Variant/debug</title>
27
+ <path fill="none" stroke="#000000" d="M67,-71.8314C67,-61 67,-47.2876 67,-36.4133"/>
28
+ </g>
29
+ <!-- Platform/linux -->
30
+ <g id="node3" class="node">
31
+ <title>Platform/linux</title>
32
+ <polygon fill="#add8e6" stroke="#000000" stroke-width="2" points="134,-180 0,-180 0,-144 134,-144 134,-180"/>
33
+ <text text-anchor="middle" x="67" y="-158.3" font-family="Monaco" font-size="14.00" fill="#000000">Platform/linux</text>
34
+ </g>
35
+ <!-- Platform/linux&#45;&gt;variant -->
36
+ <g id="edge2" class="edge">
37
+ <title>Platform/linux&#45;&gt;variant</title>
38
+ <path fill="none" stroke="#000000" d="M67,-143.8314C67,-136.131 67,-126.9743 67,-118.4166"/>
39
+ <polygon fill="#000000" stroke="#000000" points="70.5001,-118.4132 67,-108.4133 63.5001,-118.4133 70.5001,-118.4132"/>
40
+ </g>
41
+ <!-- platform -->
42
+ <g id="node4" class="node">
43
+ <title>platform</title>
44
+ <polygon fill="#d3d3d3" stroke="#000000" points="109,-252 25,-252 25,-216 109,-216 109,-252"/>
45
+ <text text-anchor="middle" x="67" y="-230.3" font-family="Monaco" font-size="14.00" fill="#000000">platform</text>
46
+ </g>
47
+ <!-- platform&#45;&gt;Platform/linux -->
48
+ <g id="edge3" class="edge">
49
+ <title>platform&#45;&gt;Platform/linux</title>
50
+ <path fill="none" stroke="#000000" d="M67,-215.8314C67,-205 67,-191.2876 67,-180.4133"/>
51
+ </g>
52
+ <!-- Compiler/clang -->
53
+ <g id="node5" class="node">
54
+ <title>Compiler/clang</title>
55
+ <polygon fill="#add8e6" stroke="#000000" points="321,-180 187,-180 187,-144 321,-144 321,-180"/>
56
+ <text text-anchor="middle" x="254" y="-158.3" font-family="Monaco" font-size="14.00" fill="#000000">Compiler/clang</text>
57
+ </g>
58
+ <!-- Language/C++14 -->
59
+ <g id="node6" class="node">
60
+ <title>Language/C++14</title>
61
+ <polygon fill="#ffffff" stroke="#000000" stroke-width="2" points="349,-324 215,-324 215,-288 349,-288 349,-324"/>
62
+ <text text-anchor="middle" x="282" y="-302.3" font-family="Monaco" font-size="14.00" fill="#000000">Language/C++14</text>
63
+ </g>
64
+ <!-- Language/C++14&#45;&gt;Compiler/clang -->
65
+ <g id="edge4" class="edge">
66
+ <title>Language/C++14&#45;&gt;Compiler/clang</title>
67
+ <path fill="none" stroke="#000000" d="M280.4243,-287.9292C278.6363,-269.6554 275.2651,-240.6656 270,-216 267.4281,-203.9513 263.5119,-190.6816 260.1978,-180.3117"/>
68
+ </g>
69
+ <!-- Language/C++17 -->
70
+ <g id="node7" class="node">
71
+ <title>Language/C++17</title>
72
+ <polygon fill="#ffffff" stroke="#000000" stroke-width="2" points="261,-252 127,-252 127,-216 261,-216 261,-252"/>
73
+ <text text-anchor="middle" x="194" y="-230.3" font-family="Monaco" font-size="14.00" fill="#000000">Language/C++17</text>
74
+ </g>
75
+ <!-- Language/C++17&#45;&gt;Compiler/clang -->
76
+ <g id="edge5" class="edge">
77
+ <title>Language/C++17&#45;&gt;Compiler/clang</title>
78
+ <path fill="none" stroke="#000000" d="M209.1405,-215.8314C218.1666,-205 229.5937,-191.2876 238.6556,-180.4133"/>
79
+ </g>
80
+ <!-- lib -->
81
+ <g id="node8" class="node">
82
+ <title>lib</title>
83
+ <polygon fill="#ffa500" stroke="#000000" stroke-width="2" points="190,-324 136,-324 136,-288 190,-288 190,-324"/>
84
+ <text text-anchor="middle" x="163" y="-302.3" font-family="Monaco" font-size="14.00" fill="#000000">lib</text>
85
+ </g>
86
+ <!-- lib&#45;&gt;platform -->
87
+ <g id="edge6" class="edge">
88
+ <title>lib&#45;&gt;platform</title>
89
+ <path fill="none" stroke="#000000" stroke-opacity="0.372549" d="M138.7751,-287.8314C126.8372,-278.8779 112.2744,-267.9558 99.394,-258.2955"/>
90
+ <polygon fill="none" stroke="#000000" stroke-opacity="0.372549" points="101.3171,-255.3629 91.2171,-252.1628 97.1171,-260.9629 101.3171,-255.3629"/>
91
+ </g>
92
+ <!-- lib&#45;&gt;Language/C++17 -->
93
+ <g id="edge7" class="edge">
94
+ <title>lib&#45;&gt;Language/C++17</title>
95
+ <path fill="none" stroke="#000000" stroke-opacity="0.372549" d="M170.8226,-287.8314C174.2109,-279.9617 178.2541,-270.5712 182.0076,-261.8533"/>
96
+ <polygon fill="none" stroke="#000000" stroke-opacity="0.372549" points="185.3321,-262.9822 186.0721,-252.4133 178.9027,-260.214 185.3321,-262.9822"/>
97
+ </g>
98
+ <!-- app -->
99
+ <g id="node9" class="node">
100
+ <title>app</title>
101
+ <polygon fill="#ffa500" stroke="#000000" stroke-width="2" points="190,-396 136,-396 136,-360 190,-360 190,-396"/>
102
+ <text text-anchor="middle" x="163" y="-374.3" font-family="Monaco" font-size="14.00" fill="#000000">app</text>
103
+ </g>
104
+ <!-- app&#45;&gt;platform -->
105
+ <g id="edge9" class="edge">
106
+ <title>app&#45;&gt;platform</title>
107
+ <path fill="none" stroke="#000000" stroke-opacity="0.372549" d="M150.8415,-359.7623C134.1067,-334.66 103.8678,-289.3017 84.6447,-260.4671"/>
108
+ <polygon fill="none" stroke="#000000" stroke-opacity="0.372549" points="87.519,-258.4687 79.0597,-252.0896 81.6946,-262.3516 87.519,-258.4687"/>
109
+ </g>
110
+ <!-- app&#45;&gt;Language/C++14 -->
111
+ <g id="edge10" class="edge">
112
+ <title>app&#45;&gt;Language/C++14</title>
113
+ <path fill="none" stroke="#000000" stroke-opacity="0.372549" d="M190.0029,-361.6621C205.7696,-352.1226 225.9279,-339.926 243.3541,-329.3824"/>
114
+ <polygon fill="none" stroke="#000000" stroke-opacity="0.372549" points="245.436,-332.2136 252.18,-324.0423 241.8123,-326.2245 245.436,-332.2136"/>
115
+ </g>
116
+ <!-- app&#45;&gt;lib -->
117
+ <g id="edge8" class="edge">
118
+ <title>app&#45;&gt;lib</title>
119
+ <path fill="none" stroke="#000000" stroke-opacity="0.372549" d="M163,-359.8314C163,-352.131 163,-342.9743 163,-334.4166"/>
120
+ <polygon fill="none" stroke="#000000" stroke-opacity="0.372549" points="166.5001,-334.4132 163,-324.4133 159.5001,-334.4133 166.5001,-334.4132"/>
121
+ </g>
122
+ </g>
123
+ </svg>
@@ -0,0 +1,26 @@
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_relative 'dependency/version'
22
+
23
+ require_relative 'dependency/unit'
24
+ require_relative 'dependency/chain'
25
+ require_relative 'dependency/partial_chain'
26
+ require_relative 'dependency/visualization'
@@ -0,0 +1,234 @@
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(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
+ class Chain < Resolver
128
+ # An `UnresolvedDependencyError` will be thrown if there are any unresolved dependencies.
129
+ def self.expand(*args)
130
+ chain = self.new(*args)
131
+
132
+ chain.freeze
133
+
134
+ if chain.unresolved.size > 0
135
+ raise UnresolvedDependencyError.new(chain)
136
+ end
137
+
138
+ return chain
139
+ end
140
+
141
+ def initialize(dependencies, providers, selection = [])
142
+ super()
143
+
144
+ # Explicitly selected dependencies which will be used when resolving ambiguity:
145
+ @selection = Set.new(selection)
146
+
147
+ # The list of dependencies that needs to be satisfied:
148
+ @dependencies = dependencies.collect{|dependency| Depends[dependency]}
149
+
150
+ # The available providers which match up to required dependencies:
151
+ @providers = providers
152
+
153
+ expand_top
154
+ end
155
+
156
+ attr :selection
157
+ attr :dependencies
158
+ attr :providers
159
+
160
+ def freeze
161
+ return unless frozen?
162
+
163
+ @selection.freeze
164
+ @dependencies.freeze
165
+ @providers.freeze
166
+
167
+ super
168
+ end
169
+
170
+ protected
171
+
172
+ def expand_top
173
+ # puts "Expanding #{@dependencies.inspect}"
174
+
175
+ expand_nested(@dependencies, TOP)
176
+ end
177
+
178
+ def filter_by_priority(viable_providers)
179
+ # Sort from highest priority to lowest priority:
180
+ viable_providers = viable_providers.sort{|a,b| b.priority <=> a.priority}
181
+
182
+ # The first item has the highest priority:
183
+ highest_priority = viable_providers.first.priority
184
+
185
+ # We compute all providers with the same highest priority (may be zero):
186
+ return viable_providers.take_while{|provider| provider.priority == highest_priority}
187
+ end
188
+
189
+ def filter_by_selection(viable_providers)
190
+ return viable_providers.select{|provider| @selection.include? provider.name}
191
+ end
192
+
193
+ def find_provider(dependency, parent)
194
+ # Mostly, only one package will satisfy the dependency...
195
+ viable_providers = @providers.select{|provider| provider.provides? dependency}
196
+
197
+ # puts "** Found #{viable_providers.collect(&:name).join(', ')} viable providers."
198
+
199
+ if viable_providers.size > 1
200
+ # ... however in some cases (typically where aliases are being used) an explicit selection must be made for the build to work correctly.
201
+ explicit_providers = filter_by_selection(viable_providers)
202
+
203
+ # puts "** Filtering to #{explicit_providers.collect(&:name).join(', ')} explicit providers."
204
+
205
+ if explicit_providers.size != 1
206
+ # If we were unable to select a single package, we may use the priority to limit the number of possible options:
207
+ explicit_providers = viable_providers if explicit_providers.empty?
208
+
209
+ explicit_providers = filter_by_priority(explicit_providers)
210
+ end
211
+
212
+ if explicit_providers.size == 0
213
+ # No provider was explicitly specified, thus we require explicit conflict resolution:
214
+ @conflicts[dependency] = viable_providers
215
+ return nil
216
+ elsif explicit_providers.size == 1
217
+ # The best outcome, a specific provider was named:
218
+ return explicit_providers.first
219
+ else
220
+ # Multiple providers were explicitly mentioned that satisfy the dependency.
221
+ @conflicts[dependency] = explicit_providers
222
+ return nil
223
+ end
224
+ else
225
+ return viable_providers.first
226
+ end
227
+ end
228
+
229
+ def provision_for(provider, dependency)
230
+ provider.provision_for(dependency)
231
+ end
232
+ end
233
+ end
234
+ end