# typed: true # frozen_string_literal: true require "os/mac/version" module OS module Mac # Class representing a macOS SDK. # # @api private class SDK attr_reader :version, :path, :source def initialize(version, path, source) @version = version @path = Pathname.new(path) @source = source end end # Base class for SDK locators. # # @api private class BaseSDKLocator class NoSDKError < StandardError; end def sdk_for(v) path = sdk_paths[v] raise NoSDKError if path.nil? SDK.new v, path, source end def latest_sdk return if sdk_paths.empty? v, path = sdk_paths.max { |(v1, _), (v2, _)| v1 <=> v2 } SDK.new v, path, source end def all_sdks sdk_paths.map { |v, p| SDK.new v, p, source } end def sdk_if_applicable(v = nil) sdk = begin if v.nil? sdk_for OS::Mac.version else sdk_for v end rescue NoSDKError latest_sdk end # Only return an SDK older than the OS version if it was specifically requested return unless v || (!sdk.nil? && sdk.version >= OS::Mac.version) sdk end def source nil end private def sdk_prefix "" end def sdk_paths @sdk_paths ||= begin # Bail out if there is no SDK prefix at all if !File.directory? sdk_prefix {} else paths = {} Dir[File.join(sdk_prefix, "MacOSX*.sdk")].each do |sdk_path| version = sdk_path[/MacOSX(\d+\.\d+)u?\.sdk$/, 1] paths[OS::Mac::Version.new(version)] = sdk_path unless version.nil? end paths end end end end private_constant :BaseSDKLocator # Helper class for locating the Xcode SDK. # # @api private class XcodeSDKLocator < BaseSDKLocator def source :xcode end private 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) 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 sdk_prefix end end end # Helper class for locating the macOS Command Line Tools SDK. # # @api private class CLTSDKLocator < BaseSDKLocator def source :clt end private # While CLT SDKs existed prior to Xcode 10, those packages also # installed a traditional Unix-style header layout and we prefer # using that # 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. def sdk_prefix @sdk_prefix ||= begin if !CLT.provides_sdk? "" else "#{CLT::PKG_PATH}/SDKs" end end end end end end