Skip to content

Commit 5a28621

Browse files
Merge pull request #313 from ruby/katei/fix-pipe-stuck
build: Fix pipe stuck without --verbose and better progress indicator
2 parents 0a3bdcf + f0dbac1 commit 5a28621

File tree

4 files changed

+114
-25
lines changed

4 files changed

+114
-25
lines changed

Steepfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ target :lib do
1313
library "open-uri"
1414
library "uri"
1515
library "shellwords"
16+
library "io-console"
1617

1718
configure_code_diagnostics(D::Ruby.default)
1819
end

lib/ruby_wasm/build.rb

Lines changed: 96 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,34 +10,67 @@ def initialize(verbose: false)
1010
@github_actions_markup = ENV["ENABLE_GITHUB_ACTIONS_MARKUP"] != nil
1111
end
1212

13-
def system(*args, chdir: nil, out: nil, env: nil)
13+
def system(*args, chdir: nil, env: nil)
14+
require "open3"
15+
1416
_print_command(args, env)
1517

16-
if @verbose
17-
out ||= $stdout
18-
else
19-
# Capture stdout by default
20-
out_pipe = IO.pipe
21-
out = out_pipe[1]
22-
end
2318
# @type var kwargs: Hash[Symbol, untyped]
24-
kwargs = { exception: true, out: out }
19+
kwargs = {}
2520
kwargs[:chdir] = chdir if chdir
26-
begin
27-
if env
28-
Kernel.system(env, *args.to_a.map(&:to_s), **kwargs)
21+
22+
args = args.to_a.map(&:to_s)
23+
# TODO: Remove __skip__ once we have open3 RBS definitions.
24+
__skip__ =
25+
if @verbose || !$stdout.tty?
26+
kwargs[:exception] = true
27+
if env
28+
Kernel.system(env, *args, **kwargs)
29+
else
30+
Kernel.system(*args, **kwargs)
31+
end
2932
else
30-
Kernel.system(*args.to_a.map(&:to_s), **kwargs)
33+
printer = StatusPrinter.new
34+
block =
35+
proc do |stdin, stdout, stderr, wait_thr|
36+
mux = Mutex.new
37+
out = String.new
38+
err = String.new
39+
readers =
40+
[
41+
[stdout, :stdout, out],
42+
[stderr, :stderr, err]
43+
].map do |io, name, str|
44+
reader =
45+
Thread.new do
46+
while (line = io.gets)
47+
mux.synchronize do
48+
printer.send(name, line)
49+
str << line
50+
end
51+
end
52+
end
53+
reader.report_on_exception = false
54+
reader
55+
end
56+
57+
readers.each(&:join)
58+
59+
[out, err, wait_thr.value]
60+
end
61+
begin
62+
if env
63+
Open3.popen3(env, *args, **kwargs, &block)
64+
else
65+
Open3.popen3(*args, **kwargs, &block)
66+
end
67+
ensure
68+
printer.done
69+
end
3170
end
32-
ensure
33-
out.close if out_pipe
34-
end
3571
rescue => e
36-
if out_pipe
37-
# Print the output of the failed command
38-
puts out_pipe[0].read
39-
end
4072
$stdout.flush
73+
$stderr.puts "Try running with `rake --verbose` for more complete output."
4174
raise e
4275
end
4376

@@ -58,9 +91,7 @@ def begin_section(klass, name, note)
5891

5992
def end_section(klass, name)
6093
took = Time.now - @start_times[[klass, name]]
61-
if @github_actions_markup
62-
puts "::endgroup::"
63-
end
94+
puts "::endgroup::" if @github_actions_markup
6495
puts "\e[1;36m==>\e[0m \e[1m#{klass}(#{name}) -- done in #{took.round(2)}s\e[0m"
6596
end
6697

@@ -98,4 +129,46 @@ def _print_command(args, env)
98129
print args.map { |arg| Shellwords.escape(arg.to_s) }.join(" ") + "\n"
99130
end
100131
end
132+
133+
# Human readable status printer for the build.
134+
class StatusPrinter
135+
def initialize
136+
@mutex = Mutex.new
137+
@counter = 0
138+
@indicators = "|/-\\"
139+
end
140+
141+
def stdout(message)
142+
require "io/console"
143+
@mutex.synchronize do
144+
$stdout.print "\e[K"
145+
first_line = message.lines(chomp: true).first || ""
146+
147+
# Make sure we don't line-wrap the output
148+
size =
149+
__skip__ =
150+
IO.respond_to?(:console_size) ? IO.console_size : IO.console.winsize
151+
terminal_width = size[1].to_i.nonzero? || 80
152+
width_limit = terminal_width / 2 - 3
153+
154+
if first_line.length > width_limit
155+
first_line = (first_line[0..width_limit - 5] || "") + "..."
156+
end
157+
indicator = @indicators[@counter] || " "
158+
to_print = " " + indicator + " " + first_line
159+
$stdout.print to_print
160+
$stdout.print "\e[1A\n"
161+
@counter += 1
162+
@counter = 0 if @counter >= @indicators.length
163+
end
164+
end
165+
166+
def stderr(message)
167+
@mutex.synchronize { $stdout.print message }
168+
end
169+
170+
def done
171+
@mutex.synchronize { $stdout.print "\e[K" }
172+
end
173+
end
101174
end

lib/ruby_wasm/build/product/wasi_vfs.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,11 @@ def build(executor)
4848
lib_wasi_vfs_url =
4949
"https://github.com/kateinoigakukun/wasi-vfs/releases/download/v#{WASI_VFS_VERSION}/libwasi_vfs-wasm32-unknown-unknown.zip"
5050
Dir.mktmpdir do |tmpdir|
51-
executor.system "curl", "-L", lib_wasi_vfs_url, "-o", "#{tmpdir}/libwasi_vfs.zip"
51+
executor.system "curl",
52+
"-L",
53+
lib_wasi_vfs_url,
54+
"-o",
55+
"#{tmpdir}/libwasi_vfs.zip"
5256
executor.system "unzip", "#{tmpdir}/libwasi_vfs.zip", "-d", tmpdir
5357
executor.mkdir_p File.dirname(lib_wasi_vfs_a)
5458
executor.mv File.join(tmpdir, "libwasi_vfs.a"), lib_wasi_vfs_a

sig/ruby_wasm/build.rbs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ module RubyWasm
227227
@start_times: Hash[[Class, String], Time]
228228

229229
def initialize: (?verbose: bool) -> void
230-
def system: (*_ToS args, ?chdir: String?, ?out: Kernel::redirect_fd?, ?env: Hash[String, String]?) -> void
230+
def system: (*_ToS args, ?chdir: String?, ?env: Hash[String, String]?) -> void
231231
def rm_rf: (FileUtils::pathlist list) -> void
232232
def rm_f: (FileUtils::pathlist list) -> void
233233
def cp_r: (FileUtils::pathlist src, path dest) -> void
@@ -241,6 +241,17 @@ module RubyWasm
241241
private def _print_command: (Array[_ToS] command, Hash[String, String]? env) -> void
242242
end
243243

244+
class StatusPrinter
245+
@mutex: Mutex
246+
@counter: Integer
247+
@indicators: String
248+
249+
def initialize: () -> void
250+
def stdout: (String message) -> void
251+
def stderr: (String message) -> void
252+
def done: () -> void
253+
end
254+
244255
class Downloader
245256
def format_size: (Integer size) -> String
246257

0 commit comments

Comments
 (0)