feat(cli): debug command output in real time (#4318)

This commit is contained in:
Lucas Fernandes Nogueira
2022-06-12 05:00:54 -07:00
committed by GitHub
parent b9e1e909a7
commit 76d1eaaebd
8 changed files with 137 additions and 59 deletions

View File

@@ -81,8 +81,7 @@ pub fn command(options: Options) -> Result<()> {
.arg(before_build)
.current_dir(app_dir())
.envs(command_env(options.debug))
.pipe()?
.status()
.piped()
.with_context(|| format!("failed to run `{}` with `cmd /C`", before_build))?;
#[cfg(not(target_os = "windows"))]
let status = Command::new("sh")
@@ -90,8 +89,7 @@ pub fn command(options: Options) -> Result<()> {
.arg(before_build)
.current_dir(app_dir())
.envs(command_env(options.debug))
.pipe()?
.status()
.piped()
.with_context(|| format!("failed to run `{}` with `sh -c`", before_build))?;
if !status.success() {

View File

@@ -9,7 +9,7 @@ use crate::{
config::{get as get_config, reload as reload_config, AppUrl, ConfigHandle, WindowUrl},
manifest::{rewrite_manifest, Manifest},
},
CommandExt, Result,
Result,
};
use clap::Parser;
@@ -111,8 +111,7 @@ fn command_internal(options: Options) -> Result<()> {
.arg("/C")
.arg(before_dev)
.current_dir(app_dir())
.envs(command_env(true))
.pipe()?; // development build always includes debug information
.envs(command_env(true));
command
};
#[cfg(not(target_os = "windows"))]
@@ -122,11 +121,12 @@ fn command_internal(options: Options) -> Result<()> {
.arg("-c")
.arg(before_dev)
.current_dir(app_dir())
.envs(command_env(true))
.pipe()?; // development build always includes debug information
.envs(command_env(true));
command
};
command.stdin(Stdio::piped());
command.stdout(os_pipe::dup_stdout()?);
command.stderr(os_pipe::dup_stderr()?);
let child = SharedChild::spawn(&mut command)
.unwrap_or_else(|_| panic!("failed to run `{}`", before_dev));

View File

@@ -101,15 +101,19 @@ struct CargoConfig {
}
pub fn build_project(runner: String, args: Vec<String>) -> crate::Result<()> {
Command::new(&runner)
let status = Command::new(&runner)
.args(&["build", "--features=custom-protocol"])
.args(args)
.env("STATIC_VCRUNTIME", "true")
.pipe()?
.output_ok()
.with_context(|| format!("Result of `{} build` operation was unsuccessful", runner))?;
Ok(())
.piped()?;
if status.success() {
Ok(())
} else {
Err(anyhow::anyhow!(
"Result of `{} build` operation was unsuccessful",
runner
))
}
}
pub struct AppSettings {

View File

@@ -19,8 +19,8 @@ use env_logger::Builder;
use log::{debug, log_enabled, Level};
use serde::Deserialize;
use std::ffi::OsString;
use std::io::Write;
use std::process::{Command, Output};
use std::io::{BufReader, Write};
use std::process::{Command, ExitStatus, Stdio};
#[derive(Deserialize)]
pub struct VersionMetadata {
@@ -104,11 +104,16 @@ where
.format_indent(Some(12))
.filter(None, level_from_usize(cli.verbose).to_level_filter())
.format(|f, record| {
let mut is_command_output = false;
if let Some(action) = record.key_values().get("action".into()) {
let mut action_style = f.style();
action_style.set_color(Color::Green).set_bold(true);
let action = action.to_str().unwrap();
is_command_output = action == "stdout" || action == "stderr";
if !is_command_output {
let mut action_style = f.style();
action_style.set_color(Color::Green).set_bold(true);
write!(f, "{:>12} ", action_style.value(action.to_str().unwrap()))?;
write!(f, "{:>12} ", action_style.value(action))?;
}
} else {
let mut level_style = f.default_level_style(record.level());
level_style.set_bold(true);
@@ -120,7 +125,7 @@ where
)?;
}
if log_enabled!(Level::Debug) {
if !is_command_output && log_enabled!(Level::Debug) {
let mut target_style = f.style();
target_style.set_color(Color::Black);
@@ -171,37 +176,61 @@ fn prettyprint_level(lvl: Level) -> &'static str {
pub trait CommandExt {
// The `pipe` function sets the stdout and stderr to properly
// show the command output in the Node.js wrapper.
fn pipe(&mut self) -> Result<&mut Self>;
fn output_ok(&mut self) -> crate::Result<Output>;
fn piped(&mut self) -> Result<ExitStatus>;
fn output_ok(&mut self) -> crate::Result<()>;
}
impl CommandExt for Command {
fn pipe(&mut self) -> Result<&mut Self> {
fn piped(&mut self) -> crate::Result<ExitStatus> {
self.stdout(os_pipe::dup_stdout()?);
self.stderr(os_pipe::dup_stderr()?);
Ok(self)
let program = self.get_program().to_string_lossy().into_owned();
debug!(action = "Running"; "Command `{} {}`", program, self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{} {}", acc, arg)));
self.status().map_err(Into::into)
}
fn output_ok(&mut self) -> crate::Result<Output> {
debug!(action = "Running"; "Command `{} {}`", self.get_program().to_string_lossy(), self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{} {}", acc, arg)));
fn output_ok(&mut self) -> crate::Result<()> {
let program = self.get_program().to_string_lossy().into_owned();
debug!(action = "Running"; "Command `{} {}`", program, self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{} {}", acc, arg)));
let output = self.output()?;
self.stdout(Stdio::piped());
self.stderr(Stdio::piped());
let stdout = String::from_utf8_lossy(&output.stdout);
if !stdout.is_empty() {
debug!("Stdout: {}", stdout);
}
let stderr = String::from_utf8_lossy(&output.stderr);
if !stderr.is_empty() {
debug!("Stderr: {}", stderr);
}
let mut child = self.spawn()?;
if output.status.success() {
Ok(output)
let mut stdout = child.stdout.take().map(BufReader::new).unwrap();
std::thread::spawn(move || {
let mut buf = Vec::new();
loop {
buf.clear();
match tauri_utils::io::read_line(&mut stdout, &mut buf) {
Ok(s) if s == 0 => break,
_ => (),
}
debug!(action = "stdout"; "{}", String::from_utf8_lossy(&buf));
}
});
let mut stderr = child.stderr.take().map(BufReader::new).unwrap();
std::thread::spawn(move || {
let mut buf = Vec::new();
loop {
buf.clear();
match tauri_utils::io::read_line(&mut stderr, &mut buf) {
Ok(s) if s == 0 => break,
_ => (),
}
debug!(action = "stderr"; "{}", String::from_utf8_lossy(&buf));
}
});
let status = child.wait()?;
if status.success() {
Ok(())
} else {
Err(anyhow::anyhow!(
String::from_utf8_lossy(&output.stderr).to_string()
))
Err(anyhow::anyhow!("failed to run {}", program))
}
}
}