# File lib/bundler/definition.rb, line 10 10: def self.build(gemfile, lockfile, unlock) 11: unlock ||= {} 12: gemfile = Pathname.new(gemfile).expand_path 13: 14: unless gemfile.file? 15: raise GemfileNotFound, "#{gemfile} not found" 16: end 17: 18: Dsl.evaluate(gemfile, lockfile, unlock) 19: end
# File lib/bundler/definition.rb, line 33 33: def initialize(lockfile, dependencies, sources, unlock) 34: @unlocking = unlock == true || !unlock.empty? 35: 36: @dependencies, @sources, @unlock = dependencies, sources, unlock 37: @remote = false 38: @specs = nil 39: @lockfile_contents = "" 40: 41: if lockfile && File.exists?(lockfile) 42: @lockfile_contents = Bundler.read_file(lockfile) 43: locked = LockfileParser.new(@lockfile_contents) 44: @platforms = locked.platforms 45: 46: if unlock != true 47: @locked_deps = locked.dependencies 48: @locked_specs = SpecSet.new(locked.specs) 49: @locked_sources = locked.sources 50: else 51: @unlock = {} 52: @locked_deps = [] 53: @locked_specs = SpecSet.new([]) 54: @locked_sources = [] 55: end 56: else 57: @unlock = {} 58: @platforms = [] 59: @locked_deps = [] 60: @locked_specs = SpecSet.new([]) 61: @locked_sources = [] 62: end 63: 64: @unlock[:gems] ||= [] 65: @unlock[:sources] ||= [] 66: 67: current_platform = Bundler.rubygems.platforms.map { |p| generic(p) }.compact.last 68: @new_platform = !@platforms.include?(current_platform) 69: @platforms |= [current_platform] 70: 71: @path_changes = @sources.any? do |source| 72: next unless source.instance_of?(Source::Path) 73: 74: locked = @locked_sources.find do |ls| 75: ls.class == source.class && ls.path == source.path 76: end 77: 78: if locked 79: unlocking = locked.specs.any? do |spec| 80: @locked_specs.any? do |locked_spec| 81: locked_spec.source != locked 82: end 83: end 84: end 85: 86: !locked || unlocking || source.specs != locked.specs 87: end 88: eager_unlock = expand_dependencies(@unlock[:gems]) 89: @unlock[:gems] = @locked_specs.for(eager_unlock).map { |s| s.name } 90: 91: @source_changes = converge_sources 92: @dependency_changes = converge_dependencies 93: 94: fixup_dependency_types! 95: end
# File lib/bundler/definition.rb, line 165 165: def current_dependencies 166: dependencies.reject { |d| !d.should_include? } 167: end
# File lib/bundler/definition.rb, line 290 290: def ensure_equivalent_gemfile_and_lockfile(explicit_flag = false) 291: changes = false 292: 293: msg = "You are trying to install in deployment mode after changing\n" "your Gemfile. Run `bundle install` elsewhere and add the\n" "updated Gemfile.lock to version control." 294: 295: unless explicit_flag 296: msg += "\n\nIf this is a development machine, remove the Gemfile " "freeze \nby running `bundle install --no-deployment`." 297: end 298: 299: added = [] 300: deleted = [] 301: changed = [] 302: 303: if @locked_sources != @sources 304: new_sources = @sources - @locked_sources 305: deleted_sources = @locked_sources - @sources 306: 307: if new_sources.any? 308: added.concat new_sources.map { |source| "* source: #{source}" } 309: end 310: 311: if deleted_sources.any? 312: deleted.concat deleted_sources.map { |source| "* source: #{source}" } 313: end 314: 315: changes = true 316: end 317: 318: both_sources = Hash.new { |h,k| h[k] = ["no specified source", "no specified source"] } 319: @dependencies.each { |d| both_sources[d.name][0] = d.source if d.source } 320: @locked_deps.each { |d| both_sources[d.name][1] = d.source if d.source } 321: both_sources.delete_if { |k,v| v[0] == v[1] } 322: 323: if @dependencies != @locked_deps 324: new_deps = @dependencies - @locked_deps 325: deleted_deps = @locked_deps - @dependencies 326: 327: if new_deps.any? 328: added.concat new_deps.map { |d| "* #{pretty_dep(d)}" } 329: end 330: 331: if deleted_deps.any? 332: deleted.concat deleted_deps.map { |d| "* #{pretty_dep(d)}" } 333: end 334: 335: both_sources.each do |name, sources| 336: changed << "* #{name} from `#{sources[0]}` to `#{sources[1]}`" 337: end 338: 339: changes = true 340: end 341: 342: msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any? 343: msg << "\n\nYou have deleted from the Gemfile:\n" << deleted.join("\n") if deleted.any? 344: msg << "\n\nYou have changed in the Gemfile:\n" << changed.join("\n") if changed.any? 345: msg << "\n" 346: 347: raise ProductionError, msg if added.any? || deleted.any? || changed.any? 348: end
# File lib/bundler/definition.rb, line 97 97: def fixup_dependency_types! 98: # XXX This is a temporary workaround for a bug when using rubygems 1.8.15 99: # where Gem::Dependency#== matches Gem::Dependency#type. As the lockfile 100: # doesn't carry a notion of the dependency type, if you use 101: # add_development_dependency in a gemspec that's loaded with the gemspec 102: # directive, the lockfile dependencies and resolved dependencies end up 103: # with a mismatch on #type. 104: # Test coverage to catch a regression on this is in gemspec_spec.rb 105: @dependencies.each do |d| 106: if ld = @locked_deps.find { |l| l.name == d.name } 107: ld.instance_variable_set(:@type, d.type) 108: end 109: end 110: end
# File lib/bundler/definition.rb, line 228 228: def groups 229: dependencies.map { |d| d.groups }.flatten.uniq 230: end
# File lib/bundler/definition.rb, line 197 197: def index 198: @index ||= Index.build do |idx| 199: dependency_names = @dependencies.dup || [] 200: dependency_names.map! {|d| d.name } 201: 202: @sources.each do |s| 203: if s.is_a?(Bundler::Source::Rubygems) 204: s.dependency_names = dependency_names.uniq 205: idx.add_source s.specs 206: else 207: source_index = s.specs 208: dependency_names += source_index.unmet_dependency_names 209: idx.add_source source_index 210: end 211: end 212: end 213: end
# File lib/bundler/definition.rb, line 232 232: def lock(file) 233: contents = to_lock 234: 235: # Convert to \r\n if the existing lock has them 236: # i.e., Windows with `git config core.autocrlf=true` 237: contents.gsub!(/\n/, "\r\n") if @lockfile_contents.match("\r\n") 238: 239: return if @lockfile_contents == contents 240: 241: if Bundler.settings[:frozen] 242: # TODO: Warn here if we got here. 243: return 244: end 245: 246: File.open(file, 'wb'){|f| f.puts(contents) } 247: end
# File lib/bundler/definition.rb, line 151 151: def missing_specs 152: missing = [] 153: resolve.materialize(requested_dependencies, missing) 154: missing 155: end
# File lib/bundler/definition.rb, line 147 147: def new_platform? 148: @new_platform 149: end
# File lib/bundler/definition.rb, line 139 139: def new_specs 140: specs - @locked_specs 141: end
# File lib/bundler/definition.rb, line 224 224: def no_sources? 225: @sources.length == 1 && @sources.first.remotes.empty? 226: end
# File lib/bundler/definition.rb, line 143 143: def removed_specs 144: @locked_specs - specs 145: end
# File lib/bundler/definition.rb, line 157 157: def requested_specs 158: @requested_specs ||= begin 159: groups = self.groups - Bundler.settings.without 160: groups.map! { |g| g.to_sym } 161: specs_for(groups) 162: end 163: end
# File lib/bundler/definition.rb, line 175 175: def resolve 176: @resolve ||= begin 177: if Bundler.settings[:frozen] || (!@unlocking && !@source_changes && !@dependency_changes && !@new_platform && !@path_changes) 178: @locked_specs 179: else 180: last_resolve = converge_locked_specs 181: 182: # Record the specs available in each gem's source, so that those 183: # specs will be available later when the resolver knows where to 184: # look for that gemspec (or its dependencies) 185: source_requirements = {} 186: dependencies.each do |dep| 187: next unless dep.source 188: source_requirements[dep.name] = dep.source.specs 189: end 190: 191: # Run a resolve against the locally available gems 192: last_resolve.merge Resolver.resolve(expanded_dependencies, index, source_requirements, last_resolve) 193: end 194: end 195: end
# File lib/bundler/definition.rb, line 118 118: def resolve_remotely! 119: raise "Specs already loaded" if @specs 120: @remote = true 121: @sources.each { |s| s.remote! } 122: specs 123: end
# File lib/bundler/definition.rb, line 112 112: def resolve_with_cache! 113: raise "Specs already loaded" if @specs 114: @sources.each { |s| s.cached! } 115: specs 116: end
used when frozen is enabled so we can find the bundler spec, even if (say) a git gem is not checked out.
# File lib/bundler/definition.rb, line 217 217: def rubygems_index 218: @rubygems_index ||= Index.build do |idx| 219: rubygems = @sources.find{|s| s.is_a?(Source::Rubygems) } 220: idx.add_source rubygems.specs 221: end 222: end
# File lib/bundler/definition.rb, line 125 125: def specs 126: @specs ||= begin 127: specs = resolve.materialize(requested_dependencies) 128: 129: unless specs["bundler"].any? 130: local = Bundler.settings[:frozen] ? rubygems_index : index 131: bundler = local.search(Gem::Dependency.new('bundler', VERSION)).last 132: specs["bundler"] = bundler if bundler 133: end 134: 135: specs 136: end 137: end
# File lib/bundler/definition.rb, line 169 169: def specs_for(groups) 170: deps = dependencies.select { |d| (d.groups & groups).any? } 171: deps.delete_if { |d| !d.should_include? } 172: specs.for(expand_dependencies(deps)) 173: end
# File lib/bundler/definition.rb, line 249 249: def to_lock 250: out = "" 251: 252: sorted_sources.each do |source| 253: # Add the source header 254: out << source.to_lock 255: # Find all specs for this source 256: resolve. 257: select { |s| s.source == source }. 258: # This needs to be sorted by full name so that 259: # gems with the same name, but different platform 260: # are ordered consistantly 261: sort_by { |s| s.full_name }. 262: each do |spec| 263: next if spec.name == 'bundler' 264: out << spec.to_lock 265: end 266: out << "\n" 267: end 268: 269: out << "PLATFORMS\n" 270: 271: platforms.map { |p| p.to_s }.sort.each do |p| 272: out << " #{p}\n" 273: end 274: 275: out << "\n" 276: out << "DEPENDENCIES\n" 277: 278: handled = [] 279: dependencies. 280: sort_by { |d| d.to_s }. 281: each do |dep| 282: next if handled.include?(dep.name) 283: out << dep.to_lock 284: handled << dep.name 285: end 286: 287: out 288: end
# File lib/bundler/definition.rb, line 400 400: def converge_dependencies 401: (@dependencies + @locked_deps).each do |dep| 402: if dep.source 403: dep.source = @sources.find { |s| dep.source == s } 404: end 405: end 406: Set.new(@dependencies) != Set.new(@locked_deps) 407: end
Remove elements from the locked specs that are expired. This will most commonly happen if the Gemfile has changed since the lockfile was last generated
# File lib/bundler/definition.rb, line 412 412: def converge_locked_specs 413: deps = [] 414: 415: # Build a list of dependencies that are the same in the Gemfile 416: # and Gemfile.lock. If the Gemfile modified a dependency, but 417: # the gem in the Gemfile.lock still satisfies it, this is fine 418: # too. 419: @dependencies.each do |dep| 420: locked_dep = @locked_deps.find { |d| dep == d } 421: 422: if in_locked_deps?(dep, locked_dep) || satisfies_locked_spec?(dep) 423: deps << dep 424: elsif dep.source.is_a?(Source::Path) && dep.current_platform? && (!locked_dep || dep.source != locked_dep.source) 425: @locked_specs.each do |s| 426: @unlock[:gems] << s.name if s.source == dep.source 427: end 428: 429: dep.source.unlock! if dep.source.respond_to?(:unlock!) 430: dep.source.specs.each { |s| @unlock[:gems] << s.name } 431: end 432: end 433: 434: converged = [] 435: @locked_specs.each do |s| 436: s.source = @sources.find { |src| s.source == src } 437: 438: # Don't add a spec to the list if its source is expired. For example, 439: # if you change a Git gem to Rubygems. 440: next if s.source.nil? || @unlock[:sources].include?(s.name) 441: # If the spec is from a path source and it doesn't exist anymore 442: # then we just unlock it. 443: 444: # Path sources have special logic 445: if s.source.instance_of?(Source::Path) 446: other = s.source.specs[s].first 447: 448: # If the spec is no longer in the path source, unlock it. This 449: # commonly happens if the version changed in the gemspec 450: next unless other 451: 452: deps2 = other.dependencies.select { |d| d.type != :development } 453: # If the dependencies of the path source have changed, unlock it 454: next unless s.dependencies.sort == deps2.sort 455: end 456: 457: converged << s 458: end 459: 460: resolve = SpecSet.new(converged) 461: resolve = resolve.for(expand_dependencies(deps, true), @unlock[:gems]) 462: diff = @locked_specs.to_a - resolve.to_a 463: 464: # Now, we unlock any sources that do not have anymore gems pinned to it 465: @sources.each do |source| 466: next unless source.respond_to?(:unlock!) 467: 468: unless resolve.any? { |s| s.source == source } 469: source.unlock! if !diff.empty? && diff.any? { |s| s.source == source } 470: end 471: end 472: 473: resolve 474: end
# File lib/bundler/definition.rb, line 362 362: def converge_sources 363: changes = false 364: 365: # Get the Rubygems source from the Gemfile.lock 366: locked_gem = @locked_sources.find { |s| Source::Rubygems === s } 367: 368: # Get the Rubygems source from the Gemfile 369: actual_gem = @sources.find { |s| Source::Rubygems === s } 370: 371: # If there is a Rubygems source in both 372: if locked_gem && actual_gem 373: # Merge the remotes from the Gemfile into the Gemfile.lock 374: changes = changes | locked_gem.replace_remotes(actual_gem) 375: end 376: 377: # Replace the sources from the Gemfile with the sources from the Gemfile.lock, 378: # if they exist in the Gemfile.lock and are `==`. If you can't find an equivalent 379: # source in the Gemfile.lock, use the one from the Gemfile. 380: @sources.map! do |source| 381: @locked_sources.find { |s| s == source } || source 382: end 383: changes = changes | (Set.new(@sources) != Set.new(@locked_sources)) 384: 385: @sources.each do |source| 386: # If the source is unlockable and the current command allows an unlock of 387: # the source (for example, you are doing a `bundle update <foo>` of a git-pinned 388: # gem), unlock it. For git sources, this means to unlock the revision, which 389: # will cause the `ref` used to be the most recent for the branch (or master) if 390: # an explicit `ref` is not used. 391: if source.respond_to?(:unlock!) && @unlock[:sources].include?(source.name) 392: source.unlock! 393: changes = true 394: end 395: end 396: 397: changes 398: end
# File lib/bundler/definition.rb, line 488 488: def expand_dependencies(dependencies, remote = false) 489: deps = [] 490: dependencies.each do |dep| 491: dep = Dependency.new(dep, ">= 0") unless dep.respond_to?(:name) 492: dep.gem_platforms(@platforms).each do |p| 493: deps << DepProxy.new(dep, p) if remote || p == generic(Gem::Platform.local) 494: end 495: end 496: deps 497: end
# File lib/bundler/definition.rb, line 484 484: def expanded_dependencies 485: @expanded_dependencies ||= expand_dependencies(dependencies, @remote) 486: end
# File lib/bundler/definition.rb, line 476 476: def in_locked_deps?(dep, d) 477: d && dep.source == d.source 478: end
# File lib/bundler/definition.rb, line 355 355: def pretty_dep(dep, source = false) 356: msg = "#{dep.name}" 357: msg << " (#{dep.requirement})" unless dep.requirement == Gem::Requirement.default 358: msg << " from the `#{dep.source}` source" if source && dep.source 359: msg 360: end
# File lib/bundler/definition.rb, line 506 506: def requested_dependencies 507: groups = self.groups - Bundler.settings.without 508: groups.map! { |g| g.to_sym } 509: dependencies.reject { |d| !d.should_include? || (d.groups & groups).empty? } 510: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.