mirror of
https://github.com/Homebrew/brew.git
synced 2025-07-14 16:09:03 +08:00
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.
This commit is contained in:
parent
b66010605d
commit
b70b5429d0
@ -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
|
||||
|
@ -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)
|
||||
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
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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?
|
||||
|
@ -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
|
||||
|
@ -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) }
|
||||
|
11
docs/Building-Against-Non-Homebrew-Dependencies.md
Normal file
11
docs/Building-Against-Non-Homebrew-Dependencies.md
Normal file
@ -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.
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user