Hi everyone. I have a question. When my package manager compile a task (basically a crystal script), I would like to print the compilation progress output via a print. But when I try to do it, the print show nothing. How should I proceed ?
Something like this (sorry, untested code but it’s based on working code):
Process.run(
command: "yourcommand",
args: args,
) do |process|
loop do
data = process.output.gets(chomp: false)
STDOUT << data
STDOUT.flush
Fiber.yield # Without this the process never ends
break if process.terminated?
end
end
Then you can check how the process finished by checking $?.exit_code
So it’s strange because I don’t have any error but my code print nothing.
This is my function:
def buildTasksFile
processResult = IO::Memory.new
Process.run("crystal build --release --progress #{ISM::Default::Filename::Task}.cr -o #{@settings.rootPath}#{ISM::Default::Path::RuntimeDataDirectory}#{ISM::Default::Filename::Task} -f json",
error: processResult,
shell: true,
chdir: "#{@settings.rootPath}#{ISM::Default::Path::RuntimeDataDirectory}") do |process|
loop do
data = process.output.gets(chomp: false)
print data
Fiber.yield
break if process.terminated?
end
end
processResult.rewind
if processResult.to_s != ""
taskError = Array(ISM::TaskBuildingProcessError).from_json(processResult.to_s.gsub("\"size\":null","\"size\":0"))[-1]
showTaskBuildingProcessErrorMessage(taskError, "#{@settings.rootPath}#{ISM::Default::Path::RuntimeDataDirectory}#{ISM::Default::Filename::Task}.cr")
exitProgram
end
end
So I did a small test you can run. If I run this command, I have no output:
Process.run("crystal build --release --progress Test.cr -o Test -f json", shell: true) do |process|
loop do
data = process.output.gets(chomp: false)
STDOUT << data
STDOUT.flush
Fiber.yield
break if process.terminated?
end
end
The issue is specifically with the --progress flag which rewrites the output on each stage update. Because a newline (\n) character is never written during this process, gets fails which is why there’s no output (or I think it just returns nil but it doesn’t matter).
As @zw963 suggested, --stats may be a better option as that prints lines. Do note however that the output can also affect lines already printed, the compiler will usually do this for stages that have longer names so that they are formatted nicely.
If you specifically want the output from --progress, this code should work:
Process.run("crystal build --release --progress test.cr -o test -f json", shell: true) do |process|
data = String.build do |io|
process.output.each_char do |char|
if char == '\r'
io << '\n'
else
io << char
end
end
end
puts data
end
Okay, thank you zw963. So now I have 2 other questions.
From that, how can I redirect this output to a IO::Memory ? Because if I try to use a IO::Memory, when I run the program, it raise an error that Process#output cannot be nil.
Basically, I would like to get this output … But not print it directly from the process. (Like the process print but silently)
Another question as well. When I use this code, it print an extra text at the end, it print a class name and it address:
Or maybe there is just another way to show the progression ? I just would like basically to show to the user how many times/steps left before the end of the compilation.