Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sorbet: Make more files (that have public APIs defined in them) typed: strict #17636

Merged
merged 10 commits into from
Jul 12, 2024
50 changes: 26 additions & 24 deletions Library/Homebrew/development_tools.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# typed: true
# typed: strict
# frozen_string_literal: true

require "version"
Expand All @@ -16,7 +16,7 @@
# Don't call tools (cc, make, strip, etc.) directly!
# Give the name of the binary you look for as a string to this method
# in order to get the full path back as a Pathname.
(@locate ||= {}).fetch(tool) do |key|
(@locate ||= T.let({}, T.nilable(T::Hash[T.any(String, Symbol), T.untyped]))).fetch(tool) do |key|
MikeMcQuaid marked this conversation as resolved.
Show resolved Hide resolved
@locate[key] = if File.executable?((path = "/usr/bin/#{tool}"))
Pathname.new path
# Homebrew GCCs most frequently; much faster to check this before xcrun
Expand Down Expand Up @@ -62,55 +62,57 @@
# @api public
sig { returns(Version) }
def clang_version
@clang_version ||= if (path = locate("clang")) &&
(build_version = `#{path} --version`[/(?:clang|LLVM) version (\d+\.\d(?:\.\d)?)/, 1])
Version.new build_version
else
Version::NULL
end
@clang_version ||= T.let(
if (path = locate("clang")) &&
(build_version = `#{path} --version`[/(?:clang|LLVM) version (\d+\.\d(?:\.\d)?)/, 1])
Version.new(build_version)
else
Version::NULL
end, T.nilable(Version)
)
end

# Get the Clang build version.
#
# @api public
sig { returns(Version) }
def clang_build_version
@clang_build_version ||= if (path = locate("clang")) &&
(build_version = `#{path} --version`[
%r{clang(-| version [^ ]+ \(tags/RELEASE_)(\d{2,})}, 2])
Version.new build_version
else
Version::NULL
end
@clang_build_version ||= T.let(
if (path = locate("clang")) &&
(build_version = `#{path} --version`[%r{clang(-| version [^ ]+ \(tags/RELEASE_)(\d{2,})}, 2])
Version.new(build_version)
else
Version::NULL
end, T.nilable(Version)
)
end

# Get the LLVM Clang build version.
#
# @api public
sig { returns(Version) }
def llvm_clang_build_version
@llvm_clang_build_version ||= begin
@llvm_clang_build_version ||= T.let(begin

Check warning on line 95 in Library/Homebrew/development_tools.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/development_tools.rb#L95

Added line #L95 was not covered by tests
path = Formulary.factory("llvm").opt_prefix/"bin/clang"
if path.executable? &&
(build_version = `#{path} --version`[/clang version (\d+\.\d\.\d)/, 1])
Version.new build_version
if path.executable? && (build_version = `#{path} --version`[/clang version (\d+\.\d\.\d)/, 1])

Check warning on line 97 in Library/Homebrew/development_tools.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/development_tools.rb#L97

Added line #L97 was not covered by tests
Version.new(build_version)
else
Version::NULL
end
end
end, T.nilable(Version))
end

# Get the GCC version.
#
# @api internal
sig { params(cc: String).returns(Version) }
def gcc_version(cc)
(@gcc_version ||= {}).fetch(cc) do
(@gcc_version ||= T.let({}, T.nilable(T::Hash[String, Version]))).fetch(cc) do
path = HOMEBREW_PREFIX/"opt/#{CompilerSelector.preferred_gcc}/bin"/cc
path = locate(cc) unless path.exist?
version = if path &&
(build_version = `#{path} --version`[/gcc(?:(?:-\d+(?:\.\d)?)? \(.+\))? (\d+\.\d\.\d)/, 1])
Version.new build_version
Version.new(build_version)
else
Version::NULL
end
Expand All @@ -120,8 +122,8 @@

sig { void }
def clear_version_cache
@clang_version = @clang_build_version = nil
@gcc_version = {}
@clang_version = @clang_build_version = T.let(nil, T.nilable(Version))
@gcc_version = T.let({}, T.nilable(T::Hash[String, Version]))

Check warning on line 126 in Library/Homebrew/development_tools.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/development_tools.rb#L125-L126

Added lines #L125 - L126 were not covered by tests
end

sig { returns(T::Boolean) }
Expand Down
2 changes: 1 addition & 1 deletion Library/Homebrew/extend/os/mac/unpack_strategy/zip.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class Zip
module MacOSZipExtension
private

sig { params(unpack_dir: Pathname, basename: Pathname, verbose: T::Boolean).returns(T.untyped) }
sig { params(unpack_dir: Pathname, basename: Pathname, verbose: T::Boolean).void }
def extract_to_dir(unpack_dir, basename:, verbose:)
with_env(TZ: "UTC") do
if merge_xattrs && contains_extended_attributes?(path)
Expand Down
8 changes: 6 additions & 2 deletions Library/Homebrew/formula_assertions.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# typed: true
# typed: strict
# frozen_string_literal: true

module Homebrew
Expand All @@ -10,15 +10,18 @@
require "minitest/assertions"
include ::Minitest::Assertions

sig { params(assertions: Integer).returns(Integer) }
attr_writer :assertions

sig { returns(Integer) }
def assertions
@assertions ||= 0
@assertions ||= T.let(0, T.nilable(Integer))

Check warning on line 18 in Library/Homebrew/formula_assertions.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/formula_assertions.rb#L18

Added line #L18 was not covered by tests
end

# Returns the output of running cmd and asserts the exit status.
#
# @api public
sig { params(cmd: String, result: Integer).returns(String) }
def shell_output(cmd, result = 0)
ohai cmd
output = `#{cmd}`
Expand All @@ -33,6 +36,7 @@
# optionally asserts the exit status.
#
# @api public
sig { params(cmd: String, input: T.nilable(String), result: T.nilable(Integer)).returns(String) }
def pipe_output(cmd, input = nil, result = nil)
ohai cmd
output = IO.popen(cmd, "w+") do |pipe|
Expand Down
81 changes: 60 additions & 21 deletions Library/Homebrew/unpack_strategy.rb
Original file line number Diff line number Diff line change
@@ -1,16 +1,35 @@
# typed: true
# typed: strict
# frozen_string_literal: true

require "system_command"

# Module containing all available strategies for unpacking archives.
module UnpackStrategy
extend T::Helpers

include SystemCommand::Mixin
abstract!

# FIXME: Enable cop again when https://github.com/sorbet/sorbet/issues/3532 is fixed.
# rubocop:disable Style/MutableConstant
UnpackStrategyType = T.type_alias { T.all(T::Class[UnpackStrategy], UnpackStrategy::ClassMethods) }
# rubocop:enable Style/MutableConstant

module ClassMethods
extend T::Helpers
abstract!

sig { abstract.returns(T::Array[String]) }
def extensions; end

sig { abstract.params(path: Pathname).returns(T::Boolean) }
def can_extract?(path); end
end

mixes_in_class_methods(ClassMethods)

sig { returns(T.nilable(T::Array[UnpackStrategyType])) }
def self.strategies
@strategies ||= [
@strategies ||= T.let([
Tar, # Needs to be before Bzip2/Gzip/Xz/Lzma/Zstd.
Pax,
Gzip,
Expand Down Expand Up @@ -43,10 +62,11 @@ def self.strategies
Sit,
Rar,
Lha,
].freeze
].freeze, T.nilable(T::Array[UnpackStrategyType]))
end
private_class_method :strategies

sig { params(type: Symbol).returns(T.nilable(UnpackStrategyType)) }
def self.from_type(type)
type = {
naked: :uncompressed,
Expand All @@ -61,23 +81,31 @@ def self.from_type(type)
end
end

sig { params(extension: String).returns(T.nilable(UnpackStrategyType)) }
def self.from_extension(extension)
strategies.sort_by { |s| s.extensions.map(&:length).max || 0 }
.reverse
.find { |s| s.extensions.any? { |ext| extension.end_with?(ext) } }
return unless strategies

strategies&.sort_by { |s| s.extensions.map(&:length).max || 0 }
&.reverse
&.find { |s| s.extensions.any? { |ext| extension.end_with?(ext) } }
issyl0 marked this conversation as resolved.
Show resolved Hide resolved
end

sig { params(path: Pathname).returns(T.nilable(UnpackStrategyType)) }
def self.from_magic(path)
strategies.find { |s| s.can_extract?(path) }
strategies&.find { |s| s.can_extract?(path) }
end

def self.detect(path, prioritize_extension: false, type: nil, ref_type: nil, ref: nil, merge_xattrs: nil)
sig {
params(path: Pathname, prioritize_extension: T::Boolean, type: T.nilable(Symbol), ref_type: T.nilable(Symbol),
ref: T.nilable(String), merge_xattrs: T::Boolean).returns(T.untyped)
}
def self.detect(path, prioritize_extension: false, type: nil, ref_type: nil, ref: nil, merge_xattrs: false)
strategy = from_type(type) if type

if prioritize_extension && path.extname.present?
strategy ||= from_extension(path.extname)
strategy ||= strategies.select { |s| s < Directory || s == Fossil }
.find { |s| s.can_extract?(path) }

strategy ||= strategies&.find { |s| (s < Directory || s == Fossil) && s.can_extract?(path) }
else
strategy ||= from_magic(path)
strategy ||= from_extension(path.extname)
Expand All @@ -88,24 +116,31 @@ def self.detect(path, prioritize_extension: false, type: nil, ref_type: nil, ref
strategy.new(path, ref_type:, ref:, merge_xattrs:)
end

attr_reader :path, :merge_xattrs
sig { returns(Pathname) }
attr_reader :path

def initialize(path, ref_type: nil, ref: nil, merge_xattrs: nil)
@path = Pathname(path).expand_path
@ref_type = ref_type
@ref = ref
@merge_xattrs = merge_xattrs
sig { returns(T::Boolean) }
attr_reader :merge_xattrs

sig {
params(path: T.any(String, Pathname), ref_type: T.nilable(Symbol), ref: T.nilable(String),
merge_xattrs: T::Boolean).void
}
def initialize(path, ref_type: nil, ref: nil, merge_xattrs: false)
@path = T.let(Pathname(path).expand_path, Pathname)
@ref_type = T.let(ref_type, T.nilable(Symbol))
@ref = T.let(ref, T.nilable(String))
@merge_xattrs = T.let(merge_xattrs, T::Boolean)
end

abstract!
sig { abstract.params(unpack_dir: Pathname, basename: Pathname, verbose: T::Boolean).returns(T.untyped) }
sig { abstract.params(unpack_dir: Pathname, basename: Pathname, verbose: T::Boolean).void }
issyl0 marked this conversation as resolved.
Show resolved Hide resolved
def extract_to_dir(unpack_dir, basename:, verbose:); end
private :extract_to_dir

sig {
params(
to: T.nilable(Pathname), basename: T.nilable(T.any(String, Pathname)), verbose: T::Boolean,
).returns(T.untyped)
).void
}
def extract(to: nil, basename: nil, verbose: false)
basename ||= path.basename
Expand All @@ -131,7 +166,10 @@ def extract_nestedly(to: nil, basename: nil, verbose: false, prioritize_extensio
children = tmp_unpack_dir.children

if children.size == 1 && !children.fetch(0).directory?
s = UnpackStrategy.detect(children.first, prioritize_extension:)
first_child = children.first
next if first_child.nil?

s = UnpackStrategy.detect(first_child, prioritize_extension:)

s.extract_nestedly(to:, verbose:, prioritize_extension:)

Expand All @@ -149,6 +187,7 @@ def extract_nestedly(to: nil, basename: nil, verbose: false, prioritize_extensio
end
end

sig { returns(T::Array[String]) }
def dependencies
[]
end
Expand Down
10 changes: 6 additions & 4 deletions Library/Homebrew/unpack_strategy/air.rb
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
# typed: true
# typed: strict
# frozen_string_literal: true

module UnpackStrategy
# Strategy for unpacking Adobe Air archives.
class Air
include UnpackStrategy

sig { returns(T::Array[String]) }
sig { override.returns(T::Array[String]) }
def self.extensions
[".air"]
end

sig { override.params(path: Pathname).returns(T::Boolean) }
def self.can_extract?(path)
mime_type = "application/vnd.adobe.air-application-installer-package+zip"
path.magic_number.match?(/.{59}#{Regexp.escape(mime_type)}/)
end

sig { returns(T.nilable(T::Array[Cask::Cask])) }
def dependencies
@dependencies ||= [Cask::CaskLoader.load("adobe-air")]
@dependencies ||= T.let([Cask::CaskLoader.load("adobe-air")], T.nilable(T::Array[Cask::Cask]))

Check warning on line 22 in Library/Homebrew/unpack_strategy/air.rb

View check run for this annotation

Codecov / codecov/patch

Library/Homebrew/unpack_strategy/air.rb#L22

Added line #L22 was not covered by tests
end

AIR_APPLICATION_INSTALLER =
Expand All @@ -27,7 +29,7 @@

private

sig { override.params(unpack_dir: Pathname, basename: Pathname, verbose: T::Boolean).returns(T.untyped) }
sig { override.params(unpack_dir: Pathname, basename: Pathname, verbose: T::Boolean).void }
def extract_to_dir(unpack_dir, basename:, verbose:)
system_command! AIR_APPLICATION_INSTALLER,
args: ["-silent", "-location", unpack_dir, path],
Expand Down
7 changes: 4 additions & 3 deletions Library/Homebrew/unpack_strategy/bazaar.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
# typed: true
# typed: strict
# frozen_string_literal: true

require_relative "directory"

module UnpackStrategy
# Strategy for unpacking Bazaar archives.
class Bazaar < Directory
sig { override.params(path: Pathname).returns(T::Boolean) }
def self.can_extract?(path)
super && (path/".bzr").directory?
!!(super && (path/".bzr").directory?)
end

private

sig { override.params(unpack_dir: Pathname, basename: Pathname, verbose: T::Boolean).returns(T.untyped) }
sig { override.params(unpack_dir: Pathname, basename: Pathname, verbose: T::Boolean).void }
def extract_to_dir(unpack_dir, basename:, verbose:)
super

Expand Down
7 changes: 4 additions & 3 deletions Library/Homebrew/unpack_strategy/bzip2.rb
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
# typed: true
# typed: strict
# frozen_string_literal: true

module UnpackStrategy
# Strategy for unpacking bzip2 archives.
class Bzip2
include UnpackStrategy

sig { returns(T::Array[String]) }
sig { override.returns(T::Array[String]) }
def self.extensions
[".bz2"]
end

sig { override.params(path: Pathname).returns(T::Boolean) }
def self.can_extract?(path)
path.magic_number.match?(/\ABZh/n)
end

private

sig { override.params(unpack_dir: Pathname, basename: Pathname, verbose: T::Boolean).returns(T.untyped) }
sig { override.params(unpack_dir: Pathname, basename: Pathname, verbose: T::Boolean).void }
def extract_to_dir(unpack_dir, basename:, verbose:)
FileUtils.cp path, unpack_dir/basename, preserve: true
quiet_flags = verbose ? [] : ["-q"]
Expand Down
Loading
Loading