Merge pull request #13753 from Bo98/iced-formulae

Freeze formula definition once first instance is created
This commit is contained in:
Mike McQuaid 2022-09-01 14:15:25 +01:00 committed by GitHub
commit f788277b69
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 514 additions and 276 deletions

View File

@ -33,9 +33,17 @@ class BuildEnvironment
module DSL module DSL
extend T::Sig extend T::Sig
# Initialise @env for each class which may use this DSL (e.g. each formula subclass).
# `env` may never be called, and it needs to be initialised before the class is frozen.
def inherited(child)
super
child.instance_eval do
@env = BuildEnvironment.new
end
end
sig { params(settings: Symbol).returns(BuildEnvironment) } sig { params(settings: Symbol).returns(BuildEnvironment) }
def env(*settings) def env(*settings)
@env ||= BuildEnvironment.new
@env.merge(settings) @env.merge(settings)
end end
end end

View File

@ -1,8 +1,17 @@
# typed: true # typed: true
# frozen_string_literal: true # frozen_string_literal: true
require "requirement"
# An adapter for casks to provide dependency information in a formula-like interface. # An adapter for casks to provide dependency information in a formula-like interface.
class CaskDependent class CaskDependent
# Defines a dependency on another cask
class Requirement < ::Requirement
satisfy(build_env: false) do
Cask::CaskLoader.load(cask).installed?
end
end
attr_reader :cask attr_reader :cask
def initialize(cask) def initialize(cask)
@ -33,11 +42,21 @@ class CaskDependent
dsl_reqs = @cask.depends_on dsl_reqs = @cask.depends_on
dsl_reqs.arch&.each do |arch| dsl_reqs.arch&.each do |arch|
requirements << ArchRequirement.new([:x86_64]) if arch[:bits] == 64 arch = if arch[:bits] == 64
requirements << ArchRequirement.new([arch[:type]]) if arch[:type] == :intel
:x86_64
else
:"#{arch[:type]}64"
end
elsif arch[:type] == :intel && arch[:bits] == 32
:i386
else
arch[:type]
end
requirements << ArchRequirement.new([arch])
end end
dsl_reqs.cask.each do |cask_ref| dsl_reqs.cask.each do |cask_ref|
requirements << Requirement.new([{ cask: cask_ref }]) requirements << CaskDependent::Requirement.new([{ cask: cask_ref }])
end end
requirements << dsl_reqs.macos if dsl_reqs.macos requirements << dsl_reqs.macos if dsl_reqs.macos

View File

@ -2,7 +2,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require "delegate" require "delegate"
require "cask_dependent"
# A collection of dependencies. # A collection of dependencies.
# #

View File

@ -26,18 +26,25 @@ class DependencyCollector
sig { void } sig { void }
def initialize def initialize
# Ensure this is synced with `initialize_dup` and `freeze` (excluding simple objects like integers and booleans)
@deps = Dependencies.new @deps = Dependencies.new
@requirements = Requirements.new @requirements = Requirements.new
init_global_dep_tree_if_needed! init_global_dep_tree_if_needed!
end end
def initialize_copy(other) def initialize_dup(other)
super super
@deps = @deps.dup @deps = @deps.dup
@requirements = @requirements.dup @requirements = @requirements.dup
end end
def freeze
@deps.freeze
@requirements.freeze
super
end
def add(spec) def add(spec)
case dep = fetch(spec) case dep = fetch(spec)
when Dependency when Dependency

View File

@ -5,11 +5,17 @@ class Module
def attr_rw(*attrs) def attr_rw(*attrs)
attrs.each do |attr| attrs.each do |attr|
module_eval <<-EOS, __FILE__, __LINE__+1 module_eval <<-EOS, __FILE__, __LINE__+1
def #{attr}(val=nil) # def prefix(val=nil) def #{attr}(val=nil) # def prefix(val=nil)
@#{attr} ||= nil # @prefix ||= nil if val.nil? # if val.nil?
return @#{attr} if val.nil? # return @prefix if val.nil? if instance_variable_defined?(:@#{attr}) # if instance_variable_defined?(:@prefix)
@#{attr} = val # @prefix = val return @#{attr} # return @prefix
end # end else # else
return nil # return nil
end # end
end # end
#
@#{attr} = val # @prefix = val
end # end
EOS EOS
end end
end end

View File

@ -181,6 +181,14 @@ class Formula
# @private # @private
def initialize(name, path, spec, alias_path: nil, force_bottle: false) def initialize(name, path, spec, alias_path: nil, force_bottle: false)
# Only allow instances of subclasses. The base class does not hold any spec information (URLs etc).
raise "Do not call `Formula.new' directly without a subclass." unless self.class < Formula
# Stop any subsequent modification of a formula's definition.
# Changes do not propagate to existing instances of formulae.
# Now that we have an instance, it's too late to make any changes to the class-level definition.
self.class.freeze
@name = name @name = name
@path = path @path = path
@alias_path = alias_path @alias_path = alias_path
@ -2692,9 +2700,26 @@ class Formula
# The methods below define the formula DSL. # The methods below define the formula DSL.
class << self class << self
extend Predicable extend Predicable
extend T::Sig
include BuildEnvironment::DSL include BuildEnvironment::DSL
include OnSystem::MacOSAndLinux include OnSystem::MacOSAndLinux
# Initialise instance variables for each subclass. These need to be initialised before the class is frozen,
# and some DSL may never be called so it can't be done lazily.
def inherited(child)
super
child.instance_eval do
# Ensure this is synced with `freeze`
@stable = SoftwareSpec.new(flags: build_flags)
@head = HeadSoftwareSpec.new(flags: build_flags)
@livecheck = Livecheck.new(self)
@conflicts = []
@skip_clean_paths = Set.new
@link_overwrite_paths = Set.new
@allowed_missing_libraries = Set.new
end
end
def method_added(method) def method_added(method)
super super
@ -2706,6 +2731,16 @@ class Formula
end end
end end
def freeze
specs.each(&:freeze)
@livecheck.freeze
@conflicts.freeze
@skip_clean_paths.freeze
@link_overwrite_paths.freeze
@allowed_missing_libraries.freeze
super
end
# Whether this formula contains OS/arch-specific blocks # Whether this formula contains OS/arch-specific blocks
# (e.g. `on_macos`, `on_arm`, `on_monterey :or_older`, `on_system :linux, macos: :big_sur_or_newer`). # (e.g. `on_macos`, `on_arm`, `on_monterey :or_older`, `on_system :linux, macos: :big_sur_or_newer`).
# @private # @private
@ -2786,6 +2821,18 @@ class Formula
# @private # @private
attr_reader :plist_manual attr_reader :plist_manual
# @private
attr_reader :conflicts
# @private
attr_reader :skip_clean_paths
# @private
attr_reader :link_overwrite_paths
# @private
attr_reader :allowed_missing_libraries
# If `pour_bottle?` returns `false` the user-visible reason to display for # If `pour_bottle?` returns `false` the user-visible reason to display for
# why they cannot use the bottle. # why they cannot use the bottle.
# @private # @private
@ -2816,7 +2863,7 @@ class Formula
# A list of the {.stable} and {.head} {SoftwareSpec}s. # A list of the {.stable} and {.head} {SoftwareSpec}s.
# @private # @private
def specs def specs
@specs ||= [stable, head].freeze [stable, head].freeze
end end
# @!attribute [w] url # @!attribute [w] url
@ -2900,8 +2947,9 @@ class Formula
# #
# Formulae which should not be bottled should be tagged with: # Formulae which should not be bottled should be tagged with:
# <pre>bottle :disable, "reasons"</pre> # <pre>bottle :disable, "reasons"</pre>
def bottle(*args, &block) sig { params(block: T.proc.bind(BottleSpecification).void).void }
stable.bottle(*args, &block) def bottle(&block)
stable.bottle(&block)
end end
# @private # @private
@ -2932,7 +2980,6 @@ class Formula
# depends_on "libffi" # depends_on "libffi"
# end</pre> # end</pre>
def stable(&block) def stable(&block)
@stable ||= SoftwareSpec.new(flags: build_flags)
return @stable unless block return @stable unless block
@stable.instance_eval(&block) @stable.instance_eval(&block)
@ -2951,7 +2998,6 @@ class Formula
# or (if autodetect fails): # or (if autodetect fails):
# <pre>head "https://hg.is.awesome.but.git.has.won.example.com/", using: :hg</pre> # <pre>head "https://hg.is.awesome.but.git.has.won.example.com/", using: :hg</pre>
def head(val = nil, specs = {}, &block) def head(val = nil, specs = {}, &block)
@head ||= HeadSoftwareSpec.new(flags: build_flags)
if block if block
@head.instance_eval(&block) @head.instance_eval(&block)
elsif val elsif val
@ -3102,11 +3148,6 @@ class Formula
@plist_manual = options[:manual] @plist_manual = options[:manual]
end end
# @private
def conflicts
@conflicts ||= []
end
# One or more formulae that conflict with this one and why. # One or more formulae that conflict with this one and why.
# <pre>conflicts_with "imagemagick", because: "both install `convert` binaries"</pre> # <pre>conflicts_with "imagemagick", because: "both install `convert` binaries"</pre>
def conflicts_with(*names) def conflicts_with(*names)
@ -3127,11 +3168,6 @@ class Formula
skip_clean_paths.merge(paths) skip_clean_paths.merge(paths)
end end
# @private
def skip_clean_paths
@skip_clean_paths ||= Set.new
end
# Software that will not be symlinked into the `brew --prefix` and will # Software that will not be symlinked into the `brew --prefix` and will
# only live in its Cellar. Other formulae can depend on it and Homebrew # only live in its Cellar. Other formulae can depend on it and Homebrew
# will add the necessary includes, libraries, and other paths while # will add the necessary includes, libraries, and other paths while
@ -3235,7 +3271,6 @@ class Formula
# regex /foo-(\d+(?:\.\d+)+)\.tar/ # regex /foo-(\d+(?:\.\d+)+)\.tar/
# end</pre> # end</pre>
def livecheck(&block) def livecheck(&block)
@livecheck ||= Livecheck.new(self)
return @livecheck unless block return @livecheck unless block
@livecheckable = true @livecheckable = true
@ -3391,11 +3426,6 @@ class Formula
link_overwrite_paths.merge(paths) link_overwrite_paths.merge(paths)
end end
# @private
def link_overwrite_paths
@link_overwrite_paths ||= Set.new
end
# Permit links to certain libraries that don't exist. Available on Linux only. # Permit links to certain libraries that don't exist. Available on Linux only.
def ignore_missing_libraries(*libs) def ignore_missing_libraries(*libs)
unless Homebrew::SimulateSystem.simulating_or_running_on_linux? unless Homebrew::SimulateSystem.simulating_or_running_on_linux?
@ -3409,11 +3439,6 @@ class Formula
allowed_missing_libraries.merge(libraries) allowed_missing_libraries.merge(libraries)
end end
# @private
def allowed_missing_libraries
@allowed_missing_libraries ||= Set.new
end
end end
end end

View File

@ -82,9 +82,20 @@ class Options
end end
def initialize(*args) def initialize(*args)
# Ensure this is synced with `initialize_dup` and `freeze` (excluding simple objects like integers and booleans)
@options = Set.new(*args) @options = Set.new(*args)
end end
def initialize_dup(other)
super
@options = @options.dup
end
def freeze
@options.dup
super
end
def each(*args, &block) def each(*args, &block)
@options.each(*args, &block) @options.each(*args, &block)
end end

View File

@ -20,6 +20,10 @@ class Requirement
attr_reader :tags, :name, :cask, :download attr_reader :tags, :name, :cask, :download
def initialize(tags = []) def initialize(tags = [])
# Only allow instances of subclasses. This base class enforces no constraints on its own.
# Individual subclasses use the `satisfy` DSL to define those constraints.
raise "Do not call `Requirement.new' directly without a subclass." unless self.class < Requirement
@cask = self.class.cask @cask = self.class.cask
@download = self.class.download @download = self.class.download
tags.each do |tag| tags.each do |tag|
@ -141,9 +145,9 @@ class Requirement
private private
def infer_name def infer_name
klass = self.class.name || self.class.to_s klass = self.class.name
klass = klass.sub(/(Dependency|Requirement)$/, "") klass = klass&.sub(/(Dependency|Requirement)$/, "")
.sub(/^(\w+::)*/, "") &.sub(/^(\w+::)*/, "")
return klass.downcase if klass.present? return klass.downcase if klass.present?
return @cask if @cask.present? return @cask if @cask.present?

View File

@ -29,6 +29,7 @@ class Resource
attr_accessor :name attr_accessor :name
def initialize(name = nil, &block) def initialize(name = nil, &block)
# Ensure this is synced with `initialize_dup` and `freeze` (excluding simple objects like integers and booleans)
@name = name @name = name
@url = nil @url = nil
@version = nil @version = nil
@ -37,11 +38,35 @@ class Resource
@checksum = nil @checksum = nil
@using = nil @using = nil
@patches = [] @patches = []
@livecheck = nil @livecheck = Livecheck.new(self)
@livecheckable = false @livecheckable = false
instance_eval(&block) if block instance_eval(&block) if block
end end
def initialize_dup(other)
super
@name = @name.dup
@version = @version.dup
@mirrors = @mirrors.dup
@specs = @specs.dup
@checksum = @checksum.dup
@using = @using.dup
@patches = @patches.dup
@livecheck = @livecheck.dup
end
def freeze
@name.freeze
@version.freeze
@mirrors.freeze
@specs.freeze
@checksum.freeze
@using.freeze
@patches.freeze
@livecheck.freeze
super
end
def owner=(owner) def owner=(owner)
@owner = owner @owner = owner
patches.each { |p| p.owner = owner } patches.each { |p| p.owner = owner }
@ -185,7 +210,6 @@ class Resource
# regex /foo-(\d+(?:\.\d+)+)\.tar/ # regex /foo-(\d+(?:\.\d+)+)\.tar/
# end</pre> # end</pre>
def livecheck(&block) def livecheck(&block)
@livecheck ||= Livecheck.new(self) if block
return @livecheck unless block return @livecheck unless block
@livecheckable = true @livecheckable = true

View File

@ -36,6 +36,7 @@ class SoftwareSpec
def_delegators :@resource, :sha256 def_delegators :@resource, :sha256
def initialize(flags: []) def initialize(flags: [])
# Ensure this is synced with `initialize_dup` and `freeze` (excluding simple objects like integers and booleans)
@resource = Resource.new @resource = Resource.new
@resources = {} @resources = {}
@dependency_collector = DependencyCollector.new @dependency_collector = DependencyCollector.new
@ -50,9 +51,36 @@ class SoftwareSpec
@uses_from_macos_elements = [] @uses_from_macos_elements = []
end end
def initialize_copy(other) def initialize_dup(other)
super super
@resource = @resource.dup
@resources = @resources.dup
@dependency_collector = @dependency_collector.dup @dependency_collector = @dependency_collector.dup
@bottle_specification = @bottle_specification.dup
@patches = @patches.dup
@options = @options.dup
@flags = @flags.dup
@deprecated_flags = @deprecated_flags.dup
@deprecated_options = @deprecated_options.dup
@build = @build.dup
@compiler_failures = @compiler_failures.dup
@uses_from_macos_elements = @uses_from_macos_elements.dup
end
def freeze
@resource.freeze
@resources.freeze
@dependency_collector.freeze
@bottle_specification.freeze
@patches.freeze
@options.freeze
@flags.freeze
@deprecated_flags.freeze
@deprecated_options.freeze
@build.freeze
@compiler_failures.freeze
@uses_from_macos_elements.freeze
super
end end
def owner=(owner) def owner=(owner)

View File

@ -30,11 +30,16 @@ describe BuildEnvironment do
end end
describe BuildEnvironment::DSL do describe BuildEnvironment::DSL do
subject(:build_environment_dsl) { double.extend(described_class) } let(:build_environment_dsl) do
klass = described_class
Class.new do
extend(klass)
end
end
context "with a single argument" do context "with a single argument" do
before do subject do
build_environment_dsl.instance_eval do Class.new(build_environment_dsl) do
env :std env :std
end end
end end

View File

@ -7,15 +7,15 @@ require "formula"
describe Cleaner do describe Cleaner do
include FileUtils include FileUtils
subject(:cleaner) { described_class.new(f) }
let(:f) { formula("cleaner_test") { url "foo-1.0" } }
before do
f.prefix.mkpath
end
describe "#clean" do describe "#clean" do
subject(:cleaner) { described_class.new(f) }
let(:f) { formula("cleaner_test") { url "foo-1.0" } }
before do
f.prefix.mkpath
end
it "cleans files" do it "cleans files" do
f.bin.mkpath f.bin.mkpath
f.lib.mkpath f.lib.mkpath
@ -159,45 +159,54 @@ describe Cleaner do
end end
describe "::skip_clean" do describe "::skip_clean" do
def stub_formula_skip_clean(skip_paths)
formula("cleaner_test") do
url "foo-1.0"
skip_clean skip_paths
end
end
it "adds paths that should be skipped" do it "adds paths that should be skipped" do
f.class.skip_clean "bin" f = stub_formula_skip_clean("bin")
f.bin.mkpath f.bin.mkpath
cleaner.clean described_class.new(f).clean
expect(f.bin).to be_a_directory expect(f.bin).to be_a_directory
end end
it "also skips empty sub-directories under the added paths" do it "also skips empty sub-directories under the added paths" do
f.class.skip_clean "bin" f = stub_formula_skip_clean("bin")
subdir = f.bin/"subdir" subdir = f.bin/"subdir"
subdir.mkpath subdir.mkpath
cleaner.clean described_class.new(f).clean
expect(f.bin).to be_a_directory expect(f.bin).to be_a_directory
expect(subdir).to be_a_directory expect(subdir).to be_a_directory
end end
it "allows skipping broken symlinks" do it "allows skipping broken symlinks" do
f.class.skip_clean "symlink" f = stub_formula_skip_clean("symlink")
f.prefix.mkpath
symlink = f.prefix/"symlink" symlink = f.prefix/"symlink"
ln_s "target", symlink ln_s "target", symlink
cleaner.clean described_class.new(f).clean
expect(symlink).to be_a_symlink expect(symlink).to be_a_symlink
end end
it "allows skipping symlinks pointing to an empty directory" do it "allows skipping symlinks pointing to an empty directory" do
f.class.skip_clean "c" f = stub_formula_skip_clean("c")
dir = f.prefix/"b" dir = f.prefix/"b"
symlink = f.prefix/"c" symlink = f.prefix/"c"
dir.mkpath dir.mkpath
ln_s dir.basename, symlink ln_s dir.basename, symlink
cleaner.clean described_class.new(f).clean
expect(dir).not_to exist expect(dir).not_to exist
expect(symlink).to be_a_symlink expect(symlink).to be_a_symlink
@ -205,14 +214,14 @@ describe Cleaner do
end end
it "allows skipping symlinks whose target was pruned before" do it "allows skipping symlinks whose target was pruned before" do
f.class.skip_clean "a" f = stub_formula_skip_clean("a")
dir = f.prefix/"b" dir = f.prefix/"b"
symlink = f.prefix/"a" symlink = f.prefix/"a"
dir.mkpath dir.mkpath
ln_s dir.basename, symlink ln_s dir.basename, symlink
cleaner.clean described_class.new(f).clean
expect(dir).not_to exist expect(dir).not_to exist
expect(symlink).to be_a_symlink expect(symlink).to be_a_symlink
@ -220,37 +229,40 @@ describe Cleaner do
end end
it "allows skipping '.la' files" do it "allows skipping '.la' files" do
f = stub_formula_skip_clean(:la)
file = f.lib/"foo.la" file = f.lib/"foo.la"
f.class.skip_clean :la
f.lib.mkpath f.lib.mkpath
touch file touch file
cleaner.clean described_class.new(f).clean
expect(file).to exist expect(file).to exist
end end
it "allows skipping sub-directories" do it "allows skipping sub-directories" do
f = stub_formula_skip_clean("lib/subdir")
dir = f.lib/"subdir" dir = f.lib/"subdir"
f.class.skip_clean "lib/subdir"
dir.mkpath dir.mkpath
cleaner.clean described_class.new(f).clean
expect(dir).to be_a_directory expect(dir).to be_a_directory
end end
it "allows skipping paths relative to prefix" do it "allows skipping paths relative to prefix" do
f = stub_formula_skip_clean("bin/a")
dir1 = f.bin/"a" dir1 = f.bin/"a"
dir2 = f.lib/"bin/a" dir2 = f.lib/"bin/a"
f.class.skip_clean "bin/a"
dir1.mkpath dir1.mkpath
dir2.mkpath dir2.mkpath
cleaner.clean described_class.new(f).clean
expect(dir1).to exist expect(dir1).to exist
expect(dir2).not_to exist expect(dir2).not_to exist

View File

@ -832,15 +832,15 @@ describe Formula do
specify "service complicated" do specify "service complicated" do
f = formula do f = formula do
url "https://brew.sh/test-1.0.tbz" url "https://brew.sh/test-1.0.tbz"
end
f.class.service do service do
run [opt_bin/"beanstalkd"] run [opt_bin/"beanstalkd"]
run_type :immediate run_type :immediate
error_log_path var/"log/beanstalkd.error.log" error_log_path var/"log/beanstalkd.error.log"
log_path var/"log/beanstalkd.log" log_path var/"log/beanstalkd.log"
working_dir var working_dir var
keep_alive true keep_alive true
end
end end
expect(f.service).not_to be_nil expect(f.service).not_to be_nil
end end

View File

@ -98,11 +98,15 @@ describe Homebrew::Livecheck do
describe "::livecheck_url_to_string" do describe "::livecheck_url_to_string" do
let(:f_livecheck_url) do let(:f_livecheck_url) do
homepage_url_s = homepage_url
stable_url_s = stable_url
head_url_s = head_url
formula("test_livecheck_url") do formula("test_livecheck_url") do
desc "Test Livecheck URL formula" desc "Test Livecheck URL formula"
homepage "https://brew.sh" homepage homepage_url_s
url "https://brew.sh/test-0.0.1.tgz" url stable_url_s
head "https://github.com/Homebrew/brew.git" head head_url_s
end end
end end
@ -120,25 +124,15 @@ describe Homebrew::Livecheck do
end end
it "returns a URL string when given a livecheck_url string" do it "returns a URL string when given a livecheck_url string" do
f_livecheck_url.livecheck.url(livecheck_url)
expect(livecheck.livecheck_url_to_string(livecheck_url, f_livecheck_url)).to eq(livecheck_url) expect(livecheck.livecheck_url_to_string(livecheck_url, f_livecheck_url)).to eq(livecheck_url)
end end
it "returns a URL symbol when given a valid livecheck_url symbol" do it "returns a URL symbol when given a valid livecheck_url symbol" do
f_livecheck_url.livecheck.url(:head) expect(livecheck.livecheck_url_to_string(:head, f_livecheck_url)).to eq(head_url)
expect(livecheck.livecheck_url_to_string(head_url, f_livecheck_url)).to eq(head_url) expect(livecheck.livecheck_url_to_string(:homepage, f_livecheck_url)).to eq(homepage_url)
expect(livecheck.livecheck_url_to_string(:homepage, c_livecheck_url)).to eq(homepage_url)
f_livecheck_url.livecheck.url(:homepage) expect(livecheck.livecheck_url_to_string(:stable, f_livecheck_url)).to eq(stable_url)
expect(livecheck.livecheck_url_to_string(homepage_url, f_livecheck_url)).to eq(homepage_url) expect(livecheck.livecheck_url_to_string(:url, c_livecheck_url)).to eq(cask_url)
c_livecheck_url.livecheck.url(:homepage)
expect(livecheck.livecheck_url_to_string(homepage_url, c_livecheck_url)).to eq(homepage_url)
f_livecheck_url.livecheck.url(:stable)
expect(livecheck.livecheck_url_to_string(stable_url, f_livecheck_url)).to eq(stable_url)
c_livecheck_url.livecheck.url(:url)
expect(livecheck.livecheck_url_to_string(cask_url, c_livecheck_url)).to eq(cask_url)
end end
it "returns nil when not given a string or valid symbol" do it "returns nil when not given a string or valid symbol" do

View File

@ -12,7 +12,7 @@ describe Requirement do
let(:klass) { Class.new(described_class) } let(:klass) { Class.new(described_class) }
describe "#tags" do describe "#tags" do
subject { described_class.new(tags) } subject { klass.new(tags) }
context "with a single tag" do context "with a single tag" do
let(:tags) { ["bar"] } let(:tags) { ["bar"] }
@ -149,7 +149,7 @@ describe Requirement do
describe "#build?" do describe "#build?" do
context "when the :build tag is specified" do context "when the :build tag is specified" do
subject { described_class.new([:build]) } subject { klass.new([:build]) }
it { is_expected.to be_a_build_requirement } it { is_expected.to be_a_build_requirement }
end end
@ -186,24 +186,24 @@ describe Requirement do
end end
describe "#eql? and #==" do describe "#eql? and #==" do
subject(:requirement) { described_class.new } subject(:requirement) { klass.new }
it "returns true if the names and tags are equal" do it "returns true if the names and tags are equal" do
other = described_class.new other = klass.new
expect(requirement).to eql(other) expect(requirement).to eql(other)
expect(requirement).to eq(other) expect(requirement).to eq(other)
end end
it "returns false if names differ" do it "returns false if names differ" do
other = described_class.new other = klass.new
allow(other).to receive(:name).and_return("foo") allow(other).to receive(:name).and_return("foo")
expect(requirement).not_to eql(other) expect(requirement).not_to eql(other)
expect(requirement).not_to eq(other) expect(requirement).not_to eq(other)
end end
it "returns false if tags differ" do it "returns false if tags differ" do
other = described_class.new([:optional]) other = klass.new([:optional])
expect(requirement).not_to eql(other) expect(requirement).not_to eql(other)
expect(requirement).not_to eq(other) expect(requirement).not_to eq(other)
@ -211,21 +211,21 @@ describe Requirement do
end end
describe "#hash" do describe "#hash" do
subject(:requirement) { described_class.new } subject(:requirement) { klass.new }
it "is equal if names and tags are equal" do it "is equal if names and tags are equal" do
other = described_class.new other = klass.new
expect(requirement.hash).to eq(other.hash) expect(requirement.hash).to eq(other.hash)
end end
it "differs if names differ" do it "differs if names differ" do
other = described_class.new other = klass.new
allow(other).to receive(:name).and_return("foo") allow(other).to receive(:name).and_return("foo")
expect(requirement.hash).not_to eq(other.hash) expect(requirement.hash).not_to eq(other.hash)
end end
it "differs if tags differ" do it "differs if tags differ" do
other = described_class.new([:optional]) other = klass.new([:optional])
expect(requirement.hash).not_to eq(other.hash) expect(requirement.hash).not_to eq(other.hash)
end end
end end

View File

@ -12,7 +12,8 @@ describe Requirements do
end end
it "merges duplicate requirements" do it "merges duplicate requirements" do
requirements << Requirement.new << Requirement.new klass = Class.new(Requirement)
requirements << klass.new << klass.new
expect(requirements.count).to eq(1) expect(requirements.count).to eq(1)
end end
end end

View File

@ -66,10 +66,6 @@ describe Resource do
end end
describe "#livecheck" do describe "#livecheck" do
it "returns nil if livecheck block is not set in resource" do
expect(resource.livecheck).to be_nil
end
specify "when livecheck block is set" do specify "when livecheck block is set" do
expect(livecheck_resource.livecheck.url).to eq("https://brew.sh/test/releases") expect(livecheck_resource.livecheck.url).to eq("https://brew.sh/test/releases")
expect(livecheck_resource.livecheck.regex).to eq(/foo[._-]v?(\d+(?:\.\d+)+)\.t/i) expect(livecheck_resource.livecheck.regex).to eq(/foo[._-]v?(\d+(?:\.\d+)+)\.t/i)

View File

@ -5,26 +5,28 @@ require "formula"
require "service" require "service"
describe Homebrew::Service do describe Homebrew::Service do
let(:klass) do let(:name) { "formula_name" }
Class.new(Formula) do
def stub_formula(&block)
formula(name) do
url "https://brew.sh/test-1.0.tbz" url "https://brew.sh/test-1.0.tbz"
instance_eval(&block) if block
end end
end end
let(:name) { "formula_name" }
let(:path) { Formulary.core_path(name) }
let(:spec) { :stable }
let(:f) { klass.new(name, path, spec) }
describe "#std_service_path_env" do describe "#std_service_path_env" do
it "returns valid std_service_path_env" do it "returns valid std_service_path_env" do
f.class.service do f = stub_formula do
run opt_bin/"beanstalkd" service do
run_type :immediate run opt_bin/"beanstalkd"
environment_variables PATH: std_service_path_env run_type :immediate
error_log_path var/"log/beanstalkd.error.log" environment_variables PATH: std_service_path_env
log_path var/"log/beanstalkd.log" error_log_path var/"log/beanstalkd.error.log"
working_dir var log_path var/"log/beanstalkd.log"
keep_alive true working_dir var
keep_alive true
end
end end
path = f.service.std_service_path_env path = f.service.std_service_path_env
@ -34,9 +36,11 @@ describe Homebrew::Service do
describe "#process_type" do describe "#process_type" do
it "throws for unexpected type" do it "throws for unexpected type" do
f.class.service do f = stub_formula do
run opt_bin/"beanstalkd" service do
process_type :cow run opt_bin/"beanstalkd"
process_type :cow
end
end end
expect { expect {
@ -47,9 +51,11 @@ describe Homebrew::Service do
describe "#keep_alive" do describe "#keep_alive" do
it "throws for unexpected keys" do it "throws for unexpected keys" do
f.class.service do f = stub_formula do
run opt_bin/"beanstalkd" service do
keep_alive test: "key" run opt_bin/"beanstalkd"
keep_alive test: "key"
end
end end
expect { expect {
@ -60,9 +66,11 @@ describe Homebrew::Service do
describe "#run_type" do describe "#run_type" do
it "throws for unexpected type" do it "throws for unexpected type" do
f.class.service do f = stub_formula do
run opt_bin/"beanstalkd" service do
run_type :cow run opt_bin/"beanstalkd"
run_type :cow
end
end end
expect { expect {
@ -73,9 +81,11 @@ describe Homebrew::Service do
describe "#sockets" do describe "#sockets" do
it "throws for missing type" do it "throws for missing type" do
f.class.service do f = stub_formula do
run opt_bin/"beanstalkd" service do
sockets "127.0.0.1:80" run opt_bin/"beanstalkd"
sockets "127.0.0.1:80"
end
end end
expect { expect {
@ -84,9 +94,11 @@ describe Homebrew::Service do
end end
it "throws for missing host" do it "throws for missing host" do
f.class.service do f = stub_formula do
run opt_bin/"beanstalkd" service do
sockets "tcp://:80" run opt_bin/"beanstalkd"
sockets "tcp://:80"
end
end end
expect { expect {
@ -95,9 +107,11 @@ describe Homebrew::Service do
end end
it "throws for missing port" do it "throws for missing port" do
f.class.service do f = stub_formula do
run opt_bin/"beanstalkd" service do
sockets "tcp://127.0.0.1" run opt_bin/"beanstalkd"
sockets "tcp://127.0.0.1"
end
end end
expect { expect {
@ -108,14 +122,16 @@ describe Homebrew::Service do
describe "#manual_command" do describe "#manual_command" do
it "returns valid manual_command" do it "returns valid manual_command" do
f.class.service do f = stub_formula do
run "#{HOMEBREW_PREFIX}/bin/beanstalkd" service do
run_type :immediate run "#{HOMEBREW_PREFIX}/bin/beanstalkd"
environment_variables PATH: std_service_path_env, ETC_DIR: etc/"beanstalkd" run_type :immediate
error_log_path var/"log/beanstalkd.error.log" environment_variables PATH: std_service_path_env, ETC_DIR: etc/"beanstalkd"
log_path var/"log/beanstalkd.log" error_log_path var/"log/beanstalkd.error.log"
working_dir var log_path var/"log/beanstalkd.log"
keep_alive true working_dir var
keep_alive true
end
end end
path = f.service.manual_command path = f.service.manual_command
@ -123,14 +139,16 @@ describe Homebrew::Service do
end end
it "returns valid manual_command without variables" do it "returns valid manual_command without variables" do
f.class.service do f = stub_formula do
run opt_bin/"beanstalkd" service do
run_type :immediate run opt_bin/"beanstalkd"
environment_variables PATH: std_service_path_env run_type :immediate
error_log_path var/"log/beanstalkd.error.log" environment_variables PATH: std_service_path_env
log_path var/"log/beanstalkd.log" error_log_path var/"log/beanstalkd.error.log"
working_dir var log_path var/"log/beanstalkd.log"
keep_alive true working_dir var
keep_alive true
end
end end
path = f.service.manual_command path = f.service.manual_command
@ -140,21 +158,23 @@ describe Homebrew::Service do
describe "#to_plist" do describe "#to_plist" do
it "returns valid plist" do it "returns valid plist" do
f.class.service do f = stub_formula do
run [opt_bin/"beanstalkd", "test"] service do
run_type :immediate run [opt_bin/"beanstalkd", "test"]
environment_variables PATH: std_service_path_env, FOO: "BAR", ETC_DIR: etc/"beanstalkd" run_type :immediate
error_log_path var/"log/beanstalkd.error.log" environment_variables PATH: std_service_path_env, FOO: "BAR", ETC_DIR: etc/"beanstalkd"
log_path var/"log/beanstalkd.log" error_log_path var/"log/beanstalkd.error.log"
input_path var/"in/beanstalkd" log_path var/"log/beanstalkd.log"
root_dir var input_path var/"in/beanstalkd"
working_dir var root_dir var
keep_alive true working_dir var
launch_only_once true keep_alive true
process_type :interactive launch_only_once true
restart_delay 30 process_type :interactive
interval 5 restart_delay 30
macos_legacy_timers true interval 5
macos_legacy_timers true
end
end end
plist = f.service.to_plist plist = f.service.to_plist
@ -216,9 +236,11 @@ describe Homebrew::Service do
end end
it "returns valid plist with socket" do it "returns valid plist with socket" do
f.class.service do f = stub_formula do
run [opt_bin/"beanstalkd", "test"] service do
sockets "tcp://127.0.0.1:80" run [opt_bin/"beanstalkd", "test"]
sockets "tcp://127.0.0.1:80"
end
end end
plist = f.service.to_plist plist = f.service.to_plist
@ -265,9 +287,11 @@ describe Homebrew::Service do
end end
it "returns valid partial plist" do it "returns valid partial plist" do
f.class.service do f = stub_formula do
run opt_bin/"beanstalkd" service do
run_type :immediate run opt_bin/"beanstalkd"
run_type :immediate
end
end end
plist = f.service.to_plist plist = f.service.to_plist
@ -299,10 +323,12 @@ describe Homebrew::Service do
end end
it "returns valid interval plist" do it "returns valid interval plist" do
f.class.service do f = stub_formula do
run opt_bin/"beanstalkd" service do
run_type :interval run opt_bin/"beanstalkd"
interval 5 run_type :interval
interval 5
end
end end
plist = f.service.to_plist plist = f.service.to_plist
@ -336,10 +362,12 @@ describe Homebrew::Service do
end end
it "returns valid cron plist" do it "returns valid cron plist" do
f.class.service do f = stub_formula do
run opt_bin/"beanstalkd" service do
run_type :cron run opt_bin/"beanstalkd"
cron "@daily" run_type :cron
cron "@daily"
end
end end
plist = f.service.to_plist plist = f.service.to_plist
@ -378,9 +406,11 @@ describe Homebrew::Service do
end end
it "returns valid keepalive-exit plist" do it "returns valid keepalive-exit plist" do
f.class.service do f = stub_formula do
run opt_bin/"beanstalkd" service do
keep_alive successful_exit: false run opt_bin/"beanstalkd"
keep_alive successful_exit: false
end
end end
plist = f.service.to_plist plist = f.service.to_plist
@ -417,9 +447,11 @@ describe Homebrew::Service do
end end
it "returns valid keepalive-crashed plist" do it "returns valid keepalive-crashed plist" do
f.class.service do f = stub_formula do
run opt_bin/"beanstalkd" service do
keep_alive crashed: true run opt_bin/"beanstalkd"
keep_alive crashed: true
end
end end
plist = f.service.to_plist plist = f.service.to_plist
@ -456,9 +488,11 @@ describe Homebrew::Service do
end end
it "returns valid keepalive-path plist" do it "returns valid keepalive-path plist" do
f.class.service do f = stub_formula do
run opt_bin/"beanstalkd" service do
keep_alive path: opt_pkgshare/"test-path" run opt_bin/"beanstalkd"
keep_alive path: opt_pkgshare/"test-path"
end
end end
plist = f.service.to_plist plist = f.service.to_plist
@ -497,19 +531,21 @@ describe Homebrew::Service do
describe "#to_systemd_unit" do describe "#to_systemd_unit" do
it "returns valid unit" do it "returns valid unit" do
f.class.service do f = stub_formula do
run [opt_bin/"beanstalkd", "test"] service do
run_type :immediate run [opt_bin/"beanstalkd", "test"]
environment_variables PATH: std_service_path_env, FOO: "BAR" run_type :immediate
error_log_path var/"log/beanstalkd.error.log" environment_variables PATH: std_service_path_env, FOO: "BAR"
log_path var/"log/beanstalkd.log" error_log_path var/"log/beanstalkd.error.log"
input_path var/"in/beanstalkd" log_path var/"log/beanstalkd.log"
root_dir var input_path var/"in/beanstalkd"
working_dir var root_dir var
keep_alive true working_dir var
process_type :interactive keep_alive true
restart_delay 30 process_type :interactive
macos_legacy_timers true restart_delay 30
macos_legacy_timers true
end
end end
unit = f.service.to_systemd_unit unit = f.service.to_systemd_unit
@ -538,10 +574,12 @@ describe Homebrew::Service do
end end
it "returns valid partial oneshot unit" do it "returns valid partial oneshot unit" do
f.class.service do f = stub_formula do
run opt_bin/"beanstalkd" service do
run_type :immediate run opt_bin/"beanstalkd"
launch_only_once true run_type :immediate
launch_only_once true
end
end end
unit = f.service.to_systemd_unit unit = f.service.to_systemd_unit
@ -562,10 +600,12 @@ describe Homebrew::Service do
describe "#to_systemd_timer" do describe "#to_systemd_timer" do
it "returns valid timer" do it "returns valid timer" do
f.class.service do f = stub_formula do
run [opt_bin/"beanstalkd", "test"] service do
run_type :interval run [opt_bin/"beanstalkd", "test"]
interval 5 run_type :interval
interval 5
end
end end
unit = f.service.to_systemd_timer unit = f.service.to_systemd_timer
@ -584,9 +624,11 @@ describe Homebrew::Service do
end end
it "returns valid partial timer" do it "returns valid partial timer" do
f.class.service do f = stub_formula do
run opt_bin/"beanstalkd" service do
run_type :immediate run opt_bin/"beanstalkd"
run_type :immediate
end
end end
unit = f.service.to_systemd_timer unit = f.service.to_systemd_timer
@ -604,10 +646,12 @@ describe Homebrew::Service do
end end
it "throws on incomplete cron" do it "throws on incomplete cron" do
f.class.service do f = stub_formula do
run opt_bin/"beanstalkd" service do
run_type :cron run opt_bin/"beanstalkd"
cron "1 2 3 4" run_type :cron
cron "1 2 3 4"
end
end end
expect { expect {
@ -627,10 +671,12 @@ describe Homebrew::Service do
} }
styles.each do |cron, calendar| styles.each do |cron, calendar|
f.class.service do f = stub_formula do
run opt_bin/"beanstalkd" service do
run_type :cron run opt_bin/"beanstalkd"
cron cron.to_s run_type :cron
cron cron.to_s
end
end end
unit = f.service.to_systemd_timer unit = f.service.to_systemd_timer
@ -653,18 +699,22 @@ describe Homebrew::Service do
describe "#timed?" do describe "#timed?" do
it "returns false for immediate" do it "returns false for immediate" do
f.class.service do f = stub_formula do
run [opt_bin/"beanstalkd", "test"] service do
run_type :immediate run [opt_bin/"beanstalkd", "test"]
run_type :immediate
end
end end
expect(f.service.timed?).to be(false) expect(f.service.timed?).to be(false)
end end
it "returns true for interval" do it "returns true for interval" do
f.class.service do f = stub_formula do
run [opt_bin/"beanstalkd", "test"] service do
run_type :interval run [opt_bin/"beanstalkd", "test"]
run_type :interval
end
end end
expect(f.service.timed?).to be(true) expect(f.service.timed?).to be(true)
@ -673,35 +723,43 @@ describe Homebrew::Service do
describe "#keep_alive?" do describe "#keep_alive?" do
it "returns true when keep_alive set to hash" do it "returns true when keep_alive set to hash" do
f.class.service do f = stub_formula do
run [opt_bin/"beanstalkd", "test"] service do
keep_alive crashed: true run [opt_bin/"beanstalkd", "test"]
keep_alive crashed: true
end
end end
expect(f.service.keep_alive?).to be(true) expect(f.service.keep_alive?).to be(true)
end end
it "returns true when keep_alive set to true" do it "returns true when keep_alive set to true" do
f.class.service do f = stub_formula do
run [opt_bin/"beanstalkd", "test"] service do
keep_alive true run [opt_bin/"beanstalkd", "test"]
keep_alive true
end
end end
expect(f.service.keep_alive?).to be(true) expect(f.service.keep_alive?).to be(true)
end end
it "returns false when keep_alive not set" do it "returns false when keep_alive not set" do
f.class.service do f = stub_formula do
run [opt_bin/"beanstalkd", "test"] service do
run [opt_bin/"beanstalkd", "test"]
end
end end
expect(f.service.keep_alive?).to be(false) expect(f.service.keep_alive?).to be(false)
end end
it "returns false when keep_alive set to false" do it "returns false when keep_alive set to false" do
f.class.service do f = stub_formula do
run [opt_bin/"beanstalkd", "test"] service do
keep_alive false run [opt_bin/"beanstalkd", "test"]
keep_alive false
end
end end
expect(f.service.keep_alive?).to be(false) expect(f.service.keep_alive?).to be(false)
@ -710,9 +768,11 @@ describe Homebrew::Service do
describe "#command" do describe "#command" do
it "returns @run data" do it "returns @run data" do
f.class.service do f = stub_formula do
run [opt_bin/"beanstalkd", "test"] service do
run_type :immediate run [opt_bin/"beanstalkd", "test"]
run_type :immediate
end
end end
command = f.service.command command = f.service.command

View File

@ -4,13 +4,22 @@
class Failball < Formula class Failball < Formula
def initialize(name = "failball", path = Pathname.new(__FILE__).expand_path, spec = :stable, def initialize(name = "failball", path = Pathname.new(__FILE__).expand_path, spec = :stable,
alias_path: nil, force_bottle: false) 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
end
super super
end end
DSL_PROC = proc do
url "file://#{TEST_FIXTURE_DIR}/tarballs/testball-0.1.tbz"
sha256 TESTBALL_SHA256
end.freeze
private_constant :DSL_PROC
DSL_PROC.call
def self.inherited(other)
super
other.instance_eval(&DSL_PROC)
end
def install def install
prefix.install "bin" prefix.install "bin"
prefix.install "libexec" prefix.install "libexec"

View File

@ -4,13 +4,22 @@
class Testball < Formula class Testball < Formula
def initialize(name = "testball", path = Pathname.new(__FILE__).expand_path, spec = :stable, def initialize(name = "testball", path = Pathname.new(__FILE__).expand_path, spec = :stable,
alias_path: nil, force_bottle: false) 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
end
super super
end end
DSL_PROC = proc do
url "file://#{TEST_FIXTURE_DIR}/tarballs/testball-0.1.tbz"
sha256 TESTBALL_SHA256
end.freeze
private_constant :DSL_PROC
DSL_PROC.call
def self.inherited(other)
super
other.instance_eval(&DSL_PROC)
end
def install def install
prefix.install "bin" prefix.install "bin"
prefix.install "libexec" prefix.install "libexec"

View File

@ -4,18 +4,29 @@
class TestballBottle < Formula class TestballBottle < Formula
def initialize(name = "testball_bottle", path = Pathname.new(__FILE__).expand_path, spec = :stable, def initialize(name = "testball_bottle", path = Pathname.new(__FILE__).expand_path, spec = :stable,
alias_path: nil, force_bottle: false) 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
root_url "file://#{TEST_FIXTURE_DIR}/bottles"
sha256 cellar: :any_skip_relocation, Utils::Bottles.tag.to_sym => "8f9aecd233463da6a4ea55f5f88fc5841718c013f3e2a7941350d6130f1dc149"
end
cxxstdlib_check :skip
end
super super
end end
DSL_PROC = proc do
url "file://#{TEST_FIXTURE_DIR}/tarballs/testball-0.1.tbz"
sha256 TESTBALL_SHA256
bottle do
root_url "file://#{TEST_FIXTURE_DIR}/bottles"
sha256 cellar: :any_skip_relocation, Utils::Bottles.tag.to_sym => "8f9aecd233463da6a4ea55f5f88fc5841718c013f3e2a7941350d6130f1dc149"
end
cxxstdlib_check :skip
end.freeze
private_constant :DSL_PROC
DSL_PROC.call
def self.inherited(other)
super
other.instance_eval(&DSL_PROC)
end
def install def install
prefix.install "bin" prefix.install "bin"
prefix.install "libexec" prefix.install "libexec"

View File

@ -4,19 +4,29 @@
class TestballBottleCellar < Formula class TestballBottleCellar < Formula
def initialize(name = "testball_bottle", path = Pathname.new(__FILE__).expand_path, spec = :stable, def initialize(name = "testball_bottle", path = Pathname.new(__FILE__).expand_path, spec = :stable,
alias_path: nil, force_bottle: false) 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
hexdigest = "8f9aecd233463da6a4ea55f5f88fc5841718c013f3e2a7941350d6130f1dc149"
stable.bottle do
root_url "file://#{TEST_FIXTURE_DIR}/bottles"
sha256 cellar: :any_skip_relocation, Utils::Bottles.tag.to_sym => hexdigest
end
cxxstdlib_check :skip
end
super super
end end
DSL_PROC = proc do
url "file://#{TEST_FIXTURE_DIR}/tarballs/testball-0.1.tbz"
sha256 TESTBALL_SHA256
bottle do
root_url "file://#{TEST_FIXTURE_DIR}/bottles"
sha256 cellar: :any_skip_relocation, Utils::Bottles.tag.to_sym => "8f9aecd233463da6a4ea55f5f88fc5841718c013f3e2a7941350d6130f1dc149"
end
cxxstdlib_check :skip
end.freeze
private_constant :DSL_PROC
DSL_PROC.call
def self.inherited(other)
super
other.instance_eval(&DSL_PROC)
end
def install def install
prefix.install "bin" prefix.install "bin"
prefix.install "libexec" prefix.install "libexec"