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.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.rspec +5 -0
- data/.travis.yml +19 -0
- data/Gemfile +14 -0
- data/Guardfile +9 -0
- data/README.md +73 -0
- data/Rakefile +8 -0
- data/build-dependency.gemspec +23 -0
- data/full.svg +123 -0
- data/lib/build/dependency.rb +26 -0
- data/lib/build/dependency/chain.rb +234 -0
- data/lib/build/dependency/partial_chain.rb +95 -0
- data/lib/build/dependency/unit.rb +135 -0
- data/lib/build/dependency/version.rb +25 -0
- data/lib/build/dependency/visualization.rb +151 -0
- data/partial.svg +93 -0
- data/spec/build/dependency/chain_spec.rb +202 -0
- data/spec/build/dependency/partial_chain_spec.rb +116 -0
- data/spec/build/dependency/visualization_spec.rb +31 -0
- data/spec/spec_helper.rb +97 -0
- data/visualization.svg +147 -0
- metadata +125 -0
checksums.yaml
ADDED
@@ -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
|
data/.gitignore
ADDED
@@ -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
data/.travis.yml
ADDED
@@ -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
|
data/Guardfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
# Build::Dependency
|
2
|
+
|
3
|
+
Build::Dependency provides dependency resolution algorithms.
|
4
|
+
|
5
|
+
[](http://travis-ci.org/ioquatix/build-dependency)
|
6
|
+
[](https://codeclimate.com/github/ioquatix/build-dependency)
|
7
|
+
[](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
|
+

|
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
|
+

|
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.
|
data/Rakefile
ADDED
@@ -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
|
data/full.svg
ADDED
@@ -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->Variant/debug -->
|
25
|
+
<g id="edge1" class="edge">
|
26
|
+
<title>variant->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->variant -->
|
36
|
+
<g id="edge2" class="edge">
|
37
|
+
<title>Platform/linux->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->Platform/linux -->
|
48
|
+
<g id="edge3" class="edge">
|
49
|
+
<title>platform->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->Compiler/clang -->
|
65
|
+
<g id="edge4" class="edge">
|
66
|
+
<title>Language/C++14->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->Compiler/clang -->
|
76
|
+
<g id="edge5" class="edge">
|
77
|
+
<title>Language/C++17->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->platform -->
|
87
|
+
<g id="edge6" class="edge">
|
88
|
+
<title>lib->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->Language/C++17 -->
|
93
|
+
<g id="edge7" class="edge">
|
94
|
+
<title>lib->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->platform -->
|
105
|
+
<g id="edge9" class="edge">
|
106
|
+
<title>app->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->Language/C++14 -->
|
111
|
+
<g id="edge10" class="edge">
|
112
|
+
<title>app->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->lib -->
|
117
|
+
<g id="edge8" class="edge">
|
118
|
+
<title>app->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
|