brew/Library/Homebrew/unpack_strategy.rb

244 lines
6.8 KiB
Ruby
Raw Permalink Normal View History

# typed: strict
# frozen_string_literal: true
require "mktemp"
require "system_command"
2020-08-09 06:09:05 +02:00
# Module containing all available strategies for unpacking archives.
module UnpackStrategy
extend T::Helpers
2024-07-05 22:29:41 -07:00
include SystemCommand::Mixin
abstract!
requires_ancestor { Kernel }
2024-07-06 08:10:16 -07:00
UnpackStrategyType = T.type_alias { T.all(T::Class[UnpackStrategy], UnpackStrategy::ClassMethods) }
2024-07-05 22:29:41 -07:00
module ClassMethods
extend T::Helpers
abstract!
2024-07-05 22:29:41 -07:00
sig { abstract.returns(T::Array[String]) }
def extensions; end
sig { abstract.params(path: Pathname).returns(T::Boolean) }
def can_extract?(path); end
end
2024-07-05 22:29:41 -07:00
mixes_in_class_methods(ClassMethods)
2024-07-06 08:10:16 -07:00
sig { returns(T.nilable(T::Array[UnpackStrategyType])) }
2018-07-01 23:35:29 +02:00
def self.strategies
@strategies ||= T.let([
2021-09-16 15:56:31 +01:00
Tar, # Needs to be before Bzip2/Gzip/Xz/Lzma/Zstd.
2018-09-11 19:21:18 +02:00
Pax,
Gzip,
Dmg, # Needs to be before Bzip2/Xz/Lzma.
Lzma,
Xz,
2021-09-16 15:56:31 +01:00
Zstd,
Lzip,
2020-08-09 06:09:05 +02:00
Air, # Needs to be before `Zip`.
Jar, # Needs to be before `Zip`.
LuaRock, # Needs to be before `Zip`.
MicrosoftOfficeXml, # Needs to be before `Zip`.
2018-07-29 10:33:47 +02:00
Zip,
2020-08-09 06:09:05 +02:00
Pkg, # Needs to be before `Xar`.
2018-07-29 10:33:47 +02:00
Xar,
Ttf,
Otf,
Git,
Mercurial,
Subversion,
Cvs,
2020-08-09 06:09:05 +02:00
SelfExtractingExecutable, # Needs to be before `Cab`.
Cab,
Executable,
2018-07-25 23:34:17 +02:00
Bzip2,
Fossil,
Bazaar,
2018-07-27 11:42:34 +02:00
Compress,
P7Zip,
Sit,
Rar,
Lha,
2024-07-06 08:10:16 -07:00
].freeze, T.nilable(T::Array[UnpackStrategyType]))
2018-07-01 23:35:29 +02:00
end
private_class_method :strategies
2024-07-06 08:10:16 -07:00
sig { params(type: Symbol).returns(T.nilable(UnpackStrategyType)) }
def self.from_type(type)
type = {
2018-11-02 17:18:07 +00:00
naked: :uncompressed,
nounzip: :uncompressed,
seven_zip: :p7zip,
}.fetch(type, type)
begin
2018-09-03 21:40:19 +02:00
const_get(type.to_s.split("_").map(&:capitalize).join.gsub(/\d+[a-z]/, &:upcase))
rescue NameError
nil
end
end
2024-07-06 08:10:16 -07:00
sig { params(extension: String).returns(T.nilable(UnpackStrategyType)) }
def self.from_extension(extension)
2024-07-05 21:14:03 +01:00
return unless strategies
strategies&.sort_by { |s| s.extensions.map(&:length).max || 0 }
&.reverse
&.find { |s| s.extensions.any? { |ext| extension.end_with?(ext) } }
end
2018-07-01 23:35:29 +02:00
2024-07-06 08:10:16 -07:00
sig { params(path: Pathname).returns(T.nilable(UnpackStrategyType)) }
def self.from_magic(path)
strategies&.find { |s| s.can_extract?(path) }
end
sig {
params(path: Pathname, prioritize_extension: T::Boolean, type: T.nilable(Symbol), ref_type: T.nilable(Symbol),
2024-07-05 21:14:03 +01:00
ref: T.nilable(String), merge_xattrs: T::Boolean).returns(T.untyped)
}
2024-07-05 21:14:03 +01:00
def self.detect(path, prioritize_extension: false, type: nil, ref_type: nil, ref: nil, merge_xattrs: false)
strategy = from_type(type) if type
2018-07-01 23:35:29 +02:00
if prioritize_extension && path.extname.present?
strategy ||= from_extension(path.extname)
2024-07-05 21:14:03 +01:00
strategy ||= strategies&.find { |s| (s < Directory || s == Fossil) && s.can_extract?(path) }
else
strategy ||= from_magic(path)
strategy ||= from_extension(path.extname)
end
strategy ||= Uncompressed
2024-03-07 16:20:20 +00:00
strategy.new(path, ref_type:, ref:, merge_xattrs:)
2018-07-01 23:35:29 +02:00
end
sig { returns(Pathname) }
attr_reader :path
2024-07-05 21:14:03 +01:00
sig { returns(T::Boolean) }
attr_reader :merge_xattrs
2018-07-01 23:35:29 +02:00
sig {
params(path: T.any(String, Pathname), ref_type: T.nilable(Symbol), ref: T.nilable(String),
2024-07-05 21:14:03 +01:00
merge_xattrs: T::Boolean).void
}
2024-07-05 21:14:03 +01:00
def initialize(path, ref_type: nil, ref: nil, merge_xattrs: false)
@path = T.let(Pathname(path).expand_path, Pathname)
@ref_type = T.let(ref_type, T.nilable(Symbol))
@ref = T.let(ref, T.nilable(String))
@merge_xattrs = T.let(merge_xattrs, T::Boolean)
2018-07-01 23:35:29 +02:00
end
sig { abstract.params(unpack_dir: Pathname, basename: Pathname, verbose: T::Boolean).void }
def extract_to_dir(unpack_dir, basename:, verbose:); end
private :extract_to_dir
sig {
params(
to: T.nilable(Pathname), basename: T.nilable(T.any(String, Pathname)), verbose: T::Boolean,
).void
}
def extract(to: nil, basename: nil, verbose: false)
2018-07-09 20:04:33 +02:00
basename ||= path.basename
2018-07-01 23:35:29 +02:00
unpack_dir = Pathname(to || Dir.pwd).expand_path
unpack_dir.mkpath
2024-03-07 16:20:20 +00:00
extract_to_dir(unpack_dir, basename: Pathname(basename), verbose:)
2018-07-01 23:35:29 +02:00
end
2018-07-16 19:00:49 +02:00
sig {
params(
to: T.nilable(Pathname),
basename: T.nilable(T.any(String, Pathname)),
verbose: T::Boolean,
prioritize_extension: T::Boolean,
).returns(T.untyped)
}
def extract_nestedly(to: nil, basename: nil, verbose: false, prioritize_extension: false)
Mktemp.new("homebrew-unpack").run(chdir: false) do |unpack_dir|
tmp_unpack_dir = T.must(unpack_dir.tmpdir)
2024-03-07 16:20:20 +00:00
extract(to: tmp_unpack_dir, basename:, verbose:)
2018-07-16 19:00:49 +02:00
children = tmp_unpack_dir.children
2023-11-05 08:55:58 -08:00
if children.size == 1 && !children.fetch(0).directory?
first_child = children.first
next if first_child.nil?
s = UnpackStrategy.detect(first_child, prioritize_extension:)
2018-07-16 19:00:49 +02:00
2024-03-07 16:20:20 +00:00
s.extract_nestedly(to:, verbose:, prioritize_extension:)
2018-07-16 19:00:49 +02:00
next
end
# Ensure all extracted directories are writable.
2023-03-20 13:15:43 -07:00
each_directory(tmp_unpack_dir) do |path|
next if path.writable?
2024-03-07 16:20:20 +00:00
FileUtils.chmod "u+w", path, verbose:
end
Directory.new(tmp_unpack_dir, move: true).extract(to:, verbose:)
2018-07-16 19:00:49 +02:00
end
end
2024-07-27 21:30:10 +10:00
sig { returns(T.any(T::Array[Cask::Cask], T::Array[Formula])) }
def dependencies
[]
end
2023-03-20 13:15:43 -07:00
# Helper method for iterating over directory trees.
sig {
params(
2023-03-20 13:16:31 -07:00
pathname: Pathname,
_block: T.proc.params(path: Pathname).void,
).returns(T.nilable(Pathname))
2023-03-20 13:15:43 -07:00
}
def each_directory(pathname, &_block)
pathname.find do |path|
yield path if path.directory?
end
end
2018-07-01 23:35:29 +02:00
end
require "unpack_strategy/air"
require "unpack_strategy/bazaar"
require "unpack_strategy/bzip2"
require "unpack_strategy/cab"
require "unpack_strategy/compress"
require "unpack_strategy/cvs"
require "unpack_strategy/directory"
require "unpack_strategy/dmg"
require "unpack_strategy/executable"
require "unpack_strategy/fossil"
require "unpack_strategy/generic_unar"
require "unpack_strategy/git"
require "unpack_strategy/gzip"
require "unpack_strategy/jar"
require "unpack_strategy/lha"
require "unpack_strategy/lua_rock"
require "unpack_strategy/lzip"
require "unpack_strategy/lzma"
require "unpack_strategy/mercurial"
require "unpack_strategy/microsoft_office_xml"
require "unpack_strategy/otf"
require "unpack_strategy/p7zip"
2018-09-11 19:21:18 +02:00
require "unpack_strategy/pax"
require "unpack_strategy/pkg"
require "unpack_strategy/rar"
require "unpack_strategy/self_extracting_executable"
require "unpack_strategy/sit"
require "unpack_strategy/subversion"
require "unpack_strategy/tar"
require "unpack_strategy/ttf"
require "unpack_strategy/uncompressed"
require "unpack_strategy/xar"
require "unpack_strategy/xz"
require "unpack_strategy/zip"
2021-09-16 15:56:31 +01:00
require "unpack_strategy/zstd"