omnizip 0.4.6 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7dc6e02021b3915af5f65944e4d734bd3f95a5624b2a01c30100b6bbc36d5856
4
- data.tar.gz: 998838c2bd3df94ced6617b9d199a0f2eab552e332d285bdc6819860fef50324
3
+ metadata.gz: f958a6bde5f52729231c66fcc680ecaf30bd3bb3e3a04f59bd843a63f5d664ec
4
+ data.tar.gz: c35f940965b7776bbab14f41258917bf4ae444899ec70d4c0713a36a12d5c05b
5
5
  SHA512:
6
- metadata.gz: f02b277ef180e076b5e0deb3847ed4cdf3f2a83abf829095779cdbfbdbc811891c8d9db9e000fb071c1d3bc164bd3cf4f031b8e19207b7d6e48232a21bf573dc
7
- data.tar.gz: 4d20378f2973be45cc1cb691894b9c54916c1f5546d45a64a5143feaf8cabf0e053d8d604b8f30b6534d123ad12084021d9815b82945f78f5bfacd64f4daef96
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)
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.
@@ -76,30 +76,47 @@ module Omnizip
76
76
  }
77
77
  end
78
78
 
79
- private
80
-
81
- def open
82
- path = resolve_path
83
- return nil if path.nil?
84
-
85
- handle = Fiddle.dlopen(path.to_s)
86
- funcs = function_table.each_with_object({}) do |(key, (name, args, ret)), h|
87
- h[key] = Fiddle::Function.new(handle[name], args, ret)
88
- end
89
- new(funcs, handle)
90
- rescue StandardError => e # includes Fiddle::DLError
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
- def resolve_path
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
- ].compact.flatten
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Omnizip
4
- VERSION = "0.4.6"
4
+ VERSION = "0.4.7"
5
5
  end
@@ -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,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: omnizip
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.6
4
+ version: 0.4.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -724,6 +724,7 @@ files:
724
724
  - lib/omnizip/zip/input_stream.rb
725
725
  - lib/omnizip/zip/output_stream.rb
726
726
  - lib/tasks/msi_test_files.rake
727
+ - lib/tasks/platform_gems.rake
727
728
  - lib/tasks/rust_backend.rake
728
729
  - readme-docs/advanced-features.adoc
729
730
  - readme-docs/api-usage.adoc