lib/bundler/definition.rb in bundler-1.13.7 vs lib/bundler/definition.rb in bundler-1.14.0.pre.1

- old
+ new

@@ -60,18 +60,20 @@ @optional_groups = optional_groups @remote = false @specs = nil @ruby_version = ruby_version + @lockfile = lockfile @lockfile_contents = String.new @locked_bundler_version = nil @locked_ruby_version = nil if lockfile && File.exist?(lockfile) @lockfile_contents = Bundler.read_file(lockfile) @locked_gems = LockfileParser.new(@lockfile_contents) - @platforms = @locked_gems.platforms + @locked_platforms = @locked_gems.platforms + @platforms = @locked_platforms.dup @locked_bundler_version = @locked_gems.bundler_version @locked_ruby_version = @locked_gems.ruby_version if unlock != true @locked_deps = @locked_gems.dependencies @@ -88,30 +90,29 @@ @platforms = [] @locked_gems = nil @locked_deps = [] @locked_specs = SpecSet.new([]) @locked_sources = [] + @locked_platforms = [] end @unlock[:gems] ||= [] @unlock[:sources] ||= [] - @unlock[:ruby] ||= if @ruby_version && @locked_ruby_version - unless locked_ruby_version_object = RubyVersion.from_string(@locked_ruby_version) - raise LockfileError, "Failed to create a `RubyVersion` object from " \ - "`#{@locked_ruby_version}` found in #{lockfile} -- try running `bundle update --ruby`." - end + @unlock[:ruby] ||= if @ruby_version && locked_ruby_version_object @ruby_version.diff(locked_ruby_version_object) end @unlocking ||= @unlock[:ruby] ||= (!@locked_ruby_version ^ !@ruby_version) - current_platform = Bundler.rubygems.platforms.map {|p| generic(p) }.compact.last - add_platform(current_platform) + add_current_platform unless Bundler.settings[:frozen] @path_changes = converge_paths - eager_unlock = expand_dependencies(@unlock[:gems]) - @unlock[:gems] = @locked_specs.for(eager_unlock).map(&:name) + unless @unlock[:lock_shared_dependencies] + eager_unlock = expand_dependencies(@unlock[:gems]) + @unlock[:gems] = @locked_specs.for(eager_unlock).map(&:name) + end + @gem_version_promoter = create_gem_version_promoter @source_changes = converge_sources @dependency_changes = converge_dependencies @local_changes = converge_locals @@ -173,11 +174,11 @@ begin specs = resolve.materialize(Bundler.settings[:cache_all_platforms] ? dependencies : requested_dependencies) rescue GemNotFound => e # Handle yanked gem gem_name, gem_version = extract_gem_info(e) locked_gem = @locked_specs[gem_name].last - raise if locked_gem.nil? || locked_gem.version.to_s != gem_version + raise if locked_gem.nil? || locked_gem.version.to_s != gem_version || !@remote raise GemNotFound, "Your bundle is locked to #{locked_gem}, but that version could not " \ "be found in any of the sources listed in your Gemfile. If you haven't changed sources, " \ "that means the author of #{locked_gem} has removed it. You'll need to update your bundle " \ "to a different version of #{locked_gem} that hasn't been removed in order to install." end @@ -245,11 +246,11 @@ Bundler.ui.debug("Found no changes, using resolution from the lockfile") last_resolve else # Run a resolve against the locally available gems Bundler.ui.debug("Found changes from the lockfile, re-resolving dependencies because #{change_reason}") - last_resolve.merge Resolver.resolve(expanded_dependencies, index, source_requirements, last_resolve, ruby_version, gem_version_promoter, additional_base_requirements_for_resolve) + last_resolve.merge Resolver.resolve(expanded_dependencies, index, source_requirements, last_resolve, gem_version_promoter, additional_base_requirements_for_resolve) end end end def index @@ -260,10 +261,12 @@ source.dependency_names = dependency_names.dup idx.add_source source.specs dependency_names -= pinned_spec_names(source.specs) dependency_names.concat(source.unmet_deps).uniq! end + idx << Gem::Specification.new("ruby\0", RubyVersion.system.to_gem_version_with_patchlevel) + idx << Gem::Specification.new("rubygems\0", Gem::VERSION) end end # used when frozen is enabled so we can find the bundler # spec, even if (say) a git gem is not checked out. @@ -336,10 +339,22 @@ else @locked_ruby_version end end + def locked_ruby_version_object + return unless @locked_ruby_version + @locked_ruby_version_object ||= begin + unless version = RubyVersion.from_string(@locked_ruby_version) + raise LockfileError, "The Ruby version #{@locked_ruby_version} from " \ + "#{@lockfile} could not be parsed. " \ + "Try running bundle update --ruby to resolve this." + end + version + end + end + def to_lock out = String.new sources.lock_sources.each do |source| # Add the source header @@ -399,10 +414,15 @@ added = [] deleted = [] changed = [] + new_platforms = @platforms - @locked_platforms + deleted_platforms = @locked_platforms - @platforms + added.concat new_platforms.map {|p| "* platform: #{p}" } + deleted.concat deleted_platforms.map {|p| "* platform: #{p}" } + gemfile_sources = sources.lock_sources new_sources = gemfile_sources - @locked_sources deleted_sources = @locked_sources - gemfile_sources @@ -447,10 +467,15 @@ msg << "\n" raise ProductionError, msg if added.any? || deleted.any? || changed.any? end + def validate_runtime! + validate_ruby! + validate_platforms! + end + def validate_ruby! return unless ruby_version if diff = ruby_version.diff(Bundler::RubyVersion.system) problem, expected, actual = diff @@ -472,20 +497,46 @@ raise RubyVersionMismatch, msg end end + def validate_platforms! + return if @platforms.any? do |bundle_platform| + Bundler.rubygems.platforms.any? do |local_platform| + MatchPlatform.platforms_match?(bundle_platform, local_platform) + end + end + + raise ProductionError, "Your bundle only supports platforms #{@platforms.map(&:to_s)} " \ + "but your local platforms are #{Bundler.rubygems.platforms.map(&:to_s)}, and " \ + "there's no compatible match between those two lists." + end + def add_platform(platform) @new_platform ||= !@platforms.include?(platform) @platforms |= [platform] end def remove_platform(platform) return if @platforms.delete(Gem::Platform.new(platform)) raise InvalidOption, "Unable to remove the platform `#{platform}` since the only platforms are #{@platforms.join ", "}" end + def add_current_platform + current_platform = Bundler.local_platform + add_platform(current_platform) if Bundler.settings[:specific_platform] + add_platform(generic(current_platform)) + end + + def find_resolved_spec(current_spec) + specs.find_by_name_and_platform(current_spec.name, current_spec.platform) + end + + def find_indexed_specs(current_spec) + index[current_spec.name].select {|spec| spec.match_platform(current_spec.platform) }.sort_by(&:version) + end + attr_reader :sources private :sources private @@ -724,20 +775,58 @@ def satisfies_locked_spec?(dep) @locked_specs.any? {|s| s.satisfies?(dep) && (!dep.source || s.source.include?(dep.source)) } end + # This list of dependencies is only used in #resolve, so it's OK to add + # the metadata dependencies here def expanded_dependencies - @expanded_dependencies ||= expand_dependencies(dependencies, @remote) + @expanded_dependencies ||= begin + ruby_versions = concat_ruby_version_requirements(@ruby_version) + if ruby_versions.empty? || !@ruby_version.exact? + concat_ruby_version_requirements(RubyVersion.system) + concat_ruby_version_requirements(locked_ruby_version_object) unless @unlock[:ruby] + end + + metadata_dependencies = [ + Dependency.new("ruby\0", ruby_versions), + Dependency.new("rubygems\0", Gem::VERSION), + ] + expand_dependencies(dependencies + metadata_dependencies, @remote) + end end + def concat_ruby_version_requirements(ruby_version, ruby_versions = []) + return ruby_versions unless ruby_version + if ruby_version.patchlevel + ruby_versions << ruby_version.to_gem_version_with_patchlevel + else + ruby_versions.concat(ruby_version.versions.map do |version| + requirement = Gem::Requirement.new(version) + if requirement.exact? + "~> #{version}.0" + else + requirement + end + end) + end + end + def expand_dependencies(dependencies, remote = false) deps = [] dependencies.each do |dep| dep = Dependency.new(dep, ">= 0") unless dep.respond_to?(:name) - next unless remote || dep.current_platform? - dep.gem_platforms(@platforms).each do |p| + next if !remote && !dep.current_platform? + platforms = dep.gem_platforms(@platforms) + if platforms.empty? + Bundler.ui.warn \ + "The dependency #{dep} will be unused by any of the platforms Bundler is installing for. " \ + "Bundler is installing for #{@platforms.join ", "} but the dependency " \ + "is only for #{dep.platforms.map {|p| Dependency::PLATFORM_MAP[p] }.join ", "}. " \ + "To add those platforms to the bundle, run `bundle lock --add-platform #{dep.platforms.join ", "}`." + end + platforms.each do |p| deps << DepProxy.new(dep, p) if remote || p == generic_local_platform end end deps end @@ -810,12 +899,13 @@ requires end end def additional_base_requirements_for_resolve - return [] unless @locked_gems && Bundler.settings[:only_update_to_newer_versions] + return [] unless @locked_gems && Bundler.feature_flag.only_update_to_newer_versions? @locked_gems.specs.reduce({}) do |requirements, locked_spec| - requirements[locked_spec.name] = Gem::Dependency.new(locked_spec.name, ">= #{locked_spec.version}") + dep = Gem::Dependency.new(locked_spec.name, ">= #{locked_spec.version}") + requirements[locked_spec.name] = DepProxy.new(dep, locked_spec.platform) requirements end.values end end end