2020-11-25 17:03:23 +01:00
|
|
|
# typed: true
|
2019-04-19 15:38:03 +09:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
require "download_strategy"
|
|
|
|
require "checksum"
|
|
|
|
require "version"
|
2018-07-13 14:42:49 +01:00
|
|
|
require "mktemp"
|
2022-06-30 16:08:13 +02:00
|
|
|
require "livecheck"
|
2022-06-30 17:46:53 +02:00
|
|
|
require "extend/on_system"
|
2013-09-17 21:25:38 -05:00
|
|
|
|
2013-09-17 21:25:38 -05:00
|
|
|
# Resource is the fundamental representation of an external resource. The
|
|
|
|
# primary formula download, along with other declared resources, are instances
|
|
|
|
# of this class.
|
2020-08-19 06:58:36 +02:00
|
|
|
#
|
|
|
|
# @api private
|
2013-08-06 19:52:58 -07:00
|
|
|
class Resource
|
2020-11-24 15:46:47 +01:00
|
|
|
extend T::Sig
|
|
|
|
|
2020-08-02 14:32:31 +02:00
|
|
|
include Context
|
2013-08-06 19:52:58 -07:00
|
|
|
include FileUtils
|
2020-12-06 18:53:00 -08:00
|
|
|
include OnOS
|
2013-08-06 19:52:58 -07:00
|
|
|
|
2018-01-21 08:29:38 -08:00
|
|
|
attr_reader :mirrors, :specs, :using, :source_modified_time, :patches, :owner
|
2015-01-12 00:37:24 -05:00
|
|
|
attr_writer :version
|
|
|
|
attr_accessor :download_strategy, :checksum
|
2013-09-17 21:25:38 -05:00
|
|
|
|
2013-09-17 21:25:38 -05:00
|
|
|
# Formula name must be set after the DSL, as we have no access to the
|
2020-11-05 17:17:03 -05:00
|
|
|
# formula name before initialization of the formula.
|
2018-01-21 08:29:38 -08:00
|
|
|
attr_accessor :name
|
2013-09-17 21:25:38 -05:00
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
def initialize(name = nil, &block)
|
2013-08-06 19:52:58 -07:00
|
|
|
@name = name
|
2013-09-23 21:39:19 -05:00
|
|
|
@url = nil
|
|
|
|
@version = nil
|
2013-09-17 21:25:38 -05:00
|
|
|
@mirrors = []
|
|
|
|
@specs = {}
|
|
|
|
@checksum = nil
|
|
|
|
@using = nil
|
2018-01-21 08:29:38 -08:00
|
|
|
@patches = []
|
2022-06-30 16:08:13 +02:00
|
|
|
@livecheck = nil
|
|
|
|
@livecheckable = false
|
2020-11-16 22:18:56 +01:00
|
|
|
instance_eval(&block) if block
|
2013-08-06 19:52:58 -07:00
|
|
|
end
|
|
|
|
|
2018-01-21 08:29:38 -08:00
|
|
|
def owner=(owner)
|
|
|
|
@owner = owner
|
|
|
|
patches.each { |p| p.owner = owner }
|
2021-10-04 14:21:03 +01:00
|
|
|
|
|
|
|
return if !owner.respond_to?(:full_name) || owner.full_name != "ca-certificates"
|
|
|
|
return if Homebrew::EnvConfig.no_insecure_redirect?
|
|
|
|
|
|
|
|
@specs[:insecure] = !specs[:bottle] && !DevelopmentTools.ca_file_handles_most_https_certificates?
|
2018-01-21 08:29:38 -08:00
|
|
|
end
|
|
|
|
|
2013-09-17 21:25:38 -05:00
|
|
|
def downloader
|
2018-11-28 20:59:16 -06:00
|
|
|
@downloader ||= download_strategy.new(url, download_name, version,
|
|
|
|
mirrors: mirrors.dup, **specs)
|
2013-08-06 19:52:58 -07:00
|
|
|
end
|
|
|
|
|
2020-11-05 17:17:03 -05:00
|
|
|
# Removes /s from resource names; this allows Go package names
|
2014-06-02 21:53:53 -07:00
|
|
|
# to be used as resource names without confusing software that
|
2020-11-05 17:17:03 -05:00
|
|
|
# interacts with {download_name}, e.g. `github.com/foo/bar`.
|
2014-06-02 21:53:53 -07:00
|
|
|
def escaped_name
|
2015-08-06 15:45:52 +08:00
|
|
|
name.tr("/", "-")
|
2014-06-02 21:53:53 -07:00
|
|
|
end
|
|
|
|
|
2013-09-17 21:25:40 -05:00
|
|
|
def download_name
|
2018-08-01 05:33:03 +02:00
|
|
|
return owner.name if name.nil?
|
|
|
|
return escaped_name if owner.nil?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2018-08-01 05:33:03 +02:00
|
|
|
"#{owner.name}--#{escaped_name}"
|
2013-09-17 21:25:40 -05:00
|
|
|
end
|
|
|
|
|
2020-05-12 12:37:54 +01:00
|
|
|
def downloaded?
|
|
|
|
cached_download.exist?
|
|
|
|
end
|
|
|
|
|
2013-09-17 21:25:40 -05:00
|
|
|
def cached_download
|
|
|
|
downloader.cached_location
|
|
|
|
end
|
|
|
|
|
2013-10-31 14:28:49 -05:00
|
|
|
def clear_cache
|
|
|
|
downloader.clear_cache
|
|
|
|
end
|
|
|
|
|
2018-10-18 21:42:43 -04:00
|
|
|
# Verifies download and unpacks it.
|
2020-08-19 06:58:36 +02:00
|
|
|
# The block may call `|resource, staging| staging.retain!` to retain the staging
|
2016-04-10 22:53:56 -04:00
|
|
|
# directory. Subclasses that override stage should implement the tmp
|
2018-10-18 21:42:43 -04:00
|
|
|
# dir using {Mktemp} so that works with all subtypes.
|
2020-08-19 06:58:36 +02:00
|
|
|
#
|
|
|
|
# @api public
|
2015-08-03 13:09:07 +01:00
|
|
|
def stage(target = nil, &block)
|
2020-05-31 20:35:48 +01:00
|
|
|
raise ArgumentError, "target directory or block is required" if !target && block.blank?
|
2014-12-13 22:51:21 -05:00
|
|
|
|
2020-05-13 11:49:17 +01:00
|
|
|
prepare_patches
|
2020-08-02 14:32:31 +02:00
|
|
|
fetch_patches(skip_downloaded: true)
|
|
|
|
fetch unless downloaded?
|
2020-03-04 17:48:17 -05:00
|
|
|
|
2013-10-30 00:43:10 -05:00
|
|
|
unpack(target, &block)
|
|
|
|
end
|
|
|
|
|
2020-05-13 11:49:17 +01:00
|
|
|
def prepare_patches
|
2018-01-21 08:29:38 -08:00
|
|
|
patches.grep(DATAPatch) { |p| p.path = owner.owner.path }
|
2020-05-13 11:49:17 +01:00
|
|
|
end
|
|
|
|
|
2020-08-02 14:32:31 +02:00
|
|
|
def fetch_patches(skip_downloaded: false)
|
2020-05-14 09:20:58 +01:00
|
|
|
external_patches = patches.select(&:external?)
|
|
|
|
external_patches.reject!(&:downloaded?) if skip_downloaded
|
2020-08-02 14:32:31 +02:00
|
|
|
external_patches.each(&:fetch)
|
2018-01-21 08:29:38 -08:00
|
|
|
end
|
|
|
|
|
|
|
|
def apply_patches
|
|
|
|
return if patches.empty?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2018-01-21 08:29:38 -08:00
|
|
|
ohai "Patching #{name}"
|
|
|
|
patches.each(&:apply)
|
|
|
|
end
|
|
|
|
|
2016-04-10 22:53:56 -04:00
|
|
|
# If a target is given, unpack there; else unpack to a temp folder.
|
2018-10-18 21:42:43 -04:00
|
|
|
# If block is given, yield to that block with `|stage|`, where stage
|
|
|
|
# is a {ResourceStageContext}.
|
2016-04-10 22:53:56 -04:00
|
|
|
# A target or a block must be given, but not both.
|
2015-08-03 13:09:07 +01:00
|
|
|
def unpack(target = nil)
|
2016-04-10 22:53:56 -04:00
|
|
|
mktemp(download_name) do |staging|
|
2021-02-12 11:10:18 -05:00
|
|
|
downloader.stage do
|
|
|
|
@source_modified_time = downloader.source_modified_time
|
|
|
|
apply_patches
|
|
|
|
if block_given?
|
|
|
|
yield ResourceStageContext.new(self, staging)
|
|
|
|
elsif target
|
|
|
|
target = Pathname(target)
|
|
|
|
target.install Pathname.pwd.children
|
|
|
|
end
|
2013-08-06 19:52:58 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-09-17 21:25:43 -05:00
|
|
|
Partial = Struct.new(:resource, :files)
|
|
|
|
|
|
|
|
def files(*files)
|
|
|
|
Partial.new(self, files)
|
|
|
|
end
|
|
|
|
|
2020-08-02 14:32:31 +02:00
|
|
|
def fetch(verify_download_integrity: true)
|
2014-02-18 15:08:03 -05:00
|
|
|
HOMEBREW_CACHE.mkpath
|
2014-10-10 20:30:29 -05:00
|
|
|
|
2020-08-02 14:32:31 +02:00
|
|
|
fetch_patches
|
2020-05-12 12:37:54 +01:00
|
|
|
|
2014-10-10 20:30:29 -05:00
|
|
|
begin
|
|
|
|
downloader.fetch
|
|
|
|
rescue ErrorDuringExecution, CurlDownloadStrategyError => e
|
|
|
|
raise DownloadError.new(self, e)
|
|
|
|
end
|
|
|
|
|
2019-06-15 17:22:45 +01:00
|
|
|
download = cached_download
|
2020-08-02 14:32:31 +02:00
|
|
|
verify_download_integrity(download) if verify_download_integrity
|
2019-06-15 17:22:45 +01:00
|
|
|
download
|
2013-08-06 19:52:58 -07:00
|
|
|
end
|
|
|
|
|
2020-08-02 14:32:31 +02:00
|
|
|
def verify_download_integrity(fn)
|
2014-12-05 18:08:21 -05:00
|
|
|
if fn.file?
|
2021-01-26 15:21:24 -05:00
|
|
|
ohai "Verifying checksum for '#{fn.basename}'" if verbose?
|
2013-10-30 00:43:10 -05:00
|
|
|
fn.verify_checksum(checksum)
|
|
|
|
end
|
2013-09-17 21:25:38 -05:00
|
|
|
rescue ChecksumMissingError
|
2020-11-19 19:44:23 +01:00
|
|
|
opoo <<~EOS
|
|
|
|
Cannot verify integrity of '#{fn.basename}'.
|
|
|
|
No checksum was provided for this resource.
|
|
|
|
For your reference, the checksum is:
|
|
|
|
sha256 "#{fn.sha256}"
|
|
|
|
EOS
|
2013-09-17 21:25:38 -05:00
|
|
|
end
|
|
|
|
|
2022-06-30 16:08:13 +02:00
|
|
|
# @!attribute [w] livecheck
|
|
|
|
# {Livecheck} can be used to check for newer versions of the software.
|
|
|
|
# This method evaluates the DSL specified in the livecheck block of the
|
|
|
|
# {Resource} (if it exists) and sets the instance variables of a {Livecheck}
|
|
|
|
# object accordingly. This is used by `brew livecheck` to check for newer
|
|
|
|
# versions of the software.
|
|
|
|
#
|
|
|
|
# <pre>livecheck do
|
|
|
|
# url "https://example.com/foo/releases"
|
|
|
|
# regex /foo-(\d+(?:\.\d+)+)\.tar/
|
|
|
|
# end</pre>
|
|
|
|
def livecheck(&block)
|
2022-06-30 17:01:11 +02:00
|
|
|
@livecheck ||= Livecheck.new(self) if block
|
2022-06-30 16:08:13 +02:00
|
|
|
return @livecheck unless block
|
|
|
|
|
|
|
|
@livecheckable = true
|
|
|
|
@livecheck.instance_eval(&block)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Whether a livecheck specification is defined or not.
|
|
|
|
# It returns true when a livecheck block is present in the {Resource} and
|
|
|
|
# false otherwise, and is used by livecheck.
|
|
|
|
def livecheckable?
|
|
|
|
@livecheckable == true
|
|
|
|
end
|
|
|
|
|
2021-01-07 08:33:57 +01:00
|
|
|
def sha256(val)
|
|
|
|
@checksum = Checksum.new(val)
|
2013-09-17 21:25:38 -05:00
|
|
|
end
|
|
|
|
|
2018-08-02 10:29:40 +02:00
|
|
|
def url(val = nil, **specs)
|
2013-09-17 21:25:38 -05:00
|
|
|
return @url if val.nil?
|
2018-09-17 02:45:00 +02:00
|
|
|
|
2021-10-04 14:21:03 +01:00
|
|
|
specs = specs.dup
|
|
|
|
# Don't allow this to be set.
|
|
|
|
specs.delete(:insecure)
|
|
|
|
|
2013-09-17 21:25:38 -05:00
|
|
|
@url = val
|
2021-10-04 14:21:03 +01:00
|
|
|
@using = specs.delete(:using)
|
2014-07-15 13:42:03 -05:00
|
|
|
@download_strategy = DownloadStrategyDetector.detect(url, using)
|
2021-10-04 14:21:03 +01:00
|
|
|
@specs.merge!(specs)
|
2021-04-15 18:06:54 +01:00
|
|
|
@downloader = nil
|
2013-09-17 21:25:38 -05:00
|
|
|
end
|
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
def version(val = nil)
|
2016-11-03 16:52:40 -07:00
|
|
|
@version ||= begin
|
|
|
|
version = detect_version(val)
|
|
|
|
version.null? ? nil : version
|
|
|
|
end
|
2013-09-17 21:25:38 -05:00
|
|
|
end
|
|
|
|
|
2015-08-03 13:09:07 +01:00
|
|
|
def mirror(val)
|
2013-09-17 21:25:38 -05:00
|
|
|
mirrors << val
|
|
|
|
end
|
|
|
|
|
2018-01-21 08:29:38 -08:00
|
|
|
def patch(strip = :p1, src = nil, &block)
|
|
|
|
p = Patch.create(strip, src, &block)
|
|
|
|
patches << p
|
|
|
|
end
|
|
|
|
|
2018-07-14 18:13:23 +01:00
|
|
|
protected
|
|
|
|
|
2020-08-19 17:12:32 +01:00
|
|
|
def mktemp(prefix, &block)
|
|
|
|
Mktemp.new(prefix).run(&block)
|
2018-07-14 18:13:23 +01:00
|
|
|
end
|
|
|
|
|
2013-09-17 21:25:38 -05:00
|
|
|
private
|
|
|
|
|
|
|
|
def detect_version(val)
|
2016-11-03 16:52:40 -07:00
|
|
|
return Version::NULL if val.nil? && url.nil?
|
2014-12-31 10:37:29 -05:00
|
|
|
|
2013-09-17 21:25:38 -05:00
|
|
|
case val
|
2020-08-09 02:59:18 +02:00
|
|
|
when nil then Version.detect(url, **specs)
|
2016-06-18 20:56:01 +03:00
|
|
|
when String then Version.create(val)
|
2014-05-27 21:41:43 -05:00
|
|
|
when Version then val
|
2013-09-17 21:25:38 -05:00
|
|
|
else
|
|
|
|
raise TypeError, "version '#{val.inspect}' should be a string"
|
|
|
|
end
|
2013-08-06 19:52:58 -07:00
|
|
|
end
|
2014-06-02 23:32:42 -07:00
|
|
|
|
2020-08-19 06:58:36 +02:00
|
|
|
# A resource containing a Go package.
|
2014-06-02 23:32:42 -07:00
|
|
|
class Go < Resource
|
2020-11-24 15:46:47 +01:00
|
|
|
def stage(target, &block)
|
|
|
|
super(target/name, &block)
|
2014-06-02 23:32:42 -07:00
|
|
|
end
|
|
|
|
end
|
2016-01-25 08:21:57 -08:00
|
|
|
|
2020-08-19 06:58:36 +02:00
|
|
|
# A resource containing a patch.
|
2018-01-21 08:29:38 -08:00
|
|
|
class PatchResource < Resource
|
2016-01-25 08:21:57 -08:00
|
|
|
attr_reader :patch_files
|
|
|
|
|
|
|
|
def initialize(&block)
|
|
|
|
@patch_files = []
|
2020-03-08 18:33:04 +00:00
|
|
|
@directory = nil
|
2016-01-25 08:21:57 -08:00
|
|
|
super "patch", &block
|
|
|
|
end
|
|
|
|
|
|
|
|
def apply(*paths)
|
|
|
|
paths.flatten!
|
|
|
|
@patch_files.concat(paths)
|
|
|
|
@patch_files.uniq!
|
|
|
|
end
|
2020-03-08 18:33:04 +00:00
|
|
|
|
|
|
|
def directory(val = nil)
|
|
|
|
return @directory if val.nil?
|
|
|
|
|
|
|
|
@directory = val
|
|
|
|
end
|
2016-01-25 08:21:57 -08:00
|
|
|
end
|
2013-08-06 19:52:58 -07:00
|
|
|
end
|
2016-04-22 16:54:09 -04:00
|
|
|
|
2020-11-05 17:17:03 -05:00
|
|
|
# The context in which a {Resource#stage} occurs. Supports access to both
|
2018-10-18 21:42:43 -04:00
|
|
|
# the {Resource} and associated {Mktemp} in a single block argument. The interface
|
|
|
|
# is back-compatible with {Resource} itself as used in that context.
|
2020-08-19 06:58:36 +02:00
|
|
|
#
|
|
|
|
# @api private
|
2016-04-22 16:54:09 -04:00
|
|
|
class ResourceStageContext
|
2020-10-20 12:03:48 +02:00
|
|
|
extend T::Sig
|
|
|
|
|
2016-04-22 16:54:09 -04:00
|
|
|
extend Forwardable
|
|
|
|
|
2020-11-05 17:17:03 -05:00
|
|
|
# The {Resource} that is being staged.
|
2016-04-22 16:54:09 -04:00
|
|
|
attr_reader :resource
|
2020-11-05 17:17:03 -05:00
|
|
|
# The {Mktemp} in which {#resource} is staged.
|
2016-04-22 16:54:09 -04:00
|
|
|
attr_reader :staging
|
|
|
|
|
|
|
|
def_delegators :@resource, :version, :url, :mirrors, :specs, :using, :source_modified_time
|
|
|
|
def_delegators :@staging, :retain!
|
|
|
|
|
|
|
|
def initialize(resource, staging)
|
|
|
|
@resource = resource
|
|
|
|
@staging = staging
|
|
|
|
end
|
|
|
|
|
2020-10-20 12:03:48 +02:00
|
|
|
sig { returns(String) }
|
2016-04-22 16:54:09 -04:00
|
|
|
def to_s
|
|
|
|
"<#{self.class}: resource=#{resource} staging=#{staging}>"
|
|
|
|
end
|
|
|
|
end
|