carat 1.9.9.pre1
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 +16 -0
- data/.rspec +3 -0
- data/.travis.yml +24 -0
- data/CHANGELOG.md +2006 -0
- data/CODE_OF_CONDUCT.md +40 -0
- data/CONTRIBUTING.md +23 -0
- data/DEVELOPMENT.md +119 -0
- data/ISSUES.md +96 -0
- data/LICENSE.md +23 -0
- data/README.md +32 -0
- data/Rakefile +308 -0
- data/bin/carat +21 -0
- data/bin/carat_ruby +56 -0
- data/carat.gemspec +32 -0
- data/lib/carat.rb +446 -0
- data/lib/carat/anonymizable_uri.rb +32 -0
- data/lib/carat/capistrano.rb +16 -0
- data/lib/carat/cli.rb +407 -0
- data/lib/carat/cli/binstubs.rb +38 -0
- data/lib/carat/cli/cache.rb +35 -0
- data/lib/carat/cli/check.rb +35 -0
- data/lib/carat/cli/clean.rb +26 -0
- data/lib/carat/cli/common.rb +56 -0
- data/lib/carat/cli/config.rb +84 -0
- data/lib/carat/cli/console.rb +38 -0
- data/lib/carat/cli/exec.rb +44 -0
- data/lib/carat/cli/gem.rb +195 -0
- data/lib/carat/cli/init.rb +33 -0
- data/lib/carat/cli/inject.rb +33 -0
- data/lib/carat/cli/install.rb +156 -0
- data/lib/carat/cli/open.rb +23 -0
- data/lib/carat/cli/outdated.rb +80 -0
- data/lib/carat/cli/package.rb +45 -0
- data/lib/carat/cli/platform.rb +43 -0
- data/lib/carat/cli/show.rb +74 -0
- data/lib/carat/cli/update.rb +73 -0
- data/lib/carat/cli/viz.rb +27 -0
- data/lib/carat/constants.rb +5 -0
- data/lib/carat/current_ruby.rb +183 -0
- data/lib/carat/definition.rb +628 -0
- data/lib/carat/dep_proxy.rb +43 -0
- data/lib/carat/dependency.rb +110 -0
- data/lib/carat/deployment.rb +59 -0
- data/lib/carat/deprecate.rb +15 -0
- data/lib/carat/dsl.rb +331 -0
- data/lib/carat/endpoint_specification.rb +76 -0
- data/lib/carat/env.rb +75 -0
- data/lib/carat/environment.rb +42 -0
- data/lib/carat/fetcher.rb +423 -0
- data/lib/carat/friendly_errors.rb +85 -0
- data/lib/carat/gem_helper.rb +180 -0
- data/lib/carat/gem_helpers.rb +26 -0
- data/lib/carat/gem_installer.rb +9 -0
- data/lib/carat/gem_path_manipulation.rb +8 -0
- data/lib/carat/gem_tasks.rb +2 -0
- data/lib/carat/graph.rb +169 -0
- data/lib/carat/index.rb +197 -0
- data/lib/carat/injector.rb +64 -0
- data/lib/carat/installer.rb +339 -0
- data/lib/carat/lazy_specification.rb +83 -0
- data/lib/carat/lockfile_parser.rb +167 -0
- data/lib/carat/match_platform.rb +13 -0
- data/lib/carat/psyched_yaml.rb +26 -0
- data/lib/carat/remote_specification.rb +57 -0
- data/lib/carat/resolver.rb +334 -0
- data/lib/carat/retry.rb +60 -0
- data/lib/carat/ruby_dsl.rb +11 -0
- data/lib/carat/ruby_version.rb +117 -0
- data/lib/carat/rubygems_ext.rb +170 -0
- data/lib/carat/rubygems_integration.rb +619 -0
- data/lib/carat/runtime.rb +289 -0
- data/lib/carat/settings.rb +208 -0
- data/lib/carat/setup.rb +24 -0
- data/lib/carat/shared_helpers.rb +149 -0
- data/lib/carat/similarity_detector.rb +63 -0
- data/lib/carat/source.rb +46 -0
- data/lib/carat/source/git.rb +294 -0
- data/lib/carat/source/git/git_proxy.rb +162 -0
- data/lib/carat/source/path.rb +226 -0
- data/lib/carat/source/path/installer.rb +43 -0
- data/lib/carat/source/rubygems.rb +381 -0
- data/lib/carat/source_list.rb +101 -0
- data/lib/carat/spec_set.rb +154 -0
- data/lib/carat/ssl_certs/.document +1 -0
- data/lib/carat/ssl_certs/AddTrustExternalCARoot-2048.pem +25 -0
- data/lib/carat/ssl_certs/AddTrustExternalCARoot.pem +32 -0
- data/lib/carat/ssl_certs/Class3PublicPrimaryCertificationAuthority.pem +14 -0
- data/lib/carat/ssl_certs/DigiCertHighAssuranceEVRootCA.pem +23 -0
- data/lib/carat/ssl_certs/EntrustnetSecureServerCertificationAuthority.pem +28 -0
- data/lib/carat/ssl_certs/GeoTrustGlobalCA.pem +20 -0
- data/lib/carat/ssl_certs/certificate_manager.rb +66 -0
- data/lib/carat/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem +21 -0
- data/lib/carat/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem +23 -0
- data/lib/carat/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem +25 -0
- data/lib/carat/templates/Executable +16 -0
- data/lib/carat/templates/Executable.standalone +12 -0
- data/lib/carat/templates/Gemfile +4 -0
- data/lib/carat/templates/newgem/.travis.yml.tt +3 -0
- data/lib/carat/templates/newgem/CODE_OF_CONDUCT.md.tt +13 -0
- data/lib/carat/templates/newgem/Gemfile.tt +4 -0
- data/lib/carat/templates/newgem/LICENSE.txt.tt +21 -0
- data/lib/carat/templates/newgem/README.md.tt +39 -0
- data/lib/carat/templates/newgem/Rakefile.tt +25 -0
- data/lib/carat/templates/newgem/bin/console.tt +14 -0
- data/lib/carat/templates/newgem/bin/setup.tt +7 -0
- data/lib/carat/templates/newgem/exe/newgem.tt +3 -0
- data/lib/carat/templates/newgem/ext/newgem/extconf.rb.tt +3 -0
- data/lib/carat/templates/newgem/ext/newgem/newgem.c.tt +9 -0
- data/lib/carat/templates/newgem/ext/newgem/newgem.h.tt +6 -0
- data/lib/carat/templates/newgem/gitignore.tt +16 -0
- data/lib/carat/templates/newgem/lib/newgem.rb.tt +12 -0
- data/lib/carat/templates/newgem/lib/newgem/version.rb.tt +7 -0
- data/lib/carat/templates/newgem/newgem.gemspec.tt +43 -0
- data/lib/carat/templates/newgem/rspec.tt +2 -0
- data/lib/carat/templates/newgem/spec/newgem_spec.rb.tt +11 -0
- data/lib/carat/templates/newgem/spec/spec_helper.rb.tt +2 -0
- data/lib/carat/templates/newgem/test/minitest_helper.rb.tt +4 -0
- data/lib/carat/templates/newgem/test/test_newgem.rb.tt +11 -0
- data/lib/carat/ui.rb +7 -0
- data/lib/carat/ui/rg_proxy.rb +21 -0
- data/lib/carat/ui/shell.rb +103 -0
- data/lib/carat/ui/silent.rb +44 -0
- data/lib/carat/vendor/molinillo/lib/molinillo.rb +5 -0
- data/lib/carat/vendor/molinillo/lib/molinillo/dependency_graph.rb +266 -0
- data/lib/carat/vendor/molinillo/lib/molinillo/errors.rb +69 -0
- data/lib/carat/vendor/molinillo/lib/molinillo/gem_metadata.rb +3 -0
- data/lib/carat/vendor/molinillo/lib/molinillo/modules/specification_provider.rb +90 -0
- data/lib/carat/vendor/molinillo/lib/molinillo/modules/ui.rb +63 -0
- data/lib/carat/vendor/molinillo/lib/molinillo/resolution.rb +415 -0
- data/lib/carat/vendor/molinillo/lib/molinillo/resolver.rb +43 -0
- data/lib/carat/vendor/molinillo/lib/molinillo/state.rb +43 -0
- data/lib/carat/vendor/net/http/faster.rb +26 -0
- data/lib/carat/vendor/net/http/persistent.rb +1230 -0
- data/lib/carat/vendor/net/http/persistent/ssl_reuse.rb +128 -0
- data/lib/carat/vendor/thor/lib/thor.rb +484 -0
- data/lib/carat/vendor/thor/lib/thor/actions.rb +319 -0
- data/lib/carat/vendor/thor/lib/thor/actions/create_file.rb +103 -0
- data/lib/carat/vendor/thor/lib/thor/actions/create_link.rb +59 -0
- data/lib/carat/vendor/thor/lib/thor/actions/directory.rb +118 -0
- data/lib/carat/vendor/thor/lib/thor/actions/empty_directory.rb +135 -0
- data/lib/carat/vendor/thor/lib/thor/actions/file_manipulation.rb +316 -0
- data/lib/carat/vendor/thor/lib/thor/actions/inject_into_file.rb +107 -0
- data/lib/carat/vendor/thor/lib/thor/base.rb +656 -0
- data/lib/carat/vendor/thor/lib/thor/command.rb +133 -0
- data/lib/carat/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +77 -0
- data/lib/carat/vendor/thor/lib/thor/core_ext/io_binary_read.rb +10 -0
- data/lib/carat/vendor/thor/lib/thor/core_ext/ordered_hash.rb +98 -0
- data/lib/carat/vendor/thor/lib/thor/error.rb +32 -0
- data/lib/carat/vendor/thor/lib/thor/group.rb +281 -0
- data/lib/carat/vendor/thor/lib/thor/invocation.rb +178 -0
- data/lib/carat/vendor/thor/lib/thor/line_editor.rb +17 -0
- data/lib/carat/vendor/thor/lib/thor/line_editor/basic.rb +35 -0
- data/lib/carat/vendor/thor/lib/thor/line_editor/readline.rb +88 -0
- data/lib/carat/vendor/thor/lib/thor/parser.rb +4 -0
- data/lib/carat/vendor/thor/lib/thor/parser/argument.rb +73 -0
- data/lib/carat/vendor/thor/lib/thor/parser/arguments.rb +175 -0
- data/lib/carat/vendor/thor/lib/thor/parser/option.rb +125 -0
- data/lib/carat/vendor/thor/lib/thor/parser/options.rb +218 -0
- data/lib/carat/vendor/thor/lib/thor/rake_compat.rb +71 -0
- data/lib/carat/vendor/thor/lib/thor/runner.rb +322 -0
- data/lib/carat/vendor/thor/lib/thor/shell.rb +81 -0
- data/lib/carat/vendor/thor/lib/thor/shell/basic.rb +421 -0
- data/lib/carat/vendor/thor/lib/thor/shell/color.rb +149 -0
- data/lib/carat/vendor/thor/lib/thor/shell/html.rb +126 -0
- data/lib/carat/vendor/thor/lib/thor/util.rb +267 -0
- data/lib/carat/vendor/thor/lib/thor/version.rb +3 -0
- data/lib/carat/vendored_fileutils.rb +9 -0
- data/lib/carat/vendored_molinillo.rb +2 -0
- data/lib/carat/vendored_persistent.rb +11 -0
- data/lib/carat/vendored_thor.rb +3 -0
- data/lib/carat/version.rb +6 -0
- data/lib/carat/vlad.rb +11 -0
- data/lib/carat/worker.rb +73 -0
- data/man/carat-config.ronn +178 -0
- data/man/carat-exec.ronn +136 -0
- data/man/carat-install.ronn +383 -0
- data/man/carat-package.ronn +66 -0
- data/man/carat-platform.ronn +42 -0
- data/man/carat-update.ronn +188 -0
- data/man/carat.ronn +98 -0
- data/man/gemfile.5.ronn +473 -0
- data/man/index.txt +7 -0
- metadata +321 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module Carat
|
|
2
|
+
module UI
|
|
3
|
+
class Silent
|
|
4
|
+
def info(message, newline = nil)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def confirm(message, newline = nil)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def warn(message, newline = nil)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def error(message, newline = nil)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def debug(message, newline = nil)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def debug?
|
|
20
|
+
false
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def quiet?
|
|
24
|
+
false
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def ask(message)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def level=(name)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def level(name = nil)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def trace(message, newline = nil)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def silence
|
|
40
|
+
yield
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
require 'carat/vendor/molinillo/lib/molinillo/gem_metadata'
|
|
2
|
+
require 'carat/vendor/molinillo/lib/molinillo/errors'
|
|
3
|
+
require 'carat/vendor/molinillo/lib/molinillo/resolver'
|
|
4
|
+
require 'carat/vendor/molinillo/lib/molinillo/modules/ui'
|
|
5
|
+
require 'carat/vendor/molinillo/lib/molinillo/modules/specification_provider'
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
require 'tsort'
|
|
3
|
+
|
|
4
|
+
module Carat::Molinillo
|
|
5
|
+
# A directed acyclic graph that is tuned to hold named dependencies
|
|
6
|
+
class DependencyGraph
|
|
7
|
+
include Enumerable
|
|
8
|
+
|
|
9
|
+
# Enumerates through the vertices of the graph.
|
|
10
|
+
# @return [Array<Vertex>] The graph's vertices.
|
|
11
|
+
def each
|
|
12
|
+
vertices.values.each { |v| yield v }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
include TSort
|
|
16
|
+
|
|
17
|
+
alias_method :tsort_each_node, :each
|
|
18
|
+
|
|
19
|
+
def tsort_each_child(vertex, &block)
|
|
20
|
+
vertex.successors.each(&block)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Topologically sorts the given vertices.
|
|
24
|
+
# @param [Enumerable<Vertex>] vertices the vertices to be sorted, which must
|
|
25
|
+
# all belong to the same graph.
|
|
26
|
+
# @return [Array<Vertex>] The sorted vertices.
|
|
27
|
+
def self.tsort(vertices)
|
|
28
|
+
TSort.tsort(
|
|
29
|
+
lambda { |b| vertices.each(&b) },
|
|
30
|
+
lambda { |v, &b| (v.successors & vertices).each(&b) }
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# A directed edge of a {DependencyGraph}
|
|
35
|
+
# @attr [Vertex] origin The origin of the directed edge
|
|
36
|
+
# @attr [Vertex] destination The destination of the directed edge
|
|
37
|
+
# @attr [Array] requirements The requirements the directed edge represents
|
|
38
|
+
Edge = Struct.new(:origin, :destination, :requirements)
|
|
39
|
+
|
|
40
|
+
# @return [{String => Vertex}] vertices that have no {Vertex#predecessors},
|
|
41
|
+
# keyed by by {Vertex#name}
|
|
42
|
+
attr_reader :root_vertices
|
|
43
|
+
# @return [{String => Vertex}] the vertices of the dependency graph, keyed
|
|
44
|
+
# by {Vertex#name}
|
|
45
|
+
attr_reader :vertices
|
|
46
|
+
# @return [Set<Edge>] the edges of the dependency graph
|
|
47
|
+
attr_reader :edges
|
|
48
|
+
|
|
49
|
+
def initialize
|
|
50
|
+
@vertices = {}
|
|
51
|
+
@edges = Set.new
|
|
52
|
+
@root_vertices = {}
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Initializes a copy of a {DependencyGraph}, ensuring that all {#vertices}
|
|
56
|
+
# have the correct {Vertex#graph} set
|
|
57
|
+
def initialize_copy(other)
|
|
58
|
+
super
|
|
59
|
+
@vertices = other.vertices.reduce({}) do |vertices, (name, vertex)|
|
|
60
|
+
vertices.tap do |hash|
|
|
61
|
+
hash[name] = vertex.dup.tap { |v| v.graph = self }
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
@root_vertices = Hash[@vertices.select { |n, _v| other.root_vertices[n] }]
|
|
65
|
+
@edges = other.edges.map do |edge|
|
|
66
|
+
Edge.new(
|
|
67
|
+
vertex_named(edge.origin.name),
|
|
68
|
+
vertex_named(edge.destination.name),
|
|
69
|
+
edge.requirements.dup
|
|
70
|
+
)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# @return [String] a string suitable for debugging
|
|
75
|
+
def inspect
|
|
76
|
+
"#{self.class}:#{vertices.values.inspect}"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# @return [Boolean] whether the two dependency graphs are equal, determined
|
|
80
|
+
# by a recursive traversal of each {#root_vertices} and its
|
|
81
|
+
# {Vertex#successors}
|
|
82
|
+
def ==(other)
|
|
83
|
+
root_vertices == other.root_vertices
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# @param [String] name
|
|
87
|
+
# @param [Object] payload
|
|
88
|
+
# @param [Array<String>] parent_names
|
|
89
|
+
# @param [Object] requirement the requirement that is requiring the child
|
|
90
|
+
# @return [void]
|
|
91
|
+
def add_child_vertex(name, payload, parent_names, requirement)
|
|
92
|
+
is_root = parent_names.include?(nil)
|
|
93
|
+
parent_nodes = parent_names.compact.map { |n| vertex_named(n) }
|
|
94
|
+
vertex = vertex_named(name) || if is_root
|
|
95
|
+
add_root_vertex(name, payload)
|
|
96
|
+
else
|
|
97
|
+
add_vertex(name, payload)
|
|
98
|
+
end
|
|
99
|
+
vertex.payload ||= payload
|
|
100
|
+
parent_nodes.each do |parent_node|
|
|
101
|
+
add_edge(parent_node, vertex, requirement)
|
|
102
|
+
end
|
|
103
|
+
vertex
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# @param [String] name
|
|
107
|
+
# @param [Object] payload
|
|
108
|
+
# @return [Vertex] the vertex that was added to `self`
|
|
109
|
+
def add_vertex(name, payload)
|
|
110
|
+
vertex = vertices[name] ||= Vertex.new(self, name, payload)
|
|
111
|
+
vertex.tap { |v| v.payload = payload }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# @param [String] name
|
|
115
|
+
# @param [Object] payload
|
|
116
|
+
# @return [Vertex] the vertex that was added to `self`
|
|
117
|
+
def add_root_vertex(name, payload)
|
|
118
|
+
add_vertex(name, payload).tap { |v| root_vertices[name] = v }
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Detaches the {#vertex_named} `name` {Vertex} from the graph, recursively
|
|
122
|
+
# removing any non-root vertices that were orphaned in the process
|
|
123
|
+
# @param [String] name
|
|
124
|
+
# @return [void]
|
|
125
|
+
def detach_vertex_named(name)
|
|
126
|
+
vertex = vertex_named(name)
|
|
127
|
+
return unless vertex
|
|
128
|
+
successors = vertex.successors
|
|
129
|
+
vertices.delete(name)
|
|
130
|
+
edges.reject! { |e| e.origin == vertex || e.destination == vertex }
|
|
131
|
+
successors.each { |v| detach_vertex_named(v.name) unless root_vertices[v.name] || v.predecessors.any? }
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# @param [String] name
|
|
135
|
+
# @return [Vertex,nil] the vertex with the given name
|
|
136
|
+
def vertex_named(name)
|
|
137
|
+
vertices[name]
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# @param [String] name
|
|
141
|
+
# @return [Vertex,nil] the root vertex with the given name
|
|
142
|
+
def root_vertex_named(name)
|
|
143
|
+
root_vertices[name]
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Adds a new {Edge} to the dependency graph
|
|
147
|
+
# @param [Vertex] origin
|
|
148
|
+
# @param [Vertex] destination
|
|
149
|
+
# @param [Object] requirement the requirement that this edge represents
|
|
150
|
+
# @return [Edge] the added edge
|
|
151
|
+
def add_edge(origin, destination, requirement)
|
|
152
|
+
if origin == destination || destination.path_to?(origin)
|
|
153
|
+
raise CircularDependencyError.new([origin, destination])
|
|
154
|
+
end
|
|
155
|
+
Edge.new(origin, destination, [requirement]).tap { |e| edges << e }
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# A vertex in a {DependencyGraph} that encapsulates a {#name} and a
|
|
159
|
+
# {#payload}
|
|
160
|
+
class Vertex
|
|
161
|
+
# @return [DependencyGraph] the graph this vertex is a node of
|
|
162
|
+
attr_accessor :graph
|
|
163
|
+
|
|
164
|
+
# @return [String] the name of the vertex
|
|
165
|
+
attr_accessor :name
|
|
166
|
+
|
|
167
|
+
# @return [Object] the payload the vertex holds
|
|
168
|
+
attr_accessor :payload
|
|
169
|
+
|
|
170
|
+
# @return [Arrary<Object>] the explicit requirements that required
|
|
171
|
+
# this vertex
|
|
172
|
+
attr_reader :explicit_requirements
|
|
173
|
+
|
|
174
|
+
# @param [DependencyGraph] graph see {#graph}
|
|
175
|
+
# @param [String] name see {#name}
|
|
176
|
+
# @param [Object] payload see {#payload}
|
|
177
|
+
def initialize(graph, name, payload)
|
|
178
|
+
@graph = graph
|
|
179
|
+
@name = name
|
|
180
|
+
@payload = payload
|
|
181
|
+
@explicit_requirements = []
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# @return [Array<Object>] all of the requirements that required
|
|
185
|
+
# this vertex
|
|
186
|
+
def requirements
|
|
187
|
+
incoming_edges.map(&:requirements).flatten + explicit_requirements
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# @return [Array<Edge>] the edges of {#graph} that have `self` as their
|
|
191
|
+
# {Edge#origin}
|
|
192
|
+
def outgoing_edges
|
|
193
|
+
graph.edges.select { |e| e.origin.shallow_eql?(self) }
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# @return [Array<Edge>] the edges of {#graph} that have `self` as their
|
|
197
|
+
# {Edge#destination}
|
|
198
|
+
def incoming_edges
|
|
199
|
+
graph.edges.select { |e| e.destination.shallow_eql?(self) }
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# @return [Set<Vertex>] the vertices of {#graph} that have an edge with
|
|
203
|
+
# `self` as their {Edge#destination}
|
|
204
|
+
def predecessors
|
|
205
|
+
incoming_edges.map(&:origin).to_set
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# @return [Set<Vertex>] the vertices of {#graph} that have an edge with
|
|
209
|
+
# `self` as their {Edge#origin}
|
|
210
|
+
def successors
|
|
211
|
+
outgoing_edges.map(&:destination).to_set
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# @return [Set<Vertex>] the vertices of {#graph} where `self` is an
|
|
215
|
+
# {#ancestor?}
|
|
216
|
+
def recursive_successors
|
|
217
|
+
successors + successors.map(&:recursive_successors).reduce(Set.new, &:+)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# @return [String] a string suitable for debugging
|
|
221
|
+
def inspect
|
|
222
|
+
"#{self.class}:#{name}(#{payload.inspect})"
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# @return [Boolean] whether the two vertices are equal, determined
|
|
226
|
+
# by a recursive traversal of each {Vertex#successors}
|
|
227
|
+
def ==(other)
|
|
228
|
+
shallow_eql?(other) &&
|
|
229
|
+
successors == other.successors
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# @return [Boolean] whether the two vertices are equal, determined
|
|
233
|
+
# solely by {#name} and {#payload} equality
|
|
234
|
+
def shallow_eql?(other)
|
|
235
|
+
other &&
|
|
236
|
+
name == other.name &&
|
|
237
|
+
payload == other.payload
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
alias_method :eql?, :==
|
|
241
|
+
|
|
242
|
+
# @return [Fixnum] a hash for the vertex based upon its {#name}
|
|
243
|
+
def hash
|
|
244
|
+
name.hash
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# Is there a path from `self` to `other` following edges in the
|
|
248
|
+
# dependency graph?
|
|
249
|
+
# @return true iff there is a path following edges within this {#graph}
|
|
250
|
+
def path_to?(other)
|
|
251
|
+
successors.include?(other) || successors.any? { |v| v.path_to?(other) }
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
alias_method :descendent?, :path_to?
|
|
255
|
+
|
|
256
|
+
# Is there a path from `other` to `self` following edges in the
|
|
257
|
+
# dependency graph?
|
|
258
|
+
# @return true iff there is a path following edges within this {#graph}
|
|
259
|
+
def ancestor?(other)
|
|
260
|
+
predecessors.include?(other) || predecessors.any? { |v| v.ancestor?(other) }
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
alias_method :is_reachable_from?, :ancestor?
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
module Carat::Molinillo
|
|
2
|
+
# An error that occurred during the resolution process
|
|
3
|
+
class ResolverError < StandardError; end
|
|
4
|
+
|
|
5
|
+
# An error caused by searching for a dependency that is completely unknown,
|
|
6
|
+
# i.e. has no versions available whatsoever.
|
|
7
|
+
class NoSuchDependencyError < ResolverError
|
|
8
|
+
# @return [Object] the dependency that could not be found
|
|
9
|
+
attr_accessor :dependency
|
|
10
|
+
|
|
11
|
+
# @return [Array<Object>] the specifications that depended upon {#dependency}
|
|
12
|
+
attr_accessor :required_by
|
|
13
|
+
|
|
14
|
+
# @param [Object] dependency @see {#dependency}
|
|
15
|
+
# @param [Array<Object>] required_by @see {#required_by}
|
|
16
|
+
def initialize(dependency, required_by = [])
|
|
17
|
+
@dependency = dependency
|
|
18
|
+
@required_by = required_by
|
|
19
|
+
super()
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def message
|
|
23
|
+
sources = required_by.map { |r| "`#{r}`" }.join(' and ')
|
|
24
|
+
message = "Unable to find a specification for `#{dependency}`"
|
|
25
|
+
message << " depended upon by #{sources}" unless sources.empty?
|
|
26
|
+
message
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# An error caused by attempting to fulfil a dependency that was circular
|
|
31
|
+
#
|
|
32
|
+
# @note This exception will be thrown iff a {Vertex} is added to a
|
|
33
|
+
# {DependencyGraph} that has a {DependencyGraph::Vertex#path_to?} an
|
|
34
|
+
# existing {DependencyGraph::Vertex}
|
|
35
|
+
class CircularDependencyError < ResolverError
|
|
36
|
+
# [Set<Object>] the dependencies responsible for causing the error
|
|
37
|
+
attr_reader :dependencies
|
|
38
|
+
|
|
39
|
+
# @param [Array<DependencyGraph::Vertex>] nodes the nodes in the dependency
|
|
40
|
+
# that caused the error
|
|
41
|
+
def initialize(nodes)
|
|
42
|
+
super "There is a circular dependency between #{nodes.map(&:name).join(' and ')}"
|
|
43
|
+
@dependencies = nodes.map(&:payload).to_set
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# An error caused by conflicts in version
|
|
48
|
+
class VersionConflict < ResolverError
|
|
49
|
+
# @return [{String => Resolution::Conflict}] the conflicts that caused
|
|
50
|
+
# resolution to fail
|
|
51
|
+
attr_reader :conflicts
|
|
52
|
+
|
|
53
|
+
# @param [{String => Resolution::Conflict}] conflicts see {#conflicts}
|
|
54
|
+
def initialize(conflicts)
|
|
55
|
+
pairs = []
|
|
56
|
+
conflicts.values.flatten.map(&:requirements).flatten.each do |conflicting|
|
|
57
|
+
conflicting.each do |source, conflict_requirements|
|
|
58
|
+
conflict_requirements.each do |c|
|
|
59
|
+
pairs << [c, source]
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
super "Unable to satisfy the following requirements:\n\n" \
|
|
65
|
+
"#{pairs.map { |r, d| "- `#{r}` required by `#{d}`" }.join("\n")}"
|
|
66
|
+
@conflicts = conflicts
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
module Carat::Molinillo
|
|
2
|
+
# Provides information about specifcations and dependencies to the resolver,
|
|
3
|
+
# allowing the {Resolver} class to remain generic while still providing power
|
|
4
|
+
# and flexibility.
|
|
5
|
+
#
|
|
6
|
+
# This module contains the methods that users of Carat::Molinillo must to implement,
|
|
7
|
+
# using knowledge of their own model classes.
|
|
8
|
+
module SpecificationProvider
|
|
9
|
+
# Search for the specifications that match the given dependency.
|
|
10
|
+
# The specifications in the returned array will be considered in reverse
|
|
11
|
+
# order, so the latest version ought to be last.
|
|
12
|
+
# @note This method should be 'pure', i.e. the return value should depend
|
|
13
|
+
# only on the `dependency` parameter.
|
|
14
|
+
#
|
|
15
|
+
# @param [Object] dependency
|
|
16
|
+
# @return [Array<Object>] the specifications that satisfy the given
|
|
17
|
+
# `dependency`.
|
|
18
|
+
def search_for(dependency)
|
|
19
|
+
[]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Returns the dependencies of `specification`.
|
|
23
|
+
# @note This method should be 'pure', i.e. the return value should depend
|
|
24
|
+
# only on the `specification` parameter.
|
|
25
|
+
#
|
|
26
|
+
# @param [Object] specification
|
|
27
|
+
# @return [Array<Object>] the dependencies that are required by the given
|
|
28
|
+
# `specification`.
|
|
29
|
+
def dependencies_for(specification)
|
|
30
|
+
[]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Determines whether the given `requirement` is satisfied by the given
|
|
34
|
+
# `spec`, in the context of the current `activated` dependency graph.
|
|
35
|
+
#
|
|
36
|
+
# @param [Object] requirement
|
|
37
|
+
# @param [DependencyGraph] activated the current dependency graph in the
|
|
38
|
+
# resolution process.
|
|
39
|
+
# @param [Object] spec
|
|
40
|
+
# @return [Boolean] whether `requirement` is satisfied by `spec` in the
|
|
41
|
+
# context of the current `activated` dependency graph.
|
|
42
|
+
def requirement_satisfied_by?(requirement, activated, spec)
|
|
43
|
+
true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Returns the name for the given `dependency`.
|
|
47
|
+
# @note This method should be 'pure', i.e. the return value should depend
|
|
48
|
+
# only on the `dependency` parameter.
|
|
49
|
+
#
|
|
50
|
+
# @param [Object] dependency
|
|
51
|
+
# @return [String] the name for the given `dependency`.
|
|
52
|
+
def name_for(dependency)
|
|
53
|
+
dependency.to_s
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# @return [String] the name of the source of explicit dependencies, i.e.
|
|
57
|
+
# those passed to {Resolver#resolve} directly.
|
|
58
|
+
def name_for_explicit_dependency_source
|
|
59
|
+
'user-specified dependency'
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @return [String] the name of the source of 'locked' dependencies, i.e.
|
|
63
|
+
# those passed to {Resolver#resolve} directly as the `base`
|
|
64
|
+
def name_for_locking_dependency_source
|
|
65
|
+
'Lockfile'
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Sort dependencies so that the ones that are easiest to resolve are first.
|
|
69
|
+
# Easiest to resolve is (usually) defined by:
|
|
70
|
+
# 1) Is this dependency already activated?
|
|
71
|
+
# 2) How relaxed are the requirements?
|
|
72
|
+
# 3) Are there any conflicts for this dependency?
|
|
73
|
+
# 4) How many possibilities are there to satisfy this dependency?
|
|
74
|
+
#
|
|
75
|
+
# @param [Array<Object>] dependencies
|
|
76
|
+
# @param [DependencyGraph] activated the current dependency graph in the
|
|
77
|
+
# resolution process.
|
|
78
|
+
# @param [{String => Array<Conflict>}] conflicts
|
|
79
|
+
# @return [Array<Object>] a sorted copy of `dependencies`.
|
|
80
|
+
def sort_dependencies(dependencies, activated, conflicts)
|
|
81
|
+
dependencies.sort_by do |dependency|
|
|
82
|
+
name = name_for(dependency)
|
|
83
|
+
[
|
|
84
|
+
activated.vertex_named(name).payload ? 0 : 1,
|
|
85
|
+
conflicts[name] ? 0 : 1,
|
|
86
|
+
]
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|