2020-10-10 14:16:11 +02:00
|
|
|
# typed: true
|
2019-04-19 15:38:03 +09:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2024-01-26 17:33:55 -08:00
|
|
|
require "system_command"
|
|
|
|
|
2015-12-04 13:58:22 -08:00
|
|
|
module OS
|
|
|
|
module Mac
|
2020-08-25 00:33:34 +02:00
|
|
|
# Class representing a macOS SDK.
|
2015-12-04 13:58:22 -08:00
|
|
|
class SDK
|
2021-03-03 04:00:58 +00:00
|
|
|
# 11.x SDKs are explicitly excluded - we want the MacOSX11.sdk symlink instead.
|
2024-01-18 22:18:42 +00:00
|
|
|
VERSIONED_SDK_REGEX = /MacOSX(10\.\d+|\d+)\.sdk$/
|
2021-02-07 04:16:36 +00:00
|
|
|
|
2023-05-09 02:15:28 +02:00
|
|
|
sig { returns(MacOSVersion) }
|
2021-09-29 15:12:53 -07:00
|
|
|
attr_reader :version
|
2015-12-04 13:58:22 -08:00
|
|
|
|
2021-09-29 15:12:53 -07:00
|
|
|
sig { returns(Pathname) }
|
|
|
|
attr_reader :path
|
|
|
|
|
|
|
|
sig { returns(Symbol) }
|
|
|
|
attr_reader :source
|
|
|
|
|
2023-05-09 02:15:28 +02:00
|
|
|
sig { params(version: MacOSVersion, path: T.any(String, Pathname), source: Symbol).void }
|
2020-04-07 16:43:32 +01:00
|
|
|
def initialize(version, path, source)
|
2020-10-05 14:46:43 +02:00
|
|
|
@version = version
|
2015-12-04 13:58:22 -08:00
|
|
|
@path = Pathname.new(path)
|
2020-04-07 16:43:32 +01:00
|
|
|
@source = source
|
2015-12-04 13:58:22 -08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-08-25 00:33:34 +02:00
|
|
|
# Base class for SDK locators.
|
2018-06-12 14:55:31 -07:00
|
|
|
class BaseSDKLocator
|
2021-09-29 15:12:53 -07:00
|
|
|
extend T::Helpers
|
2024-01-26 17:33:55 -08:00
|
|
|
include SystemCommand::Mixin
|
2021-09-29 15:12:53 -07:00
|
|
|
|
|
|
|
abstract!
|
|
|
|
|
2015-12-04 13:58:22 -08:00
|
|
|
class NoSDKError < StandardError; end
|
|
|
|
|
2023-05-09 02:15:28 +02:00
|
|
|
sig { params(version: MacOSVersion).returns(SDK) }
|
2023-03-07 23:42:00 +00:00
|
|
|
def sdk_for(version)
|
|
|
|
sdk = all_sdks.find { |s| s.version == version }
|
2021-02-07 04:16:36 +00:00
|
|
|
raise NoSDKError if sdk.nil?
|
2015-12-04 13:58:22 -08:00
|
|
|
|
2021-02-07 04:16:36 +00:00
|
|
|
sdk
|
2015-12-04 13:58:22 -08:00
|
|
|
end
|
|
|
|
|
2021-09-29 15:12:53 -07:00
|
|
|
sig { returns(T::Array[SDK]) }
|
2020-07-01 16:02:29 +01:00
|
|
|
def all_sdks
|
2021-02-07 04:16:36 +00:00
|
|
|
return @all_sdks if @all_sdks
|
|
|
|
|
|
|
|
@all_sdks = []
|
|
|
|
|
|
|
|
# Bail out if there is no SDK prefix at all
|
|
|
|
return @all_sdks unless File.directory? sdk_prefix
|
|
|
|
|
2022-05-16 16:18:48 +01:00
|
|
|
found_versions = Set.new
|
|
|
|
|
2021-02-07 04:16:36 +00:00
|
|
|
Dir["#{sdk_prefix}/MacOSX*.sdk"].each do |sdk_path|
|
|
|
|
next unless sdk_path.match?(SDK::VERSIONED_SDK_REGEX)
|
|
|
|
|
|
|
|
version = read_sdk_version(Pathname.new(sdk_path))
|
2021-03-03 04:00:58 +00:00
|
|
|
next if version.nil?
|
2021-02-07 04:16:36 +00:00
|
|
|
|
|
|
|
@all_sdks << SDK.new(version, sdk_path, source)
|
2022-05-16 16:18:48 +01:00
|
|
|
found_versions << version
|
2021-02-07 04:16:36 +00:00
|
|
|
end
|
|
|
|
|
2022-05-16 16:18:48 +01:00
|
|
|
# Use unversioned SDK only if we don't have one matching that version.
|
|
|
|
sdk_path = Pathname.new("#{sdk_prefix}/MacOSX.sdk")
|
|
|
|
if (version = read_sdk_version(sdk_path)) && found_versions.exclude?(version)
|
|
|
|
@all_sdks << SDK.new(version, sdk_path, source)
|
2021-03-03 04:00:58 +00:00
|
|
|
end
|
|
|
|
|
2021-02-07 04:16:36 +00:00
|
|
|
@all_sdks
|
2020-07-01 16:02:29 +01:00
|
|
|
end
|
|
|
|
|
2023-05-09 02:15:28 +02:00
|
|
|
sig { params(version: T.nilable(MacOSVersion)).returns(T.nilable(SDK)) }
|
2023-03-07 23:42:00 +00:00
|
|
|
def sdk_if_applicable(version = nil)
|
2018-07-27 15:44:22 -07:00
|
|
|
sdk = begin
|
2023-03-07 23:42:00 +00:00
|
|
|
if version.blank?
|
2020-12-26 01:06:12 +00:00
|
|
|
sdk_for OS::Mac.version
|
2018-07-26 17:57:11 -07:00
|
|
|
else
|
2023-03-07 23:42:00 +00:00
|
|
|
sdk_for version
|
2018-07-26 17:57:11 -07:00
|
|
|
end
|
2020-08-25 00:33:34 +02:00
|
|
|
rescue NoSDKError
|
2018-07-27 15:44:22 -07:00
|
|
|
latest_sdk
|
2018-07-26 17:57:11 -07:00
|
|
|
end
|
2020-11-30 12:58:38 +00:00
|
|
|
return if sdk.blank?
|
|
|
|
|
2020-12-26 01:06:12 +00:00
|
|
|
# On OSs lower than 11, whenever the major versions don't match,
|
2020-11-27 16:23:10 -08:00
|
|
|
# only return an SDK older than the OS version if it was specifically requested
|
2023-03-07 23:42:00 +00:00
|
|
|
return if version.blank? && sdk.version < OS::Mac.version
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2018-07-27 15:44:22 -07:00
|
|
|
sdk
|
2018-07-26 17:57:11 -07:00
|
|
|
end
|
|
|
|
|
2021-09-29 15:12:53 -07:00
|
|
|
sig { abstract.returns(Symbol) }
|
|
|
|
def source; end
|
2020-04-07 16:43:32 +01:00
|
|
|
|
2020-07-01 16:02:29 +01:00
|
|
|
private
|
2018-07-26 17:57:11 -07:00
|
|
|
|
2021-09-29 15:12:53 -07:00
|
|
|
sig { abstract.returns(String) }
|
|
|
|
def sdk_prefix; end
|
2018-06-12 14:55:31 -07:00
|
|
|
|
2021-09-29 15:12:53 -07:00
|
|
|
sig { returns(T.nilable(SDK)) }
|
2021-02-07 04:16:36 +00:00
|
|
|
def latest_sdk
|
|
|
|
all_sdks.max_by(&:version)
|
2015-12-04 13:58:22 -08:00
|
|
|
end
|
2020-12-26 01:06:12 +00:00
|
|
|
|
2023-05-09 02:15:28 +02:00
|
|
|
sig { params(sdk_path: Pathname).returns(T.nilable(MacOSVersion)) }
|
2021-02-07 04:16:36 +00:00
|
|
|
def read_sdk_version(sdk_path)
|
|
|
|
sdk_settings = sdk_path/"SDKSettings.json"
|
|
|
|
sdk_settings_string = sdk_settings.read if sdk_settings.exist?
|
2020-12-26 01:06:12 +00:00
|
|
|
|
2021-02-07 04:16:36 +00:00
|
|
|
# Pre-10.14 SDKs
|
|
|
|
sdk_settings = sdk_path/"SDKSettings.plist"
|
|
|
|
if sdk_settings_string.blank? && sdk_settings.exist?
|
|
|
|
result = system_command("plutil", args: ["-convert", "json", "-o", "-", sdk_settings])
|
|
|
|
sdk_settings_string = result.stdout if result.success?
|
|
|
|
end
|
|
|
|
|
|
|
|
return if sdk_settings_string.blank?
|
|
|
|
|
|
|
|
sdk_settings_json = JSON.parse(sdk_settings_string)
|
|
|
|
return if sdk_settings_json.blank?
|
|
|
|
|
|
|
|
version_string = sdk_settings_json.fetch("Version", nil)
|
|
|
|
return if version_string.blank?
|
|
|
|
|
|
|
|
begin
|
2023-05-09 02:15:28 +02:00
|
|
|
MacOSVersion.new(version_string).strip_patch
|
|
|
|
rescue MacOSVersion::Error
|
2021-02-07 04:16:36 +00:00
|
|
|
nil
|
|
|
|
end
|
2020-12-26 01:06:12 +00:00
|
|
|
end
|
2015-12-04 13:58:22 -08:00
|
|
|
end
|
2020-08-25 00:33:34 +02:00
|
|
|
private_constant :BaseSDKLocator
|
2018-06-12 14:55:31 -07:00
|
|
|
|
2020-08-25 00:33:34 +02:00
|
|
|
# Helper class for locating the Xcode SDK.
|
2018-06-12 14:55:31 -07:00
|
|
|
class XcodeSDKLocator < BaseSDKLocator
|
2021-09-29 15:12:53 -07:00
|
|
|
sig { override.returns(Symbol) }
|
2020-04-07 16:43:32 +01:00
|
|
|
def source
|
|
|
|
:xcode
|
|
|
|
end
|
|
|
|
|
2020-07-01 16:02:29 +01:00
|
|
|
private
|
2018-07-26 17:57:11 -07:00
|
|
|
|
2021-09-29 15:12:53 -07:00
|
|
|
sig { override.returns(String) }
|
2018-06-12 14:55:31 -07:00
|
|
|
def sdk_prefix
|
|
|
|
@sdk_prefix ||= begin
|
|
|
|
# Xcode.prefix is pretty smart, so let's look inside to find the sdk
|
|
|
|
sdk_prefix = "#{Xcode.prefix}/Platforms/MacOSX.platform/Developer/SDKs"
|
|
|
|
# Finally query Xcode itself (this is slow, so check it last)
|
2018-07-27 15:44:22 -07:00
|
|
|
sdk_platform_path = Utils.popen_read(DevelopmentTools.locate("xcrun"), "--show-sdk-platform-path").chomp
|
|
|
|
sdk_prefix = File.join(sdk_platform_path, "Developer", "SDKs") unless File.directory? sdk_prefix
|
2018-06-12 14:55:31 -07:00
|
|
|
|
|
|
|
sdk_prefix
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-08-25 00:33:34 +02:00
|
|
|
# Helper class for locating the macOS Command Line Tools SDK.
|
2018-06-12 14:55:31 -07:00
|
|
|
class CLTSDKLocator < BaseSDKLocator
|
2021-09-29 15:12:53 -07:00
|
|
|
sig { override.returns(Symbol) }
|
2020-04-07 16:43:32 +01:00
|
|
|
def source
|
|
|
|
:clt
|
|
|
|
end
|
|
|
|
|
2020-07-01 16:02:29 +01:00
|
|
|
private
|
2018-07-26 17:57:11 -07:00
|
|
|
|
2018-06-12 14:55:31 -07:00
|
|
|
# While CLT SDKs existed prior to Xcode 10, those packages also
|
|
|
|
# installed a traditional Unix-style header layout and we prefer
|
2020-11-05 17:17:03 -05:00
|
|
|
# using that.
|
2018-06-12 14:55:31 -07:00
|
|
|
# As of Xcode 10, the Unix-style headers are installed via a
|
|
|
|
# separate package, so we can't rely on their being present.
|
|
|
|
# This will only look up SDKs on Xcode 10 or newer, and still
|
|
|
|
# return nil SDKs for Xcode 9 and older.
|
2021-09-29 15:12:53 -07:00
|
|
|
sig { override.returns(String) }
|
2018-06-12 14:55:31 -07:00
|
|
|
def sdk_prefix
|
2021-03-26 14:11:03 +00:00
|
|
|
@sdk_prefix ||= if CLT.provides_sdk?
|
|
|
|
"#{CLT::PKG_PATH}/SDKs"
|
|
|
|
else
|
|
|
|
""
|
2018-06-12 14:55:31 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2015-12-04 13:58:22 -08:00
|
|
|
end
|
|
|
|
end
|