Skip to content

Commit 969f320

Browse files
Merge pull request #8428 from rubygems/deivid-rodriguez/add-arm-windows-support
Support installing arm native gems on Windows
2 parents 299fdbf + 96496e3 commit 969f320

22 files changed

+190
-260
lines changed

bundler/lib/bundler/gem_helpers.rb

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,14 @@ module Bundler
44
module GemHelpers
55
GENERIC_CACHE = { Gem::Platform::RUBY => Gem::Platform::RUBY } # rubocop:disable Style/MutableConstant
66
GENERICS = [
7-
[Gem::Platform.new("java"), Gem::Platform.new("java")],
8-
[Gem::Platform.new("mswin32"), Gem::Platform.new("mswin32")],
9-
[Gem::Platform.new("mswin64"), Gem::Platform.new("mswin64")],
10-
[Gem::Platform.new("universal-mingw32"), Gem::Platform.new("universal-mingw32")],
11-
[Gem::Platform.new("x64-mingw32"), Gem::Platform.new("x64-mingw32")],
12-
[Gem::Platform.new("x86_64-mingw32"), Gem::Platform.new("x64-mingw32")],
13-
[Gem::Platform.new("x64-mingw-ucrt"), Gem::Platform.new("x64-mingw-ucrt")],
14-
[Gem::Platform.new("mingw32"), Gem::Platform.new("x86-mingw32")],
7+
Gem::Platform::JAVA,
8+
*Gem::Platform::WINDOWS,
159
].freeze
1610

1711
def generic(p)
1812
GENERIC_CACHE[p] ||= begin
19-
_, found = GENERICS.find do |match, _generic|
20-
p.os == match.os && (!match.cpu || p.cpu == match.cpu)
13+
found = GENERICS.find do |match|
14+
p === match
2115
end
2216
found || Gem::Platform::RUBY
2317
end

bundler/lib/bundler/rubygems_ext.rb

Lines changed: 81 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,87 @@ def open_file_with_lock(path, &block)
5858
end
5959
end
6060

61+
require "rubygems/platform"
62+
63+
class Platform
64+
JAVA = Gem::Platform.new("java")
65+
MSWIN = Gem::Platform.new("mswin32")
66+
MSWIN64 = Gem::Platform.new("mswin64")
67+
MINGW = Gem::Platform.new("x86-mingw32")
68+
X64_MINGW = [Gem::Platform.new("x64-mingw32"),
69+
Gem::Platform.new("x64-mingw-ucrt")].freeze
70+
UNIVERSAL_MINGW = Gem::Platform.new("universal-mingw")
71+
WINDOWS = [MSWIN, MSWIN64, UNIVERSAL_MINGW].flatten.freeze
72+
X64_LINUX = Gem::Platform.new("x86_64-linux")
73+
X64_LINUX_MUSL = Gem::Platform.new("x86_64-linux-musl")
74+
75+
if X64_LINUX === X64_LINUX_MUSL
76+
remove_method :===
77+
78+
def ===(other)
79+
return nil unless Gem::Platform === other
80+
81+
# universal-mingw32 matches x64-mingw-ucrt
82+
return true if (@cpu == "universal" || other.cpu == "universal") &&
83+
@os.start_with?("mingw") && other.os.start_with?("mingw")
84+
85+
# cpu
86+
([nil,"universal"].include?(@cpu) || [nil, "universal"].include?(other.cpu) || @cpu == other.cpu ||
87+
(@cpu == "arm" && other.cpu.start_with?("armv"))) &&
88+
89+
# os
90+
@os == other.os &&
91+
92+
# version
93+
(
94+
(@os != "linux" && (@version.nil? || other.version.nil?)) ||
95+
(@os == "linux" && (normalized_linux_version_ext == other.normalized_linux_version_ext || ["musl#{@version}", "musleabi#{@version}", "musleabihf#{@version}"].include?(other.version))) ||
96+
@version == other.version
97+
)
98+
end
99+
100+
# This is a copy of RubyGems 3.3.23 or higher `normalized_linux_method`.
101+
# Once only 3.3.23 is supported, we can use the method in RubyGems.
102+
def normalized_linux_version_ext
103+
return nil unless @version
104+
105+
without_gnu_nor_abi_modifiers = @version.sub(/\Agnu/, "").sub(/eabi(hf)?\Z/, "")
106+
return nil if without_gnu_nor_abi_modifiers.empty?
107+
108+
without_gnu_nor_abi_modifiers
109+
end
110+
end
111+
end
112+
113+
Platform.singleton_class.module_eval do
114+
unless Platform.singleton_methods.include?(:match_spec?)
115+
def match_spec?(spec)
116+
match_gem?(spec.platform, spec.name)
117+
end
118+
119+
def match_gem?(platform, gem_name)
120+
match_platforms?(platform, Gem.platforms)
121+
end
122+
end
123+
124+
match_platforms_defined = Gem::Platform.respond_to?(:match_platforms?, true)
125+
126+
if !match_platforms_defined || Gem::Platform.send(:match_platforms?, Gem::Platform::X64_LINUX_MUSL, [Gem::Platform::X64_LINUX])
127+
128+
private
129+
130+
remove_method :match_platforms? if match_platforms_defined
131+
132+
def match_platforms?(platform, platforms)
133+
platforms.any? do |local_platform|
134+
platform.nil? ||
135+
local_platform == platform ||
136+
(local_platform != Gem::Platform::RUBY && platform =~ local_platform)
137+
end
138+
end
139+
end
140+
end
141+
61142
require "rubygems/specification"
62143

63144
# Can be removed once RubyGems 3.5.14 support is dropped
@@ -288,86 +369,6 @@ def matching_specs(platform_only = false)
288369
end
289370
end
290371

291-
require "rubygems/platform"
292-
293-
class Platform
294-
JAVA = Gem::Platform.new("java")
295-
MSWIN = Gem::Platform.new("mswin32")
296-
MSWIN64 = Gem::Platform.new("mswin64")
297-
MINGW = Gem::Platform.new("x86-mingw32")
298-
X64_MINGW = [Gem::Platform.new("x64-mingw32"),
299-
Gem::Platform.new("x64-mingw-ucrt")].freeze
300-
WINDOWS = [MSWIN, MSWIN64, MINGW, X64_MINGW].flatten.freeze
301-
X64_LINUX = Gem::Platform.new("x86_64-linux")
302-
X64_LINUX_MUSL = Gem::Platform.new("x86_64-linux-musl")
303-
304-
if X64_LINUX === X64_LINUX_MUSL
305-
remove_method :===
306-
307-
def ===(other)
308-
return nil unless Gem::Platform === other
309-
310-
# universal-mingw32 matches x64-mingw-ucrt
311-
return true if (@cpu == "universal" || other.cpu == "universal") &&
312-
@os.start_with?("mingw") && other.os.start_with?("mingw")
313-
314-
# cpu
315-
([nil,"universal"].include?(@cpu) || [nil, "universal"].include?(other.cpu) || @cpu == other.cpu ||
316-
(@cpu == "arm" && other.cpu.start_with?("armv"))) &&
317-
318-
# os
319-
@os == other.os &&
320-
321-
# version
322-
(
323-
(@os != "linux" && (@version.nil? || other.version.nil?)) ||
324-
(@os == "linux" && (normalized_linux_version_ext == other.normalized_linux_version_ext || ["musl#{@version}", "musleabi#{@version}", "musleabihf#{@version}"].include?(other.version))) ||
325-
@version == other.version
326-
)
327-
end
328-
329-
# This is a copy of RubyGems 3.3.23 or higher `normalized_linux_method`.
330-
# Once only 3.3.23 is supported, we can use the method in RubyGems.
331-
def normalized_linux_version_ext
332-
return nil unless @version
333-
334-
without_gnu_nor_abi_modifiers = @version.sub(/\Agnu/, "").sub(/eabi(hf)?\Z/, "")
335-
return nil if without_gnu_nor_abi_modifiers.empty?
336-
337-
without_gnu_nor_abi_modifiers
338-
end
339-
end
340-
end
341-
342-
Platform.singleton_class.module_eval do
343-
unless Platform.singleton_methods.include?(:match_spec?)
344-
def match_spec?(spec)
345-
match_gem?(spec.platform, spec.name)
346-
end
347-
348-
def match_gem?(platform, gem_name)
349-
match_platforms?(platform, Gem.platforms)
350-
end
351-
end
352-
353-
match_platforms_defined = Gem::Platform.respond_to?(:match_platforms?, true)
354-
355-
if !match_platforms_defined || Gem::Platform.send(:match_platforms?, Gem::Platform::X64_LINUX_MUSL, [Gem::Platform::X64_LINUX])
356-
357-
private
358-
359-
remove_method :match_platforms? if match_platforms_defined
360-
361-
def match_platforms?(platform, platforms)
362-
platforms.any? do |local_platform|
363-
platform.nil? ||
364-
local_platform == platform ||
365-
(local_platform != Gem::Platform::RUBY && platform =~ local_platform)
366-
end
367-
end
368-
end
369-
end
370-
371372
# On universal Rubies, resolve the "universal" arch to the real CPU arch, without changing the extension directory.
372373
class BasicSpecification
373374
if /^universal\.(?<arch>.*?)-/ =~ (CROSS_COMPILING || RUBY_PLATFORM)

bundler/lib/bundler/spec_set.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,10 @@ def valid?(s)
212212
s.matches_current_metadata? && valid_dependencies?(s)
213213
end
214214

215+
def to_s
216+
map(&:full_name).to_s
217+
end
218+
215219
private
216220

217221
def materialize_dependencies(dependencies, platforms = [nil], skips: [])

bundler/spec/bundler/lockfile_parser_spec.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,11 @@
111111
end
112112
let(:specs) do
113113
[
114-
Bundler::LazySpecification.new("peiji-san", v("1.2.0"), rb),
115-
Bundler::LazySpecification.new("rake", v("10.3.2"), rb),
114+
Bundler::LazySpecification.new("peiji-san", v("1.2.0"), Gem::Platform::RUBY),
115+
Bundler::LazySpecification.new("rake", v("10.3.2"), Gem::Platform::RUBY),
116116
]
117117
end
118-
let(:platforms) { [rb] }
118+
let(:platforms) { [Gem::Platform::RUBY] }
119119
let(:bundler_version) { Gem::Version.new("1.12.0.rc.2") }
120120
let(:ruby_version) { "ruby 2.1.3p242" }
121121
let(:lockfile_path) { Bundler.default_lockfile.relative_path_from(Dir.pwd) }

bundler/spec/bundler/source_spec.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class ExampleSource < Bundler::Source
2121
end
2222

2323
describe "#version_message" do
24-
let(:spec) { double(:spec, name: "nokogiri", version: ">= 1.6", platform: rb) }
24+
let(:spec) { double(:spec, name: "nokogiri", version: ">= 1.6", platform: Gem::Platform::RUBY) }
2525

2626
shared_examples_for "the lockfile specs are not relevant" do
2727
it "should return a string with the spec name and version" do
@@ -70,7 +70,7 @@ class ExampleSource < Bundler::Source
7070
end
7171

7272
context "with a more recent version" do
73-
let(:spec) { double(:spec, name: "nokogiri", version: "1.6.1", platform: rb) }
73+
let(:spec) { double(:spec, name: "nokogiri", version: "1.6.1", platform: Gem::Platform::RUBY) }
7474
let(:locked_gem) { double(:locked_gem, name: "nokogiri", version: "1.7.0") }
7575

7676
context "with color", :no_color_tty do
@@ -97,7 +97,7 @@ class ExampleSource < Bundler::Source
9797
end
9898

9999
context "with an older version" do
100-
let(:spec) { double(:spec, name: "nokogiri", version: "1.7.1", platform: rb) }
100+
let(:spec) { double(:spec, name: "nokogiri", version: "1.7.1", platform: Gem::Platform::RUBY) }
101101
let(:locked_gem) { double(:locked_gem, name: "nokogiri", version: "1.7.0") }
102102

103103
context "with color", :no_color_tty do

bundler/spec/commands/install_spec.rb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@
281281
end
282282

283283
it "installs gems for windows" do
284-
simulate_platform x86_mswin32 do
284+
simulate_platform "x86-mswin32" do
285285
install_gemfile <<-G
286286
source "https://gem.repo1"
287287
gem "platform_specific"
@@ -290,6 +290,17 @@
290290
expect(the_bundle).to include_gems("platform_specific 1.0 x86-mswin32")
291291
end
292292
end
293+
294+
it "installs gems for aarch64-mingw-ucrt" do
295+
simulate_platform "aarch64-mingw-ucrt" do
296+
install_gemfile <<-G
297+
source "https://gem.repo1"
298+
gem "platform_specific"
299+
G
300+
end
301+
302+
expect(out).to include("Installing platform_specific 1.0 (aarch64-mingw-ucrt)")
303+
end
293304
end
294305

295306
describe "doing bundle install foo" do

bundler/spec/commands/lock_spec.rb

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -765,8 +765,7 @@
765765
bundle "lock --add-platform java x86-mingw32"
766766

767767
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
768-
lockfile = Bundler::LockfileParser.new(read_lockfile)
769-
expect(lockfile.platforms).to match_array(default_platform_list(java, x86_mingw32))
768+
expect(the_bundle.locked_platforms).to match_array(default_platform_list("java", "x86-mingw32"))
770769
end
771770

772771
it "supports adding new platforms, when most specific locked platform is not the current platform, and current resolve is not compatible with the target platform" do
@@ -845,16 +844,14 @@
845844
bundle "lock --add-platform java x86-mingw32"
846845

847846
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
848-
lockfile = Bundler::LockfileParser.new(read_lockfile)
849-
expect(lockfile.platforms).to contain_exactly(rb, linux, java, x86_mingw32)
847+
expect(the_bundle.locked_platforms).to contain_exactly(Gem::Platform::RUBY, "x86_64-linux", "java", "x86-mingw32")
850848
end
851849

852850
it "supports adding the `ruby` platform" do
853851
bundle "lock --add-platform ruby"
854852

855853
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
856-
lockfile = Bundler::LockfileParser.new(read_lockfile)
857-
expect(lockfile.platforms).to match_array(default_platform_list("ruby"))
854+
expect(the_bundle.locked_platforms).to match_array(default_platform_list("ruby"))
858855
end
859856

860857
it "fails when adding an unknown platform" do
@@ -867,13 +864,11 @@
867864
bundle "lock --add-platform java x86-mingw32"
868865

869866
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
870-
lockfile = Bundler::LockfileParser.new(read_lockfile)
871-
expect(lockfile.platforms).to match_array(default_platform_list(java, x86_mingw32))
867+
expect(the_bundle.locked_platforms).to match_array(default_platform_list("java", "x86-mingw32"))
872868

873869
bundle "lock --remove-platform java"
874870

875-
lockfile = Bundler::LockfileParser.new(read_lockfile)
876-
expect(lockfile.platforms).to match_array(default_platform_list(x86_mingw32))
871+
expect(the_bundle.locked_platforms).to match_array(default_platform_list("x86-mingw32"))
877872
end
878873

879874
it "also cleans up redundant platform gems when removing platforms" do
@@ -948,7 +943,7 @@
948943
build_repo4 do
949944
build_gem "ffi", "1.9.14"
950945
build_gem "ffi", "1.9.14" do |s|
951-
s.platform = x86_mingw32
946+
s.platform = "x86-mingw32"
952947
end
953948

954949
build_gem "gssapi", "0.1"
@@ -980,7 +975,7 @@
980975
gem "gssapi"
981976
G
982977

983-
simulate_platform(x86_mingw32) { bundle :lock }
978+
simulate_platform("x86-mingw32") { bundle :lock }
984979

985980
checksums = checksums_section_when_enabled do |c|
986981
c.checksum gem_repo4, "ffi", "1.9.14", "x86-mingw32"

bundler/spec/commands/update_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,7 +1132,7 @@
11321132
a
11331133
L
11341134

1135-
simulate_platform linux, &example
1135+
simulate_platform "x86_64-linux", &example
11361136
end
11371137

11381138
it "allows updating" do
@@ -1173,7 +1173,7 @@
11731173
end
11741174

11751175
it "is not updated because it is not actually included in the bundle" do
1176-
simulate_platform linux do
1176+
simulate_platform "x86_64-linux" do
11771177
bundle "update a"
11781178
expect(last_command.stdboth).to include "Bundler attempted to update a but it was not considered because it is for a different platform from the current one"
11791179
expect(the_bundle).to_not include_gem "a"

0 commit comments

Comments
 (0)