brew/Library/Homebrew/exceptions.rb

797 lines
21 KiB
Ruby
Raw Normal View History

rubocop: Use `Sorbet/StrictSigil` as it's better than comments - Previously I thought that comments were fine to discourage people from wasting their time trying to bump things that used `undef` that Sorbet didn't support. But RuboCop is better at this since it'll complain if the comments are unnecessary. - Suggested in https://github.com/Homebrew/brew/pull/18018#issuecomment-2283369501. - I've gone for a mixture of `rubocop:disable` for the files that can't be `typed: strict` (use of undef, required before everything else, etc) and `rubocop:todo` for everything else that should be tried to make strictly typed. There's no functional difference between the two as `rubocop:todo` is `rubocop:disable` with a different name. - And I entirely disabled the cop for the docs/ directory since `typed: strict` isn't going to gain us anything for some Markdown linting config files. - This means that now it's easier to track what needs to be done rather than relying on checklists of files in our big Sorbet issue: ```shell $ git grep 'typed: true # rubocop:todo Sorbet/StrictSigil' | wc -l 268 ``` - And this is confirmed working for new files: ```shell $ git status On branch use-rubocop-for-sorbet-strict-sigils Untracked files: (use "git add <file>..." to include in what will be committed) Library/Homebrew/bad.rb Library/Homebrew/good.rb nothing added to commit but untracked files present (use "git add" to track) $ brew style Offenses: bad.rb:1:1: C: Sorbet/StrictSigil: Sorbet sigil should be at least strict got true. ^^^^^^^^^^^^^ 1340 files inspected, 1 offense detected ```
2024-08-12 10:30:59 +01:00
# typed: true # rubocop:todo Sorbet/StrictSigil
# frozen_string_literal: true
2020-08-14 05:28:26 +02:00
# Raised when a command is used wrong.
2024-04-22 21:05:48 +02:00
#
# @api internal
class UsageError < RuntimeError
attr_reader :reason
def initialize(reason = nil)
2020-08-19 17:12:32 +01:00
super
@reason = reason
end
2020-10-20 12:03:48 +02:00
sig { returns(String) }
def to_s
s = "Invalid usage"
s += ": #{reason}" if reason
s
end
end
2020-08-14 05:28:26 +02:00
# Raised when a command expects a formula and none was specified.
class FormulaUnspecifiedError < UsageError
def initialize
super "this command requires a formula argument"
end
end
2020-10-08 11:12:17 +02:00
# Raised when a command expects a formula or cask and none was specified.
class FormulaOrCaskUnspecifiedError < UsageError
def initialize
super "this command requires a formula or cask argument"
end
end
2020-08-14 05:28:26 +02:00
# Raised when a command expects a keg and none was specified.
class KegUnspecifiedError < UsageError
def initialize
super "this command requires a keg argument"
end
end
class UnsupportedInstallationMethod < RuntimeError; end
class MultipleVersionsInstalledError < RuntimeError; end
2010-11-12 20:59:53 -08:00
2024-04-23 19:10:33 +02:00
# Raised when a path is not a keg.
#
# @api internal
2010-11-12 20:59:53 -08:00
class NotAKegError < RuntimeError; end
2020-08-14 05:28:26 +02:00
# Raised when a keg doesn't exist.
2011-03-12 23:06:45 -08:00
class NoSuchKegError < RuntimeError
attr_reader :name
2010-11-12 20:59:53 -08:00
def initialize(name)
2010-11-12 20:59:53 -08:00
@name = name
super "No such keg: #{HOMEBREW_CELLAR}/#{name}"
end
end
# Raised when a keg from a specific tap doesn't exist.
class NoSuchKegFromTapError < RuntimeError
attr_reader :name, :tap
sig { params(name: String, tap: Tap).void }
def initialize(name, tap)
@name = name
@tap = tap
super "No such keg: #{HOMEBREW_CELLAR}/#{name} from tap #{tap}"
end
end
2020-08-14 05:28:26 +02:00
# Raised when an invalid attribute is used in a formula.
class FormulaValidationError < StandardError
attr_reader :attr, :formula
def initialize(formula, attr, value)
@attr = attr
@formula = formula
super "invalid attribute for formula '#{formula}': #{attr} (#{value.inspect})"
end
end
2013-08-06 21:48:05 -07:00
class FormulaSpecificationError < StandardError; end
2020-08-14 05:28:26 +02:00
# Raised when a deprecated method is used.
class MethodDeprecatedError < StandardError
attr_accessor :issues_url
end
# Raised when neither a formula nor a cask with the given name is available.
class FormulaOrCaskUnavailableError < RuntimeError
attr_reader :name
def initialize(name)
super()
2020-08-19 17:12:32 +01:00
@name = name
# Store the state of these envs at the time the exception is thrown.
# This is so we do the fuzzy search for "did you mean" etc under that same mode,
# in case the list of formulae are different.
@without_api = Homebrew::EnvConfig.no_install_from_api?
@auto_without_api = Homebrew::EnvConfig.automatically_set_no_install_from_api?
end
sig { returns(String) }
def did_you_mean
require "formula"
similar_formula_names = Homebrew.with_no_api_env_if_needed(@without_api) { Formula.fuzzy_search(name) }
return "" if similar_formula_names.blank?
"Did you mean #{similar_formula_names.to_sentence two_words_connector: " or ", last_word_connector: " or "}?"
end
2020-10-20 12:03:48 +02:00
sig { returns(String) }
def to_s
s = "No available formula or cask with the name \"#{name}\". #{did_you_mean}".strip
if @auto_without_api && !CoreTap.instance.installed?
s += "\nA full git tap clone is required to use this command on core packages."
end
s
end
end
# Raised when a formula or cask in a specific tap is not available.
class TapFormulaOrCaskUnavailableError < FormulaOrCaskUnavailableError
attr_reader :tap
def initialize(tap, name)
super "#{tap}/#{name}"
@tap = tap
end
sig { returns(String) }
def to_s
s = super
s += "\nPlease tap it and then try again: brew tap #{tap}" unless tap.installed?
s
end
end
# Raised when a formula is not available.
2024-04-22 21:05:48 +02:00
#
# @api internal
class FormulaUnavailableError < FormulaOrCaskUnavailableError
attr_accessor :dependent
2020-10-20 12:03:48 +02:00
sig { returns(T.nilable(String)) }
def dependent_s
2020-10-20 12:03:48 +02:00
" (dependency of #{dependent})" if dependent && dependent != name
end
2020-10-20 12:03:48 +02:00
sig { returns(String) }
def to_s
"No available formula with the name \"#{name}\"#{dependent_s}. #{did_you_mean}".strip
end
end
2020-08-14 05:28:26 +02:00
# Shared methods for formula class errors.
module FormulaClassUnavailableErrorModule
2020-07-07 11:29:33 +01:00
attr_reader :path, :class_name, :class_list
2024-04-26 14:04:55 +02:00
sig { returns(String) }
def to_s
s = super
s += "\nIn formula file: #{path}"
s += "\nExpected to find class #{class_name}, but #{class_list_s}."
s
end
private
2020-10-20 12:03:48 +02:00
sig { returns(String) }
def class_list_s
formula_class_list = class_list.select { |klass| klass < Formula }
if class_list.empty?
"found no classes"
elsif formula_class_list.empty?
"only found: #{format_list(class_list)} (not derived from Formula!)"
else
"only found: #{format_list(formula_class_list)}"
end
end
def format_list(class_list)
2018-09-17 19:44:12 +02:00
class_list.map { |klass| klass.name.split("::").last }.join(", ")
end
end
2020-08-14 05:28:26 +02:00
# Raised when a formula does not contain a formula class.
class FormulaClassUnavailableError < FormulaUnavailableError
include FormulaClassUnavailableErrorModule
def initialize(name, path, class_name, class_list)
@path = path
@class_name = class_name
@class_list = class_list
super name
end
end
2020-08-14 05:28:26 +02:00
# Shared methods for formula unreadable errors.
module FormulaUnreadableErrorModule
attr_reader :formula_error
2020-10-20 12:03:48 +02:00
sig { returns(String) }
def to_s
"#{name}: " + formula_error.to_s
end
end
2020-08-14 05:28:26 +02:00
# Raised when a formula is unreadable.
class FormulaUnreadableError < FormulaUnavailableError
include FormulaUnreadableErrorModule
def initialize(name, error)
super(name)
@formula_error = error
set_backtrace(error.backtrace)
end
end
2020-08-14 05:28:26 +02:00
# Raised when a formula in a specific tap is unavailable.
class TapFormulaUnavailableError < FormulaUnavailableError
attr_reader :tap, :user, :repo
def initialize(tap, name)
@tap = tap
@user = tap.user
@repo = tap.repo
super "#{tap}/#{name}"
end
2024-04-26 14:04:55 +02:00
sig { returns(String) }
def to_s
s = super
s += "\nPlease tap it and then try again: brew tap #{tap}" unless tap.installed?
s
end
end
2020-08-14 05:28:26 +02:00
# Raised when a formula in a specific tap does not contain a formula class.
class TapFormulaClassUnavailableError < TapFormulaUnavailableError
include FormulaClassUnavailableErrorModule
attr_reader :tap
def initialize(tap, name, path, class_name, class_list)
@path = path
@class_name = class_name
@class_list = class_list
super tap, name
end
end
2020-08-14 05:28:26 +02:00
# Raised when a formula in a specific tap is unreadable.
class TapFormulaUnreadableError < TapFormulaUnavailableError
include FormulaUnreadableErrorModule
def initialize(tap, name, error)
super(tap, name)
@formula_error = error
set_backtrace(error.backtrace)
end
end
# Raised when a formula with the same name is found in multiple taps.
2015-05-08 19:16:06 +08:00
class TapFormulaAmbiguityError < RuntimeError
attr_reader :name, :taps, :loaders
2015-05-08 19:16:06 +08:00
def initialize(name, loaders)
2015-05-08 19:16:06 +08:00
@name = name
@loaders = loaders
@taps = loaders.map(&:tap)
2024-02-08 12:14:26 +01:00
formulae = taps.map { |tap| "#{tap}/#{name}" }
formula_list = formulae.map { |f| "\n * #{f}" }.join
2015-05-08 19:16:06 +08:00
2017-10-15 02:28:32 +02:00
super <<~EOS
Formulae found in multiple taps:#{formula_list}
2015-05-08 19:16:06 +08:00
Please use the fully-qualified name (e.g. #{formulae.first}) to refer to a specific formula.
2015-05-08 19:16:06 +08:00
EOS
end
end
2020-08-14 05:28:26 +02:00
# Raised when a tap is unavailable.
2015-06-13 14:32:10 +08:00
class TapUnavailableError < RuntimeError
attr_reader :name
def initialize(name)
2015-06-13 14:32:10 +08:00
@name = name
message = "No available tap #{name}.\n"
if [CoreTap.instance.name, CoreCaskTap.instance.name].include?(name)
command = "brew tap --force #{name}"
message += <<~EOS
Run #{Formatter.identifier(command)} to tap #{name}!
EOS
else
command = "brew tap-new #{name}"
message += <<~EOS
Run #{Formatter.identifier(command)} to create a new #{name} tap!
EOS
end
super message.freeze
2015-06-13 14:32:10 +08:00
end
end
2020-08-14 05:28:26 +02:00
# Raised when a tap's remote does not match the actual remote.
class TapRemoteMismatchError < RuntimeError
2020-07-07 11:29:33 +01:00
attr_reader :name, :expected_remote, :actual_remote
def initialize(name, expected_remote, actual_remote)
@name = name
@expected_remote = expected_remote
@actual_remote = actual_remote
super message
end
def message
<<~EOS
Tap #{name} remote mismatch.
#{expected_remote} != #{actual_remote}
EOS
end
end
2023-02-10 23:15:40 -05:00
# Raised when the remote of homebrew/core does not match HOMEBREW_CORE_GIT_REMOTE.
class TapCoreRemoteMismatchError < TapRemoteMismatchError
def message
<<~EOS
Tap #{name} remote does not match HOMEBREW_CORE_GIT_REMOTE.
#{expected_remote} != #{actual_remote}
Please set HOMEBREW_CORE_GIT_REMOTE="#{actual_remote}" and run `brew update` instead.
EOS
end
end
2020-08-14 05:28:26 +02:00
# Raised when a tap is already installed.
2015-11-07 16:01:05 +08:00
class TapAlreadyTappedError < RuntimeError
attr_reader :name
def initialize(name)
@name = name
2017-10-15 02:28:32 +02:00
super <<~EOS
2015-11-07 16:01:05 +08:00
Tap #{name} already tapped.
EOS
end
end
# Raised when run `brew tap --custom-remote` without a remote URL.
class TapNoCustomRemoteError < RuntimeError
attr_reader :name
def initialize(name)
@name = name
super <<~EOS
Tap #{name} with option `--custom-remote` but without a remote URL.
EOS
end
end
2020-08-14 05:28:26 +02:00
# Raised when another Homebrew operation is already in progress.
class OperationInProgressError < RuntimeError
sig { params(locked_path: Pathname).void }
def initialize(locked_path)
full_command = Homebrew.running_command_with_args.presence || "brew"
lock_context = if (env_lock_context = Homebrew::EnvConfig.lock_context.presence)
"\n#{env_lock_context}"
end
2017-10-15 02:28:32 +02:00
message = <<~EOS
A `#{full_command}` process has already locked #{locked_path}.#{lock_context}
Please wait for it to finish or terminate it to continue.
2018-06-06 23:34:19 -04:00
EOS
super message
end
end
2013-08-06 21:48:05 -07:00
class CannotInstallFormulaError < RuntimeError; end
2020-08-14 05:28:26 +02:00
# Raised when a formula installation was already attempted.
class FormulaInstallationAlreadyAttemptedError < RuntimeError
2014-09-12 21:28:25 -05:00
def initialize(formula)
2015-05-27 22:16:48 +08:00
super "Formula installation already attempted: #{formula.full_name}"
end
end
2020-08-14 05:28:26 +02:00
# Raised when there are unsatisfied requirements.
class UnsatisfiedRequirements < RuntimeError
def initialize(reqs)
if reqs.length == 1
super "An unsatisfied requirement failed this build."
else
super "Unsatisfied requirements failed this build."
end
end
end
2020-08-14 05:28:26 +02:00
# Raised when a formula conflicts with another one.
class FormulaConflictError < RuntimeError
attr_reader :formula, :conflicts
def initialize(formula, conflicts)
@formula = formula
@conflicts = conflicts
super message
end
def conflict_message(conflict)
message = []
message << " #{conflict.name}"
message << ": because #{conflict.reason}" if conflict.reason
message.join
end
2020-10-20 12:03:48 +02:00
sig { returns(String) }
def message
message = []
message << "Cannot install #{formula.full_name} because conflicting formulae are installed."
message.concat conflicts.map { |c| conflict_message(c) } << ""
2017-10-15 02:28:32 +02:00
message << <<~EOS
2017-06-01 16:06:51 +02:00
Please `brew unlink #{conflicts.map(&:name) * " "}` before continuing.
Unlinking removes a formula's symlinks from #{HOMEBREW_PREFIX}. You can
2024-02-04 15:19:29 +01:00
link the formula again after the install finishes. You can `--force` this
2019-04-08 12:47:15 -04:00
install, but the build may fail or cause obscure side effects in the
resulting software.
2018-06-06 23:34:19 -04:00
EOS
message.join("\n")
end
end
2020-08-14 05:28:26 +02:00
# Raise when the Python version cannot be detected automatically.
class FormulaUnknownPythonError < RuntimeError
def initialize(formula)
super <<~EOS
The version of Python to use with the virtualenv in the `#{formula.full_name}` formula
cannot be guessed automatically because a recognised Python dependency could not be found.
If you are using a non-standard Python dependency, please add `:using => "python@x.y"`
to 'virtualenv_install_with_resources' to resolve the issue manually.
EOS
end
end
2020-08-14 05:28:26 +02:00
# Raise when two Python versions are detected simultaneously.
class FormulaAmbiguousPythonError < RuntimeError
def initialize(formula)
2017-10-15 02:28:32 +02:00
super <<~EOS
The version of Python to use with the virtualenv in the `#{formula.full_name}` formula
cannot be guessed automatically.
If the simultaneous use of multiple Pythons is intentional, please add `:using => "python@x.y"`
to 'virtualenv_install_with_resources' to resolve the ambiguity manually.
EOS
end
end
2020-08-14 05:28:26 +02:00
# Raised when an error occurs during a formula build.
class BuildError < RuntimeError
attr_reader :cmd, :args, :env
attr_accessor :formula, :options
sig {
params(
formula: T.nilable(Formula),
cmd: T.any(String, Pathname),
args: T::Array[T.any(String, Integer, Pathname, Symbol)],
env: T::Hash[String, T.untyped],
).void
}
def initialize(formula, cmd, args, env)
@formula = formula
@cmd = cmd
@args = args
@env = env
pretty_args = Array(args).map { |arg| arg.to_s.gsub(/[\\ ]/, "\\\\\\0") }.join(" ")
super "Failed executing: #{cmd} #{pretty_args}".strip
end
sig { returns(T::Array[T.untyped]) }
def issues
@issues ||= fetch_issues
end
sig { returns(T::Array[T.untyped]) }
def fetch_issues
GitHub.issues_for_formula(formula.name, tap: formula.tap, state: "open", type: "issue")
rescue GitHub::API::Error => e
opoo "Unable to query GitHub for recent issues on the tap\n#{e.message}"
[]
end
sig { params(verbose: T::Boolean).void }
def dump(verbose: false)
puts
if verbose
require "system_config"
require "build_environment"
ohai "Formula"
puts "Tap: #{formula.tap}" if formula.tap?
puts "Path: #{formula.path}"
2012-09-27 15:39:16 -04:00
ohai "Configuration"
SystemConfig.dump_verbose_config
2012-09-27 15:39:16 -04:00
ohai "ENV"
BuildEnvironment.dump env
puts
2015-05-27 22:16:48 +08:00
onoe "#{formula.full_name} #{formula.version} did not build"
2015-04-25 22:07:06 -04:00
unless (logs = Dir["#{formula.logs}/*"]).empty?
puts "Logs:"
puts logs.map { |fn| " #{fn}" }.join("\n")
end
2012-09-27 15:39:16 -04:00
end
if formula.tap && !OS.unsupported_configuration?
if formula.tap.official?
puts Formatter.error(Formatter.url(OS::ISSUES_URL), label: "READ THIS")
elsif (issues_url = formula.tap.issues_url)
2017-10-15 02:28:32 +02:00
puts <<~EOS
2023-02-10 23:15:40 -05:00
If reporting this issue please do so at (not Homebrew/brew or Homebrew/homebrew-core):
2019-04-01 16:02:13 -04:00
#{Formatter.url(issues_url)}
EOS
else
2017-10-15 02:28:32 +02:00
puts <<~EOS
2023-02-10 23:15:40 -05:00
If reporting this issue please do so to (not Homebrew/brew or Homebrew/homebrew-core):
2019-04-01 16:02:13 -04:00
#{formula.tap}
EOS
end
else
2017-10-15 02:28:32 +02:00
puts <<~EOS
2023-02-10 23:15:40 -05:00
Do not report this issue to Homebrew/brew or Homebrew/homebrew-core!
EOS
end
2012-08-13 09:50:15 -04:00
puts
if issues.present?
puts "These open issues may also help:"
puts issues.map { |i| "#{i["title"]} #{i["html_url"]}" }.join("\n")
end
require "diagnostic"
checks = Homebrew::Diagnostic::Checks.new
checks.build_error_checks.each do |check|
out = checks.send(check)
next if out.nil?
2018-09-17 02:45:00 +02:00
puts
ofail out
end
end
end
# Raised if the formula or its dependencies are not bottled and are being
# installed in a situation where a bottle is required.
class UnbottledError < RuntimeError
def initialize(formulae)
require "utils"
msg = +<<~EOS
The following #{Utils.pluralize("formula", formulae.count, plural: "e")} cannot be installed from #{Utils.pluralize("bottle", formulae.count)} and must be
built from source.
#{formulae.to_sentence}
EOS
msg += "#{DevelopmentTools.installation_instructions}\n" unless DevelopmentTools.installed?
msg.freeze
super(msg)
end
end
# Raised by `Homebrew.install`, `Homebrew.reinstall` and `Homebrew.upgrade`
# if the user passes any flags/environment that would case a bottle-only
# installation on a system without build tools to fail.
class BuildFlagsError < RuntimeError
def initialize(flags, bottled: true)
if flags.length > 1
flag_text = "flags"
require_text = "require"
else
flag_text = "flag"
require_text = "requires"
end
bottle_text = if bottled
<<~EOS
Alternatively, remove the #{flag_text} to attempt bottle installation.
EOS
end
message = <<~EOS
The following #{flag_text}:
2015-08-22 13:15:33 +08:00
#{flags.join(", ")}
#{require_text} building tools, but none are installed.
#{DevelopmentTools.installation_instructions} #{bottle_text}
EOS
super message
end
end
# Raised by {CompilerSelector} if the formula fails with all of
# the compilers available on the user's system.
class CompilerSelectionError < RuntimeError
def initialize(formula)
2017-10-15 02:28:32 +02:00
super <<~EOS
2016-07-16 21:03:36 +01:00
#{formula.full_name} cannot be built with any available compilers.
#{DevelopmentTools.custom_installation_instructions}
EOS
end
end
# Raised in {Downloadable#fetch}.
class DownloadError < RuntimeError
attr_reader :cause
def initialize(downloadable, cause)
2017-10-15 02:28:32 +02:00
super <<~EOS
Failed to download resource #{downloadable.download_name.inspect}
2014-12-29 22:51:55 -05:00
#{cause.message}
2018-06-06 23:34:19 -04:00
EOS
@cause = cause
2014-12-29 22:51:55 -05:00
set_backtrace(cause.backtrace)
end
end
# Raised in {CurlDownloadStrategy#fetch}.
class CurlDownloadStrategyError < RuntimeError
def initialize(url)
case url
when %r{^file://(.+)}
super "File does not exist: #{Regexp.last_match(1)}"
else
super "Download failed: #{url}"
end
end
end
# Raised in {HomebrewCurlDownloadStrategy#fetch}.
class HomebrewCurlDownloadStrategyError < CurlDownloadStrategyError
def initialize(url)
super "Homebrew-installed `curl` is not installed for: #{url}"
end
end
# Raised by {Kernel#safe_system} in `utils.rb`.
class ErrorDuringExecution < RuntimeError
2020-07-07 11:29:33 +01:00
attr_reader :cmd, :status, :output
2018-07-30 10:11:00 +02:00
def initialize(cmd, status:, output: nil, secrets: [])
@cmd = cmd
2018-07-30 10:11:00 +02:00
@status = status
@output = output
2018-07-30 10:11:00 +02:00
raise ArgumentError, "Status cannot be nil." if status.nil?
2020-12-17 15:45:50 +01:00
exitstatus = case status
when Integer
status
when Hash
status["exitstatus"]
else
status.exitstatus
end
termsig = case status
when Integer
nil
when Hash
status["termsig"]
2020-12-17 15:45:50 +01:00
else
status.termsig
end
redacted_cmd = redact_secrets(cmd.shelljoin.gsub('\=', "="), secrets)
2020-12-17 15:45:50 +01:00
reason = if exitstatus
"exited with #{exitstatus}"
elsif termsig
"was terminated by uncaught signal #{Signal.signame(termsig)}"
2020-12-17 15:45:50 +01:00
else
raise ArgumentError, "Status neither has `exitstatus` nor `termsig`."
2020-12-17 15:45:50 +01:00
end
s = +"Failure while executing; `#{redacted_cmd}` #{reason}."
2018-07-16 23:17:16 +02:00
2020-07-13 22:48:53 +10:00
if Array(output).present?
format_output_line = lambda do |type_line|
type, line = *type_line
if type == :stderr
Formatter.error(line)
else
line
end
end
2018-07-16 23:17:16 +02:00
s << " Here's the output:\n"
s << output.map(&format_output_line).join
s << "\n" unless s.end_with?("\n")
2018-07-16 23:17:16 +02:00
end
2019-04-20 14:07:29 +09:00
super s.freeze
end
2020-10-20 12:03:48 +02:00
sig { returns(String) }
def stderr
2020-07-13 22:48:53 +10:00
Array(output).select { |type,| type == :stderr }.map(&:last).join
end
end
# Raised by {Pathname#verify_checksum} when "expected" is nil or empty.
2013-08-06 21:48:05 -07:00
class ChecksumMissingError < ArgumentError; end
# Raised by {Pathname#verify_checksum} when verification fails.
class ChecksumMismatchError < RuntimeError
attr_reader :expected
2020-11-19 19:44:23 +01:00
def initialize(path, expected, actual)
@expected = expected
2017-10-15 02:28:32 +02:00
super <<~EOS
SHA256 mismatch
2020-11-19 19:44:23 +01:00
Expected: #{Formatter.success(expected.to_s)}
Actual: #{Formatter.error(actual.to_s)}
File: #{path}
To retry an incomplete download, remove the file above.
2018-06-06 23:34:19 -04:00
EOS
end
end
2020-08-14 05:28:26 +02:00
# Raised when a resource is missing.
class ResourceMissingError < ArgumentError
def initialize(formula, resource)
2015-05-27 22:16:48 +08:00
super "#{formula.full_name} does not define resource #{resource.inspect}"
end
end
2020-08-14 05:28:26 +02:00
# Raised when a resource is specified multiple times.
class DuplicateResourceError < ArgumentError
def initialize(resource)
super "Resource #{resource.inspect} is defined more than once"
end
end
# Raised when a single patch file is not found and apply hasn't been specified.
class MissingApplyError < RuntimeError; end
2020-08-14 05:28:26 +02:00
# Raised when a bottle does not contain a formula file.
class BottleFormulaUnavailableError < RuntimeError
def initialize(bottle_path, formula_path)
2017-10-15 02:28:32 +02:00
super <<~EOS
This bottle does not contain the formula file:
#{bottle_path}
#{formula_path}
EOS
end
end
# Raised when a `Utils.safe_fork` exits with a non-zero code.
class ChildProcessError < RuntimeError
attr_reader :status
def initialize(status)
@status = status
super "Forked child process failed: #{status}"
end
end
# Raised when `detected_perl_shebang` etc cannot detect the shebang.
class ShebangDetectionError < RuntimeError
def initialize(type, reason)
super "Cannot detect #{type} shebang: #{reason}."
end
end
# Raised when one or more formulae have cyclic dependencies.
class CyclicDependencyError < RuntimeError
def initialize(strongly_connected_components)
super <<~EOS
The following packages contain cyclic dependencies:
#{strongly_connected_components.select { |packages| packages.count > 1 }.map(&:to_sentence).join("\n ")}
EOS
end
end