unpack_strategy: add zstd

This commit is contained in:
Alexander Bayandin 2021-09-16 15:56:31 +01:00
parent 109f24fd60
commit fe9a039774
No known key found for this signature in database
GPG Key ID: 444BD9CA93262701
7 changed files with 74 additions and 10 deletions

View File

@ -81,6 +81,10 @@ class DependencyCollector
Dependency.new("xz", tags) unless which("xz") Dependency.new("xz", tags) unless which("xz")
end end
def zstd_dep_if_needed(tags)
Dependency.new("zstd", tags) unless which("zstd")
end
def unzip_dep_if_needed(tags) def unzip_dep_if_needed(tags)
Dependency.new("unzip", tags) unless which("unzip") Dependency.new("unzip", tags) unless which("unzip")
end end
@ -171,6 +175,7 @@ class DependencyCollector
def parse_url_spec(url, tags) def parse_url_spec(url, tags)
case File.extname(url) case File.extname(url)
when ".xz" then xz_dep_if_needed(tags) when ".xz" then xz_dep_if_needed(tags)
when ".zst" then zstd_dep_if_needed(tags)
when ".zip" then unzip_dep_if_needed(tags) when ".zip" then unzip_dep_if_needed(tags)
when ".bz2" then bzip2_dep_if_needed(tags) when ".bz2" then bzip2_dep_if_needed(tags)
when ".lha", ".lzh" then Dependency.new("lha", tags) when ".lha", ".lzh" then Dependency.new("lha", tags)

View File

@ -231,7 +231,7 @@ class Pathname
bottle_ext, = HOMEBREW_BOTTLES_EXTNAME_REGEX.match(basename).to_a bottle_ext, = HOMEBREW_BOTTLES_EXTNAME_REGEX.match(basename).to_a
return bottle_ext if bottle_ext return bottle_ext if bottle_ext
archive_ext = basename[/(\.(tar|cpio|pax)\.(gz|bz2|lz|xz|Z))\Z/, 1] archive_ext = basename[/(\.(tar|cpio|pax)\.(gz|bz2|lz|xz|zst|Z))\Z/, 1]
return archive_ext if archive_ext return archive_ext if archive_ext
# Don't treat version numbers as extname. # Don't treat version numbers as extname.

View File

@ -0,0 +1,10 @@
# typed: false
# frozen_string_literal: true
require_relative "shared_examples"
describe UnpackStrategy::Zstd do
let(:path) { TEST_FIXTURE_DIR/"cask/container.tar.zst" }
include_examples "UnpackStrategy::detect"
end

View File

@ -44,12 +44,13 @@ module UnpackStrategy
def self.strategies def self.strategies
@strategies ||= [ @strategies ||= [
Tar, # Needs to be before Bzip2/Gzip/Xz/Lzma. Tar, # Needs to be before Bzip2/Gzip/Xz/Lzma/Zstd.
Pax, Pax,
Gzip, Gzip,
Dmg, # Needs to be before Bzip2/Xz/Lzma. Dmg, # Needs to be before Bzip2/Xz/Lzma.
Lzma, Lzma,
Xz, Xz,
Zstd,
Lzip, Lzip,
Air, # Needs to be before `Zip`. Air, # Needs to be before `Zip`.
Jar, # Needs to be before `Zip`. Jar, # Needs to be before `Zip`.
@ -203,3 +204,4 @@ require "unpack_strategy/uncompressed"
require "unpack_strategy/xar" require "unpack_strategy/xar"
require "unpack_strategy/xz" require "unpack_strategy/xz"
require "unpack_strategy/zip" require "unpack_strategy/zip"
require "unpack_strategy/zstd"

View File

@ -20,14 +20,15 @@ module UnpackStrategy
".tbz", ".tbz2", ".tar.bz2", ".tbz", ".tbz2", ".tar.bz2",
".tgz", ".tar.gz", ".tgz", ".tar.gz",
".tlzma", ".tar.lzma", ".tlzma", ".tar.lzma",
".txz", ".tar.xz" ".txz", ".tar.xz",
".tar.zst"
] ]
end end
def self.can_extract?(path) def self.can_extract?(path)
return true if path.magic_number.match?(/\A.{257}ustar/n) return true if path.magic_number.match?(/\A.{257}ustar/n)
return false unless [Bzip2, Gzip, Lzip, Xz].any? { |s| s.can_extract?(path) } return false unless [Bzip2, Gzip, Lzip, Xz, Zstd].any? { |s| s.can_extract?(path) }
# Check if `tar` can list the contents, then it can also extract it. # Check if `tar` can list the contents, then it can also extract it.
stdout, _, status = system_command("tar", args: ["--list", "--file", path], print_stderr: false) stdout, _, status = system_command("tar", args: ["--list", "--file", path], print_stderr: false)
@ -39,12 +40,12 @@ module UnpackStrategy
sig { override.params(unpack_dir: Pathname, basename: Pathname, verbose: T::Boolean).returns(T.untyped) } sig { override.params(unpack_dir: Pathname, basename: Pathname, verbose: T::Boolean).returns(T.untyped) }
def extract_to_dir(unpack_dir, basename:, verbose:) def extract_to_dir(unpack_dir, basename:, verbose:)
Dir.mktmpdir do |tmpdir| Dir.mktmpdir do |tmpdir|
tar_path = path tar_path = if DependencyCollector.tar_needs_xz_dependency? && Xz.can_extract?(path)
subextract(Xz, Pathname(tmpdir), verbose)
if DependencyCollector.tar_needs_xz_dependency? && Xz.can_extract?(path) elsif Zstd.can_extract?(path)
tmpdir = Pathname(tmpdir) subextract(Zstd, Pathname(tmpdir), verbose)
Xz.new(path).extract(to: tmpdir, verbose: verbose) else
tar_path = tmpdir.children.first path
end end
system_command! "tar", system_command! "tar",
@ -54,5 +55,13 @@ module UnpackStrategy
verbose: verbose verbose: verbose
end end
end end
sig {
params(extractor: T.any(T.class_of(Xz), T.class_of(Zstd)), dir: Pathname, verbose: T::Boolean).returns(Pathname)
}
def subextract(extractor, dir, verbose)
extractor.new(path).extract(to: dir, verbose: verbose)
T.must(dir.children.first)
end
end end
end end

View File

@ -0,0 +1,38 @@
# typed: true
# frozen_string_literal: true
module UnpackStrategy
# Strategy for unpacking zstd archives.
class Zstd
extend T::Sig
include UnpackStrategy
using Magic
sig { returns(T::Array[String]) }
def self.extensions
[".zst"]
end
def self.can_extract?(path)
path.magic_number.match?(/\x28\xB5\x2F\xFD/n)
end
def dependencies
@dependencies ||= [Formula["zstd"]]
end
private
sig { override.params(unpack_dir: Pathname, basename: Pathname, verbose: T::Boolean).returns(T.untyped) }
def extract_to_dir(unpack_dir, basename:, verbose:)
FileUtils.cp path, unpack_dir/basename, preserve: true
quiet_flags = verbose ? [] : ["-q"]
system_command! "unzstd",
args: [*quiet_flags, "-T0", "--", unpack_dir/basename],
env: { "PATH" => PATH.new(Formula["zstd"].opt_bin, ENV["PATH"]) },
verbose: verbose
end
end
end