Skip to content

build: Fix pipe stuck without --verbose and better progress indicator #313

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Steepfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ target :lib do
library "open-uri"
library "uri"
library "shellwords"
library "io-console"

configure_code_diagnostics(D::Ruby.default)
end
119 changes: 96 additions & 23 deletions lib/ruby_wasm/build.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,34 +10,67 @@ def initialize(verbose: false)
@github_actions_markup = ENV["ENABLE_GITHUB_ACTIONS_MARKUP"] != nil
end

def system(*args, chdir: nil, out: nil, env: nil)
def system(*args, chdir: nil, env: nil)
require "open3"

_print_command(args, env)

if @verbose
out ||= $stdout
else
# Capture stdout by default
out_pipe = IO.pipe
out = out_pipe[1]
end
# @type var kwargs: Hash[Symbol, untyped]
kwargs = { exception: true, out: out }
kwargs = {}
kwargs[:chdir] = chdir if chdir
begin
if env
Kernel.system(env, *args.to_a.map(&:to_s), **kwargs)

args = args.to_a.map(&:to_s)
# TODO: Remove __skip__ once we have open3 RBS definitions.
__skip__ =
if @verbose || !$stdout.tty?
kwargs[:exception] = true
if env
Kernel.system(env, *args, **kwargs)
else
Kernel.system(*args, **kwargs)
end
else
Kernel.system(*args.to_a.map(&:to_s), **kwargs)
printer = StatusPrinter.new
block =
proc do |stdin, stdout, stderr, wait_thr|
mux = Mutex.new
out = String.new
err = String.new
readers =
[
[stdout, :stdout, out],
[stderr, :stderr, err]
].map do |io, name, str|
reader =
Thread.new do
while (line = io.gets)
mux.synchronize do
printer.send(name, line)
str << line
end
end
end
reader.report_on_exception = false
reader
end

readers.each(&:join)

[out, err, wait_thr.value]
end
begin
if env
Open3.popen3(env, *args, **kwargs, &block)
else
Open3.popen3(*args, **kwargs, &block)
end
ensure
printer.done
end
end
ensure
out.close if out_pipe
end
rescue => e
if out_pipe
# Print the output of the failed command
puts out_pipe[0].read
end
$stdout.flush
$stderr.puts "Try running with `rake --verbose` for more complete output."
raise e
end

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

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

Expand Down Expand Up @@ -98,4 +129,46 @@ def _print_command(args, env)
print args.map { |arg| Shellwords.escape(arg.to_s) }.join(" ") + "\n"
end
end

# Human readable status printer for the build.
class StatusPrinter
def initialize
@mutex = Mutex.new
@counter = 0
@indicators = "|/-\\"
end

def stdout(message)
require "io/console"
@mutex.synchronize do
$stdout.print "\e[K"
first_line = message.lines(chomp: true).first || ""

# Make sure we don't line-wrap the output
size =
__skip__ =
IO.respond_to?(:console_size) ? IO.console_size : IO.console.winsize
terminal_width = size[1].to_i.nonzero? || 80
width_limit = terminal_width / 2 - 3

if first_line.length > width_limit
first_line = (first_line[0..width_limit - 5] || "") + "..."
end
indicator = @indicators[@counter] || " "
to_print = " " + indicator + " " + first_line
$stdout.print to_print
$stdout.print "\e[1A\n"
@counter += 1
@counter = 0 if @counter >= @indicators.length
end
end

def stderr(message)
@mutex.synchronize { $stdout.print message }
end

def done
@mutex.synchronize { $stdout.print "\e[K" }
end
end
end
6 changes: 5 additions & 1 deletion lib/ruby_wasm/build/product/wasi_vfs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ def build(executor)
lib_wasi_vfs_url =
"https://github.com/kateinoigakukun/wasi-vfs/releases/download/v#{WASI_VFS_VERSION}/libwasi_vfs-wasm32-unknown-unknown.zip"
Dir.mktmpdir do |tmpdir|
executor.system "curl", "-L", lib_wasi_vfs_url, "-o", "#{tmpdir}/libwasi_vfs.zip"
executor.system "curl",
"-L",
lib_wasi_vfs_url,
"-o",
"#{tmpdir}/libwasi_vfs.zip"
executor.system "unzip", "#{tmpdir}/libwasi_vfs.zip", "-d", tmpdir
executor.mkdir_p File.dirname(lib_wasi_vfs_a)
executor.mv File.join(tmpdir, "libwasi_vfs.a"), lib_wasi_vfs_a
Expand Down
13 changes: 12 additions & 1 deletion sig/ruby_wasm/build.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ module RubyWasm
@start_times: Hash[[Class, String], Time]

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

class StatusPrinter
@mutex: Mutex
@counter: Integer
@indicators: String

def initialize: () -> void
def stdout: (String message) -> void
def stderr: (String message) -> void
def done: () -> void
end

class Downloader
def format_size: (Integer size) -> String

Expand Down