omnizip 0.4.5 → 0.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +30 -0
- data/README.adoc +22 -0
- data/lib/omnizip/archive_handlers/zip_handler.rb +11 -0
- data/lib/omnizip/backends.rb +20 -0
- data/lib/omnizip/implementations/rust/archive.rb +93 -0
- data/lib/omnizip/implementations/rust/library.rb +72 -18
- data/lib/omnizip/implementations/rust.rb +1 -0
- data/lib/omnizip/version.rb +1 -1
- data/lib/tasks/platform_gems.rake +107 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f958a6bde5f52729231c66fcc680ecaf30bd3bb3e3a04f59bd843a63f5d664ec
|
|
4
|
+
data.tar.gz: c35f940965b7776bbab14f41258917bf4ae444899ec70d4c0713a36a12d5c05b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6e219d9d7052b667c112709d0ebaa4a4e3b6925e374694f1fd646114e6ab7d2157e255a07a8e05b9a42a9234b4e61841617ce4682b6e79727ee6a4381a4e2b35
|
|
7
|
+
data.tar.gz: f6921e7de8606e19dd28e62d4a00a8bec55b2f0bc6f4a4478805df6299f88b912aa6b8ea56fbf774f6df42a7f29cc707d709c4dc250c702a5d3b4ca91d785601
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
### Added
|
|
11
|
+
- Prebuilt-binary distribution (parsanol model): platform gems
|
|
12
|
+
(x86_64/aarch64/arm Linux glibc+musl, arm64/x86_64 macOS,
|
|
13
|
+
x64/aarch64-mingw-ucrt, x64-mingw32) now vendor the prebuilt
|
|
14
|
+
`omnizip-ffi` cdylib — `gem install omnizip` gives Rust
|
|
15
|
+
acceleration with zero compilation on those platforms. The plain
|
|
16
|
+
`ruby` gem and every platform without a binary (JRuby,
|
|
17
|
+
TruffleRuby, anything else) stay fully functional on the pure-Ruby
|
|
18
|
+
core. The cdylibs are cross-built by omnizip-rs's release-binary
|
|
19
|
+
workflow and attached to each `omnizip-ffi-v*` release;
|
|
20
|
+
`rake platform_gems:build` packages them and `gem-build.yml`
|
|
21
|
+
smoke-tests each installed gem (tier active + pure-Ruby path with
|
|
22
|
+
`OMNIZIP_NO_RUST=1`).
|
|
23
|
+
- `OMNIZIP_NO_RUST=1` kill switch forces the pure-Ruby core
|
|
24
|
+
everywhere; `Library.resolve_path` (public now) prefers the
|
|
25
|
+
vendored cdylib over dev checkouts; `Library.dylib_version`
|
|
26
|
+
reports the Rust release a loaded cdylib was built from
|
|
27
|
+
(`ozip_version`, new in omnizip-rs 0.21.109).
|
|
28
|
+
|
|
10
29
|
### Fixed
|
|
11
30
|
- PPMd works for the first time: the pure-Ruby ppmd7 core cannot
|
|
12
31
|
decode its own output (root-only decoder, 100-symbol cap — proven)
|
|
@@ -25,6 +44,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
25
44
|
route decode through the Rust backend when it loads, each pinned by
|
|
26
45
|
a cross-implementation differential. A Rust failure falls back to
|
|
27
46
|
the Ruby core — auto mode is never worse than pure Ruby.
|
|
47
|
+
- Archive-level acceleration: `Implementations::Rust::Archive` binds
|
|
48
|
+
the cdylib's whole-archive surface (open, list entries, read entry)
|
|
49
|
+
over every multi-entry format — zip, tar, cpio, 7z, rar3/4/5, iso,
|
|
50
|
+
rpm, xar, ole, encrypted archives included. `ZipHandler#list` and
|
|
51
|
+
`#read_entry` route through it first (auto mode; any Rust error or
|
|
52
|
+
a missing cdylib falls back to the pure-Ruby path). The tier's
|
|
53
|
+
600-mutant fuzz gate also caught a Rust decompression bomb
|
|
54
|
+
(truncated deflate streams decoded an endless phantom bit-stream;
|
|
55
|
+
fixed upstream in omnizip 0.21.108).
|
|
56
|
+
- `Library#bind` / `Library#take_buffer` seams the archive surface
|
|
57
|
+
rides (Fiddle symbol binding + copy-out-and-free).
|
|
28
58
|
|
|
29
59
|
### Fixed
|
|
30
60
|
- RAR recovery-record repair fails loudly instead of pretending:
|
data/README.adoc
CHANGED
|
@@ -28,6 +28,28 @@ It provides both a command-line interface and a programmatic Ruby API, making it
|
|
|
28
28
|
|
|
29
29
|
See link:readme-docs/compression-algorithms.adoc[Compression Algorithms Guide] for detailed information.
|
|
30
30
|
|
|
31
|
+
==== Rust Acceleration (prebuilt, zero compilation)
|
|
32
|
+
|
|
33
|
+
Where the platform gem exists, `gem install omnizip` ships a prebuilt
|
|
34
|
+
`omnizip-ffi` cdylib and the codec + archive tiers route through it
|
|
35
|
+
automatically — no Rust toolchain, no compilation:
|
|
36
|
+
|
|
37
|
+
* x86_64-linux, aarch64-linux, arm-linux (glibc and musl)
|
|
38
|
+
* arm64-darwin, x86_64-darwin
|
|
39
|
+
* x64-mingw-ucrt, aarch64-mingw-ucrt, x64-mingw32
|
|
40
|
+
|
|
41
|
+
Everywhere else — including JRuby/TruffleRuby and any platform without
|
|
42
|
+
a binary — the gem stays fully functional on its pure-Ruby core (the
|
|
43
|
+
acceleration is strictly optional). Three escape hatches:
|
|
44
|
+
|
|
45
|
+
* `OMNIZIP_NO_RUST=1` — force the pure-Ruby core everywhere.
|
|
46
|
+
* `OMNIZIP_FFI_DYLIB=/path/to/libomnizip_ffi` — load a specific build.
|
|
47
|
+
* `rake rust:build` — build and vendor the cdylib yourself from a
|
|
48
|
+
checkout (developers).
|
|
49
|
+
|
|
50
|
+
Any Rust-side failure also falls back to the Ruby core mid-operation,
|
|
51
|
+
so `auto` mode is never worse than pure Ruby.
|
|
52
|
+
|
|
31
53
|
==== SDK-Compatible LZMA Mode (v0.2.0)
|
|
32
54
|
|
|
33
55
|
Omnizip now provides SDK-compatible LZMA encoding and decoding for full XZ/LZMA tool compatibility.
|
|
@@ -48,6 +48,14 @@ module Omnizip
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def list(path, details: false, **_)
|
|
51
|
+
# Whole-archive tier (names-only path): the Rust archive
|
|
52
|
+
# surface lists any supported format; the Ruby reader stays
|
|
53
|
+
# the fallback and the details path's source.
|
|
54
|
+
unless details
|
|
55
|
+
tier_names = Omnizip::Backends.archive_entry_names(path)
|
|
56
|
+
return tier_names if tier_names
|
|
57
|
+
end
|
|
58
|
+
|
|
51
59
|
reader = Formats::Zip::Reader.new(path)
|
|
52
60
|
return reader.entries.map(&:filename) unless details
|
|
53
61
|
|
|
@@ -65,6 +73,9 @@ module Omnizip
|
|
|
65
73
|
end
|
|
66
74
|
|
|
67
75
|
def read_entry(path, entry_name)
|
|
76
|
+
tier = Omnizip::Backends.archive_read_entry(path, entry_name)
|
|
77
|
+
return tier unless tier.nil?
|
|
78
|
+
|
|
68
79
|
Formats::Zip::Reader.new(path).read_entry(entry_name)
|
|
69
80
|
end
|
|
70
81
|
|
data/lib/omnizip/backends.rb
CHANGED
|
@@ -94,6 +94,26 @@ module Omnizip
|
|
|
94
94
|
end
|
|
95
95
|
end
|
|
96
96
|
|
|
97
|
+
# Whole-archive tier helpers: nil means "Rust unavailable or
|
|
98
|
+
# entry not found — caller falls back to the Ruby reader".
|
|
99
|
+
def archive_entry_names(path)
|
|
100
|
+
Omnizip::Implementations::Rust::Archive.open(File.binread(path)) { |a| return a.entry_names }
|
|
101
|
+
rescue Omnizip::Implementations::Rust::Archive::LibraryMissing, Omnizip::Implementations::Rust::Error, StandardError
|
|
102
|
+
nil
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# rubocop:disable-next Lint/ReturnInVoidContext
|
|
106
|
+
def archive_read_entry(path, entry_name)
|
|
107
|
+
Omnizip::Implementations::Rust::Archive.open(File.binread(path)) do |a|
|
|
108
|
+
idx = a.entry_names.index(entry_name)
|
|
109
|
+
return nil if idx.nil?
|
|
110
|
+
|
|
111
|
+
return a.read_entry(idx)
|
|
112
|
+
end
|
|
113
|
+
rescue Omnizip::Implementations::Rust::Archive::LibraryMissing, Omnizip::Implementations::Rust::Error, StandardError
|
|
114
|
+
nil
|
|
115
|
+
end
|
|
116
|
+
|
|
97
117
|
private
|
|
98
118
|
|
|
99
119
|
def backend_rust
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Omnizip
|
|
4
|
+
module Implementations
|
|
5
|
+
module Rust
|
|
6
|
+
# Whole-archive acceleration over the cdylib: open any supported
|
|
7
|
+
# format (auto-detected), list entries, read entry bytes. This is
|
|
8
|
+
# the archive-level tier surface — the Ruby core remains the
|
|
9
|
+
# fallback (the cdylib unavailable ⇒ these raise LibraryMissing
|
|
10
|
+
# and callers fall back).
|
|
11
|
+
class Archive
|
|
12
|
+
class LibraryMissing < StandardError; end
|
|
13
|
+
|
|
14
|
+
# Built lazily inside #initialize: the Fiddle::TYPE_* constants
|
|
15
|
+
# only resolve after a successful require, and Ruby 4.0 moved
|
|
16
|
+
# fiddle out of the default gems (same reason
|
|
17
|
+
# Library.function_table is a method, not a constant).
|
|
18
|
+
def initialize(data, password: nil)
|
|
19
|
+
lib = Library.instance
|
|
20
|
+
raise LibraryMissing, "omnizip-ffi cdylib not loaded" unless lib
|
|
21
|
+
|
|
22
|
+
@lib = lib
|
|
23
|
+
funcs = function_table
|
|
24
|
+
@funcs = funcs.each_with_object({}) do |(key, (name, args, ret)), h|
|
|
25
|
+
h[key] = lib.bind(name, args, ret)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
pw = password&.to_s
|
|
29
|
+
pw_ptr = pw && !pw.empty? ? Fiddle::Pointer[pw] : nil
|
|
30
|
+
@handle = @funcs[:arch_open].call(Fiddle::Pointer[data], data.bytesize, pw_ptr)
|
|
31
|
+
return unless @handle.null?
|
|
32
|
+
|
|
33
|
+
raise Error, @lib.last_error
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def entry_count
|
|
37
|
+
@funcs[:arch_count].call(@handle)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def entry_names
|
|
41
|
+
(0...entry_count).map do |i|
|
|
42
|
+
ptr = @funcs[:arch_entry_name].call(@handle, i)
|
|
43
|
+
next nil if ptr.null?
|
|
44
|
+
|
|
45
|
+
ptr.to_s
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def entry_size(index)
|
|
50
|
+
@funcs[:arch_entry_size].call(@handle, index)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Read one entry's uncompressed bytes.
|
|
54
|
+
#
|
|
55
|
+
# @return [String] binary
|
|
56
|
+
def read_entry(index)
|
|
57
|
+
out_len = [0].pack("Q")
|
|
58
|
+
ptr = @funcs[:arch_read_entry].call(@handle, index, out_len)
|
|
59
|
+
raise Error, @lib.last_error if ptr.null?
|
|
60
|
+
|
|
61
|
+
@lib.take_buffer(ptr, out_len.unpack1("Q"))
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def close
|
|
65
|
+
return unless @handle
|
|
66
|
+
|
|
67
|
+
@funcs[:arch_close].call(@handle)
|
|
68
|
+
@handle = nil
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def self.open(data, password: nil)
|
|
72
|
+
archive = new(data, password: password)
|
|
73
|
+
yield archive
|
|
74
|
+
ensure
|
|
75
|
+
archive&.close
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
def function_table
|
|
81
|
+
{
|
|
82
|
+
arch_open: ["ozip_arch_open", %i[voidp size_t voidp], Fiddle::TYPE_VOIDP],
|
|
83
|
+
arch_count: ["ozip_arch_count", [:voidp], Fiddle::TYPE_SIZE_T],
|
|
84
|
+
arch_entry_name: ["ozip_arch_entry_name", %i[voidp size_t], Fiddle::TYPE_VOIDP],
|
|
85
|
+
arch_entry_size: ["ozip_arch_entry_size", %i[voidp size_t], Fiddle::TYPE_VOIDP],
|
|
86
|
+
arch_read_entry: ["ozip_arch_read_entry", %i[voidp size_t voidp], Fiddle::TYPE_VOIDP],
|
|
87
|
+
arch_close: ["ozip_arch_close", [:voidp], Fiddle::TYPE_VOID],
|
|
88
|
+
}
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -76,30 +76,47 @@ module Omnizip
|
|
|
76
76
|
}
|
|
77
77
|
end
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
rescue StandardError
|
|
91
|
-
warn "omnizip: rust backend unavailable (#{e.message}); using pure Ruby" if ENV["OMNIZIP_BACKEND"] == "rust"
|
|
79
|
+
# The Rust release the loaded cdylib was built from (e.g.
|
|
80
|
+
# "0.21.108"), read through the ozip_version symbol — the
|
|
81
|
+
# platform-gem smoke gate asserts it matches. nil when the
|
|
82
|
+
# library is absent or predates the symbol.
|
|
83
|
+
def dylib_version
|
|
84
|
+
handle = instance&.instance_handle
|
|
85
|
+
return nil if handle.nil?
|
|
86
|
+
|
|
87
|
+
func = Fiddle::Function.new(handle["ozip_version"], [], Fiddle::TYPE_VOIDP)
|
|
88
|
+
ptr = func.call
|
|
89
|
+
ptr.null? ? nil : ptr.to_s
|
|
90
|
+
rescue StandardError
|
|
92
91
|
nil
|
|
93
92
|
end
|
|
94
93
|
|
|
95
|
-
|
|
94
|
+
# Resolve the cdylib path. Precedence:
|
|
95
|
+
#
|
|
96
|
+
# 1. OMNIZIP_NO_RUST=1 — hard kill switch (always nil, so
|
|
97
|
+
# the pure-Ruby core handles everything; also the smoke
|
|
98
|
+
# gate for "works without Rust").
|
|
99
|
+
# 2. OMNIZIP_FFI_DYLIB — explicit path (developer override).
|
|
100
|
+
# 3. vendor/ — the prebuilt cdylib. Platform gems ship it
|
|
101
|
+
# here and `rake rust:build` writes here; rubygems
|
|
102
|
+
# already matched the platform at install time, so no
|
|
103
|
+
# runtime architecture probing is needed and the wrong
|
|
104
|
+
# arch cannot be picked.
|
|
105
|
+
# 4. ext/ (legacy location).
|
|
106
|
+
# 5. A sibling omnizip-rs checkout.
|
|
107
|
+
#
|
|
108
|
+
# `dirs` overrides 3-5 (spec seam: staged candidate paths).
|
|
109
|
+
def resolve_path(dirs = nil)
|
|
110
|
+
return nil if ENV.fetch("OMNIZIP_NO_RUST", nil) == "1"
|
|
111
|
+
|
|
96
112
|
explicit = ENV.fetch("OMNIZIP_FFI_DYLIB", nil)
|
|
97
113
|
return Pathname.new(explicit) if explicit && File.file?(explicit)
|
|
98
114
|
|
|
99
|
-
candidates = [
|
|
115
|
+
candidates = dirs || [
|
|
116
|
+
Pathname.new(__dir__).join("../../../../vendor"),
|
|
100
117
|
Pathname.new(__dir__).join("../../../../ext/libomnizip_ffi"),
|
|
101
|
-
sibling_checkout,
|
|
102
|
-
]
|
|
118
|
+
*sibling_checkout,
|
|
119
|
+
]
|
|
103
120
|
|
|
104
121
|
%w[.dylib .so .dll].each do |ext|
|
|
105
122
|
candidates.each do |dir|
|
|
@@ -110,6 +127,22 @@ module Omnizip
|
|
|
110
127
|
nil
|
|
111
128
|
end
|
|
112
129
|
|
|
130
|
+
private
|
|
131
|
+
|
|
132
|
+
def open
|
|
133
|
+
path = resolve_path
|
|
134
|
+
return nil if path.nil?
|
|
135
|
+
|
|
136
|
+
handle = Fiddle.dlopen(path.to_s)
|
|
137
|
+
funcs = function_table.each_with_object({}) do |(key, (name, args, ret)), h|
|
|
138
|
+
h[key] = Fiddle::Function.new(handle[name], args, ret)
|
|
139
|
+
end
|
|
140
|
+
new(funcs, handle)
|
|
141
|
+
rescue StandardError => e # includes Fiddle::DLError
|
|
142
|
+
warn "omnizip: rust backend unavailable (#{e.message}); using pure Ruby" if ENV["OMNIZIP_BACKEND"] == "rust"
|
|
143
|
+
nil
|
|
144
|
+
end
|
|
145
|
+
|
|
113
146
|
# A sibling omnizip-rs checkout: ../omnizip-rs relative to
|
|
114
147
|
# this gem's repository root.
|
|
115
148
|
def sibling_checkout
|
|
@@ -120,8 +153,9 @@ module Omnizip
|
|
|
120
153
|
end
|
|
121
154
|
end
|
|
122
155
|
|
|
123
|
-
def initialize(functions)
|
|
156
|
+
def initialize(functions, handle = nil)
|
|
124
157
|
@functions = functions.freeze
|
|
158
|
+
@handle = handle
|
|
125
159
|
end
|
|
126
160
|
|
|
127
161
|
def compress(codec, data, level)
|
|
@@ -136,11 +170,31 @@ module Omnizip
|
|
|
136
170
|
take(ptr, out_len.unpack1("Q"))
|
|
137
171
|
end
|
|
138
172
|
|
|
173
|
+
# Bind one extra C symbol from the loaded cdylib (the archive
|
|
174
|
+
# surface uses this; Fiddle::Function is immutable once built).
|
|
175
|
+
def bind(symbol, args, ret)
|
|
176
|
+
handle = instance_handle
|
|
177
|
+
raise Error, "cdylib not loaded" if handle.nil?
|
|
178
|
+
|
|
179
|
+
Fiddle::Function.new(handle[symbol.to_s], args, ret)
|
|
180
|
+
end
|
|
181
|
+
|
|
139
182
|
def last_error
|
|
140
183
|
ptr = @functions[:last_error].call
|
|
141
184
|
ptr.null? ? "unknown error" : ptr.to_s
|
|
142
185
|
end
|
|
143
186
|
|
|
187
|
+
# The raw Fiddle::Handle (for extra symbol binds).
|
|
188
|
+
def instance_handle
|
|
189
|
+
@handle
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Copy out + free one returned buffer (public seam for the
|
|
193
|
+
# archive surface).
|
|
194
|
+
def take_buffer(ptr, len)
|
|
195
|
+
take(ptr, len)
|
|
196
|
+
end
|
|
197
|
+
|
|
144
198
|
private
|
|
145
199
|
|
|
146
200
|
def call(name, codec, input, input_len, a, out_len)
|
data/lib/omnizip/version.rb
CHANGED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Build per-platform gems that vendor a prebuilt omnizip-ffi cdylib,
|
|
4
|
+
# so `gem install omnizip` gives Rust acceleration with zero
|
|
5
|
+
# compilation (parsanol distribution model, adopted 2026-09-21).
|
|
6
|
+
#
|
|
7
|
+
# # after downloading the release-binary artifacts:
|
|
8
|
+
# gh release download omnizip-ffi-v0.21.108 -R omnizip/omnizip-rs \
|
|
9
|
+
# -p 'libomnizip_ffi-*.tar.gz' -D artifacts
|
|
10
|
+
# bundle exec rake platform_gems:build DIR=artifacts
|
|
11
|
+
#
|
|
12
|
+
# Each target tarball maps to one rubygems platform; the built gems
|
|
13
|
+
# land in pkg/. The plain `ruby` platform gem keeps shipping without
|
|
14
|
+
# a binary (pure-Ruby core; OMNIZIP_NO_RUST=1 disables the tier).
|
|
15
|
+
|
|
16
|
+
# rust target → rubygems platform (keep in sync with
|
|
17
|
+
# omnizip-rs .github/workflows/release-binary.yml)
|
|
18
|
+
PLATFORM_GEM_TARGETS = {
|
|
19
|
+
"x86_64-unknown-linux-gnu" => "x86_64-linux",
|
|
20
|
+
"aarch64-unknown-linux-gnu" => "aarch64-linux",
|
|
21
|
+
"arm-unknown-linux-gnueabihf" => "arm-linux",
|
|
22
|
+
"x86_64-unknown-linux-musl" => "x86_64-linux-musl",
|
|
23
|
+
"aarch64-unknown-linux-musl" => "aarch64-linux-musl",
|
|
24
|
+
"arm-unknown-linux-musleabihf" => "arm-linux-musl",
|
|
25
|
+
"aarch64-apple-darwin" => "arm64-darwin",
|
|
26
|
+
"x86_64-apple-darwin" => "x86_64-darwin",
|
|
27
|
+
"x86_64-pc-windows-msvc" => "x64-mingw-ucrt",
|
|
28
|
+
"aarch64-pc-windows-msvc" => "aarch64-mingw-ucrt",
|
|
29
|
+
"x86_64-pc-windows-gnu" => "x64-mingw32",
|
|
30
|
+
}.freeze
|
|
31
|
+
|
|
32
|
+
namespace :platform_gems do
|
|
33
|
+
desc "Build one platform gem: TARGET=<rust target> DYLIB=<cdylib path>"
|
|
34
|
+
task :one do
|
|
35
|
+
target = ENV.fetch("TARGET") { abort "TARGET=<rust target> required" }
|
|
36
|
+
dylib = ENV.fetch("DYLIB") { abort "DYLIB=<cdylib path> required" }
|
|
37
|
+
platform = PLATFORM_GEM_TARGETS[target] or abort "unknown target #{target} (see lib/tasks/platform_gems.rake)"
|
|
38
|
+
out = build_platform_gem(platform, dylib)
|
|
39
|
+
puts "built: #{out}"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
desc "Build platform gems for every tarball in DIR=libomnizip_ffi-<target>.tar.gz"
|
|
43
|
+
task :build do
|
|
44
|
+
dir = ENV.fetch("DIR") { abort "DIR=<artifacts dir> required" }
|
|
45
|
+
Dir.glob(File.join(dir, "libomnizip_ffi-*.tar.gz")).each do |tarball|
|
|
46
|
+
target = File.basename(tarball, ".tar.gz").delete_prefix("libomnizip_ffi-")
|
|
47
|
+
platform = PLATFORM_GEM_TARGETS[target] or abort "no rubygems platform mapping for #{target}"
|
|
48
|
+
require "tmpdir"
|
|
49
|
+
Dir.mktmpdir do |tmp|
|
|
50
|
+
binary = extract_binary(tarball, tmp)
|
|
51
|
+
out = build_platform_gem(platform, binary)
|
|
52
|
+
puts "built: #{out} (#{File.size(out)} bytes)"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def extract_binary(tarball, tmp)
|
|
58
|
+
system("tar", "-xzf", tarball, "-C", tmp) or abort "tar failed for #{tarball}"
|
|
59
|
+
Dir.glob(File.join(tmp, "*")).first or abort "empty tarball #{tarball}"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Stage the tracked gem files plus the vendored cdylib, flip the
|
|
63
|
+
# gemspec platform, and build.
|
|
64
|
+
def build_platform_gem(platform, dylib)
|
|
65
|
+
require "fileutils"
|
|
66
|
+
require "rubygems/package"
|
|
67
|
+
require "tmpdir"
|
|
68
|
+
|
|
69
|
+
gem_root = File.expand_path("../..", __dir__)
|
|
70
|
+
spec = Gem::Specification.load(File.join(gem_root, "omnizip.gemspec"))
|
|
71
|
+
abort "omnizip.gemspec failed to load" unless spec
|
|
72
|
+
|
|
73
|
+
Dir.mktmpdir do |tmp|
|
|
74
|
+
stage = File.join(tmp, "stage")
|
|
75
|
+
spec.files.each do |rel|
|
|
76
|
+
src = File.join(gem_root, rel)
|
|
77
|
+
next unless File.file?(src)
|
|
78
|
+
|
|
79
|
+
dst = File.join(stage, rel)
|
|
80
|
+
FileUtils.mkdir_p(File.dirname(dst))
|
|
81
|
+
FileUtils.cp(src, dst)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
vendored = "vendor/libomnizip_ffi#{File.extname(dylib)}"
|
|
85
|
+
FileUtils.mkdir_p(File.join(stage, "vendor"))
|
|
86
|
+
FileUtils.cp(dylib, File.join(stage, vendored))
|
|
87
|
+
|
|
88
|
+
spec.platform = Gem::Platform.new(platform)
|
|
89
|
+
spec.files += [vendored]
|
|
90
|
+
spec.test_files = []
|
|
91
|
+
|
|
92
|
+
gemspec_path = File.join(stage, "omnizip.gemspec")
|
|
93
|
+
File.write(gemspec_path, spec.to_ruby)
|
|
94
|
+
|
|
95
|
+
pkg = File.join(tmp, "pkg")
|
|
96
|
+
FileUtils.mkdir_p(pkg)
|
|
97
|
+
Dir.chdir(stage) do
|
|
98
|
+
Gem::Package.build(spec)
|
|
99
|
+
end
|
|
100
|
+
built = Dir.glob(File.join(stage, "omnizip-*.gem")).first or
|
|
101
|
+
abort "gem build produced nothing for #{platform}"
|
|
102
|
+
FileUtils.mkdir_p(File.join(gem_root, "pkg"))
|
|
103
|
+
FileUtils.mv(built, File.join(gem_root, "pkg", File.basename(built)))
|
|
104
|
+
File.join(gem_root, "pkg", File.basename(built))
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: omnizip
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: base64
|
|
@@ -601,6 +601,7 @@ files:
|
|
|
601
601
|
- lib/omnizip/implementations/base/lzma_encoder_base.rb
|
|
602
602
|
- lib/omnizip/implementations/base/state_machine_base.rb
|
|
603
603
|
- lib/omnizip/implementations/rust.rb
|
|
604
|
+
- lib/omnizip/implementations/rust/archive.rb
|
|
604
605
|
- lib/omnizip/implementations/rust/library.rb
|
|
605
606
|
- lib/omnizip/implementations/seven_zip.rb
|
|
606
607
|
- lib/omnizip/implementations/seven_zip/lzma.rb
|
|
@@ -723,6 +724,7 @@ files:
|
|
|
723
724
|
- lib/omnizip/zip/input_stream.rb
|
|
724
725
|
- lib/omnizip/zip/output_stream.rb
|
|
725
726
|
- lib/tasks/msi_test_files.rake
|
|
727
|
+
- lib/tasks/platform_gems.rake
|
|
726
728
|
- lib/tasks/rust_backend.rake
|
|
727
729
|
- readme-docs/advanced-features.adoc
|
|
728
730
|
- readme-docs/api-usage.adoc
|