require'set'# This is the latest iteration of the gem dependency resolving algorithm. As of now,# it can resolve (as a success or failure) any set of gem dependencies we throw at it# in a reasonable amount of time. The most iterations I've seen it take is about 150.# The actual implementation of the algorithm is not as good as it could be yet, but that# can come later.moduleBundlerclassResolverrequire'bundler/vendored_molinillo'classMolinillo::VersionConflictdefclean_req(req)ifreq.to_s.include?(">= 0")req.to_s.gsub(/ \(.*?\)$/,'')elsereq.to_s.gsub(/\, (runtime|development)\)$/,')')endenddefmessageconflicts.values.flatten.reduce('')do|o,conflict|o<<%(Bundler could not find compatible versions for gem "#{conflict.requirement.name}":\n)ifconflict.locked_requiremento<<%( In snapshot (Gemfile.lock):\n)o<<%( #{clean_reqconflict.locked_requirement}\n)o<<%(\n)endo<<%( In Gemfile:\n)o<<conflict.requirement_trees.mapdo|tree|t=''depth=2tree.eachdo|req|t<<' '*depth<<%(#{clean_reqreq})t<<%( depends on)unlesstree[-1]==reqt<<%(\n)depth+=1endtend.join("\n")ifconflict.requirement.name=='bundler'o<<%(\n Current Bundler version:\n bundler (#{Bundler::VERSION}))other_bundler_required=!conflict.requirement.requirement.satisfied_by?(Gem::Version.newBundler::VERSION)endifconflict.requirement.name=="bundler"&&other_bundler_requiredo<<"\n"o<<"This Gemfile requires a different version of Bundler.\n"o<<"Perhaps you need to update Bundler by running `gem install bundler`?\n"endifconflict.locked_requiremento<<"\n"o<<%(Running `bundle update` will rebuild your snapshot from scratch, using only\n)o<<%(the gems in your Gemfile, which may resolve the conflict.\n)elsif!conflict.existingifconflict.requirement_trees.first.size>1o<<"Could not find gem '#{clean_req(conflict.requirement)}', which is required by "o<<"gem '#{clean_req(conflict.requirement_trees.first[-2])}', in any of the sources."elseo<<"Could not find gem '#{clean_req(conflict.requirement)} in any of the sources\n"endendoendendendALL=Bundler::Dependency::PLATFORM_MAP.values.uniq.freezeclassSpecGroup<ArrayincludeGemHelpersattr_reader:activated,:required_bydefinitialize(a)super@required_by=[]@activated=[]@dependencies=nil@specs={}ALL.eachdo|p|@specs[p]=reverse.find{|s|s.match_platform(p)}endenddefinitialize_copy(o)super@required_by=o.required_by.dup@activated=o.activated.dupenddefto_specsspecs={}@activated.eachdo|p|ifs=@specs[p]platform=generic(Gem::Platform.new(s.platform))nextifspecs[platform]lazy_spec=LazySpecification.new(name,version,platform,source)lazy_spec.dependencies.replaces.dependenciesspecs[platform]=lazy_specendendspecs.valuesenddefactivate_platform(platform)unless@activated.include?(platform)iffor?(platform)@activated<<platformreturn__dependencies[platform]||[]endend[]enddefname@name||=first.nameenddefversion@version||=first.versionenddefsource@source||=first.sourceenddeffor?(platform)@specs[platform]enddefto_s"#{name} (#{version})"enddefdependencies_for_activated_platforms@activated.map{|p|__dependencies[p]}.flattenenddefplatforms_for_dependency_named(dependency)__dependencies.select{|p,deps|deps.map(&:name).include?dependency}.keysendprivatedef__dependencies@dependencies||=begindependencies={}ALL.eachdo|p|ifspec=@specs[p]dependencies[p]=[]spec.dependencies.eachdo|dep|nextifdep.type==:developmentdependencies[p]<<DepProxy.new(dep,p)endendenddependenciesendendend# Figures out the best possible configuration of gems that satisfies# the list of passed dependencies and any child dependencies without# causing any gem activation errors.## ==== Parameters# *dependencies<Gem::Dependency>:: The list of dependencies to resolve## ==== Returns# <GemBundle>,nil:: If the list of dependencies can be resolved, a# collection of gemspecs is returned. Otherwise, nil is returned.defself.resolve(requirements,index,source_requirements={},base=[])base=SpecSet.new(base)unlessbase.is_a?(SpecSet)resolver=new(index,source_requirements,base)result=resolver.start(requirements)SpecSet.new(result)enddefinitialize(index,source_requirements,base)@index=index@source_requirements=source_requirements@base=base@resolver=Molinillo::Resolver.new(self,self)@search_for={}@prereleases_cache=Hash.new{|h,k|h[k]=k.prerelease?}@base_dg=Molinillo::DependencyGraph.new@base.each{|ls|@base_dg.add_root_vertexls.name,Dependency.new(ls.name,ls.version)}enddefstart(requirements)verify_gemfile_dependencies_are_found!(requirements)dg=@resolver.resolve(requirements,@base_dg)dg.map(&:payload).map(&:to_specs).flattenrescueMolinillo::VersionConflict=>eraiseVersionConflict.new(e.conflicts.keys.uniq,e.message)rescueMolinillo::CircularDependencyError=>enames=e.dependencies.sort_by(&:name).map{|d|"gem '#{d.name}'"}raiseCyclicDependencyError,"Your Gemfile requires gems that depend"\" on each other, creating an infinite loop. Please remove"\" #{names.count>1?'either ':''}#{names.join(' or ')}"\" and try again."endincludeMolinillo::UI# Conveys debug information to the user.## @param [Integer] depth the current depth of the resolution process.# @return [void]defdebug(depth=0)ifdebug?debug_info=yielddebug_info=debug_info.inspectunlessdebug_info.is_a?(String)STDERR.putsdebug_info.split("\n").map{|s|' '*depth+s}endenddefdebug?ENV['DEBUG_RESOLVER']||ENV['DEBUG_RESOLVER_TREE']enddefbefore_resolutionBundler.ui.info'Resolving dependencies...',falseenddefafter_resolutionBundler.ui.info''enddefindicate_progressBundler.ui.info'.',falseendprivateincludeMolinillo::SpecificationProviderdefdependencies_for(specification)specification.dependencies_for_activated_platformsenddefsearch_for(dependency)platform=dependency.__platformdependency=dependency.depunlessdependency.is_a?Gem::Dependencysearch=@search_for[dependency]||=beginindex=@source_requirements[dependency.name]||@indexresults=index.search(dependency,@base[dependency.name])ifvertex=@base_dg.vertex_named(dependency.name)locked_requirement=vertex.payload.requirementendifresults.any?version=results.first.versionnested=[[]]results.eachdo|spec|ifspec.version!=versionnested<<[]version=spec.versionendnested.last<<specendgroups=nested.map{|a|SpecGroup.new(a)}!locked_requirement?groups:groups.select{|sg|locked_requirement.satisfied_by?sg.version}else[]endendsearch.select{|sg|sg.for?(platform)}.each{|sg|sg.activate_platform(platform)}enddefname_for(dependency)dependency.nameenddefname_for_explicit_dependency_source'Gemfile'enddefname_for_locking_dependency_source'Gemfile.lock'enddefrequirement_satisfied_by?(requirement,activated,spec)requirement.matches_spec?(spec)enddefsort_dependencies(dependencies,activated,conflicts)dependencies.sort_bydo|dependency|name=name_for(dependency)[activated.vertex_named(name).payload?0:1,@prereleases_cache[dependency.requirement]?0:1,conflicts[name]?0:1,activated.vertex_named(name).payload?0:search_for(dependency).count,]endenddefverify_gemfile_dependencies_are_found!(requirements)requirements.eachdo|requirement|nextifrequirement.name=='bundler'ifsearch_for(requirement).empty?ifbase=@base[requirement.name]and!base.empty?version=base.first.versionmessage="You have requested:\n"\" #{requirement.name}#{requirement.requirement}\n\n"\"The bundle currently has #{requirement.name} locked at #{version}.\n"\"Try running `bundle update #{requirement.name}`"elsifrequirement.sourcename=requirement.nameversions=@source_requirements[name][name].map{|s|s.version}message="Could not find gem '#{requirement}' in #{requirement.source}.\n"ifversions.any?message<<"Source contains '#{name}' at: #{versions.join(', ')}"elsemessage<<"Source does not contain any versions of '#{requirement}'"endelsemessage="Could not find gem '#{requirement}' in any of the gem sources listed in your Gemfile or installed on this machine."endraiseGemNotFound,messageendendendendend