Merge pull request #12149 from FnControlOption/development_tools

development_tools: add type signatures
This commit is contained in:
Bo Anderson 2021-09-30 12:59:51 +01:00 committed by GitHub
commit 24543c63e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 121 additions and 27 deletions

View File

@ -1,9 +1,14 @@
# typed: false
# typed: true
# frozen_string_literal: true
require "version"
# @private
class DevelopmentTools
class << self
extend T::Sig
sig { params(tool: String).returns(T.nilable(Pathname)) }
def locate(tool)
# Don't call tools (cc, make, strip, etc.) directly!
# Give the name of the binary you look for as a string to this method
@ -18,28 +23,27 @@ class DevelopmentTools
end
end
sig { returns(T::Boolean) }
def installed?
locate("clang") || locate("gcc")
locate("clang").present? || locate("gcc").present?
end
sig { returns(String) }
def installation_instructions
"Install Clang or run `brew install gcc`."
end
alias custom_installation_instructions installation_instructions
def default_cc
cc = DevelopmentTools.locate "cc"
begin
cc.realpath.basename.to_s
rescue
nil
end
sig { returns(String) }
def custom_installation_instructions
installation_instructions
end
sig { returns(Symbol) }
def default_compiler
:clang
end
sig { returns(Version) }
def clang_version
@clang_version ||= if (path = locate("clang")) &&
(build_version = `#{path} --version`[/(?:clang|LLVM) version (\d+\.\d(?:\.\d)?)/, 1])
@ -49,6 +53,7 @@ class DevelopmentTools
end
end
sig { returns(Version) }
def clang_build_version
@clang_build_version ||= if (path = locate("clang")) &&
(build_version = `#{path} --version`[
@ -59,6 +64,7 @@ class DevelopmentTools
end
end
sig { returns(Version) }
def llvm_clang_build_version
@llvm_clang_build_version ||= begin
path = Formulary.factory("llvm").opt_prefix/"bin/clang"
@ -71,6 +77,7 @@ class DevelopmentTools
end
end
sig { params(cc: String).returns(Version) }
def non_apple_gcc_version(cc)
(@non_apple_gcc_version ||= {}).fetch(cc) do
path = HOMEBREW_PREFIX/"opt/#{CompilerSelector.preferred_gcc}/bin"/cc
@ -85,24 +92,28 @@ class DevelopmentTools
end
end
sig { void }
def clear_version_cache
@clang_version = @clang_build_version = nil
@non_apple_gcc_version = {}
end
sig { returns(T::Boolean) }
def curl_handles_most_https_certificates?
true
end
sig { returns(T::Boolean) }
def subversion_handles_most_https_certificates?
true
end
sig { returns(T::Hash[String, T.nilable(String)]) }
def build_system_info
{
"os" => ENV["HOMEBREW_SYSTEM"],
"os_version" => OS_VERSION,
"cpu_family" => Hardware::CPU.family,
"cpu_family" => Hardware::CPU.family.to_s,
}
end
alias generic_build_system_info build_system_info

View File

@ -5,6 +5,7 @@ class DevelopmentTools
class << self
extend T::Sig
sig { params(tool: String).returns(T.nilable(Pathname)) }
def locate(tool)
(@locate ||= {}).fetch(tool) do |key|
@locate[key] = if (path = HOMEBREW_PREFIX/"bin/#{tool}").executable?
@ -20,10 +21,11 @@ class DevelopmentTools
:gcc
end
sig { returns(T::Hash[String, T.nilable(String)]) }
def build_system_info
generic_build_system_info.merge({
"glibc_version" => OS::Linux::Glibc.version,
"oldest_cpu_family" => Hardware.oldest_cpu,
"glibc_version" => OS::Linux::Glibc.version.to_s.presence,
"oldest_cpu_family" => Hardware.oldest_cpu.to_s,
})
end
end

View File

@ -12,6 +12,7 @@ class DevelopmentTools
undef installed?, default_compiler, curl_handles_most_https_certificates?,
subversion_handles_most_https_certificates?
sig { params(tool: String).returns(T.nilable(Pathname)) }
def locate(tool)
(@locate ||= {}).fetch(tool) do |key|
@locate[key] = if (located_tool = generic_locate(tool))
@ -26,6 +27,7 @@ class DevelopmentTools
# Checks if the user has any developer tools installed, either via Xcode
# or the CLT. Convenient for guarding against formula builds when building
# is impossible.
sig { returns(T::Boolean) }
def installed?
MacOS::Xcode.installed? || MacOS::CLT.installed?
end
@ -62,6 +64,7 @@ class DevelopmentTools
EOS
end
sig { returns(T::Hash[String, T.nilable(String)]) }
def build_system_info
build_info = {
"xcode" => MacOS::Xcode.version.to_s.presence,

View File

@ -18,8 +18,8 @@ module SharedEnvExtension
def no_weak_imports_support?
return false unless compiler == :clang
return false if MacOS::Xcode.version && MacOS::Xcode.version < "8.0"
return false if MacOS::CLT.version && MacOS::CLT.version < "8.0"
return false if !MacOS::Xcode.version.null? && MacOS::Xcode.version < "8.0"
return false if !MacOS::CLT.version.null? && MacOS::CLT.version < "8.0"
true
end

View File

@ -4,7 +4,12 @@
module Hardware
extend T::Sig
sig { params(version: T.nilable(Version)).returns(Symbol) }
def self.oldest_cpu(version = MacOS.version)
def self.oldest_cpu(version = nil)
version = if version
MacOS::Version.new(version.to_s)
else
MacOS.version
end
if CPU.arch == :arm64
:arm_vortex_tempest
# TODO: this cannot be re-enabled until either Rosetta 2 supports AVX

View File

@ -11,6 +11,7 @@ module OS
module_function
sig { returns(Version) }
def system_version
@system_version ||= begin
version = Utils.popen_read("/usr/bin/ldd", "--version")[/ (\d+\.\d+)/, 1]
@ -22,6 +23,7 @@ module OS
end
end
sig { returns(Version) }
def version
@version ||= begin
version = Utils.popen_read(HOMEBREW_PREFIX/"opt/glibc/bin/ldd", "--version")[/ (\d+\.\d+)/, 1]
@ -38,6 +40,7 @@ module OS
Version.new(ENV.fetch("HOMEBREW_LINUX_MINIMUM_GLIBC_VERSION"))
end
sig { returns(T::Boolean) }
def below_minimum_version?
system_version < minimum_version
end

View File

@ -24,16 +24,19 @@ module OS
# This can be compared to numerics, strings, or symbols
# using the standard Ruby Comparable methods.
sig { returns(Version) }
def version
@version ||= full_version.strip_patch
end
# This can be compared to numerics, strings, or symbols
# using the standard Ruby Comparable methods.
sig { returns(Version) }
def full_version
@full_version ||= Version.new((ENV["HOMEBREW_MACOS_VERSION"]).chomp)
end
sig { params(version: Version).void }
def full_version=(version)
@full_version = Version.new(version.chomp)
@version = nil
@ -72,6 +75,7 @@ module OS
languages.first
end
sig { returns(String) }
def active_developer_dir
@active_developer_dir ||= Utils.popen_read("/usr/bin/xcode-select", "-print-path").strip
end
@ -178,6 +182,7 @@ module OS
paths.uniq
end
sig { params(ids: String).returns(T.nilable(Pathname)) }
def app_with_bundle_id(*ids)
path = mdfind(*ids)
.reject { |p| p.include?("/Backups.backupdb/") }
@ -185,6 +190,7 @@ module OS
Pathname.new(path) if path.present?
end
sig { params(ids: String).returns(T::Array[String]) }
def mdfind(*ids)
(@mdfind ||= {}).fetch(ids) do
@mdfind[ids] = Utils.popen_read("/usr/bin/mdfind", mdfind_query(*ids)).split("\n")
@ -197,6 +203,7 @@ module OS
end
end
sig { params(ids: String).returns(String) }
def mdfind_query(*ids)
ids.map! { |id| "kMDItemCFBundleIdentifier == #{id}" }.join(" || ")
end

View File

@ -9,11 +9,21 @@ module OS
#
# @api private
class SDK
extend T::Sig
# 11.x SDKs are explicitly excluded - we want the MacOSX11.sdk symlink instead.
VERSIONED_SDK_REGEX = /MacOSX(10\.\d+|\d+)\.sdk$/.freeze
attr_reader :version, :path, :source
sig { returns(OS::Mac::Version) }
attr_reader :version
sig { returns(Pathname) }
attr_reader :path
sig { returns(Symbol) }
attr_reader :source
sig { params(version: OS::Mac::Version, path: T.any(String, Pathname), source: Symbol).void }
def initialize(version, path, source)
@version = version
@path = Pathname.new(path)
@ -25,8 +35,14 @@ module OS
#
# @api private
class BaseSDKLocator
extend T::Sig
extend T::Helpers
abstract!
class NoSDKError < StandardError; end
sig { params(v: OS::Mac::Version).returns(SDK) }
def sdk_for(v)
sdk = all_sdks.find { |s| s.version == v }
raise NoSDKError if sdk.nil?
@ -34,6 +50,7 @@ module OS
sdk
end
sig { returns(T::Array[SDK]) }
def all_sdks
return @all_sdks if @all_sdks
@ -62,6 +79,7 @@ module OS
@all_sdks
end
sig { params(v: T.nilable(OS::Mac::Version)).returns(T.nilable(SDK)) }
def sdk_if_applicable(v = nil)
sdk = begin
if v.blank?
@ -81,20 +99,20 @@ module OS
sdk
end
def source
nil
end
sig { abstract.returns(Symbol) }
def source; end
private
def sdk_prefix
""
end
sig { abstract.returns(String) }
def sdk_prefix; end
sig { returns(T.nilable(SDK)) }
def latest_sdk
all_sdks.max_by(&:version)
end
sig { params(sdk_path: Pathname).returns(T.nilable(OS::Mac::Version)) }
def read_sdk_version(sdk_path)
sdk_settings = sdk_path/"SDKSettings.json"
sdk_settings_string = sdk_settings.read if sdk_settings.exist?
@ -129,13 +147,14 @@ module OS
class XcodeSDKLocator < BaseSDKLocator
extend T::Sig
sig { returns(Symbol) }
sig { override.returns(Symbol) }
def source
:xcode
end
private
sig { override.returns(String) }
def sdk_prefix
@sdk_prefix ||= begin
# Xcode.prefix is pretty smart, so let's look inside to find the sdk
@ -155,7 +174,7 @@ module OS
class CLTSDKLocator < BaseSDKLocator
extend T::Sig
sig { returns(Symbol) }
sig { override.returns(Symbol) }
def source
:clt
end
@ -169,6 +188,7 @@ module OS
# 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.
sig { override.returns(String) }
def sdk_prefix
@sdk_prefix ||= if CLT.provides_sdk?
"#{CLT::PKG_PATH}/SDKs"

View File

@ -1,4 +1,4 @@
# typed: false
# typed: true
# frozen_string_literal: true
module OS
@ -89,7 +89,11 @@ module OS
# Returns a Pathname object corresponding to Xcode.app's Developer
# directory or nil if Xcode.app is not installed.
sig { returns(T.nilable(Pathname)) }
def prefix
return @prefix if defined?(@prefix)
@prefix = T.let(@prefix, T.nilable(Pathname))
@prefix ||=
begin
dir = MacOS.active_developer_dir
@ -109,6 +113,7 @@ module OS
Pathname("#{prefix}/Toolchains/XcodeDefault.xctoolchain")
end
sig { returns(T.nilable(Pathname)) }
def bundle_path
# Use the default location if it exists.
return DEFAULT_BUNDLE_PATH if DEFAULT_BUNDLE_PATH.exist?
@ -124,18 +129,22 @@ module OS
!prefix.nil?
end
sig { returns(XcodeSDKLocator) }
def sdk_locator
@sdk_locator ||= XcodeSDKLocator.new
end
sig { params(v: T.nilable(MacOS::Version)).returns(T.nilable(SDK)) }
def sdk(v = nil)
sdk_locator.sdk_if_applicable(v)
end
sig { params(v: T.nilable(MacOS::Version)).returns(T.nilable(Pathname)) }
def sdk_path(v = nil)
sdk(v)&.path
end
sig { returns(String) }
def installation_instructions
if OS::Mac.version.prerelease?
<<~EOS
@ -163,6 +172,7 @@ module OS
end
end
sig { returns(::Version) }
def version
# may return a version string
# that is guessed based on the compiler, so do not
@ -174,6 +184,7 @@ module OS
end
end
sig { returns(T.nilable(String)) }
def detect_version
# This is a separate function as you can't cache the value out of a block
# if return is used in the middle, which we do many times in here.
@ -231,6 +242,7 @@ module OS
end
end
sig { returns(T::Boolean) }
def default_prefix?
prefix.to_s == "/Applications/Xcode.app/Contents/Developer"
end
@ -255,26 +267,32 @@ module OS
!version.null?
end
sig { returns(T::Boolean) }
def separate_header_package?
version >= "10" && MacOS.version >= "10.14"
end
sig { returns(T::Boolean) }
def provides_sdk?
version >= "8"
end
sig { returns(CLTSDKLocator) }
def sdk_locator
@sdk_locator ||= CLTSDKLocator.new
end
sig { params(v: T.nilable(MacOS::Version)).returns(T.nilable(SDK)) }
def sdk(v = nil)
sdk_locator.sdk_if_applicable(v)
end
sig { params(v: T.nilable(MacOS::Version)).returns(T.nilable(Pathname)) }
def sdk_path(v = nil)
sdk(v)&.path
end
sig { returns(String) }
def installation_instructions
if MacOS.version == "10.14"
# This is not available from `xcode-select`
@ -344,6 +362,7 @@ module OS
end
end
sig { returns(T::Boolean) }
def below_minimum_version?
return false unless installed?
@ -358,11 +377,13 @@ module OS
::Version.new(clang_version) < latest_clang_version
end
sig { returns(T.nilable(String)) }
def detect_clang_version
version_output = Utils.popen_read("#{PKG_PATH}/usr/bin/clang", "--version")
version_output[/clang-(\d+\.\d+\.\d+(\.\d+)?)/, 1]
end
sig { returns(T.nilable(String)) }
def detect_version_from_clang_version
detect_clang_version&.sub(/^(\d+)0(\d)\./, "\\1.\\2.")
end
@ -370,6 +391,7 @@ module OS
# Version string (a pretty long one) of the CLT package.
# Note that the different ways of installing the CLTs lead to different
# version numbers.
sig { returns(::Version) }
def version
if @version ||= detect_version
::Version.new @version
@ -378,8 +400,9 @@ module OS
end
end
sig { returns(T.nilable(String)) }
def detect_version
version = nil
version = T.let(nil, T.nilable(String))
[EXECUTABLE_PKG_ID, MAVERICKS_NEW_PKG_ID].each do |id|
next unless File.exist?("#{PKG_PATH}/usr/bin/clang")

View File

@ -0,0 +1,9 @@
# typed: strict
module OS
module Mac
module Xcode
include Kernel
end
end
end

View File

@ -21,3 +21,14 @@ class Module
end
def define_method(arg0, arg1=T.unsafe(nil), &blk); end
end
class Pathname
# https://github.com/sorbet/sorbet/pull/4660
sig do
params(
consider_symlink: T::Boolean,
)
.returns(Pathname)
end
def cleanpath(consider_symlink=T.unsafe(nil)); end
end