Mini Shell
# Phusion Passenger - https://www.phusionpassenger.com/
# Copyright (c) 2010-2025 Asynchronous B.V.
#
# "Passenger", "Phusion Passenger" and "Union Station" are registered
# trademarks of Asynchronous B.V.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
require 'rbconfig'
PhusionPassenger.require_passenger_lib 'platform_info'
PhusionPassenger.require_passenger_lib 'platform_info/ruby'
PhusionPassenger.require_passenger_lib 'platform_info/operating_system'
module PhusionPassenger
module PlatformInfo
# Returns a string that describes the current Ruby
# interpreter's extension binary compatibility. A Ruby extension
# compiled for a certain Ruby interpreter can also be loaded on
# a different Ruby interpreter with the same binary compatibility
# identifier.
#
# The result depends on the following factors:
# - Ruby engine name.
# - Ruby extension version.
# This is not the same as the Ruby language version, which
# identifies language-level compatibility. This is rather about
# binary compatibility of extensions.
# MRI seems to break source compatibility between tiny releases,
# though patchlevel releases tend to be source and binary
# compatible.
# - Ruby extension architecture.
# This is not necessarily the same as the operating system
# runtime architecture or the CPU architecture.
# For example, in case of JRuby, the extension architecture is
# just "java" because all extensions target the Java platform;
# the architecture the JVM was compiled for has no effect on
# compatibility.
# On systems with universal binaries support there may be multiple
# architectures. In this case the architecture is "universal"
# because extensions must be able to support all of the Ruby
# executable's architectures.
# - The operating system for which the Ruby interpreter was compiled.
def self.ruby_extension_binary_compatibility_id
ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : "ruby"
ruby_ext_version = RUBY_VERSION
if RUBY_PLATFORM =~ /darwin/
if RUBY_PLATFORM =~ /universal/
ruby_arch = "universal"
else
# OS X often prints something like:
# "/opt/ruby-enterprise/bin/ruby: Mach-O 64-bit executable x86_64"
output = `file -L "#{ruby_executable}"`.lines.first.strip
ruby_arch = output.split.last
if ruby_arch == "executable"
# some OS X print something like:
# "/opt/ruby-enterprise/bin/ruby: Mach-O 64-bit executable"
if output =~ /arm64/
ruby_arch = "arm64"
elsif output =~ /Mach-O 64-bit/
ruby_arch = "x86_64"
else
raise "Cannot autodetect the Ruby interpreter's architecture"
end
end
end
elsif RUBY_PLATFORM == "java"
ruby_arch = "java"
else
ruby_arch = cpu_architectures[0]
end
return "#{ruby_engine}-#{ruby_ext_version}-#{ruby_arch}-#{os_name_simple}"
end
memoize :ruby_extension_binary_compatibility_id
# Returns an identifier string that describes the current
# platform's binary compatibility with regard to C/C++
# binaries. Two systems with the same binary compatibility
# identifiers should be able to run the same C/C++ binaries.
#
# The the string depends on the following factors:
# - The operating system name.
# - Operating system runtime identifier.
# This may include the kernel version, libc version, C++ ABI version,
# etc. Everything that is of interest for binary compatibility with
# regard to C/C++ binaries.
# - Operating system default runtime architecture.
# This is not the same as the CPU architecture; some CPUs support
# multiple architectures, e.g. Intel Core 2 Duo supports x86 and
# x86_64. Some operating systems actually support multiple runtime
# architectures: a lot of x86_64 Linux distributions also include
# 32-bit runtimes, and OS X Snow Leopard is x86_64 by default but
# all system libraries also support x86.
# This component identifies the architecture that is used when
# compiling a binary with the system's C++ compiler with its default
# options.
def self.cxx_binary_compatibility_id
if os_name_simple == "macosx"
# RUBY_PLATFORM gives us the kernel version, but we want
# the OS X version.
# sw_vers returns something like "10.6.2". We're only
# interested in the first two digits (MAJOR.MINOR) since
# tiny releases tend to be binary compatible with each
# other. After macOS 10.15 Apple switched to bumping major
# versions (like 11.0, 12.0) so we need to look at that.
# However, if the env var SYSTEM_VERSION_COMPAT=1 then
# macOS 11 reports as 10.16 (and so on), so we need to deal with that too.
major, minor, *rest = os_version.split(".").map(&:to_i)
os_version_string = if major >= 11
major
elsif minor >= 16
# 10.16 -> 11
# 10.17 -> 12
minor - 5
else
"#{major}.#{minor}"
end
os_runtime = os_version_string.to_s
else
os_runtime = nil
end
os_arch = cpu_architectures[0]
return [os_arch, os_name_simple, os_runtime].compact.join("-")
end
memoize :cxx_binary_compatibility_id
end
end # module PhusionPassenger