diff --git a/Library/Homebrew/cli/args.rb b/Library/Homebrew/cli/args.rb index cc7a571764..cbc90fe507 100644 --- a/Library/Homebrew/cli/args.rb +++ b/Library/Homebrew/cli/args.rb @@ -86,7 +86,7 @@ module Homebrew require "formula" @formulae ||= (downcased_unique_named - casks).map do |name| - Formulary.factory(name, spec) + Formulary.factory(name, spec, force_bottle: force_bottle?, flags: flags_only) end.uniq(&:name).freeze end @@ -94,7 +94,7 @@ module Homebrew require "formula" @resolved_formulae ||= (downcased_unique_named - casks).map do |name| - Formulary.resolve(name, spec: spec(nil)) + Formulary.resolve(name, spec: spec(nil), force_bottle: force_bottle?, flags: flags_only) end.uniq(&:name).freeze end @@ -104,7 +104,8 @@ module Homebrew casks = [] downcased_unique_named.each do |name| - resolved_formulae << Formulary.resolve(name, spec: spec(nil)) + resolved_formulae << Formulary.resolve(name, spec: spec(nil), + force_bottle: force_bottle?, flags: flags_only) rescue FormulaUnavailableError begin casks << Cask::CaskLoader.load(name) diff --git a/Library/Homebrew/cli/parser.rb b/Library/Homebrew/cli/parser.rb index dafa7a59d2..64a5c3c495 100644 --- a/Library/Homebrew/cli/parser.rb +++ b/Library/Homebrew/cli/parser.rb @@ -360,7 +360,7 @@ module Homebrew named_args.map do |arg| next if arg.match?(HOMEBREW_CASK_TAP_CASK_REGEX) - Formulary.factory(arg, spec) + Formulary.factory(arg, spec, flags: @args.flags_only) end.compact.uniq(&:name) end end diff --git a/Library/Homebrew/cmd/--cache.rb b/Library/Homebrew/cmd/--cache.rb index 191f018c3f..9e2cf7acb5 100644 --- a/Library/Homebrew/cmd/--cache.rb +++ b/Library/Homebrew/cmd/--cache.rb @@ -33,13 +33,13 @@ module Homebrew end def __cache - __cache_args.parse + args = __cache_args.parse if args.no_named? puts HOMEBREW_CACHE elsif args.formula? args.named.each do |name| - print_formula_cache name + print_formula_cache name, args: args end elsif args.cask? args.named.each do |name| @@ -47,7 +47,7 @@ module Homebrew end else args.named.each do |name| - print_formula_cache name + print_formula_cache name, args: args rescue FormulaUnavailableError begin print_cask_cache name @@ -58,8 +58,8 @@ module Homebrew end end - def print_formula_cache(name) - formula = Formulary.factory name + def print_formula_cache(name, args:) + formula = Formulary.factory(name, force_bottle: args.force_bottle?, flags: args.flags_only) if fetch_bottle?(formula) puts formula.bottle.cached_download else diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index ab4734cf57..e1782990ab 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -101,6 +101,10 @@ class Formula # @private attr_reader :tap + # Whether or not to force the use of a bottle. + # @private + attr_reader :force_bottle + # The stable (and default) {SoftwareSpec} for this {Formula} # This contains all the attributes (e.g. URL, checksum) that apply to the # stable version of this formula. @@ -181,7 +185,7 @@ class Formula alias follow_installed_alias? follow_installed_alias # @private - def initialize(name, path, spec, alias_path: nil) + def initialize(name, path, spec, alias_path: nil, force_bottle: false) @name = name @path = path @alias_path = alias_path @@ -189,6 +193,8 @@ class Formula @revision = self.class.revision || 0 @version_scheme = self.class.version_scheme || 0 + @force_bottle = force_bottle + @tap = if path == Formulary.core_path(name) CoreTap.instance else @@ -273,7 +279,7 @@ class Formula # and is specified to this instance. def installed_alias_path path = build.source["path"] if build.is_a?(Tab) - return unless path&.match?(%r{#{HOMEBREW_TAP_DIR_REGEX}/Aliases}) + return unless path&.to_s&.match?(%r{#{HOMEBREW_TAP_DIR_REGEX}/Aliases}) return unless File.symlink?(path) path @@ -2372,6 +2378,15 @@ class Formula stable.build end + # @private + def build_flags + mod_name = to_s.split("::")[0..-2].join("::") + return [] if mod_name.empty? + + mod = const_get(mod_name) + mod.const_get(:BUILD_FLAGS) + end + # @!attribute [w] stable # Allows adding {.depends_on} and {Patch}es just to the {.stable} {SoftwareSpec}. # This is required instead of using a conditional. @@ -2385,7 +2400,7 @@ class Formula # depends_on "libffi" # end def stable(&block) - @stable ||= SoftwareSpec.new + @stable ||= SoftwareSpec.new(flags: build_flags) return @stable unless block_given? @stable.instance_eval(&block) @@ -2405,7 +2420,7 @@ class Formula # end # @private def devel(&block) - @devel ||= SoftwareSpec.new + @devel ||= SoftwareSpec.new(flags: build_flags) return @devel unless block_given? odeprecated "'devel' blocks in formulae", "'head' blocks or @-versioned formulae" @@ -2425,7 +2440,7 @@ class Formula # or (if autodetect fails): #
head "https://hg.is.awesome.but.git.has.won.example.com/", :using => :hg
def head(val = nil, specs = {}, &block) - @head ||= HeadSoftwareSpec.new + @head ||= HeadSoftwareSpec.new(flags: build_flags) if block_given? @head.instance_eval(&block) elsif val diff --git a/Library/Homebrew/formulary.rb b/Library/Homebrew/formulary.rb index 336f34f757..65ca197f4e 100644 --- a/Library/Homebrew/formulary.rb +++ b/Library/Homebrew/formulary.rb @@ -27,12 +27,14 @@ module Formulary cache.fetch(path) end - def self.load_formula(name, path, contents, namespace) + def self.load_formula(name, path, contents, namespace, flags:) raise "Formula loading disabled by HOMEBREW_DISABLE_LOAD_FORMULA!" if Homebrew::EnvConfig.disable_load_formula? mod = Module.new const_set(namespace, mod) + begin + mod.const_set(:BUILD_FLAGS, flags) mod.module_eval(contents, path) rescue NameError, ArgumentError, ScriptError => e $stderr.puts e.backtrace if Homebrew::EnvConfig.developer? @@ -51,16 +53,16 @@ module Formulary end end - def self.load_formula_from_path(name, path) + def self.load_formula_from_path(name, path, flags:) contents = path.open("r") { |f| ensure_utf8_encoding(f).read } namespace = "FormulaNamespace#{Digest::MD5.hexdigest(path.to_s)}" - klass = load_formula(name, path, contents, namespace) + klass = load_formula(name, path, contents, namespace, flags: flags) cache[path] = klass end - def self.resolve(name, spec: nil) + def self.resolve(name, spec: nil, force_bottle: false, flags: []) if name.include?("/") || File.exist?(name) - f = factory(name, *spec) + f = factory(name, *spec, force_bottle: force_bottle, flags: flags) if f.any_version_installed? tab = Tab.for_formula(f) resolved_spec = spec || tab.spec @@ -73,8 +75,8 @@ module Formulary end else rack = to_rack(name) - alias_path = factory(name).alias_path - f = from_rack(rack, *spec, alias_path: alias_path) + alias_path = factory(name, force_bottle: force_bottle, flags: flags).alias_path + f = from_rack(rack, *spec, alias_path: alias_path, force_bottle: force_bottle, flags: flags) end # If this formula was installed with an alias that has since changed, @@ -121,22 +123,23 @@ module Formulary # # `alias_path` can be overridden here in case an alias was used to refer to # a formula that was loaded in another way. - def get_formula(spec, alias_path: nil) - klass.new(name, path, spec, alias_path: alias_path || self.alias_path) + def get_formula(spec, alias_path: nil, force_bottle: false, flags: []) + klass(flags: flags) + .new(name, path, spec, alias_path: alias_path || self.alias_path, force_bottle: force_bottle) end - def klass - load_file unless Formulary.formula_class_defined?(path) + def klass(flags:) + load_file(flags: flags) unless Formulary.formula_class_defined?(path) Formulary.formula_class_get(path) end private - def load_file + def load_file(flags:) $stderr.puts "#{$PROGRAM_NAME} (#{self.class.name}): loading #{path}" if Homebrew.args.debug? raise FormulaUnavailableError, name unless path.file? - Formulary.load_formula_from_path(name, path) + Formulary.load_formula_from_path(name, path, flags: flags) end end @@ -161,10 +164,10 @@ module Formulary super name, Formulary.path(full_name) end - def get_formula(spec, **) + def get_formula(spec, force_bottle: false, flags: [], **) contents = Utils::Bottles.formula_contents @bottle_filename, name: name formula = begin - Formulary.from_contents name, @bottle_filename, contents, spec + Formulary.from_contents(name, @bottle_filename, contents, spec, force_bottle: force_bottle, flags: flags) rescue FormulaUnreadableError => e opoo <<~EOS Unreadable formula in #{@bottle_filename}: @@ -205,7 +208,7 @@ module Formulary super formula, HOMEBREW_CACHE_FORMULA/File.basename(uri.path) end - def load_file + def load_file(flags:) if url =~ %r{githubusercontent.com/[\w-]+/[\w-]+/[a-f0-9]{40}(/Formula)?/([\w+-.@]+).rb} formula_name = Regexp.last_match(2) odeprecated "Installation of #{formula_name} from a GitHub commit URL", @@ -270,7 +273,7 @@ module Formulary [name, path] end - def get_formula(spec, alias_path: nil) + def get_formula(spec, alias_path: nil, force_bottle: false, flags: []) super rescue FormulaUnreadableError => e raise TapFormulaUnreadableError.new(tap, name, e.formula_error), "", e.backtrace @@ -280,7 +283,7 @@ module Formulary raise TapFormulaUnavailableError.new(tap, name), "", e.backtrace end - def load_file + def load_file(flags:) super rescue MethodDeprecatedError => e e.issues_url = tap.issues_url || tap.to_s @@ -308,10 +311,10 @@ module Formulary super name, path end - def klass + def klass(flags:) $stderr.puts "#{$PROGRAM_NAME} (#{self.class.name}): loading #{path}" if Homebrew.args.debug? - namespace = "FormulaNamespace#{Digest::MD5.hexdigest(contents)}" - Formulary.load_formula(name, path, contents, namespace) + namespace = "FormulaNamespace#{Digest::MD5.hexdigest(contents.to_s)}" + Formulary.load_formula(name, path, contents, namespace, flags: flags) end end @@ -322,7 +325,7 @@ module Formulary # * a formula pathname # * a formula URL # * a local bottle reference - def self.factory(ref, spec = :stable, alias_path: nil, from: nil) + def self.factory(ref, spec = :stable, alias_path: nil, from: nil, force_bottle: false, flags: []) raise ArgumentError, "Formulae must have a ref!" unless ref cache_key = "#{ref}-#{spec}-#{alias_path}-#{from}" @@ -331,7 +334,8 @@ module Formulary return cache[:formulary_factory][cache_key] end - formula = loader_for(ref, from: from).get_formula(spec, alias_path: alias_path) + formula = loader_for(ref, from: from).get_formula(spec, alias_path: alias_path, + force_bottle: force_bottle, flags: flags) if factory_cached? cache[:formulary_factory] ||= {} cache[:formulary_factory][cache_key] ||= formula @@ -345,14 +349,15 @@ module Formulary # The :alias_path option will be used if the formula is found not to be # installed, and discarded if it is installed because the alias_path used # to install the formula will be set instead. - def self.from_rack(rack, spec = nil, alias_path: nil) + def self.from_rack(rack, spec = nil, alias_path: nil, force_bottle: false, flags: []) kegs = rack.directory? ? rack.subdirs.map { |d| Keg.new(d) } : [] keg = kegs.find(&:linked?) || kegs.find(&:optlinked?) || kegs.max_by(&:version) if keg from_keg(keg, spec, alias_path: alias_path) else - factory(rack.basename.to_s, spec || :stable, alias_path: alias_path, from: :rack) + factory(rack.basename.to_s, spec || :stable, alias_path: alias_path, from: :rack, + force_bottle: force_bottle, flags: flags) end end @@ -365,19 +370,22 @@ module Formulary # Return a Formula instance for the given keg. # It will auto resolve formula's spec when requested spec is nil - def self.from_keg(keg, spec = nil, alias_path: nil) + def self.from_keg(keg, spec = nil, alias_path: nil, force_bottle: false, flags: []) tab = Tab.for_keg(keg) tap = tab.tap spec ||= tab.spec f = if tap.nil? - factory(keg.rack.basename.to_s, spec, alias_path: alias_path, from: :keg) + factory(keg.rack.basename.to_s, spec, alias_path: alias_path, from: :keg, + force_bottle: force_bottle, flags: flags) else begin - factory("#{tap}/#{keg.rack.basename}", spec, alias_path: alias_path, from: :keg) + factory("#{tap}/#{keg.rack.basename}", spec, alias_path: alias_path, from: :keg, + force_bottle: force_bottle, flags: flags) rescue FormulaUnavailableError # formula may be migrated to different tap. Try to search in core and all taps. - factory(keg.rack.basename.to_s, spec, alias_path: alias_path, from: :keg) + factory(keg.rack.basename.to_s, spec, alias_path: alias_path, from: :keg, + force_bottle: force_bottle, flags: flags) end end f.build = tab @@ -387,8 +395,9 @@ module Formulary end # Return a Formula instance directly from contents - def self.from_contents(name, path, contents, spec = :stable) - FormulaContentsLoader.new(name, path, contents).get_formula(spec) + def self.from_contents(name, path, contents, spec = :stable, alias_path: nil, force_bottle: false, flags: []) + FormulaContentsLoader.new(name, path, contents) + .get_formula(spec, alias_path: alias_path, force_bottle: force_bottle, flags: flags) end def self.to_rack(ref) diff --git a/Library/Homebrew/software_spec.rb b/Library/Homebrew/software_spec.rb index 130bbd456b..170b4e5d8f 100644 --- a/Library/Homebrew/software_spec.rb +++ b/Library/Homebrew/software_spec.rb @@ -28,14 +28,14 @@ class SoftwareSpec :cached_download, :clear_cache, :checksum, :mirrors, :specs, :using, :version, :mirror, :downloader, *Checksum::TYPES - def initialize + def initialize(flags: []) @resource = Resource.new @resources = {} @dependency_collector = DependencyCollector.new @bottle_specification = BottleSpecification.new @patches = [] @options = Options.new - @flags = Homebrew.args.flags_only + @flags = flags @deprecated_flags = [] @deprecated_options = [] @build = BuildOptions.new(Options.create(@flags), options) @@ -87,7 +87,7 @@ class SoftwareSpec def bottled? bottle_specification.tag?(Utils::Bottles.tag) && \ - (bottle_specification.compatible_cellar? || Homebrew.args.force_bottle?) + (bottle_specification.compatible_cellar? || owner.force_bottle) end def bottle(disable_type = nil, disable_reason = nil, &block) @@ -234,7 +234,7 @@ class SoftwareSpec end class HeadSoftwareSpec < SoftwareSpec - def initialize + def initialize(flags: []) super @resource.version = Version.create("HEAD") end diff --git a/Library/Homebrew/test/formulary_spec.rb b/Library/Homebrew/test/formulary_spec.rb index 5458fc6e02..bbd6aca88a 100644 --- a/Library/Homebrew/test/formulary_spec.rb +++ b/Library/Homebrew/test/formulary_spec.rb @@ -16,7 +16,7 @@ describe Formulary do bottle do cellar :any_skip_relocation root_url "file://#{bottle_dir}" - sha256 "d48bbbe583dcfbfa608579724fc6f0328b3cd316935c6ea22f134610aaf2952f" => :#{Utils::Bottles.tag} + sha256 "8f9aecd233463da6a4ea55f5f88fc5841718c013f3e2a7941350d6130f1dc149" => :#{Utils::Bottles.tag} end def install diff --git a/Library/Homebrew/test/rubocop_spec.rb b/Library/Homebrew/test/rubocop_spec.rb index 533df81076..a5750a430c 100644 --- a/Library/Homebrew/test/rubocop_spec.rb +++ b/Library/Homebrew/test/rubocop_spec.rb @@ -13,7 +13,8 @@ describe "RuboCop" do end it "loads all Formula cops without errors" do - _, _, status = Open3.capture3("rubocop", TEST_FIXTURE_DIR/"testball.rb") + stdout, _, status = Open3.capture3("rubocop", TEST_FIXTURE_DIR/"testball.rb") + expect(stdout).to include("no offenses detected") expect(status).to be_a_success end end diff --git a/Library/Homebrew/test/support/fixtures/bottles/testball_bottle-0.1.yosemite.bottle.tar.gz b/Library/Homebrew/test/support/fixtures/bottles/testball_bottle-0.1.yosemite.bottle.tar.gz index 62ea6c264b..ee5eb8b4b4 100644 Binary files a/Library/Homebrew/test/support/fixtures/bottles/testball_bottle-0.1.yosemite.bottle.tar.gz and b/Library/Homebrew/test/support/fixtures/bottles/testball_bottle-0.1.yosemite.bottle.tar.gz differ diff --git a/Library/Homebrew/test/support/fixtures/failball.rb b/Library/Homebrew/test/support/fixtures/failball.rb index 439645df26..5ba1692eff 100644 --- a/Library/Homebrew/test/support/fixtures/failball.rb +++ b/Library/Homebrew/test/support/fixtures/failball.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true class Failball < Formula - def initialize(name = "failball", path = Pathname.new(__FILE__).expand_path, spec = :stable, alias_path: nil) + def initialize(name = "failball", path = Pathname.new(__FILE__).expand_path, spec = :stable, + alias_path: nil, force_bottle: false) self.class.instance_eval do stable.url "file://#{TEST_FIXTURE_DIR}/tarballs/testball-0.1.tbz" stable.sha256 TESTBALL_SHA256 diff --git a/Library/Homebrew/test/support/fixtures/testball.rb b/Library/Homebrew/test/support/fixtures/testball.rb index c2d4aa2bbc..27ff8dafc0 100644 --- a/Library/Homebrew/test/support/fixtures/testball.rb +++ b/Library/Homebrew/test/support/fixtures/testball.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true class Testball < Formula - def initialize(name = "testball", path = Pathname.new(__FILE__).expand_path, spec = :stable, alias_path: nil) + def initialize(name = "testball", path = Pathname.new(__FILE__).expand_path, spec = :stable, + alias_path: nil, force_bottle: false) self.class.instance_eval do stable.url "file://#{TEST_FIXTURE_DIR}/tarballs/testball-0.1.tbz" stable.sha256 TESTBALL_SHA256 diff --git a/Library/Homebrew/test/support/fixtures/testball_bottle.rb b/Library/Homebrew/test/support/fixtures/testball_bottle.rb index f6268ed4fc..76d8d7e7c0 100644 --- a/Library/Homebrew/test/support/fixtures/testball_bottle.rb +++ b/Library/Homebrew/test/support/fixtures/testball_bottle.rb @@ -1,14 +1,15 @@ # frozen_string_literal: true class TestballBottle < Formula - def initialize(name = "testball_bottle", path = Pathname.new(__FILE__).expand_path, spec = :stable, alias_path: nil) + def initialize(name = "testball_bottle", path = Pathname.new(__FILE__).expand_path, spec = :stable, + alias_path: nil, force_bottle: false) self.class.instance_eval do stable.url "file://#{TEST_FIXTURE_DIR}/tarballs/testball-0.1.tbz" stable.sha256 TESTBALL_SHA256 stable.bottle do cellar :any_skip_relocation root_url "file://#{TEST_FIXTURE_DIR}/bottles" - sha256 "d48bbbe583dcfbfa608579724fc6f0328b3cd316935c6ea22f134610aaf2952f" => Utils::Bottles.tag + sha256 "8f9aecd233463da6a4ea55f5f88fc5841718c013f3e2a7941350d6130f1dc149" => Utils::Bottles.tag end cxxstdlib_check :skip end