tebako-runtime 0.7.0 → 0.8.1

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.
@@ -36,9 +36,33 @@ class Jing
36
36
 
37
37
  def initialize(schema, options = nil)
38
38
  original_initialize(TebakoRuntime.extract_memfs(schema, wild: true), options)
39
+ # ruby-jing captures `:default => DEFAULT_JAR` at class-load time —
40
+ # before this adapter replaced the constant with the extracted host
41
+ # path. Force the extracted path per-instance (a nil option hash
42
+ # leaves @options empty, and the builder's stale default would win).
43
+ @options[:jar] = DEFAULT_JAR
44
+ # The mounted openjdk toolkit payload's java wins over PATH (the
45
+ # memfs-binary exec path; the bare "java" keeps the PATH answer).
46
+ @options[:java] = TebakoRuntime.mounted_exe("openjdk", "java")
39
47
  end
40
48
 
41
49
  def validate(xml)
42
50
  original_validate(TebakoRuntime.extract_memfs(xml))
43
51
  end
52
+
53
+ # The spawn shape, array form (the stock execute() backticks a shell
54
+ # string, which the tebako spawn hook deliberately skips; the mounted
55
+ # java only execs through the hook from an absolute-path array spawn).
56
+ def execute(options) # rubocop:disable Metrics/AbcSize
57
+ cmd = [options[:java]]
58
+ cmd += options[:java_opts].split if options[:java_opts]
59
+ cmd += ["-jar", options[:jar].to_s]
60
+ cmd << "-c" if options[:compact]
61
+ cmd += ["-e", options[:encoding]] if options[:encoding]
62
+ cmd << "-i" if options[:id_check]
63
+ cmd += [options[:schema].to_s, options[:xmlfile].to_s]
64
+ IO.popen(cmd, err: %i[child out], &:read)
65
+ rescue SystemCallError => e
66
+ raise ExecutionError, "jing execution failed: #{e}"
67
+ end
44
68
  end
@@ -55,3 +55,50 @@ module Mn2pdf
55
55
  options)
56
56
  end
57
57
  end
58
+
59
+ # The java resolution + the spawn shape (the memfs-binary exec path):
60
+ # when the openjdk toolkit payload is mounted, its /opt/openjdk/bin/java
61
+ # wins over PATH; the spawn is the ARRAY form — the shell form
62
+ # (`Open3.capture3(string)`) goes through /bin/sh, which the tebako
63
+ # spawn hook deliberately skips.
64
+ module Jvm
65
+ singleton_class.send(:alias_method, :run_orig, :run)
66
+ singleton_class.send(:remove_method, :run)
67
+
68
+ def self.run(args = [])
69
+ java = TebakoRuntime.mounted_exe("openjdk", "java")
70
+ return run_orig(args) if java == "java"
71
+
72
+ # The stock form joins everything into a SHELL string: values ride
73
+ # shell-quoted (whole-value and inner forms), and legacy callers pass
74
+ # whole fragments in one element (`--param baseassetpath="/dir"`).
75
+ # The array spawn needs shell-word rules applied — split on unquoted
76
+ # spaces, shed the quotes — exactly what the shell did with the
77
+ # stock string.
78
+ clean = args.flat_map { |a| TebakoRuntime.shell_split(a.to_s) }
79
+ cmd = [java, *options, "-jar", MN2PDF_JAR_PATH.to_s, *clean]
80
+ puts cmd.join(" ")
81
+ Open3.capture3(*cmd).then { |stdout, stderr, status| [stdout, stderr, status] }
82
+ end
83
+ end
84
+
85
+ # options_to_cmd's joined form (`--param "k=v"` in ONE shell word) is
86
+ # shell-string machinery; the array spawn takes flag and value as
87
+ # separate elements with the shell quotes removed. Stock behavior
88
+ # stands for the PATH java.
89
+ module Mn2pdf
90
+ singleton_class.send(:alias_method, :options_to_cmd_orig, :options_to_cmd)
91
+ singleton_class.send(:remove_method, :options_to_cmd)
92
+
93
+ def self.options_to_cmd(options, cmd)
94
+ return options_to_cmd_orig(options, cmd) if TebakoRuntime.mounted_exe("openjdk", "java") == "java"
95
+
96
+ options.each do |k, v|
97
+ if k.to_s.end_with?("=")
98
+ cmd << "#{k}#{v.to_s.delete('"')}"
99
+ else
100
+ cmd << k.to_s << v.to_s.delete('"')
101
+ end
102
+ end
103
+ end
104
+ end
@@ -29,12 +29,38 @@ require "fileutils"
29
29
  require "pathname"
30
30
  require "rubygems"
31
31
  require "tempfile"
32
+ require "fiddle"
32
33
 
33
34
  require_relative "string"
34
35
 
35
36
  # Module TebakoRuntime
36
37
  # Methods to extract files from memfs to temporary folder
37
38
  module TebakoRuntime
39
+ # The v2 multi-mount world: an extractable path is one HELD by the TFS
40
+ # mounts (the env image at COMPILER_MEMFS, payloads at their declared
41
+ # points) — the compiled-in prefix can no longer express it. The
42
+ # runtime executable's own tebako_fs_stat is the discriminator: it
43
+ # answers only mounted content (0); host paths (and jail-denied ones)
44
+ # answer otherwise. Fiddle::Handle::DEFAULT addresses the process
45
+ # image WITHOUT this gem's own fiddle adapter (dlopen(nil) routes
46
+ # through it and chokes on the nil).
47
+ MEMFS_STAT_FN = begin
48
+ Fiddle::Function.new(Fiddle::Handle::DEFAULT["tebako_fs_stat"],
49
+ [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
50
+ rescue StandardError
51
+ nil
52
+ end
53
+
54
+ # struct stat is at most 512 bytes on every supported platform
55
+ # (darwin-arm64 and linux x86_64 use 144).
56
+ MEMFS_STAT_BUF = "\0".b * 512
57
+
58
+ def self.embedded_path?(path)
59
+ return path.start_with?(COMPILER_MEMFS) if MEMFS_STAT_FN.nil?
60
+
61
+ MEMFS_STAT_FN.call(path, MEMFS_STAT_BUF).zero? || path.start_with?(COMPILER_MEMFS)
62
+ end
63
+
38
64
  def self.initialize_compiler_memfs_lib_cache
39
65
  Pathname.new(Dir.mktmpdir("tebako-runtime-"))
40
66
  rescue StandardError
@@ -64,13 +90,36 @@ module TebakoRuntime
64
90
  def self.extract_memfs(file, wild: false, cache_path: COMPILER_MEMFS_LIB_CACHE)
65
91
  is_quoted = file.quoted?
66
92
  file = file.unquote if is_quoted
67
- return is_quoted ? file.quote : file unless File.exist?(file) && file.start_with?(COMPILER_MEMFS)
93
+ return is_quoted ? file.quote : file unless File.exist?(file) && embedded_path?(file)
68
94
 
69
95
  memfs_extracted_file = cache_path + File.basename(file)
70
96
  extract(file, wild, cache_path) unless memfs_extracted_file.exist?
71
97
 
72
98
  is_quoted ? memfs_extracted_file.to_path.quote : memfs_extracted_file.to_path
73
99
  end
100
+
101
+ # The mounted-toolkit resolution (spec 03 §2.2): an executable the
102
+ # named toolkit payload provides at its declared mount. Returns the
103
+ # in-image absolute path when the mounts hold it (the spawn hook
104
+ # execs those through dlmap2file + the preload), the bare name
105
+ # otherwise (the consumer's PATH answer stands).
106
+ #
107
+ # TebakoRuntime.mounted_exe("openjdk", "java")
108
+ # # => "/opt/openjdk/bin/java" when the openjdk payload is mounted,
109
+ # else "java"
110
+ def self.mounted_exe(toolkit, name)
111
+ mounted = "/opt/#{toolkit}/bin/#{name}"
112
+ embedded_path?(mounted) ? mounted : name
113
+ end
114
+
115
+ # Shell-word split for the array spawn (the memfs-binary exec path):
116
+ # legacy adapters compose arguments as a SHELL fragment in ONE element
117
+ # (`--param baseassetpath="/dir"`), and shell-quoted whole values ride
118
+ # as single elements. Split on unquoted spaces, then shed the quotes —
119
+ # exactly the shell's own word rules (a quoted space never splits).
120
+ def self.shell_split(word)
121
+ word.scan(/"([^"]*)"|(\S+)/).map { |quoted, bare| quoted || bare }
122
+ end
74
123
  end
75
124
 
76
125
  at_exit do
@@ -28,4 +28,7 @@
28
28
  # For some reason on Windows an attempt to require "seven_zip_ruby" from excavate fails
29
29
  # I cannot debug it effectively because of https://github.com/tamatebako/tebako/issues/119
30
30
 
31
- require "seven_zip_ruby"
31
+ # Only legacy excavate loads seven_zip_ruby; omnizip-era excavate has no
32
+ # seven-zip gem at all — wiring it then would crash the require chain
33
+ # with Gem::MissingSpecError (metanorma dogfood, 2026-08-11).
34
+ require "seven_zip_ruby" if Gem::Specification.find_all_by_name("seven-zip").any?
@@ -28,15 +28,20 @@
28
28
  require_relative "../memfs"
29
29
  require_relative "../../tebako-runtime"
30
30
 
31
- # Fix path for 7zip load
31
+ # Fix path for 7zip load.
32
+ # Legacy-excavate only: omnizip-era excavate ships no seven-zip gem —
33
+ # when it is absent there is nothing to excavate, so the hook is a
34
+ # no-op (never a Gem::MissingSpecError — metanorma dogfood, 2026-08-11).
32
35
  module TebakoRuntime
33
- sevenz_lib = RUBY_PLATFORM.downcase.match(/mswin|mingw/) ? "7z*.dll" : "7z.so"
34
- sevenz_path = File.join(full_gem_path("seven-zip"), "lib", "seven_zip_ruby", sevenz_lib)
35
- sevenz_paths = Dir.glob(sevenz_path)
36
- sevenz_new_folder = COMPILER_MEMFS_LIB_CACHE / "seven_zip_ruby"
37
- FileUtils.mkdir_p(sevenz_new_folder)
38
- sevenz_paths.each do |file|
39
- FileUtils.cp(file, sevenz_new_folder)
36
+ if Gem::Specification.find_all_by_name("seven-zip").any?
37
+ sevenz_lib = RUBY_PLATFORM.downcase.match(/mswin|mingw/) ? "7z*.dll" : "7z.so"
38
+ sevenz_path = File.join(full_gem_path("seven-zip"), "lib", "seven_zip_ruby", sevenz_lib)
39
+ sevenz_paths = Dir.glob(sevenz_path)
40
+ sevenz_new_folder = COMPILER_MEMFS_LIB_CACHE / "seven_zip_ruby"
41
+ FileUtils.mkdir_p(sevenz_new_folder)
42
+ sevenz_paths.each do |file|
43
+ FileUtils.cp(file, sevenz_new_folder)
44
+ end
45
+ $LOAD_PATH.unshift(COMPILER_MEMFS_LIB_CACHE.to_s)
40
46
  end
41
- $LOAD_PATH.unshift(COMPILER_MEMFS_LIB_CACHE.to_s)
42
47
  end
@@ -26,5 +26,5 @@
26
26
  # POSSIBILITY OF SUCH DAMAGE.
27
27
 
28
28
  module TebakoRuntime
29
- VERSION = "0.7.0"
29
+ VERSION = "0.8.1"
30
30
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tebako-runtime
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-03-18 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rspec
@@ -243,7 +243,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
243
243
  - !ruby/object:Gem::Version
244
244
  version: '0'
245
245
  requirements: []
246
- rubygems_version: 3.6.2
246
+ rubygems_version: 3.6.9
247
247
  specification_version: 4
248
248
  summary: Run-time support of tebako executable packager
249
249
  test_files: []