diff options
author | Homu <homu@barosl.com> | 2016-08-26 01:20:26 +0900 |
---|---|---|
committer | Homu <homu@barosl.com> | 2016-08-26 01:20:26 +0900 |
commit | c71f43fbcb9ee25bc4466137cc29545cda1375e2 (patch) | |
tree | 123d017caf490fc82b3f8549d3f83246f3ec245c | |
parent | 8513ed1481dde5b47eead96dd0a1cfe3d77c4c84 (diff) | |
parent | 0ac668d261fc6c0adec89d7befa21e8de880b0f9 (diff) | |
download | bundler-c71f43fbcb9ee25bc4466137cc29545cda1375e2.tar.gz |
Auto merge of #4836 - bundler:seg-resolve-for-specific-platforms, r=indirect
Resolve for specific platforms
Closes #4295.
This will require adding a bunch of tests, as well as figuring out how to put this new behavior behind a feature flag (thus fixing all of the existing tests).
27 files changed, 290 insertions, 60 deletions
diff --git a/lib/bundler.rb b/lib/bundler.rb index 18b9e752..8806ae01 100644 --- a/lib/bundler.rb +++ b/lib/bundler.rb @@ -91,7 +91,7 @@ module Bundler # Return if all groups are already loaded return @setup if defined?(@setup) && @setup - definition.validate_ruby! + definition.validate_runtime! SharedHelpers.print_major_deprecations! diff --git a/lib/bundler/cli/binstubs.rb b/lib/bundler/cli/binstubs.rb index f7a27b01..95103b7d 100644 --- a/lib/bundler/cli/binstubs.rb +++ b/lib/bundler/cli/binstubs.rb @@ -10,7 +10,7 @@ module Bundler end def run - Bundler.definition.validate_ruby! + Bundler.definition.validate_runtime! Bundler.settings[:bin] = options["path"] if options["path"] Bundler.settings[:bin] = nil if options["path"] && options["path"].empty? installer = Installer.new(Bundler.root, Bundler.definition) diff --git a/lib/bundler/cli/cache.rb b/lib/bundler/cli/cache.rb index c8c63e92..5ba105a3 100644 --- a/lib/bundler/cli/cache.rb +++ b/lib/bundler/cli/cache.rb @@ -7,7 +7,7 @@ module Bundler end def run - Bundler.definition.validate_ruby! + Bundler.definition.validate_runtime! Bundler.definition.resolve_with_cache! setup_cache_all Bundler.settings[:cache_all_platforms] = options["all-platforms"] if options.key?("all-platforms") diff --git a/lib/bundler/cli/check.rb b/lib/bundler/cli/check.rb index 738d40b6..3f504ff6 100644 --- a/lib/bundler/cli/check.rb +++ b/lib/bundler/cli/check.rb @@ -15,7 +15,7 @@ module Bundler begin definition = Bundler.definition - definition.validate_ruby! + definition.validate_runtime! not_installed = definition.missing_specs rescue GemNotFound, VersionConflict Bundler.ui.error "Bundler can't satisfy your Gemfile's dependencies." diff --git a/lib/bundler/cli/install.rb b/lib/bundler/cli/install.rb index 9802ea80..f1632c9e 100644 --- a/lib/bundler/cli/install.rb +++ b/lib/bundler/cli/install.rb @@ -66,7 +66,7 @@ module Bundler Plugin.gemfile_install(Bundler.default_gemfile) if Bundler.feature_flag.plugins? definition = Bundler.definition - definition.validate_ruby! + definition.validate_runtime! installer = Installer.install(Bundler.root, definition, options) Bundler.load.cache if Bundler.app_cache.exist? && !options["no-cache"] && !Bundler.settings[:frozen] diff --git a/lib/bundler/cli/outdated.rb b/lib/bundler/cli/outdated.rb index 09b2d714..de710755 100644 --- a/lib/bundler/cli/outdated.rb +++ b/lib/bundler/cli/outdated.rb @@ -18,7 +18,7 @@ module Bundler Bundler::CLI::Common.select_spec(gem_name) end - Bundler.definition.validate_ruby! + Bundler.definition.validate_runtime! current_specs = Bundler.ui.silence { Bundler.load.specs } current_dependencies = {} Bundler.ui.silence { Bundler.load.dependencies.each {|dep| current_dependencies[dep.name] = dep } } diff --git a/lib/bundler/cli/platform.rb b/lib/bundler/cli/platform.rb index b5f906bf..9fdab0a5 100644 --- a/lib/bundler/cli/platform.rb +++ b/lib/bundler/cli/platform.rb @@ -29,7 +29,7 @@ module Bundler output << "Your Gemfile specifies a Ruby version requirement:\n* #{ruby_version}" begin - Bundler.definition.validate_ruby! + Bundler.definition.validate_runtime! output << "Your current platform satisfies the Ruby version requirement." rescue RubyVersionMismatch => e output << e.message diff --git a/lib/bundler/cli/show.rb b/lib/bundler/cli/show.rb index d67b086d..77e845a6 100644 --- a/lib/bundler/cli/show.rb +++ b/lib/bundler/cli/show.rb @@ -13,7 +13,7 @@ module Bundler def run Bundler.ui.silence do - Bundler.definition.validate_ruby! + Bundler.definition.validate_runtime! Bundler.load.lock end diff --git a/lib/bundler/cli/update.rb b/lib/bundler/cli/update.rb index ea2a4b8d..5aac47bd 100644 --- a/lib/bundler/cli/update.rb +++ b/lib/bundler/cli/update.rb @@ -57,7 +57,7 @@ module Bundler # rubygems plugins sometimes hook into the gem install process Gem.load_env_plugins if Gem.respond_to?(:load_env_plugins) - Bundler.definition.validate_ruby! + Bundler.definition.validate_runtime! Installer.install Bundler.root, Bundler.definition, opts Bundler.load.cache if Bundler.app_cache.exist? diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index dc3b4690..0cb6e7e3 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -70,7 +70,8 @@ module Bundler 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 @@ -91,6 +92,7 @@ module Bundler @locked_deps = [] @locked_specs = SpecSet.new([]) @locked_sources = [] + @locked_platforms = [] end @unlock[:gems] ||= [] @@ -102,8 +104,7 @@ module Bundler @gem_version_promoter = create_gem_version_promoter - 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]) @@ -414,6 +415,11 @@ module Bundler 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 @@ -462,6 +468,11 @@ module Bundler 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 @@ -487,6 +498,22 @@ module Bundler end end + # TODO: refactor this so that `match_platform` can be called with two platforms + DummyPlatform = Struct.new(:platform) + class DummyPlatform; include MatchPlatform; end + def validate_platforms! + return if @platforms.any? do |bundle_platform| + bundle_platform = DummyPlatform.new(bundle_platform) + Bundler.rubygems.platforms.any? do |local_platform| + bundle_platform.match_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] @@ -497,6 +524,12 @@ module Bundler raise InvalidOption, "Unable to remove the platform `#{platform}` since the only platforms are #{@platforms.join ", "}" end + def add_current_platform + current_platform = Bundler.rubygems.platforms.last + add_platform(current_platform) if Bundler.settings[:specific_platform] + add_platform(generic(current_platform)) + end + attr_reader :sources private :sources diff --git a/lib/bundler/dependency.rb b/lib/bundler/dependency.rb index 1b1808b4..66162d74 100644 --- a/lib/bundler/dependency.rb +++ b/lib/bundler/dependency.rb @@ -52,7 +52,7 @@ module Bundler :x64_mingw_20 => Gem::Platform::X64_MINGW, :x64_mingw_21 => Gem::Platform::X64_MINGW, :x64_mingw_22 => Gem::Platform::X64_MINGW, - :x64_mingw_23 => Gem::Platform::X64_MINGW + :x64_mingw_23 => Gem::Platform::X64_MINGW, }.freeze REVERSE_PLATFORM_MAP = {}.tap do |reverse_platform_map| diff --git a/lib/bundler/gem_helpers.rb b/lib/bundler/gem_helpers.rb index 5c824ffe..6d926ce8 100644 --- a/lib/bundler/gem_helpers.rb +++ b/lib/bundler/gem_helpers.rb @@ -28,5 +28,73 @@ module Bundler generic(Gem::Platform.local) end module_function :generic_local_platform + + def platform_specificity_match(spec_platform, user_platform) + spec_platform = Gem::Platform.new(spec_platform) + return PlatformMatch::EXACT_MATCH if spec_platform == user_platform + return PlatformMatch::WORST_MATCH if spec_platform.nil? || spec_platform == Gem::Platform::RUBY || user_platform == Gem::Platform::RUBY + + PlatformMatch.new( + PlatformMatch.os_match(spec_platform, user_platform), + PlatformMatch.cpu_match(spec_platform, user_platform), + PlatformMatch.platform_version_match(spec_platform, user_platform) + ) + end + module_function :platform_specificity_match + + def select_best_platform_match(specs, platform) + specs.select {|spec| spec.match_platform(platform) }. + min_by {|spec| platform_specificity_match(spec.platform, platform) } + end + module_function :select_best_platform_match + + PlatformMatch = Struct.new(:os_match, :cpu_match, :platform_version_match) + class PlatformMatch + def <=>(other) + return nil unless other.is_a?(PlatformMatch) + + m = os_match <=> other.os_match + return m unless m.zero? + + m = cpu_match <=> other.cpu_match + return m unless m.zero? + + m = platform_version_match <=> other.platform_version_match + m + end + + EXACT_MATCH = new(-1, -1, -1).freeze + WORST_MATCH = new(1_000_000, 1_000_000, 1_000_000).freeze + + def self.os_match(spec_platform, user_platform) + if spec_platform.os == user_platform.os + 0 + else + 1 + end + end + + def self.cpu_match(spec_platform, user_platform) + if spec_platform.cpu == user_platform.cpu + 0 + elsif spec_platform.cpu == "arm" && user_platform.cpu.to_s.start_with?("arm") + 0 + elsif spec_platform.cpu.nil? || spec_platform.cpu == "universal" + 1 + else + 2 + end + end + + def self.platform_version_match(spec_platform, user_platform) + if spec_platform.version == user_platform.version + 0 + elsif spec_platform.version.nil? + 1 + else + 2 + end + end + end end end diff --git a/lib/bundler/inline.rb b/lib/bundler/inline.rb index af542f1e..dec3be3e 100644 --- a/lib/bundler/inline.rb +++ b/lib/bundler/inline.rb @@ -47,7 +47,7 @@ def gemfile(install = false, options = {}, &gemfile) definition = builder.to_definition(nil, true) def definition.lock(*); end - definition.validate_ruby! + definition.validate_runtime! missing_specs = proc do begin diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb index 36ff5c59..0b667f7d 100644 --- a/lib/bundler/lazy_specification.rb +++ b/lib/bundler/lazy_specification.rb @@ -6,6 +6,20 @@ require "bundler/match_platform" module Bundler class LazySpecification Identifier = Struct.new(:name, :version, :source, :platform, :dependencies) + class Identifier + include Comparable + def <=>(other) + return unless other.is_a?(Identifier) + [name, version, platform_string] <=> [other.name, other.version, other.platform_string] + end + + protected + + def platform_string + platform_string = platform.to_s + platform_string == Index::RUBY ? Index::NULL : platform_string + end + end include MatchPlatform @@ -55,7 +69,8 @@ module Bundler end def __materialize__ - @specification = source.specs.search(Gem::Dependency.new(name, version)).last + search_object = Bundler.settings[:specific_platform] ? self : Dependency.new(name, version) + @specification = source.specs.search(search_object).last end def respond_to?(*args) diff --git a/lib/bundler/lockfile_parser.rb b/lib/bundler/lockfile_parser.rb index 063a1887..51148ab6 100644 --- a/lib/bundler/lockfile_parser.rb +++ b/lib/bundler/lockfile_parser.rb @@ -92,7 +92,7 @@ module Bundler end end @sources << @rubygems_aggregate - @specs = @specs.values + @specs = @specs.values.sort_by(&:identifier) warn_for_outdated_bundler_version rescue ArgumentError => e Bundler.ui.debug(e) diff --git a/lib/bundler/match_platform.rb b/lib/bundler/match_platform.rb index fed418b5..0a4e4c7e 100644 --- a/lib/bundler/match_platform.rb +++ b/lib/bundler/match_platform.rb @@ -8,7 +8,8 @@ module Bundler def match_platform(p) Gem::Platform::RUBY == platform || platform.nil? || p == platform || - generic(Gem::Platform.new(platform)) === p + generic(Gem::Platform.new(platform)) === p || + Gem::Platform.new(platform) === p end end end diff --git a/lib/bundler/plugin.rb b/lib/bundler/plugin.rb index 015be99a..8fb41193 100644 --- a/lib/bundler/plugin.rb +++ b/lib/bundler/plugin.rb @@ -62,7 +62,9 @@ module Bundler save_plugins plugins, installed_specs, builder.inferred_plugins rescue => e - Bundler.ui.error "Failed to install plugin: #{e.message}\n #{e.backtrace[0]}" + unless e.is_a?(GemfileError) + Bundler.ui.error "Failed to install plugin: #{e.message}\n #{e.backtrace[0]}" + end raise end diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb index 3ab4ae6f..b8016b37 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -66,44 +66,33 @@ module Bundler end end - ALL = Bundler::Dependency::PLATFORM_MAP.values.uniq.freeze - class SpecGroup < Array include GemHelpers - attr_reader :activated, :required_by + attr_reader :activated def initialize(a) super @required_by = [] @activated_platforms = [] @dependencies = nil - @specs = {} - - ALL.each do |p| - @specs[p] = reverse.find {|s| s.match_platform(p) } + @specs = Hash.new do |specs, platform| + specs[platform] = select_best_platform_match(self, platform) end end def initialize_copy(o) super - @required_by = o.required_by.dup @activated_platforms = o.activated.dup end def to_specs - specs = {} - - @activated_platforms.each do |p| + @activated_platforms.map do |p| next unless s = @specs[p] - platform = generic(Gem::Platform.new(s.platform)) - next if specs[platform] - - lazy_spec = LazySpecification.new(name, version, platform, source) + lazy_spec = LazySpecification.new(name, version, s.platform, source) lazy_spec.dependencies.replace s.dependencies - specs[platform] = lazy_spec - end - specs.values + lazy_spec + end.compact end def activate_platform!(platform) @@ -148,17 +137,15 @@ module Bundler private def __dependencies - @dependencies ||= begin - dependencies = {} - ALL.each do |p| - next unless spec = @specs[p] - dependencies[p] = [] + @dependencies = Hash.new do |dependencies, platform| + dependencies[platform] = [] + if spec = @specs[platform] spec.dependencies.each do |dep| next if dep.type == :development - dependencies[p] << DepProxy.new(dep, p) + dependencies[platform] << DepProxy.new(dep, platform) end end - dependencies + dependencies[platform] end end diff --git a/lib/bundler/runtime.rb b/lib/bundler/runtime.rb index fda499cf..45f445ae 100644 --- a/lib/bundler/runtime.rb +++ b/lib/bundler/runtime.rb @@ -140,7 +140,8 @@ module Bundler Bundler.ui.info "Updating files in #{Bundler.settings.app_cache_path}" - specs.each do |spec| + specs_to_cache = Bundler.settings[:cache_all_platforms] ? @definition.resolve.materialized_for_all_platforms : specs + specs_to_cache.each do |spec| next if spec.name == "bundler" next if spec.source.is_a?(Source::Gemspec) spec.source.send(:fetch_gem, spec) if Bundler.settings[:cache_all_platforms] && spec.source.respond_to?(:fetch_gem, true) diff --git a/lib/bundler/source.rb b/lib/bundler/source.rb index afa7d918..b6f3a431 100644 --- a/lib/bundler/source.rb +++ b/lib/bundler/source.rb @@ -14,6 +14,7 @@ module Bundler def version_message(spec) message = "#{spec.name} #{spec.version}" + message += " (#{spec.platform})" if spec.platform != Gem::Platform::RUBY if Bundler.locked_gems locked_spec = Bundler.locked_gems.specs.find {|s| s.name == spec.name } diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb index f2ccac47..fe31b17f 100644 --- a/lib/bundler/spec_set.rb +++ b/lib/bundler/spec_set.rb @@ -24,17 +24,9 @@ module Bundler dep = deps.shift next if handled[dep] || skip.include?(dep.name) - spec = lookup[dep.name].find do |s| - if match_current_platform - Gem::Platform.match(s.platform) - else - s.match_platform(dep.__platform) - end - end - handled[dep] = true - if spec + if spec = spec_for_dependency(dep, match_current_platform) specs << spec spec.dependencies.each do |d| @@ -99,6 +91,20 @@ module Bundler SpecSet.new(materialized.compact) end + # Materialize for all the specs in the spec set, regardless of what platform they're for + # This is in contrast to how for does platform filtering (and specifically different from how `materialize` calls `for` only for the current platform) + # @return [Array<Gem::Specification>] + def materialized_for_all_platforms + names = @specs.map(&:name).uniq + @specs.map do |s| + next s unless s.is_a?(LazySpecification) + s.source.dependency_names = names if s.source.respond_to?(:dependency_names=) + spec = s.__materialize__ + raise GemNotFound, "Could not find #{s.full_name} in any of the sources" unless spec + spec + end + end + def merge(set) arr = sorted.dup set.each do |s| @@ -133,10 +139,7 @@ module Bundler def lookup @lookup ||= begin lookup = Hash.new {|h, k| h[k] = [] } - specs = @specs.sort_by do |s| - s.platform.to_s == "ruby" ? "\0" : s.platform.to_s - end - specs.reverse_each do |s| + Index.sort_specs(@specs).reverse_each do |s| lookup[s.name] << s end lookup @@ -147,6 +150,18 @@ module Bundler @specs.each {|s| yield s } end + def spec_for_dependency(dep, match_current_platform) + if match_current_platform + Bundler.rubygems.platforms.reverse_each do |pl| + match = GemHelpers.select_best_platform_match(lookup[dep.name], pl) + return match if match + end + nil + else + GemHelpers.select_best_platform_match(lookup[dep.name], dep.__platform) + end + end + def tsort_each_child(s) s.dependencies.sort_by(&:name).each do |d| next if d.type == :development diff --git a/spec/bundler/source_spec.rb b/spec/bundler/source_spec.rb index 25abd90c..4e99411a 100644 --- a/spec/bundler/source_spec.rb +++ b/spec/bundler/source_spec.rb @@ -22,7 +22,7 @@ describe Bundler::Source do end describe "#version_message" do - let(:spec) { double(:spec, :name => "nokogiri", :version => ">= 1.6") } + let(:spec) { double(:spec, :name => "nokogiri", :version => ">= 1.6", :platform => rb) } shared_examples_for "the lockfile specs are not relevant" do it "should return a string with the spec name and version" do diff --git a/spec/install/gemfile/specific_platform_spec.rb b/spec/install/gemfile/specific_platform_spec.rb new file mode 100644 index 00000000..3e12f94c --- /dev/null +++ b/spec/install/gemfile/specific_platform_spec.rb @@ -0,0 +1,95 @@ +# frozen_string_literal: true +require "spec_helper" + +describe "bundle install with specific_platform enabled" do + before do + bundle "config specific_platform true" + + build_repo2 do + build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") + build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x86_64-linux" } + build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x86-mingw32" } + build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x86-linux" } + build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x64-mingw32" } + build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "universal-darwin" } + + build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x86_64-linux" } + build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x86-linux" } + build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x64-mingw32" } + build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x86-mingw32" } + build_gem("google-protobuf", "3.0.0.alpha.5.0.5") + + build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "universal-darwin" } + build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "x86_64-linux" } + build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "x86-mingw32" } + build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "x86-linux" } + build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "x64-mingw32" } + build_gem("google-protobuf", "3.0.0.alpha.5.0.4") + + build_gem("google-protobuf", "3.0.0.alpha.5.0.3") + build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "x86_64-linux" } + build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "x86-mingw32" } + build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "x86-linux" } + build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "x64-mingw32" } + build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "universal-darwin" } + + build_gem("google-protobuf", "3.0.0.alpha.4.0") + build_gem("google-protobuf", "3.0.0.alpha.3.1.pre") + build_gem("google-protobuf", "3.0.0.alpha.3") + build_gem("google-protobuf", "3.0.0.alpha.2.0") + build_gem("google-protobuf", "3.0.0.alpha.1.1") + build_gem("google-protobuf", "3.0.0.alpha.1.0") + end + end + + let(:google_protobuf) { <<-G } + source "file:#{gem_repo2}" + gem "google-protobuf" + G + + context "when on a darwin machine" do + before { simulate_platform "x86_64-darwin-15" } + + it "locks to both the specific darwin platform and ruby" do + install_gemfile!(google_protobuf) + expect(the_bundle.locked_gems.platforms).to eq([pl("ruby"), pl("x86_64-darwin-15")]) + expect(the_bundle).to include_gem("google-protobuf 3.0.0.alpha.5.0.5.1 universal-darwin") + expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w( + google-protobuf-3.0.0.alpha.5.0.5.1 + google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin + )) + end + + it "caches both the universal-darwin and ruby gems when --all-platforms is passed" do + gemfile(google_protobuf) + bundle! "package --all-platforms" + expect([cached_gem("google-protobuf-3.0.0.alpha.5.0.5.1"), cached_gem("google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin")]). + to all(exist) + end + + context "when adding a platform via lock --add_platform" do + it "adds the foreign platform" do + install_gemfile!(google_protobuf) + bundle! "lock --add-platform=#{x64_mingw}" + + expect(the_bundle.locked_gems.platforms).to eq([rb, x64_mingw, pl("x86_64-darwin-15")]) + expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w( + google-protobuf-3.0.0.alpha.5.0.5.1 + google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin + google-protobuf-3.0.0.alpha.5.0.5.1-x64-mingw32 + )) + end + + it "falls back on plain ruby when that version doesnt have a platform-specific gem" do + install_gemfile!(google_protobuf) + bundle! "lock --add-platform=#{java}" + + expect(the_bundle.locked_gems.platforms).to eq([java, rb, pl("x86_64-darwin-15")]) + expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w( + google-protobuf-3.0.0.alpha.5.0.5.1 + google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin + )) + end + end + end +end diff --git a/spec/resolver/platform_spec.rb b/spec/resolver/platform_spec.rb index d5f21768..fa91eab9 100644 --- a/spec/resolver/platform_spec.rb +++ b/spec/resolver/platform_spec.rb @@ -50,7 +50,7 @@ describe "Resolving platform craziness" do # mingw is _not_ hardcoded to add CPU x86 in rubygems platforms "x86-mingw32" dep "thin" - should_resolve_as %w(thin-1.2.7-x86-mingw32) + should_resolve_as %w(thin-1.2.7-mingw32) end it "finds x64-mingw gems" do diff --git a/spec/support/builders.rb b/spec/support/builders.rb index 337234f1..7436779d 100644 --- a/spec/support/builders.rb +++ b/spec/support/builders.rb @@ -610,7 +610,10 @@ module Spec end def _default_files - @_default_files ||= { "lib/#{name}.rb" => "#{Builders.constantize(name)} = '#{version}'" } + @_default_files ||= begin + platform_string = " #{@spec.platform}" unless @spec.platform == Gem::Platform::RUBY + { "lib/#{name}.rb" => "#{Builders.constantize(name)} = '#{version}#{platform_string}'" } + end end def _default_path diff --git a/spec/support/platforms.rb b/spec/support/platforms.rb index b1dedb05..a2a3afba 100644 --- a/spec/support/platforms.rb +++ b/spec/support/platforms.rb @@ -11,6 +11,10 @@ module Spec Gem::Platform.new("x86-darwin-10") end + def x64_mac + Gem::Platform.new("x86_64-darwin-15") + end + def java Gem::Platform.new([nil, "java", nil]) end diff --git a/spec/support/the_bundle.rb b/spec/support/the_bundle.rb index 86df9cd9..742d3934 100644 --- a/spec/support/the_bundle.rb +++ b/spec/support/the_bundle.rb @@ -27,5 +27,10 @@ module Spec def lockfile bundle_dir.join("Gemfile.lock") end + + def locked_gems + raise "Cannot read lockfile if it doesn't exist" unless locked? + Bundler::LockfileParser.new(lockfile.read) + end end end |