build-dependency 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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