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,202 @@
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 "valid dependency resolution" do
23
+ let(:a) do
24
+ Package.new.tap do |package|
25
+ package.provides 'apple' do
26
+ fruit ['apple']
27
+ end
28
+ end
29
+ end
30
+
31
+ let(:b) do
32
+ Package.new.tap do |package|
33
+ package.provides 'orange' do
34
+ fruit ['orange']
35
+ end
36
+ end
37
+ end
38
+
39
+ let(:c) do
40
+ Package.new.tap do |package|
41
+ package.provides 'fruit-juice' do
42
+ juice ['ice', 'cold']
43
+ end
44
+
45
+ package.depends 'apple'
46
+ package.depends 'orange'
47
+ end
48
+ end
49
+
50
+ it "should resolve direct dependency chain" do
51
+ chain = Build::Dependency::Chain.expand(['fruit-juice'], [a, b, c])
52
+ expect(chain.ordered.collect(&:first)).to be == [a, b, c]
53
+ expect(chain.unresolved).to be == []
54
+ end
55
+
56
+ let(:d) do
57
+ Package.new.tap do |package|
58
+ package.provides 'pie'
59
+ package.depends 'apple'
60
+ end
61
+ end
62
+
63
+ it "shouldn't include unrelated units" do
64
+ chain = Build::Dependency::Chain.expand(['pie'], [a, b, c, d])
65
+
66
+ expect(chain.unresolved).to be == []
67
+ expect(chain.ordered.collect(&:first)).to be == [a, d]
68
+ end
69
+ end
70
+
71
+ describe "incomplete dependency resolution" do
72
+ it "should report conflicts" do
73
+ apple = Package.new('apple')
74
+ apple.provides 'apple'
75
+ apple.provides 'fruit'
76
+
77
+ bananna = Package.new('bananna')
78
+ bananna.provides 'fruit'
79
+
80
+ salad = Package.new('salad')
81
+ salad.depends 'fruit'
82
+ salad.provides 'salad'
83
+
84
+ chain = Build::Dependency::Chain.new(['salad'], [apple, bananna, salad])
85
+ expect(chain.unresolved.first).to be == [Build::Dependency::Depends.new("fruit"), salad]
86
+ expect(chain.conflicts).to be == {Build::Dependency::Depends.new("fruit") => [apple, bananna]}
87
+
88
+ chain = Build::Dependency::Chain.new(['salad'], [apple, bananna, salad], ['apple'])
89
+ expect(chain.unresolved).to be == []
90
+ expect(chain.conflicts).to be == {}
91
+ end
92
+ end
93
+
94
+ describe "multiple provisions" do
95
+ let(:fruit) do
96
+ Package.new('fruit').tap do |package|
97
+ package.provides 'apple' do
98
+ end
99
+
100
+ package.provides 'orange' do
101
+ end
102
+ end
103
+ end
104
+
105
+ let(:salad) do
106
+ Package.new('salad').tap do |package|
107
+ package.depends 'apple'
108
+ package.depends 'orange'
109
+ package.provides 'salad'
110
+ end
111
+ end
112
+
113
+ let(:lunch) do
114
+ Package.new('lunch').tap do |package|
115
+ package.depends 'apple'
116
+ package.depends 'salad'
117
+ package.provides 'lunch'
118
+ end
119
+ end
120
+
121
+ let(:chain) {Build::Dependency::Chain.new(['lunch'], [fruit, salad, lunch])}
122
+
123
+ it "should include both provisions" do
124
+ expect(chain.provisions.count).to be == 4
125
+ expect(chain.provisions.collect(&:name)).to be == ['apple', 'orange', 'salad', 'lunch']
126
+ end
127
+
128
+ it "should include both provisions in partial chain" do
129
+ partial_chain = chain.partial(lunch)
130
+ expect(partial_chain.provisions.count).to be == 3
131
+ expect(partial_chain.provisions.collect(&:name)).to be == ['apple', 'orange', 'salad']
132
+ end
133
+ end
134
+
135
+ it "should resolve aliases" do
136
+ apple = Package.new('apple')
137
+ apple.provides 'apple'
138
+ apple.provides :fruit => 'apple'
139
+
140
+ bananna = Package.new('bananna')
141
+ bananna.provides 'bananna'
142
+ bananna.provides :fruit => 'bananna'
143
+
144
+ salad = Package.new('salad')
145
+ salad.depends :fruit
146
+ salad.provides 'salad'
147
+
148
+ chain = Build::Dependency::Chain.expand(['salad'], [apple, bananna, salad], ['apple'])
149
+ expect(chain.unresolved).to be == []
150
+ expect(chain.conflicts).to be == {}
151
+
152
+ expect(chain.ordered.size).to be == 2
153
+ expect(chain.ordered[0]).to be == Build::Dependency::Resolution.new(apple, Build::Dependency::Depends.new("apple"))
154
+ expect(chain.ordered[1]).to be == Build::Dependency::Resolution.new(salad, Build::Dependency::Depends.new("salad"))
155
+ end
156
+
157
+ it "should select dependencies with high priority" do
158
+ bad_apple = Package.new('bad_apple')
159
+ bad_apple.provides 'apple'
160
+ bad_apple.priority = 20
161
+
162
+ good_apple = Package.new('good_apple')
163
+ good_apple.provides 'apple'
164
+ good_apple.priority = 40
165
+
166
+ chain = Build::Dependency::Chain.expand(['apple'], [bad_apple, good_apple])
167
+
168
+ expect(chain.unresolved).to be == []
169
+ expect(chain.conflicts).to be == {}
170
+
171
+ # Should select higher priority package by default:
172
+ expect(chain.ordered).to be == [Build::Dependency::Resolution.new(good_apple, Build::Dependency::Depends.new('apple'))]
173
+ end
174
+
175
+ it "should expose direct dependencies" do
176
+ system = Package.new('linux')
177
+ system.provides 'linux'
178
+ system.provides 'clang'
179
+ system.provides system: 'linux'
180
+ system.provides compiler: 'clang'
181
+
182
+ library = Package.new('library')
183
+ library.provides 'library'
184
+ library.depends :system
185
+ library.depends :compiler
186
+
187
+ application = Package.new('application')
188
+ application.provides 'application'
189
+ application.depends :compiler
190
+ application.depends 'library'
191
+
192
+ chain = Build::Dependency::Chain.expand(['application'], [system, library, application])
193
+
194
+ expect(chain.unresolved).to be == []
195
+ expect(chain.conflicts).to be == {}
196
+ expect(chain.ordered).to be == [
197
+ Build::Dependency::Resolution.new(system, Build::Dependency::Depends.new('clang')),
198
+ Build::Dependency::Resolution.new(library, Build::Dependency::Depends.new('library')),
199
+ Build::Dependency::Resolution.new(application, Build::Dependency::Depends.new('application')),
200
+ ]
201
+ end
202
+ end
@@ -0,0 +1,116 @@
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::PartialChain do
22
+ describe "app chain" do
23
+ include_context "app packages"
24
+
25
+ let(:chain) {Build::Dependency::Chain.expand(['app', 'lib'], packages)}
26
+
27
+ it "should generate full list of ordered providers" do
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')),
34
+ ]
35
+ end
36
+
37
+ it "should generate a full list of provisions" do
38
+ expect(chain.provisions).to be == [
39
+ variant.provision_for(Build::Dependency::Depends['Variant/debug']),
40
+ platform.provision_for(Build::Dependency::Depends['Platform/linux']),
41
+ compiler.provision_for(Build::Dependency::Depends['Language/C++17']),
42
+ lib.provision_for(Build::Dependency::Depends.new('lib')),
43
+ compiler.provision_for(Build::Dependency::Depends['Language/C++14']),
44
+ app.provision_for(Build::Dependency::Depends.new('app')),
45
+ ]
46
+
47
+ graph = visualization.generate(chain)
48
+
49
+ Graphviz::output(graph, path: "full.svg")
50
+ end
51
+
52
+ subject {described_class.new(chain, app.dependencies)}
53
+
54
+ it "should select app packages" do
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")),
60
+ ]
61
+
62
+ graph = visualization.generate(subject)
63
+
64
+ Graphviz::output(graph, path: "partial.svg")
65
+ end
66
+ end
67
+
68
+ describe "private dependencies" do
69
+ let(:a) do
70
+ Package.new('a').tap do |package|
71
+ package.provides 'a'
72
+ end
73
+ end
74
+
75
+ let(:b) do
76
+ Package.new('b').tap do |package|
77
+ package.provides 'b'
78
+ package.depends 'a', private: true
79
+ end
80
+ end
81
+
82
+ let(:c) do
83
+ Package.new('c').tap do |package|
84
+ package.provides 'c'
85
+ package.depends 'b'
86
+ end
87
+ end
88
+
89
+ let(:d) do
90
+ Package.new('d').tap do |package|
91
+ package.provides 'd'
92
+ package.depends 'c'
93
+ end
94
+ end
95
+
96
+ let(:chain) {Build::Dependency::Chain.expand(['d'], [a, b, c, d])}
97
+
98
+ it "should include direct private dependencies" do
99
+ partial_chain = chain.partial(b)
100
+
101
+ expect(partial_chain.ordered.collect(&:first)).to be == [a]
102
+ end
103
+
104
+ it "shouldn't include nested private dependencies" do
105
+ partial_chain = chain.partial(c)
106
+
107
+ expect(partial_chain.ordered.collect(&:first)).to be == [b]
108
+ end
109
+
110
+ it "should follow non-private dependencies" do
111
+ partial_chain = chain.partial(d)
112
+
113
+ expect(partial_chain.ordered.collect(&:first)).to be == [b, c]
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,31 @@
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::Visualization do
22
+ include_context "app packages"
23
+
24
+ it "should visualize dependency chain" do
25
+ chain = Build::Dependency::Chain.expand(['app', 'tests'], packages)
26
+
27
+ graph = subject.generate(chain)
28
+
29
+ Graphviz::output(graph, path: "visualization.svg")
30
+ end
31
+ end
@@ -0,0 +1,97 @@
1
+
2
+ if ENV['COVERAGE']
3
+ begin
4
+ require 'simplecov'
5
+
6
+ SimpleCov.start do
7
+ add_filter "/spec/"
8
+ end
9
+
10
+ if ENV['TRAVIS']
11
+ require 'coveralls'
12
+ Coveralls.wear!
13
+ end
14
+ rescue LoadError
15
+ warn "Could not load simplecov: #{$!}"
16
+ end
17
+ end
18
+
19
+ require "bundler/setup"
20
+ require "build/dependency"
21
+
22
+ class Package
23
+ include Build::Dependency
24
+
25
+ def initialize(name = nil)
26
+ @name = name
27
+ end
28
+
29
+ attr :name
30
+
31
+ def inspect
32
+ "<Package:#{@name}>"
33
+ end
34
+ end
35
+
36
+ RSpec.shared_context "app packages" do
37
+ let(:app) do
38
+ Package.new('app').tap do |package|
39
+ package.provides 'app'
40
+ package.depends 'lib', private: true
41
+ package.depends :platform, private: true
42
+ package.depends 'Language/C++14', private: true
43
+ end
44
+ end
45
+
46
+ let(:tests) do
47
+ Package.new('tests').tap do |package|
48
+ package.provides 'tests'
49
+ package.depends 'lib', private: true
50
+ package.depends :platform, private: true
51
+ package.depends 'Language/C++17', private: true
52
+ end
53
+ end
54
+
55
+ let(:lib) do
56
+ Package.new('lib').tap do |package|
57
+ package.provides 'lib'
58
+ package.depends :platform, private: true
59
+ package.depends 'Language/C++17', private: true
60
+ end
61
+ end
62
+
63
+ let(:platform) do
64
+ Package.new('Platform/linux').tap do |package|
65
+ package.provides platform: 'Platform/linux'
66
+ package.provides 'Platform/linux'
67
+ package.depends :variant
68
+ end
69
+ end
70
+
71
+ let(:variant) do
72
+ Package.new('Variant/debug').tap do |package|
73
+ package.provides variant: 'Variant/debug'
74
+ package.provides 'Variant/debug'
75
+ end
76
+ end
77
+
78
+ let(:compiler) do
79
+ Package.new('Compiler/clang').tap do |package|
80
+ package.provides 'Language/C++14'
81
+ package.provides 'Language/C++17'
82
+ end
83
+ end
84
+
85
+ let(:packages) {[app, tests, lib, platform, variant, compiler]}
86
+
87
+ let(:visualization) {Build::Dependency::Visualization.new}
88
+ end
89
+
90
+ RSpec.configure do |config|
91
+ # Enable flags like --only-failures and --next-failure
92
+ config.example_status_persistence_file_path = ".rspec_status"
93
+
94
+ config.expect_with :rspec do |c|
95
+ c.syntax = :expect
96
+ end
97
+ end
@@ -0,0 +1,147 @@
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="385pt" height="404pt"
8
+ viewBox="0.00 0.00 385.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 381,-400 381,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="282,-36 156,-36 156,0 282,0 282,-36"/>
16
+ <text text-anchor="middle" x="219" 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="256.5,-108 181.5,-108 181.5,-72 256.5,-72 256.5,-108"/>
22
+ <text text-anchor="middle" x="219" 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="M219,-71.8314C219,-61 219,-47.2876 219,-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="286,-180 152,-180 152,-144 286,-144 286,-180"/>
33
+ <text text-anchor="middle" x="219" 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="M219,-143.8314C219,-136.131 219,-126.9743 219,-118.4166"/>
39
+ <polygon fill="#000000" stroke="#000000" points="222.5001,-118.4132 219,-108.4133 215.5001,-118.4133 222.5001,-118.4132"/>
40
+ </g>
41
+ <!-- platform -->
42
+ <g id="node4" class="node">
43
+ <title>platform</title>
44
+ <polygon fill="#d3d3d3" stroke="#000000" points="225,-252 141,-252 141,-216 225,-216 225,-252"/>
45
+ <text text-anchor="middle" x="183" 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="M192.0843,-215.8314C197.5,-205 204.3562,-191.2876 209.7934,-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="134,-180 0,-180 0,-144 134,-144 134,-180"/>
56
+ <text text-anchor="middle" x="67" 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="134,-324 0,-324 0,-288 134,-288 134,-324"/>
62
+ <text text-anchor="middle" x="67" 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="M67,-287.7623C67,-260.0633 67,-207.7005 67,-180.0896"/>
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="377,-252 243,-252 243,-216 377,-216 377,-252"/>
73
+ <text text-anchor="middle" x="310" 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="M248.9945,-215.9243C212.0197,-204.9688 165.0099,-191.04 128.0306,-180.0831"/>
79
+ </g>
80
+ <!-- lib -->
81
+ <g id="node8" class="node">
82
+ <title>lib</title>
83
+ <polygon fill="#ffffff" stroke="#000000" stroke-width="2" points="282,-324 228,-324 228,-288 282,-288 282,-324"/>
84
+ <text text-anchor="middle" x="255" 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="M236.8314,-287.8314C228.2848,-279.2848 217.9443,-268.9443 208.6198,-259.6198"/>
90
+ <polygon fill="none" stroke="#000000" stroke-opacity="0.372549" points="210.9592,-257.0095 201.4133,-252.4133 206.0095,-261.9592 210.9592,-257.0095"/>
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="M268.8788,-287.8314C275.2136,-279.5386 282.8384,-269.557 289.7926,-260.4533"/>
96
+ <polygon fill="none" stroke="#000000" stroke-opacity="0.372549" points="292.6452,-262.4847 295.9343,-252.4133 287.0825,-258.2353 292.6452,-262.4847"/>
97
+ </g>
98
+ <!-- app -->
99
+ <g id="node9" class="node">
100
+ <title>app</title>
101
+ <polygon fill="#ffa500" stroke="#000000" stroke-width="2" points="189,-396 135,-396 135,-360 189,-360 189,-396"/>
102
+ <text text-anchor="middle" x="162" 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="M164.6597,-359.7623C168.2415,-335.201 174.6514,-291.2474 178.865,-262.3541"/>
108
+ <polygon fill="none" stroke="#000000" stroke-opacity="0.372549" points="182.3821,-262.4901 180.3619,-252.0896 175.4554,-261.4798 182.3821,-262.4901"/>
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="M138.0275,-359.8314C126.2139,-350.8779 111.8028,-339.9558 99.0565,-330.2955"/>
114
+ <polygon fill="none" stroke="#000000" stroke-opacity="0.372549" points="101.0486,-327.4137 90.9648,-324.1628 96.8205,-332.9925 101.0486,-327.4137"/>
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="M185.4678,-359.8314C197.0327,-350.8779 211.1404,-339.9558 223.6183,-330.2955"/>
120
+ <polygon fill="none" stroke="#000000" stroke-opacity="0.372549" points="225.775,-333.0521 231.5397,-324.1628 221.4898,-327.5171 225.775,-333.0521"/>
121
+ </g>
122
+ <!-- tests -->
123
+ <g id="node10" class="node">
124
+ <title>tests</title>
125
+ <polygon fill="#ffa500" stroke="#000000" stroke-width="2" points="284.5,-396 225.5,-396 225.5,-360 284.5,-360 284.5,-396"/>
126
+ <text text-anchor="middle" x="255" y="-374.3" font-family="Monaco" font-size="14.00" fill="#000000">tests</text>
127
+ </g>
128
+ <!-- tests&#45;&gt;platform -->
129
+ <g id="edge12" class="edge">
130
+ <title>tests&#45;&gt;platform</title>
131
+ <path fill="none" stroke="#000000" stroke-opacity="0.372549" d="M241.3445,-359.5973C234.154,-349.4209 225.5044,-336.3651 219,-324 208.4065,-303.8615 198.9605,-279.8887 192.405,-261.7501"/>
132
+ <polygon fill="none" stroke="#000000" stroke-opacity="0.372549" points="195.6176,-260.3365 188.9822,-252.0768 189.0185,-262.6716 195.6176,-260.3365"/>
133
+ </g>
134
+ <!-- tests&#45;&gt;Language/C++17 -->
135
+ <g id="edge13" class="edge">
136
+ <title>tests&#45;&gt;Language/C++17</title>
137
+ <path fill="none" stroke="#000000" stroke-opacity="0.372549" d="M269.8853,-359.5185C277.2462,-349.5296 285.672,-336.6716 291,-324 299.3306,-304.1876 304.1533,-280.3321 306.8541,-262.1605"/>
138
+ <polygon fill="none" stroke="#000000" stroke-opacity="0.372549" points="310.3302,-262.5721 308.2095,-252.1917 303.3941,-261.6289 310.3302,-262.5721"/>
139
+ </g>
140
+ <!-- tests&#45;&gt;lib -->
141
+ <g id="edge11" class="edge">
142
+ <title>tests&#45;&gt;lib</title>
143
+ <path fill="none" stroke="#000000" stroke-opacity="0.372549" d="M255,-359.8314C255,-352.131 255,-342.9743 255,-334.4166"/>
144
+ <polygon fill="none" stroke="#000000" stroke-opacity="0.372549" points="258.5001,-334.4132 255,-324.4133 251.5001,-334.4133 258.5001,-334.4132"/>
145
+ </g>
146
+ </g>
147
+ </svg>