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,63 @@
|
|
|
1
|
+
module Carat::Molinillo
|
|
2
|
+
# Conveys information about the resolution process to a user.
|
|
3
|
+
module UI
|
|
4
|
+
# The {IO} object that should be used to print output. `STDOUT`, by default.
|
|
5
|
+
#
|
|
6
|
+
# @return [IO]
|
|
7
|
+
def output
|
|
8
|
+
STDOUT
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Called roughly every {#progress_rate}, this method should convey progress
|
|
12
|
+
# to the user.
|
|
13
|
+
#
|
|
14
|
+
# @return [void]
|
|
15
|
+
def indicate_progress
|
|
16
|
+
output.print '.' unless debug?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# How often progress should be conveyed to the user via
|
|
20
|
+
# {#indicate_progress}, in seconds. A third of a second, by default.
|
|
21
|
+
#
|
|
22
|
+
# @return [Float]
|
|
23
|
+
def progress_rate
|
|
24
|
+
0.33
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Called before resolution begins.
|
|
28
|
+
#
|
|
29
|
+
# @return [void]
|
|
30
|
+
def before_resolution
|
|
31
|
+
output.print 'Resolving dependencies...'
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Called after resolution ends (either successfully or with an error).
|
|
35
|
+
# By default, prints a newline.
|
|
36
|
+
#
|
|
37
|
+
# @return [void]
|
|
38
|
+
def after_resolution
|
|
39
|
+
output.puts
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Conveys debug information to the user.
|
|
43
|
+
#
|
|
44
|
+
# @param [Integer] depth the current depth of the resolution process.
|
|
45
|
+
# @return [void]
|
|
46
|
+
def debug(depth = 0)
|
|
47
|
+
if debug?
|
|
48
|
+
debug_info = yield
|
|
49
|
+
debug_info = debug_info.inspect unless debug_info.is_a?(String)
|
|
50
|
+
output.puts debug_info.split("\n").map { |s| ' ' * depth + s }
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Whether or not debug messages should be printed.
|
|
55
|
+
# By default, whether or not the `MOLINILLO_DEBUG` environment variable is
|
|
56
|
+
# set.
|
|
57
|
+
#
|
|
58
|
+
# @return [Boolean]
|
|
59
|
+
def debug?
|
|
60
|
+
@debug_mode ||= ENV['MOLINILLO_DEBUG']
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
module Carat::Molinillo
|
|
2
|
+
class Resolver
|
|
3
|
+
# A specific resolution from a given {Resolver}
|
|
4
|
+
class Resolution
|
|
5
|
+
# A conflict that the resolution process encountered
|
|
6
|
+
# @attr [Object] requirement the requirement that immediately led to the conflict
|
|
7
|
+
# @attr [{String,Nil=>[Object]}] requirements the requirements that caused the conflict
|
|
8
|
+
# @attr [Object, nil] existing the existing spec that was in conflict with
|
|
9
|
+
# the {#possibility}
|
|
10
|
+
# @attr [Object] possibility the spec that was unable to be activated due
|
|
11
|
+
# to a conflict
|
|
12
|
+
# @attr [Object] locked_requirement the relevant locking requirement.
|
|
13
|
+
# @attr [Array<Array<Object>>] requirement_trees the different requirement
|
|
14
|
+
# trees that led to every requirement for the conflicting name.
|
|
15
|
+
Conflict = Struct.new(
|
|
16
|
+
:requirement,
|
|
17
|
+
:requirements,
|
|
18
|
+
:existing,
|
|
19
|
+
:possibility,
|
|
20
|
+
:locked_requirement,
|
|
21
|
+
:requirement_trees
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# @return [SpecificationProvider] the provider that knows about
|
|
25
|
+
# dependencies, requirements, specifications, versions, etc.
|
|
26
|
+
attr_reader :specification_provider
|
|
27
|
+
|
|
28
|
+
# @return [UI] the UI that knows how to communicate feedback about the
|
|
29
|
+
# resolution process back to the user
|
|
30
|
+
attr_reader :resolver_ui
|
|
31
|
+
|
|
32
|
+
# @return [DependencyGraph] the base dependency graph to which
|
|
33
|
+
# dependencies should be 'locked'
|
|
34
|
+
attr_reader :base
|
|
35
|
+
|
|
36
|
+
# @return [Array] the dependencies that were explicitly required
|
|
37
|
+
attr_reader :original_requested
|
|
38
|
+
|
|
39
|
+
# @param [SpecificationProvider] specification_provider
|
|
40
|
+
# see {#specification_provider}
|
|
41
|
+
# @param [UI] resolver_ui see {#resolver_ui}
|
|
42
|
+
# @param [Array] requested see {#original_requested}
|
|
43
|
+
# @param [DependencyGraph] base see {#base}
|
|
44
|
+
def initialize(specification_provider, resolver_ui, requested, base)
|
|
45
|
+
@specification_provider = specification_provider
|
|
46
|
+
@resolver_ui = resolver_ui
|
|
47
|
+
@original_requested = requested
|
|
48
|
+
@base = base
|
|
49
|
+
@states = []
|
|
50
|
+
@iteration_counter = 0
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Resolves the {#original_requested} dependencies into a full dependency
|
|
54
|
+
# graph
|
|
55
|
+
# @raise [ResolverError] if successful resolution is impossible
|
|
56
|
+
# @return [DependencyGraph] the dependency graph of successfully resolved
|
|
57
|
+
# dependencies
|
|
58
|
+
def resolve
|
|
59
|
+
start_resolution
|
|
60
|
+
|
|
61
|
+
while state
|
|
62
|
+
break unless state.requirements.any? || state.requirement
|
|
63
|
+
indicate_progress
|
|
64
|
+
if state.respond_to?(:pop_possibility_state) # DependencyState
|
|
65
|
+
debug(depth) { "Creating possibility state for #{requirement} (#{possibilities.count} remaining)" }
|
|
66
|
+
state.pop_possibility_state.tap { |s| states.push(s) if s }
|
|
67
|
+
end
|
|
68
|
+
process_topmost_state
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
activated.freeze
|
|
72
|
+
ensure
|
|
73
|
+
end_resolution
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# @return [Integer] the number of resolver iterations in between calls to
|
|
77
|
+
# {#resolver_ui}'s {UI#indicate_progress} method
|
|
78
|
+
attr_accessor :iteration_rate
|
|
79
|
+
private :iteration_rate
|
|
80
|
+
|
|
81
|
+
# @return [Time] the time at which resolution began
|
|
82
|
+
attr_accessor :started_at
|
|
83
|
+
private :started_at
|
|
84
|
+
|
|
85
|
+
# @return [Array<ResolutionState>] the stack of states for the resolution
|
|
86
|
+
attr_accessor :states
|
|
87
|
+
private :states
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
|
|
91
|
+
# Sets up the resolution process
|
|
92
|
+
# @return [void]
|
|
93
|
+
def start_resolution
|
|
94
|
+
@started_at = Time.now
|
|
95
|
+
|
|
96
|
+
states.push(initial_state)
|
|
97
|
+
|
|
98
|
+
debug { "Starting resolution (#{@started_at})" }
|
|
99
|
+
resolver_ui.before_resolution
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Ends the resolution process
|
|
103
|
+
# @return [void]
|
|
104
|
+
def end_resolution
|
|
105
|
+
resolver_ui.after_resolution
|
|
106
|
+
debug do
|
|
107
|
+
"Finished resolution (#{@iteration_counter} steps) " \
|
|
108
|
+
"(Took #{(ended_at = Time.now) - @started_at} seconds) (#{ended_at})"
|
|
109
|
+
end
|
|
110
|
+
debug { 'Unactivated: ' + Hash[activated.vertices.reject { |_n, v| v.payload }].keys.join(', ') } if state
|
|
111
|
+
debug { 'Activated: ' + Hash[activated.vertices.select { |_n, v| v.payload }].keys.join(', ') } if state
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
require 'carat/vendor/molinillo/lib/molinillo/state'
|
|
115
|
+
require 'carat/vendor/molinillo/lib/molinillo/modules/specification_provider'
|
|
116
|
+
|
|
117
|
+
ResolutionState.new.members.each do |member|
|
|
118
|
+
define_method member do |*args, &block|
|
|
119
|
+
state.send(member, *args, &block)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
SpecificationProvider.instance_methods(false).each do |instance_method|
|
|
124
|
+
define_method instance_method do |*args, &block|
|
|
125
|
+
begin
|
|
126
|
+
specification_provider.send(instance_method, *args, &block)
|
|
127
|
+
rescue NoSuchDependencyError => error
|
|
128
|
+
if state
|
|
129
|
+
vertex = activated.vertex_named(name_for error.dependency)
|
|
130
|
+
error.required_by += vertex.incoming_edges.map { |e| e.origin.name }
|
|
131
|
+
error.required_by << name_for_explicit_dependency_source unless vertex.explicit_requirements.empty?
|
|
132
|
+
end
|
|
133
|
+
raise
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Processes the topmost available {RequirementState} on the stack
|
|
139
|
+
# @return [void]
|
|
140
|
+
def process_topmost_state
|
|
141
|
+
if possibility
|
|
142
|
+
attempt_to_activate
|
|
143
|
+
else
|
|
144
|
+
create_conflict if state.is_a? PossibilityState
|
|
145
|
+
unwind_for_conflict until possibility && state.is_a?(DependencyState)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# @return [Object] the current possibility that the resolution is trying
|
|
150
|
+
# to activate
|
|
151
|
+
def possibility
|
|
152
|
+
possibilities.last
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# @return [RequirementState] the current state the resolution is
|
|
156
|
+
# operating upon
|
|
157
|
+
def state
|
|
158
|
+
states.last
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Creates the initial state for the resolution, based upon the
|
|
162
|
+
# {#requested} dependencies
|
|
163
|
+
# @return [DependencyState] the initial state for the resolution
|
|
164
|
+
def initial_state
|
|
165
|
+
graph = DependencyGraph.new.tap do |dg|
|
|
166
|
+
original_requested.each { |r| dg.add_root_vertex(name_for(r), nil).tap { |v| v.explicit_requirements << r } }
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
requirements = sort_dependencies(original_requested, graph, {})
|
|
170
|
+
initial_requirement = requirements.shift
|
|
171
|
+
DependencyState.new(
|
|
172
|
+
initial_requirement && name_for(initial_requirement),
|
|
173
|
+
requirements,
|
|
174
|
+
graph,
|
|
175
|
+
initial_requirement,
|
|
176
|
+
initial_requirement && search_for(initial_requirement),
|
|
177
|
+
0,
|
|
178
|
+
{}
|
|
179
|
+
)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Unwinds the states stack because a conflict has been encountered
|
|
183
|
+
# @return [void]
|
|
184
|
+
def unwind_for_conflict
|
|
185
|
+
debug(depth) { "Unwinding for conflict: #{requirement}" }
|
|
186
|
+
conflicts.tap do |c|
|
|
187
|
+
states.slice!((state_index_for_unwind + 1)..-1)
|
|
188
|
+
raise VersionConflict.new(c) unless state
|
|
189
|
+
state.conflicts = c
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# @return [Integer] The index to which the resolution should unwind in the
|
|
194
|
+
# case of conflict.
|
|
195
|
+
def state_index_for_unwind
|
|
196
|
+
current_requirement = requirement
|
|
197
|
+
existing_requirement = requirement_for_existing_name(name)
|
|
198
|
+
until current_requirement.nil?
|
|
199
|
+
current_state = find_state_for(current_requirement)
|
|
200
|
+
return states.index(current_state) if state_any?(current_state)
|
|
201
|
+
current_requirement = parent_of(current_requirement)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
until existing_requirement.nil?
|
|
205
|
+
existing_state = find_state_for(existing_requirement)
|
|
206
|
+
return states.index(existing_state) if state_any?(existing_state)
|
|
207
|
+
existing_requirement = parent_of(existing_requirement)
|
|
208
|
+
end
|
|
209
|
+
-1
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# @return [Object] the requirement that led to `requirement` being added
|
|
213
|
+
# to the list of requirements.
|
|
214
|
+
def parent_of(requirement)
|
|
215
|
+
return nil unless requirement
|
|
216
|
+
seen = false
|
|
217
|
+
state = states.reverse_each.find do |s|
|
|
218
|
+
seen ||= s.requirement == requirement
|
|
219
|
+
seen && s.requirement != requirement && !s.requirements.include?(requirement)
|
|
220
|
+
end
|
|
221
|
+
state && state.requirement
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# @return [Object] the requirement that led to a version of a possibility
|
|
225
|
+
# with the given name being activated.
|
|
226
|
+
def requirement_for_existing_name(name)
|
|
227
|
+
return nil unless activated.vertex_named(name).payload
|
|
228
|
+
states.reverse_each.find { |s| !s.activated.vertex_named(name).payload }.requirement
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# @return [ResolutionState] the state whose `requirement` is the given
|
|
232
|
+
# `requirement`.
|
|
233
|
+
def find_state_for(requirement)
|
|
234
|
+
return nil unless requirement
|
|
235
|
+
states.reverse_each.find { |i| requirement == i.requirement && i.is_a?(DependencyState) }
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# @return [Boolean] whether or not the given state has any possibilities
|
|
239
|
+
# left.
|
|
240
|
+
def state_any?(state)
|
|
241
|
+
state && state.possibilities.any?
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# @return [Conflict] a {Conflict} that reflects the failure to activate
|
|
245
|
+
# the {#possibility} in conjunction with the current {#state}
|
|
246
|
+
def create_conflict
|
|
247
|
+
vertex = activated.vertex_named(name)
|
|
248
|
+
requirements = {
|
|
249
|
+
name_for_explicit_dependency_source => vertex.explicit_requirements,
|
|
250
|
+
name_for_locking_dependency_source => Array(locked_requirement_named(name)),
|
|
251
|
+
}
|
|
252
|
+
vertex.incoming_edges.each { |edge| (requirements[edge.origin.payload] ||= []).unshift(*edge.requirements) }
|
|
253
|
+
conflicts[name] = Conflict.new(
|
|
254
|
+
requirement,
|
|
255
|
+
Hash[requirements.select { |_, r| !r.empty? }],
|
|
256
|
+
vertex.payload,
|
|
257
|
+
possibility,
|
|
258
|
+
locked_requirement_named(name),
|
|
259
|
+
requirement_trees
|
|
260
|
+
)
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# @return [Array<Array<Object>>] The different requirement
|
|
264
|
+
# trees that led to every requirement for the current spec.
|
|
265
|
+
def requirement_trees
|
|
266
|
+
activated.vertex_named(name).requirements.map { |r| requirement_tree_for(r) }
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# @return [Array<Object>] the list of requirements that led to
|
|
270
|
+
# `requirement` being required.
|
|
271
|
+
def requirement_tree_for(requirement)
|
|
272
|
+
tree = []
|
|
273
|
+
while requirement
|
|
274
|
+
tree.unshift(requirement)
|
|
275
|
+
requirement = parent_of(requirement)
|
|
276
|
+
end
|
|
277
|
+
tree
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# Indicates progress roughly once every second
|
|
281
|
+
# @return [void]
|
|
282
|
+
def indicate_progress
|
|
283
|
+
@iteration_counter += 1
|
|
284
|
+
@progress_rate ||= resolver_ui.progress_rate
|
|
285
|
+
if iteration_rate.nil?
|
|
286
|
+
if Time.now - started_at >= @progress_rate
|
|
287
|
+
self.iteration_rate = @iteration_counter
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
if iteration_rate && (@iteration_counter % iteration_rate) == 0
|
|
292
|
+
resolver_ui.indicate_progress
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# Calls the {#resolver_ui}'s {UI#debug} method
|
|
297
|
+
# @param [Integer] depth the depth of the {#states} stack
|
|
298
|
+
# @param [Proc] block a block that yields a {#to_s}
|
|
299
|
+
# @return [void]
|
|
300
|
+
def debug(depth = 0, &block)
|
|
301
|
+
resolver_ui.debug(depth, &block)
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
# Attempts to activate the current {#possibility}
|
|
305
|
+
# @return [void]
|
|
306
|
+
def attempt_to_activate
|
|
307
|
+
debug(depth) { 'Attempting to activate ' + possibility.to_s }
|
|
308
|
+
existing_node = activated.vertex_named(name)
|
|
309
|
+
if existing_node.payload
|
|
310
|
+
debug(depth) { "Found existing spec (#{existing_node.payload})" }
|
|
311
|
+
attempt_to_activate_existing_spec(existing_node)
|
|
312
|
+
else
|
|
313
|
+
attempt_to_activate_new_spec
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# Attempts to activate the current {#possibility} (given that it has
|
|
318
|
+
# already been activated)
|
|
319
|
+
# @return [void]
|
|
320
|
+
def attempt_to_activate_existing_spec(existing_node)
|
|
321
|
+
existing_spec = existing_node.payload
|
|
322
|
+
if requirement_satisfied_by?(requirement, activated, existing_spec)
|
|
323
|
+
new_requirements = requirements.dup
|
|
324
|
+
push_state_for_requirements(new_requirements)
|
|
325
|
+
else
|
|
326
|
+
return if attempt_to_swap_possibility
|
|
327
|
+
create_conflict
|
|
328
|
+
debug(depth) { "Unsatisfied by existing spec (#{existing_node.payload})" }
|
|
329
|
+
unwind_for_conflict
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
# Attempts to swp the current {#possibility} with the already-activated
|
|
334
|
+
# spec with the given name
|
|
335
|
+
# @return [Boolean] Whether the possibility was swapped into {#activated}
|
|
336
|
+
def attempt_to_swap_possibility
|
|
337
|
+
swapped = activated.dup
|
|
338
|
+
swapped.vertex_named(name).payload = possibility
|
|
339
|
+
return unless swapped.vertex_named(name).requirements.
|
|
340
|
+
all? { |r| requirement_satisfied_by?(r, swapped, possibility) }
|
|
341
|
+
attempt_to_activate_new_spec
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
# Attempts to activate the current {#possibility} (given that it hasn't
|
|
345
|
+
# already been activated)
|
|
346
|
+
# @return [void]
|
|
347
|
+
def attempt_to_activate_new_spec
|
|
348
|
+
satisfied = begin
|
|
349
|
+
locked_requirement = locked_requirement_named(name)
|
|
350
|
+
requested_spec_satisfied = requirement_satisfied_by?(requirement, activated, possibility)
|
|
351
|
+
locked_spec_satisfied = !locked_requirement ||
|
|
352
|
+
requirement_satisfied_by?(locked_requirement, activated, possibility)
|
|
353
|
+
debug(depth) { 'Unsatisfied by requested spec' } unless requested_spec_satisfied
|
|
354
|
+
debug(depth) { 'Unsatisfied by locked spec' } unless locked_spec_satisfied
|
|
355
|
+
requested_spec_satisfied && locked_spec_satisfied
|
|
356
|
+
end
|
|
357
|
+
if satisfied
|
|
358
|
+
activate_spec
|
|
359
|
+
else
|
|
360
|
+
create_conflict
|
|
361
|
+
unwind_for_conflict
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
# @param [String] requirement_name the spec name to search for
|
|
366
|
+
# @return [Object] the locked spec named `requirement_name`, if one
|
|
367
|
+
# is found on {#base}
|
|
368
|
+
def locked_requirement_named(requirement_name)
|
|
369
|
+
vertex = base.vertex_named(requirement_name)
|
|
370
|
+
vertex && vertex.payload
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
# Add the current {#possibility} to the dependency graph of the current
|
|
374
|
+
# {#state}
|
|
375
|
+
# @return [void]
|
|
376
|
+
def activate_spec
|
|
377
|
+
conflicts.delete(name)
|
|
378
|
+
debug(depth) { 'Activated ' + name + ' at ' + possibility.to_s }
|
|
379
|
+
vertex = activated.vertex_named(name)
|
|
380
|
+
vertex.payload = possibility
|
|
381
|
+
require_nested_dependencies_for(possibility)
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
# Requires the dependencies that the recently activated spec has
|
|
385
|
+
# @param [Object] activated_spec the specification that has just been
|
|
386
|
+
# activated
|
|
387
|
+
# @return [void]
|
|
388
|
+
def require_nested_dependencies_for(activated_spec)
|
|
389
|
+
nested_dependencies = dependencies_for(activated_spec)
|
|
390
|
+
debug(depth) { "Requiring nested dependencies (#{nested_dependencies.map(&:to_s).join(', ')})" }
|
|
391
|
+
nested_dependencies.each { |d| activated.add_child_vertex name_for(d), nil, [name_for(activated_spec)], d }
|
|
392
|
+
|
|
393
|
+
push_state_for_requirements(requirements + nested_dependencies)
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
# Pushes a new {DependencyState} that encapsulates both existing and new
|
|
397
|
+
# requirements
|
|
398
|
+
# @param [Array] new_requirements
|
|
399
|
+
# @return [void]
|
|
400
|
+
def push_state_for_requirements(new_requirements)
|
|
401
|
+
new_requirements = sort_dependencies(new_requirements.uniq, activated, conflicts)
|
|
402
|
+
new_requirement = new_requirements.shift
|
|
403
|
+
states.push DependencyState.new(
|
|
404
|
+
new_requirement ? name_for(new_requirement) : '',
|
|
405
|
+
new_requirements,
|
|
406
|
+
activated.dup,
|
|
407
|
+
new_requirement,
|
|
408
|
+
new_requirement ? search_for(new_requirement) : [],
|
|
409
|
+
depth,
|
|
410
|
+
conflicts.dup
|
|
411
|
+
)
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
end
|