binaryen 1.1.6.6 → 1.1.6.8
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/lib/binaryen/command.rb +71 -94
- data/lib/binaryen/error.rb +1 -0
- data/lib/binaryen/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4217d4168d453c8e5316e9c66929d5e09057ec356dc6f80802c41521de6fd612
|
4
|
+
data.tar.gz: 2cb4b068c0456d72dcc55cf0d945a5357df0d3ad8c82166fca8a57dcd2908ec4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9e3fd947316b466a8d592014bc57b1c34148da5b3042e0630b2bc5f56e54c287ab4c6416e78c1ef392e88429cbbcba0c0c3549bdd8b99d7068972c1adeb0c698
|
7
|
+
data.tar.gz: 2cdfe8e57ab692f16e911e7a819cb6cfecbf9b31027a04c8863eb919fd952cb6d92832d3884768cf05704507c7c408bcfb19eed3c47b90dfcbb6f1e7eaefc679
|
data/lib/binaryen/command.rb
CHANGED
@@ -1,131 +1,108 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "English"
|
4
|
-
require "shellwords"
|
5
|
-
require "timeout"
|
6
3
|
require "posix/spawn"
|
4
|
+
require "timeout"
|
5
|
+
require "tempfile"
|
7
6
|
|
8
7
|
module Binaryen
|
9
|
-
# Wrapper around a binaryen executable command with a timeout and streaming IO.
|
10
|
-
#
|
11
|
-
# @example Running wasm-opt
|
12
|
-
#
|
13
|
-
# ```ruby
|
14
|
-
# command = Binaryen::Command.new("wasm-opt", timeout: 10)
|
15
|
-
# optimized_wasm = command.run("-O4", stdin: "(module)")
|
16
|
-
# ```
|
17
8
|
class Command
|
18
9
|
include POSIX::Spawn
|
19
|
-
|
10
|
+
DEFAULT_MAX_OUTPUT_SIZE = 256 * 1024 * 1024
|
11
|
+
DEFAULT_TIMEOUT = 10
|
20
12
|
DEFAULT_ARGS_FOR_COMMAND = {
|
21
13
|
"wasm-opt" => ["--output=-"],
|
22
14
|
}.freeze
|
23
15
|
|
24
|
-
|
25
|
-
def initialize(end_time:, pid:)
|
26
|
-
@end_time = end_time
|
27
|
-
@pid = pid
|
28
|
-
end
|
29
|
-
|
30
|
-
def check!
|
31
|
-
now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
32
|
-
if now >= @end_time
|
33
|
-
Process.kill("TERM", @pid)
|
34
|
-
raise Timeout::Error, "Command timed out"
|
35
|
-
end
|
36
|
-
remaining_time = @end_time - now
|
37
|
-
remaining_time
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def initialize(cmd, timeout: 10, ignore_missing: false)
|
16
|
+
def initialize(cmd, timeout: DEFAULT_TIMEOUT, max_output_size: DEFAULT_MAX_OUTPUT_SIZE, ignore_missing: false)
|
42
17
|
@cmd = command_path(cmd, ignore_missing) || raise(ArgumentError, "command not found: #{cmd}")
|
43
18
|
@timeout = timeout
|
44
19
|
@default_args = DEFAULT_ARGS_FOR_COMMAND.fetch(cmd, [])
|
20
|
+
@max_output_size = max_output_size
|
45
21
|
end
|
46
22
|
|
47
23
|
def run(*arguments, stdin: nil, stderr: nil)
|
48
|
-
|
49
|
-
command = build_command(*arguments)
|
50
|
-
pid, iwr, ord, erd = popen4(*command)
|
51
|
-
timeout_checker = TimeoutChecker.new(end_time: end_time, pid: pid)
|
52
|
-
|
53
|
-
write_to_pipe(iwr, stdin, timeout_checker, pid) if stdin
|
24
|
+
args = [@cmd] + arguments + @default_args
|
54
25
|
|
55
|
-
if
|
56
|
-
|
57
|
-
|
26
|
+
if stdin
|
27
|
+
with_stdin_tempfile(stdin) { |path| spawn_command(*args, path, stderr: stderr) }
|
28
|
+
else
|
29
|
+
spawn_command(*args, stderr: stderr)
|
58
30
|
end
|
59
|
-
output = read_from_pipe(ord, timeout_checker)
|
60
|
-
wait_or_kill(pid, timeout_checker)
|
61
|
-
|
62
|
-
output
|
63
31
|
end
|
64
32
|
|
65
33
|
private
|
66
34
|
|
67
|
-
def
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
next unless IO.select(nil, [pipe], nil, remaining_time)
|
75
|
-
|
76
|
-
begin
|
77
|
-
written = pipe.write_nonblock(stdin.byteslice(offset, length), exception: false)
|
78
|
-
offset += written if written.is_a?(Integer)
|
79
|
-
rescue Errno::EPIPE
|
80
|
-
wait_or_kill(pid, timeout_checker, pid)
|
81
|
-
end
|
35
|
+
def with_stdin_tempfile(content)
|
36
|
+
Tempfile.open("binaryen-stdin") do |f|
|
37
|
+
f.binmode
|
38
|
+
f.write(content)
|
39
|
+
f.close
|
40
|
+
yield f.path
|
82
41
|
end
|
83
|
-
ensure
|
84
|
-
pipe.close_write if close_write
|
85
42
|
end
|
86
43
|
|
87
|
-
def
|
88
|
-
|
89
|
-
|
90
|
-
while (result = pipe.read_nonblock(8192, exception: false))
|
91
|
-
remaining_time = timeout_checker.check!
|
92
|
-
|
93
|
-
case result
|
94
|
-
when :wait_readable
|
95
|
-
IO.select([pipe], nil, nil, remaining_time)
|
96
|
-
when nil
|
97
|
-
break
|
98
|
-
else
|
99
|
-
output << result
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
output
|
104
|
-
ensure
|
105
|
-
pipe.close_read if close_read
|
44
|
+
def command_path(cmd, ignore_missing)
|
45
|
+
Dir[File.join(Binaryen.bindir, cmd)].first || (ignore_missing && cmd)
|
106
46
|
end
|
107
47
|
|
108
|
-
def
|
109
|
-
|
110
|
-
|
48
|
+
def spawn_command(*args, stderr: nil)
|
49
|
+
out = "".b
|
50
|
+
data_buffer = "".b
|
51
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
52
|
+
pid, stdin, stdout, stderr_stream = popen4(*args)
|
53
|
+
stdin.close
|
54
|
+
@pid = pid
|
55
|
+
readers = [stdout, stderr_stream]
|
56
|
+
|
57
|
+
while readers.any?
|
58
|
+
elapsed_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
|
59
|
+
remaining_time = @timeout - elapsed_time
|
60
|
+
ready = IO.select(readers, nil, nil, remaining_time)
|
61
|
+
raise Timeout::Error, "command timed out after #{@timeout} seconds" if ready.nil?
|
62
|
+
|
63
|
+
ready[0].each do |io|
|
64
|
+
max_amount_to_read = @max_output_size - out.bytesize + 1
|
65
|
+
data = io.read_nonblock(max_amount_to_read, data_buffer, exception: false)
|
66
|
+
|
67
|
+
if data == :wait_readable
|
68
|
+
# If the IO object is not ready for reading, read_nonblock returns :wait_readable.
|
69
|
+
# This isn't an error, but a notification.
|
70
|
+
next
|
71
|
+
elsif data.nil?
|
72
|
+
# At EOF, read_nonblock returns nil instead of raising EOFError.
|
73
|
+
readers.delete(io)
|
74
|
+
elsif io == stdout
|
75
|
+
out << data_buffer
|
76
|
+
elsif io == stderr_stream && stderr
|
77
|
+
stderr << data_buffer
|
78
|
+
end
|
79
|
+
rescue Errno::EPIPE, Errno::EINTR
|
80
|
+
# Handle EPIPE and EINTR errors
|
81
|
+
readers.delete(io)
|
82
|
+
end
|
111
83
|
|
112
|
-
if
|
113
|
-
|
114
|
-
|
84
|
+
if out.bytesize > @max_output_size
|
85
|
+
Process.kill("TERM", @pid)
|
86
|
+
raise Binaryen::MaximumOutputExceeded, "maximum output size exceeded (#{@max_output_size} bytes)"
|
87
|
+
end
|
115
88
|
|
116
|
-
|
117
|
-
|
118
|
-
|
89
|
+
if remaining_time < 0
|
90
|
+
Process.kill("TERM", @pid)
|
91
|
+
raise Timeout::Error, "command timed out after #{@timeout} seconds"
|
119
92
|
end
|
120
93
|
end
|
121
|
-
end
|
122
94
|
|
123
|
-
|
124
|
-
[@cmd] + arguments + @default_args
|
125
|
-
end
|
95
|
+
_, status = Process.waitpid2(pid, Process::WUNTRACED)
|
126
96
|
|
127
|
-
|
128
|
-
|
97
|
+
raise Binaryen::NonZeroExitStatus, "command exited with status #{status.exitstatus}" unless status.success?
|
98
|
+
|
99
|
+
out
|
100
|
+
ensure
|
101
|
+
[stdin, stdout, stderr_stream].each do |io|
|
102
|
+
io.close
|
103
|
+
rescue
|
104
|
+
nil
|
105
|
+
end
|
129
106
|
end
|
130
107
|
end
|
131
108
|
end
|
data/lib/binaryen/error.rb
CHANGED
data/lib/binaryen/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: binaryen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.6.
|
4
|
+
version: 1.1.6.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-12-
|
11
|
+
date: 2023-12-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: posix-spawn
|