From b70b5429d09d13526ccc08c67fd6a6373b471409 Mon Sep 17 00:00:00 2001 From: Mike McQuaid Date: Sun, 14 Jan 2018 13:27:43 +0000 Subject: [PATCH] Deprecate default_formula Requirement DSL This has been a nightmare in terms of the complexity to our dependency system and the whack-a-mole required on bugs. If a Requirement resolves to a Formula it should just use `depends_on "formula"` instead. This matches the effective behaviour all users of bottles (the vast majority of users and installs) and what we're doing in Homebrew/homebrew-core. --- Library/Homebrew/build.rb | 3 - Library/Homebrew/cmd/deps.rb | 21 +------ Library/Homebrew/cmd/uses.rb | 4 +- Library/Homebrew/formula.rb | 8 +-- Library/Homebrew/formula_installer.rb | 18 ------ Library/Homebrew/os/mac/linkage_checker.rb | 3 +- Library/Homebrew/requirement.rb | 39 ++---------- .../Homebrew/test/formula_installer_spec.rb | 60 ------------------- Library/Homebrew/test/requirement_spec.rb | 55 ----------------- ...lding-Against-Non-Homebrew-Dependencies.md | 11 ++++ docs/README.md | 1 + 11 files changed, 24 insertions(+), 199 deletions(-) create mode 100644 docs/Building-Against-Non-Homebrew-Dependencies.md diff --git a/Library/Homebrew/build.rb b/Library/Homebrew/build.rb index 836b360daa..d61c3672a1 100644 --- a/Library/Homebrew/build.rb +++ b/Library/Homebrew/build.rb @@ -48,9 +48,6 @@ class Build Requirement.prune elsif req.build? && dependent != formula Requirement.prune - elsif req.satisfied? && (dep = req.to_dependency) && dep.installed? - deps << dep - Requirement.prune end end end diff --git a/Library/Homebrew/cmd/deps.rb b/Library/Homebrew/cmd/deps.rb index ae758e1433..0627e84bdc 100644 --- a/Library/Homebrew/cmd/deps.rb +++ b/Library/Homebrew/cmd/deps.rb @@ -94,26 +94,14 @@ module Homebrew if ARGV.include?("--include-requirements") deps else - deps.map do |dep| - if dep.is_a? Dependency - dep - elsif dep.default_formula? - dep.to_dependency - end - end.compact + deps.select { |dep| dep.is_a? Dependency } end end def dep_display_name(dep) str = if dep.is_a? Requirement if ARGV.include?("--include-requirements") - if dep.default_formula? - ":#{dep.display_s} (#{dep_display_name(dep.to_dependency)})" - else - ":#{dep.display_s}" - end - elsif dep.default_formula? - dep_display_name(dep.to_dependency) + ":#{dep.display_s}" else # This shouldn't happen, but we'll put something here to help debugging "::#{dep.name}" @@ -207,7 +195,7 @@ module Homebrew max = dependables.length - 1 @dep_stack.push f.name dependables.each_with_index do |dep, i| - next if !ARGV.include?("--include-requirements") && dep.is_a?(Requirement) && !dep.default_formula? + next if !ARGV.include?("--include-requirements") && dep.is_a?(Requirement) tree_lines = if i == max "└──" else @@ -223,9 +211,6 @@ module Homebrew else "│ " end - if dep.is_a?(Requirement) && dep.default_formula? - recursive_deps_tree(Formulary.factory(dep.to_dependency.name), prefix + prefix_addition, true) - end if dep.is_a? Dependency recursive_deps_tree(Formulary.factory(dep.name), prefix + prefix_addition, true) end diff --git a/Library/Homebrew/cmd/uses.rb b/Library/Homebrew/cmd/uses.rb index 1688899f9d..d5c9210f6b 100644 --- a/Library/Homebrew/cmd/uses.rb +++ b/Library/Homebrew/cmd/uses.rb @@ -113,9 +113,7 @@ module Homebrew end end - reqs.any? do |req| - req.name == ff.name || [ff.name, ff.full_name].include?(req.default_formula) - end + reqs.any? { |req| req.name == ff.name } rescue FormulaUnavailableError # Silently ignore this case as we don't care about things used in # taps that aren't currently tapped. diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 8e4f80260b..eb499d958e 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1486,15 +1486,10 @@ class Formula # Returns a list of Dependency objects that are required at runtime. # @private def runtime_dependencies - runtime_dependencies = recursive_dependencies do |_, dependency| + recursive_dependencies do |_, dependency| Dependency.prune if dependency.build? Dependency.prune if !dependency.required? && build.without?(dependency) end - runtime_requirement_deps = recursive_requirements do |_, requirement| - Requirement.prune if requirement.build? - Requirement.prune if !requirement.required? && build.without?(requirement) - end.map(&:to_dependency).compact - runtime_dependencies + runtime_requirement_deps end # Returns a list of formulae depended on by this formula that aren't @@ -1552,7 +1547,6 @@ class Formula hsh["requirements"] = requirements.map do |req| { "name" => req.name, - "default_formula" => req.default_formula, "cask" => req.cask, "download" => req.download, } diff --git a/Library/Homebrew/formula_installer.rb b/Library/Homebrew/formula_installer.rb index 48109310ee..6d4fb07bd0 100644 --- a/Library/Homebrew/formula_installer.rb +++ b/Library/Homebrew/formula_installer.rb @@ -415,16 +415,6 @@ class FormulaInstaller raise UnsatisfiedRequirements, fatals end - def install_requirement_formula?(req_dependency, req, dependent, install_bottle_for_dependent) - return false unless req_dependency - return false if req.build? && dependent.installed? - return true unless req.satisfied? - return false if req.run? - return true if build_bottle? - return true if req.satisfied_by_formula? - install_bottle_for_dependent - end - def runtime_requirements(formula) runtime_deps = formula.runtime_dependencies.map(&:to_formula) recursive_requirements = formula.recursive_requirements do |dependent, _| @@ -443,17 +433,9 @@ class FormulaInstaller f.recursive_requirements do |dependent, req| build = effective_build_options_for(dependent) install_bottle_for_dependent = install_bottle_for?(dependent, build) - use_default_formula = install_bottle_for_dependent || build_bottle? - req_dependency = req.to_dependency(use_default_formula: use_default_formula) if (req.optional? || req.recommended?) && build.without?(req) Requirement.prune - elsif req.build? && use_default_formula && req_dependency&.installed? - Requirement.prune - elsif install_requirement_formula?(req_dependency, req, dependent, install_bottle_for_dependent) - deps.unshift(req_dependency) - formulae.unshift(req_dependency.to_formula) - Requirement.prune elsif req.satisfied? Requirement.prune elsif !runtime_requirements.include?(req) && install_bottle_for_dependent diff --git a/Library/Homebrew/os/mac/linkage_checker.rb b/Library/Homebrew/os/mac/linkage_checker.rb index 941a5a847a..e1709d1b45 100644 --- a/Library/Homebrew/os/mac/linkage_checker.rb +++ b/Library/Homebrew/os/mac/linkage_checker.rb @@ -62,8 +62,7 @@ class LinkageChecker formula.build.without?(dep) end declared_deps = formula.deps.reject { |dep| filter_out.call(dep) }.map(&:name) - declared_requirement_deps = formula.requirements.reject { |req| filter_out.call(req) }.map(&:default_formula).compact - declared_dep_names = (declared_deps + declared_requirement_deps).map { |dep| dep.split("/").last } + declared_dep_names = declared_deps.map { |dep| dep.split("/").last } undeclared_deps = @brewed_dylibs.keys.reject do |full_name| name = full_name.split("/").last next true if name == formula.name diff --git a/Library/Homebrew/requirement.rb b/Library/Homebrew/requirement.rb index 91f84157e7..e2b1afbb92 100644 --- a/Library/Homebrew/requirement.rb +++ b/Library/Homebrew/requirement.rb @@ -9,13 +9,11 @@ require "build_environment" class Requirement include Dependable - attr_reader :tags, :name, :cask, :download, :default_formula + attr_reader :tags, :name, :cask, :download def initialize(tags = []) - @default_formula = self.class.default_formula @cask ||= self.class.cask @download ||= self.class.download - @formula = nil tags.each do |tag| next unless tag.is_a? Hash @cask ||= tag[:cask] @@ -56,12 +54,6 @@ class Requirement result = self.class.satisfy.yielder { |p| instance_eval(&p) } @satisfied_result = result return false unless result - - if parent = satisfied_result_parent - parent.to_s =~ %r{(#{Regexp.escape(HOMEBREW_CELLAR)}|#{Regexp.escape(HOMEBREW_PREFIX)}/opt)/([\w+-.@]+)} - @formula = Regexp.last_match(2) - end - true end @@ -71,10 +63,6 @@ class Requirement self.class.fatal || false end - def default_formula? - self.class.default_formula || false - end - def satisfied_result_parent return unless @satisfied_result.is_a?(Pathname) parent = @satisfied_result.resolved_path.parent @@ -124,24 +112,6 @@ class Requirement "#<#{self.class.name}: #{name.inspect} #{tags.inspect}>" end - def formula - @formula || self.class.default_formula - end - - def satisfied_by_formula? - !@formula.nil? - end - - def to_dependency(use_default_formula: false) - if use_default_formula && default_formula? - Dependency.new(self.class.default_formula, tags, method(:modify_build_environment), name) - elsif formula =~ HOMEBREW_TAP_FORMULA_REGEX - TapDependency.new(formula, tags, method(:modify_build_environment), name) - elsif formula - Dependency.new(formula, tags, method(:modify_build_environment), name) - end - end - def display_s name end @@ -167,8 +137,11 @@ class Requirement include BuildEnvironment::DSL attr_reader :env_proc, :build - attr_rw :fatal, :default_formula - attr_rw :cask, :download + attr_rw :fatal, :cask, :download + + def default_formula(val = nil) + # odeprecated "Requirement.default_formula" + end def satisfy(options = nil, &block) return @satisfied if options.nil? && !block_given? diff --git a/Library/Homebrew/test/formula_installer_spec.rb b/Library/Homebrew/test/formula_installer_spec.rb index d3710a4cbc..93f23b18ff 100644 --- a/Library/Homebrew/test/formula_installer_spec.rb +++ b/Library/Homebrew/test/formula_installer_spec.rb @@ -132,64 +132,4 @@ describe FormulaInstaller do fi.check_install_sanity }.to raise_error(CannotInstallFormulaError) end - - describe "#install_requirement_formula?", :needs_compat do - before do - @requirement = Python3Requirement.new - @requirement_dependency = @requirement.to_dependency - @install_bottle_for_dependent = false - allow(@requirement).to receive(:satisfied?).and_return(satisfied?) - allow(@requirement).to receive(:satisfied_by_formula?).and_return(satisfied_by_formula?) - allow(@requirement).to receive(:build?).and_return(build?) - @dependent = formula do - url "foo" - version "0.1" - depends_on :python3 - end - allow(@dependent).to receive(:installed?).and_return(installed?) - @fi = FormulaInstaller.new(@dependent) - end - - subject { @fi.install_requirement_formula?(@requirement_dependency, @requirement, @dependent, @install_bottle_for_dependent) } - - context "it returns false when requirement is satisfied" do - let(:satisfied?) { true } - let(:satisfied_by_formula?) { false } - let(:build?) { false } - let(:installed?) { false } - it { is_expected.to be false } - end - - context "it returns false when requirement is satisfied but default formula is installed" do - let(:satisfied?) { true } - let(:satisfied_by_formula?) { false } - let(:build?) { false } - let(:installed?) { false } - it { is_expected.to be false } - end - - context "it returns false when requirement is :build and dependent is installed" do - let(:satisfied?) { false } - let(:satisfied_by_formula?) { false } - let(:build?) { true } - let(:installed?) { true } - it { is_expected.to be false } - end - - context "it returns true when requirement isn't satisfied" do - let(:satisfied?) { false } - let(:satisfied_by_formula?) { false } - let(:build?) { false } - let(:installed?) { false } - it { is_expected.to be true } - end - - context "it returns true when requirement is satisfied by a formula" do - let(:satisfied?) { true } - let(:satisfied_by_formula?) { true } - let(:build?) { false } - let(:installed?) { false } - it { is_expected.to be true } - end - end end diff --git a/Library/Homebrew/test/requirement_spec.rb b/Library/Homebrew/test/requirement_spec.rb index 2d0d86c862..1f3d872f31 100644 --- a/Library/Homebrew/test/requirement_spec.rb +++ b/Library/Homebrew/test/requirement_spec.rb @@ -2,7 +2,6 @@ require "extend/ENV" require "requirement" describe Requirement do - alias_matcher :have_a_default_formula, :be_a_default_formula alias_matcher :be_a_build_requirement, :be_a_build subject { klass.new } @@ -173,60 +172,6 @@ describe Requirement do its(:option_names) { are_expected.to eq(["foo"]) } end - describe "#default_formula?" do - context "#default_formula specified" do - let(:klass) do - Class.new(described_class) do - default_formula "foo" - end - end - - it { is_expected.to have_a_default_formula } - end - - context "#default_formula omitted" do - it { is_expected.not_to have_a_default_formula } - end - end - - describe "#to_dependency" do - let(:klass) do - Class.new(described_class) do - default_formula "foo" - end - end - - it "returns a Dependency for its default Formula" do - expect(subject.to_dependency).to eq(Dependency.new("foo")) - end - - context "#modify_build_environment" do - context "with error" do - let(:klass) do - Class.new(described_class) do - class ModifyBuildEnvironmentError < StandardError; end - - default_formula "foo" - - satisfy do - true - end - - env do - raise ModifyBuildEnvironmentError - end - end - end - - it "raises an error" do - expect { - subject.to_dependency.modify_build_environment - }.to raise_error(klass.const_get(:ModifyBuildEnvironmentError)) - end - end - end - end - describe "#modify_build_environment" do context "without env proc" do let(:klass) { Class.new(described_class) } diff --git a/docs/Building-Against-Non-Homebrew-Dependencies.md b/docs/Building-Against-Non-Homebrew-Dependencies.md new file mode 100644 index 0000000000..4e85757a1b --- /dev/null +++ b/docs/Building-Against-Non-Homebrew-Dependencies.md @@ -0,0 +1,11 @@ +# Building Against Non-Homebrew Dependencies + +## History + +Originally Homebrew was a build-from-source package manager and all user environment variables and non-Homebrew-installed software were available to builds. Since then Homebrew added `Requirement`s to specify dependencies on non-Homebrew software (such as those provided by `brew cask` like X11/XQuartz), the `superenv` build system to strip out unspecified dependencies, environment filtering to stop the user environment leaking into Homebrew builds and `default_formula` to specify that a `Requirement` can be satisifed by a particular formula. + +As Homebrew became primarily a binary package manager, most users were fulfilling `Requirement`s with the `default_formula`, not with arbitrary alternatives. To improve quality and reduce variation, Homebrew now exclusively supports using the default formula, as an ordinary dependency, and no longer supports using arbitrary alternatives. + +## Today + +If you wish to build against custom non-Homebrew dependencies that are provided by Homebrew (e.g. a non-Homebrew, non-macOS `ruby`) then you must [create and maintain your own tap](How-to-Create-and-Maintain-a-Tap.md) as these formulae will not be accepted in Homebrew/homebrew-core. Once you have done that you can specify `env :std` in the formula which will allow a e.g. `which ruby` to access your existing `PATH` variable and allow compilation to link against this Ruby. diff --git a/docs/README.md b/docs/README.md index f069ae5625..fe227291bb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -33,6 +33,7 @@ - [Python for Formula Authors](Python-for-Formula-Authors.md) - [Migrating A Formula To A Tap](Migrating-A-Formula-To-A-Tap.md) - [Rename A Formula](Rename-A-Formula.md) +- [Building Against Non-Homebrew Dependencies](Building-Against-Non-Homebrew-Dependencies.md) - [How To Create (And Maintain) A Tap](How-to-Create-and-Maintain-a-Tap.md) - [Brew Test Bot](Brew-Test-Bot.md) - [Prose Style Guidelines](Prose-Style-Guidelines.md)