diff --git a/Library/Homebrew/bottle_specification.rb b/Library/Homebrew/bottle_specification.rb index eab4311ac2..83d81f62cc 100644 --- a/Library/Homebrew/bottle_specification.rb +++ b/Library/Homebrew/bottle_specification.rb @@ -4,18 +4,27 @@ class BottleSpecification RELOCATABLE_CELLARS = [:any, :any_skip_relocation].freeze + sig { returns(T.nilable(Tap)) } attr_accessor :tap - attr_reader :collector, :root_url_specs, :repository + + attr_reader :collector + + sig { returns(T::Hash[String, T.untyped]) } + attr_reader :root_url_specs + + sig { returns(String) } + attr_reader :repository sig { void } def initialize - @rebuild = 0 - @repository = Homebrew::DEFAULT_REPOSITORY - @collector = Utils::Bottles::Collector.new - @root_url_specs = {} + @rebuild = T.let(0, Integer) + @repository = T.let(Homebrew::DEFAULT_REPOSITORY, String) + @collector = T.let(Utils::Bottles::Collector.new, Utils::Bottles::Collector) + @root_url_specs = T.let({}, T::Hash[String, T.untyped]) + @root_url = T.let(nil, T.nilable(String)) end - sig { params(val: Integer).returns(T.nilable(Integer)) } + sig { params(val: Integer).returns(Integer) } def rebuild(val = T.unsafe(nil)) val.nil? ? @rebuild : @rebuild = val end diff --git a/Library/Homebrew/cleanup.rb b/Library/Homebrew/cleanup.rb index 7f879e044e..37ed04e13a 100644 --- a/Library/Homebrew/cleanup.rb +++ b/Library/Homebrew/cleanup.rb @@ -171,7 +171,7 @@ module Homebrew stable = formula.stable if resource_name == "patch" - patch_hashes = stable&.patches&.filter_map { _1.resource.version if _1.external? } + patch_hashes = stable&.patches&.filter_map { T.cast(_1, ExternalPatch).resource.version if _1.external? } return true unless patch_hashes&.include?(Checksum.new(version.to_s)) elsif resource_name && stable && (resource_version = stable.resources[resource_name]&.version) return true if resource_version != version diff --git a/Library/Homebrew/dependable.rb b/Library/Homebrew/dependable.rb index f0d089491c..353ced54d1 100644 --- a/Library/Homebrew/dependable.rb +++ b/Library/Homebrew/dependable.rb @@ -5,12 +5,19 @@ require "options" # Shared functions for classes which can be depended upon. module Dependable + extend T::Helpers + # `:run` and `:linked` are no longer used but keep them here to avoid their # misuse in future. RESERVED_TAGS = [:build, :optional, :recommended, :run, :test, :linked, :implicit].freeze attr_reader :tags + abstract! + + sig { abstract.returns(T::Array[String]) } + def option_names; end + def build? tags.include? :build end diff --git a/Library/Homebrew/dependency_collector.rb b/Library/Homebrew/dependency_collector.rb index cddae5fcdc..6fb78130ec 100644 --- a/Library/Homebrew/dependency_collector.rb +++ b/Library/Homebrew/dependency_collector.rb @@ -80,10 +80,10 @@ class DependencyCollector parse_spec(spec, Array(tags)) end - sig { params(related_formula_names: T::Array[String]).returns(T.nilable(Dependency)) } + sig { params(related_formula_names: T::Set[String]).returns(T.nilable(Dependency)) } def gcc_dep_if_needed(related_formula_names); end - sig { params(related_formula_names: T::Array[String]).returns(T.nilable(Dependency)) } + sig { params(related_formula_names: T::Set[String]).returns(T.nilable(Dependency)) } def glibc_dep_if_needed(related_formula_names); end def git_dep_if_needed(tags) diff --git a/Library/Homebrew/downloadable.rb b/Library/Homebrew/downloadable.rb index 9d732e237a..a2365da4b5 100644 --- a/Library/Homebrew/downloadable.rb +++ b/Library/Homebrew/downloadable.rb @@ -32,7 +32,7 @@ module Downloadable @download_name = T.let(nil, T.nilable(String)) end - sig { params(other: Object).void } + sig { overridable.params(other: Downloadable).void } def initialize_dup(other) super @checksum = @checksum.dup diff --git a/Library/Homebrew/extend/os/linux/formula.rb b/Library/Homebrew/extend/os/linux/formula.rb index 46489c1c0d..106f49a9a1 100644 --- a/Library/Homebrew/extend/os/linux/formula.rb +++ b/Library/Homebrew/extend/os/linux/formula.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:disable Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true module OS @@ -30,6 +30,7 @@ module OS def add_global_deps_to_spec(spec) return unless ::DevelopmentTools.needs_build_formulae? + @global_deps ||= T.let(nil, T.nilable(T::Array[Dependency])) @global_deps ||= begin dependency_collector = spec.dependency_collector related_formula_names = Set.new([ diff --git a/Library/Homebrew/resource.rb b/Library/Homebrew/resource.rb index 813d1486f5..a6a4c0e9fd 100644 --- a/Library/Homebrew/resource.rb +++ b/Library/Homebrew/resource.rb @@ -37,7 +37,7 @@ class Resource instance_eval(&block) if block end - sig { params(other: Object).void } + sig { override.params(other: T.any(Resource, Downloadable)).void } def initialize_dup(other) super @name = @name.dup diff --git a/Library/Homebrew/software_spec.rb b/Library/Homebrew/software_spec.rb index cb88a3c84c..b69655d2b3 100644 --- a/Library/Homebrew/software_spec.rb +++ b/Library/Homebrew/software_spec.rb @@ -1,4 +1,4 @@ -# typed: true # rubocop:todo Sorbet/StrictSigil +# typed: strict # frozen_string_literal: true require "resource" @@ -20,13 +20,46 @@ class SoftwareSpec extend Forwardable include OnSystem::MacOSAndLinux - PREDEFINED_OPTIONS = { + PREDEFINED_OPTIONS = T.let({ universal: Option.new("universal", "Build a universal binary"), cxx11: Option.new("c++11", "Build using C++11 mode"), - }.freeze + }.freeze, T::Hash[T.any(Symbol, String), Option]) - attr_reader :name, :full_name, :owner, :build, :resources, :patches, :options, :deprecated_flags, - :deprecated_options, :dependency_collector, :bottle_specification, :compiler_failures + sig { returns(T.nilable(String)) } + attr_reader :name + + sig { returns(T.nilable(String)) } + attr_reader :full_name + + sig { returns(T.nilable(T.any(Formula, Cask::Cask))) } + attr_reader :owner + + sig { returns(BuildOptions) } + attr_reader :build + + sig { returns(T::Hash[String, Resource]) } + attr_reader :resources + + sig { returns(T::Array[T.any(EmbeddedPatch, ExternalPatch)]) } + attr_reader :patches + + sig { returns(Options) } + attr_reader :options + + sig { returns(T::Array[DeprecatedOption]) } + attr_reader :deprecated_flags + + sig { returns(T::Array[DeprecatedOption]) } + attr_reader :deprecated_options + + sig { returns(DependencyCollector) } + attr_reader :dependency_collector + + sig { returns(BottleSpecification) } + attr_reader :bottle_specification + + sig { returns(T::Array[CompilerFailure]) } + attr_reader :compiler_failures def_delegators :@resource, :stage, :fetch, :verify_download_integrity, :source_modified_time, :cached_download, :clear_cache, :checksum, :mirrors, :specs, :using, :version, :mirror, @@ -34,23 +67,29 @@ class SoftwareSpec def_delegators :@resource, :sha256 + sig { params(flags: T::Array[String]).void } def initialize(flags: []) super() + @name = T.let(nil, T.nilable(String)) + @full_name = T.let(nil, T.nilable(String)) + @owner = T.let(nil, T.nilable(T.any(Formula, Cask::Cask))) + # Ensure this is synced with `initialize_dup` and `freeze` (excluding simple objects like integers and booleans) @resource = T.let(Resource::Formula.new, Resource::Formula) - @resources = {} - @dependency_collector = DependencyCollector.new - @bottle_specification = BottleSpecification.new - @patches = [] - @options = Options.new - @flags = flags - @deprecated_flags = [] - @deprecated_options = [] - @build = BuildOptions.new(Options.create(@flags), options) - @compiler_failures = [] + @resources = T.let({}, T::Hash[String, Resource]) + @dependency_collector = T.let(DependencyCollector.new, DependencyCollector) + @bottle_specification = T.let(BottleSpecification.new, BottleSpecification) + @patches = T.let([], T::Array[T.any(EmbeddedPatch, ExternalPatch)]) + @options = T.let(Options.new, Options) + @flags = T.let(flags, T::Array[String]) + @deprecated_flags = T.let([], T::Array[DeprecatedOption]) + @deprecated_options = T.let([], T::Array[DeprecatedOption]) + @build = T.let(BuildOptions.new(Options.create(@flags), options), BuildOptions) + @compiler_failures = T.let([], T::Array[CompilerFailure]) end + sig { override.params(other: T.any(SoftwareSpec, Downloadable)).void } def initialize_dup(other) super @resource = @resource.dup @@ -66,6 +105,7 @@ class SoftwareSpec @compiler_failures = @compiler_failures.dup end + sig { override.returns(T.self_type) } def freeze @resource.freeze @resources.freeze @@ -81,6 +121,7 @@ class SoftwareSpec super end + sig { params(owner: T.any(Formula, Cask::Cask)).void } def owner=(owner) @name = owner.name @full_name = owner.full_name @@ -106,23 +147,35 @@ class SoftwareSpec @resource.url end + sig { returns(T::Boolean) } def bottle_defined? !bottle_specification.collector.tags.empty? end + sig { params(tag: T.nilable(Utils::Bottles::Tag)).returns(T::Boolean) } def bottle_tag?(tag = nil) bottle_specification.tag?(Utils::Bottles.tag(tag)) end + sig { params(tag: T.nilable(Utils::Bottles::Tag)).returns(T::Boolean) } def bottled?(tag = nil) - bottle_tag?(tag) && - (tag.present? || bottle_specification.compatible_locations? || owner.force_bottle) + return false unless bottle_tag?(tag) + + return true if tag.present? + return true if bottle_specification.compatible_locations? + + owner = self.owner + return false unless owner.is_a?(Formula) + + owner.force_bottle end + sig { params(block: T.proc.bind(BottleSpecification).void).void } def bottle(&block) bottle_specification.instance_eval(&block) end + sig { params(name: String).returns(T::Boolean) } def resource_defined?(name) resources.key?(name) end @@ -149,15 +202,14 @@ class SoftwareSpec end end + sig { params(name: String).returns(T::Boolean) } def option_defined?(name) options.include?(name) end + sig { params(name: T.any(Symbol, String), description: String).void } def option(name, description = "") opt = PREDEFINED_OPTIONS.fetch(name) do - unless name.is_a?(String) - raise ArgumentError, "option name must be string or symbol; got a #{name.class}: #{name}" - end raise ArgumentError, "option name is required" if name.empty? raise ArgumentError, "option name must be longer than one character: #{name}" if name.length <= 1 raise ArgumentError, "option name must not start with dashes: #{name}" if name.start_with?("-") @@ -167,6 +219,7 @@ class SoftwareSpec options << opt end + sig { params(hash: T::Hash[T.any(String, Symbol), T.any(String, Symbol)]).void } def deprecated_option(hash) raise ArgumentError, "deprecated_option hash must not be empty" if hash.empty? @@ -189,6 +242,7 @@ class SoftwareSpec @build = BuildOptions.new(Options.create(@flags), options) end + sig { params(spec: T.any(String, Symbol, T::Hash[String, T.untyped], T::Class[Requirement], Dependable)).void } def depends_on(spec) dep = dependency_collector.add(spec) add_dep_option(dep) if dep @@ -214,14 +268,17 @@ class SoftwareSpec depends_on UsesFromMacOSDependency.new(dep, tags, bounds:) end + sig { returns(Dependencies) } def deps dependency_collector.deps.dup_without_system_deps end + sig { returns(Dependencies) } def declared_deps dependency_collector.deps end + sig { returns(T::Array[Dependable]) } def recursive_dependencies deps_f = [] recursive_dependencies = deps.filter_map do |dep| @@ -239,15 +296,21 @@ class SoftwareSpec recursive_dependencies end + sig { returns(Requirements) } def requirements dependency_collector.requirements end + sig { returns(Requirements) } def recursive_requirements Requirement.expand(self) end - def patch(strip = :p1, src = nil, &block) + sig { + params(strip: T.any(Symbol, String), src: T.nilable(T.any(String, Symbol)), + block: T.nilable(T.proc.bind(Patch).void)).void + } + def patch(strip = :p1, src = T.unsafe(nil), &block) p = Patch.create(strip, src, &block) return if p.is_a?(ExternalPatch) && p.url.blank? @@ -255,16 +318,19 @@ class SoftwareSpec patches << p end + sig { params(compiler: T.any(T::Hash[Symbol, String], Symbol), block: T.nilable(T.proc.bind(CompilerFailure).void)).void } def fails_with(compiler, &block) compiler_failures << CompilerFailure.create(compiler, &block) end + sig { params(standards: T::Array[String]).void } def needs(*standards) standards.each do |standard| compiler_failures.concat CompilerFailure.for_standard(standard) end end + sig { params(dep: Dependable).void } def add_dep_option(dep) dep.option_names.each do |name| if dep.optional? && !option_defined?("with-#{name}")