diff --git a/Library/Homebrew/PATH.rb b/Library/Homebrew/PATH.rb index a69709e9c97055831eba8341dd7ba847ce0f5d18..6e48ef8553ea94b2d3b7504f2700f5946ef80cda 100644 --- a/Library/Homebrew/PATH.rb +++ b/Library/Homebrew/PATH.rb @@ -10,7 +10,7 @@ class PATH include Enumerable extend Forwardable - def_delegator :@paths, :each + delegate each: :@paths # FIXME: Enable cop again when https://github.com/sorbet/sorbet/issues/3532 is fixed. # rubocop:disable Style/MutableConstant diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index b7db3129475c368ce8f261c873c55ba1029c2a7a..62c72ec1e423f57394e33fe8f4f1af27301ff567 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1,4 +1,4 @@ -# typed: false +# typed: true # frozen_string_literal: true require "cache_store" @@ -124,6 +124,7 @@ class Formula # The currently active {SoftwareSpec}. # @see #determine_active_spec + sig { returns(SoftwareSpec) } attr_reader :active_spec protected :active_spec @@ -939,6 +940,7 @@ class Formula end # The generated launchd {.plist} file path. + sig { returns(Pathname) } def plist_path prefix/"#{plist_name}.plist" end @@ -961,38 +963,47 @@ class Formula HOMEBREW_PREFIX/"opt"/name end + sig { returns(Pathname) } def opt_bin opt_prefix/"bin" end + sig { returns(Pathname) } def opt_include opt_prefix/"include" end + sig { returns(Pathname) } def opt_lib opt_prefix/"lib" end + sig { returns(Pathname) } def opt_libexec opt_prefix/"libexec" end + sig { returns(Pathname) } def opt_sbin opt_prefix/"sbin" end + sig { returns(Pathname) } def opt_share opt_prefix/"share" end + sig { returns(Pathname) } def opt_pkgshare opt_prefix/"share"/name end + sig { returns(Pathname) } def opt_elisp opt_prefix/"share/emacs/site-lisp"/name end + sig { returns(Pathname) } def opt_frameworks opt_prefix/"Frameworks" end @@ -1012,40 +1023,45 @@ class Formula delegate pour_bottle_check_unsatisfied_reason: :"self.class" # Can be overridden to run commands on both source and bottle installation. + sig { overridable.void } def post_install; end # @private + sig { void } def run_post_install @prefix_returns_versioned_prefix = true build = self.build - self.build = Tab.for_formula(self) - new_env = { - TMPDIR: HOMEBREW_TEMP, - TEMP: HOMEBREW_TEMP, - TMP: HOMEBREW_TEMP, - _JAVA_OPTIONS: "-Djava.io.tmpdir=#{HOMEBREW_TEMP}", - HOMEBREW_PATH: nil, - PATH: ENV["HOMEBREW_PATH"], - } + begin + self.build = Tab.for_formula(self) - with_env(new_env) do - ENV.clear_sensitive_environment! + new_env = { + TMPDIR: HOMEBREW_TEMP, + TEMP: HOMEBREW_TEMP, + TMP: HOMEBREW_TEMP, + _JAVA_OPTIONS: "-Djava.io.tmpdir=#{HOMEBREW_TEMP}", + HOMEBREW_PATH: nil, + PATH: ENV["HOMEBREW_PATH"], + } - etc_var_dirs = [bottle_prefix/"etc", bottle_prefix/"var"] - Find.find(*etc_var_dirs.select(&:directory?)) do |path| - path = Pathname.new(path) - path.extend(InstallRenamed) - path.cp_path_sub(bottle_prefix, HOMEBREW_PREFIX) - end + with_env(new_env) do + ENV.clear_sensitive_environment! - with_logging("post_install") do - post_install + etc_var_dirs = [bottle_prefix/"etc", bottle_prefix/"var"] + T.unsafe(Find).find(*etc_var_dirs.select(&:directory?)) do |path| + path = Pathname.new(path) + path.extend(InstallRenamed) + path.cp_path_sub(bottle_prefix, HOMEBREW_PREFIX) + end + + with_logging("post_install") do + post_install + end end + ensure + self.build = build + @prefix_returns_versioned_prefix = false end - ensure - self.build = build - @prefix_returns_versioned_prefix = false end # Warn the user about any Homebrew-specific issues or quirks for this package. @@ -1067,6 +1083,7 @@ class Formula # s += "Some issue only on older systems" if MacOS.version < :el_capitan # s # end</pre> + sig { overridable.returns(T.nilable(String)) } def caveats nil end @@ -1089,6 +1106,8 @@ class Formula # keep .la files with: # skip_clean :la # @private + sig { params(path: Pathname).returns(T::Boolean) } + def skip_clean?(path) return true if path.extname == ".la" && self.class.skip_clean_paths.include?(:la) @@ -1241,7 +1260,7 @@ class Formula Formula.cache[:outdated_kegs] ||= {} Formula.cache[:outdated_kegs][cache_key] ||= begin all_kegs = [] - current_version = false + current_version = T.let(false, T::Boolean) installed_kegs.each do |keg| all_kegs << keg @@ -1427,13 +1446,15 @@ class Formula # Standard parameters for cabal-v2 builds. sig { returns(T::Array[String]) } def std_cabal_v2_args + env = T.cast(ENV, T.any(Stdenv, Superenv)) + # cabal-install's dependency-resolution backtracking strategy can # easily need more than the default 2,000 maximum number of # "backjumps," since Hackage is a fast-moving, rolling-release # target. The highest known needed value by a formula was 43,478 # for git-annex, so 100,000 should be enough to avoid most # gratuitous backjumps build failures. - ["--jobs=#{ENV.make_jobs}", "--max-backjumps=100000", "--install-method=copy", "--installdir=#{bin}"] + ["--jobs=#{env.make_jobs}", "--max-backjumps=100000", "--install-method=copy", "--installdir=#{bin}"] end # Standard parameters for meson builds. @@ -1960,6 +1981,7 @@ class Formula # # # If there is a "make install" available, please use it! # system "make", "install"</pre> + sig { params(cmd: T.any(String, Pathname), args: T.any(String, Pathname)).void } def system(cmd, *args) verbose_using_dots = Homebrew::EnvConfig.verbose_using_dots? @@ -2026,10 +2048,12 @@ class Formula rd.close end else - pid = fork { exec_cmd(cmd, args, log, logfn) } + pid = fork do + exec_cmd(cmd, args, log, logfn) + end end - Process.wait(pid) + Process.wait(T.must(pid)) $stdout.flush @@ -2114,11 +2138,15 @@ class Formula end # Runs `xcodebuild` without Homebrew's compiler environment variables set. + sig { params(args: T.any(String, Pathname)).void } def xcodebuild(*args) removed = ENV.remove_cc_etc - system "xcodebuild", *args - ensure - ENV.update(removed) + + begin + T.unsafe(self).system("xcodebuild", *args) + ensure + ENV.update(removed) + end end def fetch_patches @@ -2148,7 +2176,8 @@ class Formula if cmd == "python" setup_py_in_args = %w[setup.py build.py].include?(args.first) setuptools_shim_in_args = args.any? { |a| a.to_s.start_with? "import setuptools" } - ENV.refurbish_args if setup_py_in_args || setuptools_shim_in_args + env = T.cast(ENV, T.any(Stdenv, Superenv)) + env.refurbish_args if setup_py_in_args || setuptools_shim_in_args end $stdout.reopen(out) @@ -2156,7 +2185,7 @@ class Formula out.close args.map!(&:to_s) begin - exec(cmd, *args) + T.unsafe(Kernel).exec(cmd, *args) rescue nil end @@ -2420,7 +2449,7 @@ class Formula # Get the `BUILD_FLAGS` from the formula's namespace set in `Formulary::load_formula`. # @private def build_flags - namespace = to_s.split("::")[0..-2].join("::") + namespace = T.must(to_s.split("::")[0..-2]).join("::") return [] if namespace.empty? mod = const_get(namespace) diff --git a/Library/Homebrew/resource.rb b/Library/Homebrew/resource.rb index c2a2b749a851690f5e9e0ec0f5ac77886af13b6f..d17bc7be56362bcc7ad8372c9e569b9fbfe07cdb 100644 --- a/Library/Homebrew/resource.rb +++ b/Library/Homebrew/resource.rb @@ -12,6 +12,8 @@ require "mktemp" # # @api private class Resource + extend T::Sig + include Context include FileUtils @@ -221,8 +223,8 @@ class Resource # A resource containing a Go package. class Go < Resource - def stage(target) - super(target/name) + def stage(target, &block) + super(target/name, &block) end end diff --git a/Library/Homebrew/software_spec.rb b/Library/Homebrew/software_spec.rb index f5f817ba05c8ddb9734f4fa9b10534c84cafa5fe..513efdbafe12a3e8d5bc7de82a9b7699bcb9821f 100644 --- a/Library/Homebrew/software_spec.rb +++ b/Library/Homebrew/software_spec.rb @@ -29,7 +29,9 @@ class SoftwareSpec def_delegators :@resource, :stage, :fetch, :verify_download_integrity, :source_modified_time, :download_name, :cached_download, :clear_cache, :checksum, :mirrors, :specs, :using, :version, :mirror, - :downloader, *Checksum::TYPES + :downloader + + def_delegators :@resource, *Checksum::TYPES def initialize(flags: []) @resource = Resource.new diff --git a/Library/Homebrew/sorbet/plugins/attr_rw.rb b/Library/Homebrew/sorbet/plugins/attr_rw.rb new file mode 100644 index 0000000000000000000000000000000000000000..2363d3bffae4af277aa159576df64adfaf2ca177 --- /dev/null +++ b/Library/Homebrew/sorbet/plugins/attr_rw.rb @@ -0,0 +1,13 @@ +# typed: strict +# frozen_string_literal: true + +source = ARGV[5] + +source.scan(/:([^\s,]+)/).flatten.each do |method| + puts <<~RUBY + # typed: strict + + sig { params(arg: T.untyped).returns(T.untyped) } + def #{method}(arg = T.unsafe(nil)); end + RUBY +end diff --git a/Library/Homebrew/sorbet/plugins/def_delegator.rb b/Library/Homebrew/sorbet/plugins/def_delegator.rb new file mode 100644 index 0000000000000000000000000000000000000000..7eea2d6bd00618929a5c7038a599ed71591961ea --- /dev/null +++ b/Library/Homebrew/sorbet/plugins/def_delegator.rb @@ -0,0 +1,17 @@ +# typed: strict +# frozen_string_literal: true + +source = ARGV[5] + +match = source.match(/\s*def_delegator\s+.*:(?<method>[^:]+)\s*\Z/m) + +raise if match.nil? + +method = match[:method] + +puts <<~RUBY + # typed: strict + + sig {params(arg0: T.untyped, blk: T.untyped).returns(T.untyped)} + def #{method}(*arg0, &blk); end +RUBY diff --git a/Library/Homebrew/sorbet/plugins/def_delegators.rb b/Library/Homebrew/sorbet/plugins/def_delegators.rb new file mode 100644 index 0000000000000000000000000000000000000000..ac09880d3c7350f6cb4239692ab7351910a71954 --- /dev/null +++ b/Library/Homebrew/sorbet/plugins/def_delegators.rb @@ -0,0 +1,17 @@ +# typed: strict +# frozen_string_literal: true + +source = ARGV[5] + +symbols = source.scan(/:[^\s,]+/) + +_, *methods = symbols.map { |s| s.delete_prefix(":") } + +methods.each do |method| + puts <<~RUBY + # typed: strict + + sig {params(arg0: T.untyped, blk: T.untyped).returns(T.untyped)} + def #{method}(*arg0, &blk); end + RUBY +end diff --git a/Library/Homebrew/sorbet/plugins/delegate.rb b/Library/Homebrew/sorbet/plugins/delegate.rb index 2f308cc45d8128ad3f3eb347370e2020f4e9ac9d..8a249d188c3c5a0e6ad57baaf67bcf1a91d81ffb 100644 --- a/Library/Homebrew/sorbet/plugins/delegate.rb +++ b/Library/Homebrew/sorbet/plugins/delegate.rb @@ -15,7 +15,7 @@ methods.each do |method| puts <<~RUBY # typed: strict - sig {params(arg0: T.untyped).returns(T.untyped)} - def #{method}(*arg0); end + sig {params(arg0: T.untyped, blk: T.untyped).returns(T.untyped)} + def #{method}(*arg0, &blk); end RUBY end diff --git a/Library/Homebrew/sorbet/rbi/upstream.rbi b/Library/Homebrew/sorbet/rbi/upstream.rbi index d3295ae20c7eefa6c141d9ec52c16dd9ac3b1d0f..4a77637e5af87806d24391028fd2c126b55bf02e 100644 --- a/Library/Homebrew/sorbet/rbi/upstream.rbi +++ b/Library/Homebrew/sorbet/rbi/upstream.rbi @@ -9,3 +9,48 @@ class Pathname sig { params(with_directory: T::Boolean).returns(T::Array[Pathname]) } def children(with_directory = true); end end + +module FileUtils + # https://github.com/sorbet/sorbet/pull/3730 + module_function + + sig do + params( + src: T.untyped, + dest: T.untyped, + preserve: T.nilable(T::Boolean), + noop: T.nilable(T::Boolean), + verbose: T.nilable(T::Boolean) + ).returns(T.untyped) + end + def cp(src, dest, preserve: nil, noop: nil, verbose: nil); end + + sig do + params( + list: T.any(String, Pathname), + mode: T.nilable(Integer), + noop: T.nilable(T::Boolean), + verbose: T.nilable(T::Boolean) + ).returns(T::Array[String]) + end + def mkdir_p(list, mode: nil, noop: nil, verbose: nil); end +end + +class Module + # https://github.com/sorbet/sorbet/pull/3732 + sig do + params( + arg0: T.any(Symbol, String), + arg1: T.any(Proc, Method, UnboundMethod) + ) + .returns(Symbol) + end + sig do + params( + arg0: T.any(Symbol, String), + blk: T.proc.bind(T.untyped).returns(T.untyped), + ) + .returns(Symbol) + end + def define_method(arg0, arg1=T.unsafe(nil), &blk); end +end diff --git a/Library/Homebrew/sorbet/triggers.yml b/Library/Homebrew/sorbet/triggers.yml index 747e297c2ef4c0ed11ec58ae6e153124c7bf8150..ccf1647ba40e9b85b6170a502d12ad742c3075ba 100644 --- a/Library/Homebrew/sorbet/triggers.yml +++ b/Library/Homebrew/sorbet/triggers.yml @@ -4,4 +4,7 @@ ruby_extra_args: triggers: using: sorbet/plugins/using.rb attr_predicate: sorbet/plugins/attr_predicate.rb + attr_rw: sorbet/plugins/attr_rw.rb + def_delegator: sorbet/plugins/def_delegator.rb + def_delegators: sorbet/plugins/def_delegators.rb delegate: sorbet/plugins/delegate.rb