88# See `gem help platform` for information on platform matching.
99
1010class Gem ::Platform
11+ require_relative "platform/elffile"
12+ require_relative "platform/manylinux"
13+ require_relative "platform/musllinux"
14+ require_relative "platform/wheel"
15+ require_relative "platform/specific"
16+
1117 @local = nil
1218
1319 attr_accessor :cpu , :os , :version
@@ -49,19 +55,23 @@ def self.match_gem?(platform, gem_name)
4955 raise "Not a string: #{ gem_name . inspect } " unless String === gem_name
5056
5157 if REUSE_AS_BINARY_ON_TRUFFLERUBY . include? ( gem_name )
52- match_platforms? ( platform , [ Gem ::Platform ::RUBY , Gem ::Platform . local ] )
58+ match_platforms? ( platform , [ Gem ::Platform ::RUBY , Gem ::Platform :: Specific . local ] )
5359 else
54- match_platforms? ( platform , Gem . platforms )
60+ match_platforms? ( platform , Gem . platforms . map { | pl | Specific . local ( pl ) } )
5561 end
5662 end
5763 else
5864 def self . match_gem? ( platform , gem_name )
59- match_platforms? ( platform , Gem . platforms )
65+ match_platforms? ( platform , Gem . platforms . map { | pl | Specific . local ( pl ) } )
6066 end
6167 end
6268
6369 def self . sort_priority ( platform )
64- platform == Gem ::Platform ::RUBY ? -1 : 1
70+ case platform
71+ when Gem ::Platform ::RUBY then -1
72+ when Gem ::Platform ::Wheel then 2 # Higher priority than traditional platforms
73+ else 1
74+ end
6575 end
6676
6777 def self . installable? ( spec )
@@ -78,64 +88,79 @@ def self.new(arch) # :nodoc:
7888 Gem ::Platform . local
7989 when Gem ::Platform ::RUBY , nil , "" then
8090 Gem ::Platform ::RUBY
91+ when /^whl-/ then
92+ Gem ::Platform ::Wheel . new ( arch )
93+ when Wheel then
94+ Wheel . new ( arch )
95+ when Specific then
96+ Specific . new ( arch )
97+ when / v:\d +/
98+ Gem ::Platform ::Specific . parse ( arch )
8199 else
82100 super
83101 end
84102 end
85103
86104 def initialize ( arch )
87105 case arch
106+ when String then
88107 when Array then
108+ raise "Array #{ arch . inspect } is not a valid platform" unless arch . size <= 3
89109 @cpu , @os , @version = arch
90- when String then
91- cpu , os = arch . sub ( /-+$/ , "" ) . split ( "-" , 2 )
92-
93- @cpu = if cpu &.match? ( /i\d 86/ )
94- "x86"
95- else
96- cpu
97- end
98-
99- if os . nil?
100- @cpu = nil
101- os = cpu
102- end # legacy jruby
103-
104- @os , @version = case os
105- when /aix-?(\d +)?/ then [ "aix" , $1]
106- when /cygwin/ then [ "cygwin" , nil ]
107- when /darwin-?(\d +)?/ then [ "darwin" , $1]
108- when "macruby" then [ "macruby" , nil ]
109- when /^macruby-?(\d +(?:\. \d +)*)?/ then [ "macruby" , $1]
110- when /freebsd-?(\d +)?/ then [ "freebsd" , $1]
111- when "java" , "jruby" then [ "java" , nil ]
112- when /^java-?(\d +(?:\. \d +)*)?/ then [ "java" , $1]
113- when /^dalvik-?(\d +)?$/ then [ "dalvik" , $1]
114- when /^dotnet$/ then [ "dotnet" , nil ]
115- when /^dotnet-?(\d +(?:\. \d +)*)?/ then [ "dotnet" , $1]
116- when /linux-?(\w +)?/ then [ "linux" , $1]
117- when /mingw32/ then [ "mingw32" , nil ]
118- when /mingw-?(\w +)?/ then [ "mingw" , $1]
119- when /(mswin\d +)(?:[_-](\d +))?/ then
120- os = $1
121- version = $2
122- @cpu = "x86" if @cpu . nil? && os . end_with? ( "32" )
123- [ os , version ]
124- when /netbsdelf/ then [ "netbsdelf" , nil ]
125- when /openbsd-?(\d +\. \d +)?/ then [ "openbsd" , $1]
126- when /solaris-?(\d +\. \d +)?/ then [ "solaris" , $1]
127- when /wasi/ then [ "wasi" , nil ]
128- # test
129- when /^(\w +_platform)-?(\d +)?/ then [ $1, $2]
130- else [ "unknown" , nil ]
131- end
132- when Gem ::Platform then
110+ return
111+ when Gem ::Platform
133112 @cpu = arch . cpu
134113 @os = arch . os
135114 @version = arch . version
115+ return
136116 else
137117 raise ArgumentError , "invalid argument #{ arch . inspect } "
138118 end
119+
120+ cpu , os = arch . sub ( /-+$/ , "" ) . split ( "-" , 2 )
121+
122+ @cpu = if cpu &.match? ( /i\d 86/ )
123+ "x86"
124+ elsif cpu == "dotnet"
125+ os = "dotnet-#{ os } "
126+ nil
127+ else
128+ cpu
129+ end
130+
131+ if os . nil?
132+ @cpu = nil
133+ os = cpu
134+ end # legacy jruby
135+
136+ @os , @version = case os
137+ when /aix-?(\d +)?/ then [ "aix" , $1]
138+ when /cygwin/ then [ "cygwin" , nil ]
139+ when /darwin-?(\d +)?/ then [ "darwin" , $1]
140+ when "macruby" then [ "macruby" , nil ]
141+ when /^macruby-?(\d +(?:\. \d +)*)?/ then [ "macruby" , $1]
142+ when /freebsd-?(\d +)?/ then [ "freebsd" , $1]
143+ when "java" , "jruby" then [ "java" , nil ]
144+ when /^java-?(\d +(?:\. \d +)*)?/ then [ "java" , $1]
145+ when /^dalvik-?(\d +)?$/ then [ "dalvik" , $1]
146+ when "dotnet" then [ "dotnet" , nil ]
147+ when /^dotnet-?(\d +(?:\. \d +)*)?/ then [ "dotnet" , $1]
148+ when /linux-?(\w +)?/ then [ "linux" , $1]
149+ when /mingw32/ then [ "mingw32" , nil ]
150+ when /mingw-?(\w +)?/ then [ "mingw" , $1]
151+ when /(mswin\d +)(?:[_-](\d +))?/ then
152+ os = $1
153+ version = $2
154+ @cpu = "x86" if @cpu . nil? && os . end_with? ( "32" )
155+ [ os , version ]
156+ when /netbsdelf/ then [ "netbsdelf" , nil ]
157+ when /openbsd-?(\d +\. \d +)?/ then [ "openbsd" , $1]
158+ when /solaris-?(\d +\. \d +)?/ then [ "solaris" , $1]
159+ when /wasi/ then [ "wasi" , nil ]
160+ # test
161+ when /^(\w +_platform)-?(\d +)?/ then [ $1, $2]
162+ else [ "unknown" , nil ]
163+ end
139164 end
140165
141166 def to_a
@@ -218,25 +243,9 @@ def normalized_linux_version
218243
219244 def =~( other )
220245 case other
221- when Gem ::Platform then # nop
222- when String then
223- # This data is from http://gems.rubyforge.org/gems/yaml on 19 Aug 2007
224- other = case other
225- when /^i686-darwin(\d )/ then [ "x86" , "darwin" , $1]
226- when /^i\d 86-linux/ then [ "x86" , "linux" , nil ]
227- when "java" , "jruby" then [ nil , "java" , nil ]
228- when /^dalvik(\d +)?$/ then [ nil , "dalvik" , $1]
229- when /dotnet(\- (\d +\. \d +))?/ then [ "universal" , "dotnet" , $2]
230- when /mswin32(\_ (\d +))?/ then [ "x86" , "mswin32" , $2]
231- when /mswin64(\_ (\d +))?/ then [ "x64" , "mswin64" , $2]
232- when "powerpc-darwin" then [ "powerpc" , "darwin" , nil ]
233- when /powerpc-darwin(\d )/ then [ "powerpc" , "darwin" , $1]
234- when /sparc-solaris2.8/ then [ "sparc" , "solaris" , "2.8" ]
235- when /universal-darwin(\d )/ then [ "universal" , "darwin" , $1]
236- else other
237- end
238-
239- other = Gem ::Platform . new other
246+ when Gem ::Platform , Gem ::Platform ::Wheel
247+ when Gem ::Platform ::Specific then other = other . platform
248+ when String then other = Gem ::Platform . new ( other )
240249 else
241250 return nil
242251 end
@@ -278,7 +287,15 @@ class << self
278287 # Returns the generic platform for the given platform.
279288
280289 def generic ( platform )
281- return Gem ::Platform ::RUBY if platform . nil? || platform == Gem ::Platform ::RUBY
290+ case platform
291+ when NilClass , Gem ::Platform ::RUBY
292+ return Gem ::Platform ::RUBY
293+ when Gem ::Platform ::Wheel
294+ return platform
295+ when Gem ::Platform
296+ else
297+ raise ArgumentError , "invalid argument #{ platform . inspect } "
298+ end
282299
283300 GENERIC_CACHE [ platform ] ||= begin
284301 found = GENERICS . find do |match |
@@ -295,6 +312,48 @@ def platform_specificity_match(spec_platform, user_platform)
295312 return -1 if spec_platform == user_platform
296313 return 1_000_000 if spec_platform . nil? || spec_platform == Gem ::Platform ::RUBY || user_platform == Gem ::Platform ::RUBY
297314
315+ # Handle Specific user platforms
316+ if user_platform . is_a? ( Gem ::Platform ::Specific )
317+ case spec_platform
318+ when Gem ::Platform ::Wheel
319+ # Use each_possible_match to find the best match for wheels
320+ # Return negative values to indicate better matches than traditional platforms
321+ index = user_platform . each_possible_match . to_a . index do |abi_tag , platform_tag |
322+ # Check if the wheel matches this generated tag pair
323+ spec_platform . ruby_abi_tag . split ( "." ) . include? ( abi_tag ) && spec_platform . platform_tags . split ( "." ) . include? ( platform_tag )
324+ end
325+ return ( if index == 0
326+ -10
327+ elsif index
328+ index
329+ else
330+ 1_000_000
331+ end )
332+ when Gem ::Platform
333+ # For traditional platforms with Specific user platforms, use original scoring
334+ user_platform = user_platform . platform
335+ return -1 if spec_platform == user_platform # Better than non-matching wheels but worse than matching wheels
336+ else
337+ raise ArgumentError , "spec_platform must be Gem::Platform or Gem::Platform::Wheel, given #{ spec_platform . inspect } "
338+ end
339+ end
340+
341+ # Handle traditional Platform user platforms
342+ case user_platform
343+ when Gem ::Platform
344+ # For wheel spec platforms with traditional user platforms, create a Specific user platform
345+ if spec_platform . is_a? ( Gem ::Platform ::Wheel )
346+ specific_user = Gem ::Platform ::Specific . local ( user_platform )
347+ return platform_specificity_match ( spec_platform , specific_user )
348+ end
349+ when Gem ::Platform ::Specific
350+ # TODO: also match on ruby ABI tags!
351+ user_platform = user_platform . platform
352+ return -1 if spec_platform == user_platform
353+ else
354+ raise ArgumentError , "user_platform must be Gem::Platform or Gem::Platform::Specific, given #{ user_platform . inspect } "
355+ end
356+
298357 os_match ( spec_platform , user_platform ) +
299358 cpu_match ( spec_platform , user_platform ) * 10 +
300359 version_match ( spec_platform , user_platform ) * 100
@@ -303,34 +362,34 @@ def platform_specificity_match(spec_platform, user_platform)
303362 ##
304363 # Sorts and filters the best platform match for the given matching specs and platform.
305364
306- def sort_and_filter_best_platform_match ( matching , platform )
365+ def sort_and_filter_best_platform_match ( matching , user_platform )
307366 return matching if matching . one?
308367
309- exact = matching . select { |spec | spec . platform == platform }
368+ exact = matching . select { |spec | spec . platform == user_platform }
310369 return exact if exact . any?
311370
312- sorted_matching = sort_best_platform_match ( matching , platform )
371+ sorted_matching = sort_best_platform_match ( matching , user_platform )
313372 exemplary_spec = sorted_matching . first
314373
315- sorted_matching . take_while { |spec | same_specificity? ( platform , spec , exemplary_spec ) && same_deps? ( spec , exemplary_spec ) }
374+ sorted_matching . take_while { |spec | same_specificity? ( user_platform , spec , exemplary_spec ) && same_deps? ( spec , exemplary_spec ) }
316375 end
317376
318377 ##
319378 # Sorts the best platform match for the given matching specs and platform.
320379
321- def sort_best_platform_match ( matching , platform )
380+ def sort_best_platform_match ( matching , user_platform )
322381 matching . sort_by . with_index do |spec , i |
323382 [
324- platform_specificity_match ( spec . platform , platform ) ,
383+ platform_specificity_match ( spec . platform , user_platform ) ,
325384 i , # for stable sort
326385 ]
327386 end
328387 end
329388
330389 private
331390
332- def same_specificity? ( platform , spec , exemplary_spec )
333- platform_specificity_match ( spec . platform , platform ) == platform_specificity_match ( exemplary_spec . platform , platform )
391+ def same_specificity? ( user_platform , spec , exemplary_spec )
392+ platform_specificity_match ( spec . platform , user_platform ) == platform_specificity_match ( exemplary_spec . platform , user_platform )
334393 end
335394
336395 def same_deps? ( spec , exemplary_spec )
0 commit comments