refactor(cli): reuse Rust build logic for dev and build commands (#4578)

This commit is contained in:
Lucas Fernandes Nogueira
2022-07-05 10:21:20 -03:00
committed by GitHub
parent f8a3becb28
commit 64416100c6
2 changed files with 128 additions and 145 deletions

View File

@@ -20,7 +20,6 @@ use shared_child::SharedChild;
use std::{
env::set_current_dir,
io::Write,
process::{exit, Command, ExitStatus, Stdio},
sync::{
atomic::{AtomicBool, Ordering},
@@ -284,6 +283,7 @@ fn kill_before_dev_process() {
.status();
#[cfg(unix)]
{
use std::io::Write;
let mut kill_children_script_path = std::env::temp_dir();
kill_children_script_path.push("kill-children.sh");

View File

@@ -145,12 +145,18 @@ impl Interface for Rust {
&self.app_settings
}
fn build(&mut self, options: Options) -> crate::Result<()> {
fn build(&mut self, mut options: Options) -> crate::Result<()> {
let bin_path = self.app_settings.app_binary_path(&options)?;
let out_dir = bin_path.parent().unwrap();
let bin_name = bin_path.file_stem().unwrap();
options
.features
.get_or_insert(Vec::new())
.push("custom-protocol".into());
std::env::set_var("STATIC_VCRUNTIME", "true");
if options.target == Some("universal-apple-darwin".into()) {
std::fs::create_dir_all(&out_dir)
.with_context(|| "failed to create project out directory")?;
@@ -169,7 +175,7 @@ impl Interface for Rust {
.out_dir(Some(triple.into()), options.debug)
.with_context(|| format!("failed to get {} out dir", triple))?;
self
.build_app(options)
.build_app_blocking(options)
.with_context(|| format!("failed to build {} binary", triple))?;
lipo_cmd.arg(triple_out_dir.join(&bin_name));
@@ -184,11 +190,11 @@ impl Interface for Rust {
}
} else {
self
.build_app(options)
.build_app_blocking(options)
.with_context(|| "failed to build app")?;
}
rename_app(bin_path, self.product_name.as_deref())?;
rename_app(&bin_path, self.product_name.as_deref())?;
Ok(())
}
@@ -271,37 +277,12 @@ impl Rust {
fn run_dev<F: Fn(ExitStatus, ExitReason) + Send + Sync + 'static>(
&mut self,
options: Options,
mut options: Options,
on_exit: F,
) -> crate::Result<DevChild> {
let bin_path = self.app_settings.app_binary_path(&options)?;
let product_name = self.product_name.clone();
let runner = options.runner.unwrap_or_else(|| "cargo".into());
if let Some(target) = &options.target {
self.fetch_available_targets();
self.validate_target(target)?;
}
let mut build_cmd = Command::new(&runner);
build_cmd
.env(
"CARGO_TERM_PROGRESS_WIDTH",
terminal::stderr_width()
.map(|width| {
if cfg!(windows) {
std::cmp::min(60, width)
} else {
width
}
})
.unwrap_or(if cfg!(windows) { 60 } else { 80 })
.to_string(),
)
.env("CARGO_TERM_PROGRESS_WHEN", "always");
build_cmd.arg("build").arg("--color").arg("always");
if !options.args.contains(&"--no-default-features".into()) {
let manifest_features = self.app_settings.manifest.features();
let enable_features: Vec<String> = manifest_features
@@ -317,28 +298,16 @@ impl Rust {
}
})
.collect();
build_cmd.arg("--no-default-features");
options.args.push("--no-default-features".into());
if !enable_features.is_empty() {
build_cmd.args(&["--features", &enable_features.join(",")]);
options
.features
.get_or_insert(Vec::new())
.extend(enable_features);
}
}
if !options.debug {
build_cmd.args(&["--release"]);
}
if let Some(target) = &options.target {
build_cmd.args(&["--target", target]);
}
let mut features = self.config_features.clone();
if let Some(f) = options.features {
features.extend(f);
}
if !features.is_empty() {
build_cmd.args(&["--features", &features.join(",")]);
}
let mut args = Vec::new();
let mut run_args = Vec::new();
let mut reached_run_args = false;
for arg in options.args.clone() {
@@ -347,66 +316,20 @@ impl Rust {
} else if arg == "--" {
reached_run_args = true;
} else {
build_cmd.arg(arg);
args.push(arg);
}
}
build_cmd.stdout(os_pipe::dup_stdout()?);
build_cmd.stderr(Stdio::piped());
options.args = args;
let manually_killed_app = Arc::new(AtomicBool::default());
let manually_killed_app_ = manually_killed_app.clone();
let build_child = match SharedChild::spawn(&mut build_cmd) {
Ok(c) => c,
Err(e) => {
if e.kind() == ErrorKind::NotFound {
return Err(anyhow::anyhow!(
"`{}` command not found.{}",
runner,
if runner == "cargo" {
" Please follow the Tauri setup guide: https://tauri.app/v1/guides/getting-started/prerequisites"
} else {
""
}
));
} else {
return Err(e.into());
}
}
};
let build_child = Arc::new(build_child);
let build_child_stderr = build_child.take_stderr().unwrap();
let mut stderr = BufReader::new(build_child_stderr);
let stderr_lines = Arc::new(Mutex::new(Vec::new()));
let stderr_lines_ = stderr_lines.clone();
std::thread::spawn(move || {
let mut buf = Vec::new();
let mut lines = stderr_lines_.lock().unwrap();
let mut io_stderr = std::io::stderr();
loop {
buf.clear();
match tauri_utils::io::read_line(&mut stderr, &mut buf) {
Ok(s) if s == 0 => break,
_ => (),
}
let _ = io_stderr.write_all(&buf);
if !buf.ends_with(&[b'\r']) {
let _ = io_stderr.write_all(b"\n");
}
lines.push(String::from_utf8_lossy(&buf).into_owned());
}
});
let build_child_ = build_child.clone();
let app_child = Arc::new(Mutex::new(None));
let app_child_ = app_child.clone();
std::thread::spawn(move || {
let status = build_child_.wait().expect("failed to wait on build");
let build_child = self.build_app(options, move |status, reason| {
if status.success() {
let bin_path = rename_app(bin_path, product_name.as_deref()).expect("failed to rename app");
let bin_path =
rename_app(&bin_path, product_name.as_deref()).expect("failed to rename app");
let mut app = Command::new(bin_path);
app.stdout(os_pipe::dup_stdout().unwrap());
app.stderr(os_pipe::dup_stderr().unwrap());
@@ -427,24 +350,9 @@ impl Rust {
app_child_.lock().unwrap().replace(app_child);
} else {
let is_cargo_compile_error = stderr_lines
.lock()
.unwrap()
.last()
.map(|l| l.contains("could not compile"))
.unwrap_or_default();
stderr_lines.lock().unwrap().clear();
on_exit(
status,
if status.code() == Some(101) && is_cargo_compile_error {
ExitReason::CompilationFailed
} else {
ExitReason::NormalExit
},
);
on_exit(status, reason);
}
});
})?;
Ok(DevChild {
manually_killed_app,
@@ -537,7 +445,22 @@ impl Rust {
}
}
fn build_app(&mut self, options: Options) -> crate::Result<()> {
fn build_app_blocking(&mut self, options: Options) -> crate::Result<()> {
let (tx, rx) = channel();
self.build_app(options, move |status, _| tx.send(status).unwrap())?;
let status = rx.recv().unwrap();
if status.success() {
Ok(())
} else {
Err(anyhow::anyhow!("failed to build app"))
}
}
fn build_app<F: FnOnce(ExitStatus, ExitReason) + Send + 'static>(
&mut self,
options: Options,
on_exit: F,
) -> crate::Result<Arc<SharedChild>> {
let runner = options.runner.unwrap_or_else(|| "cargo".into());
if let Some(target) = &options.target {
@@ -552,11 +475,13 @@ impl Rust {
args.extend(options.args);
}
if let Some(features) = options.features {
if !features.is_empty() {
args.push("--features".into());
args.push(features.join(","));
}
let mut features = self.config_features.clone();
if let Some(f) = options.features {
features.extend(f);
}
if !features.is_empty() {
args.push("--features".into());
args.push(features.join(","));
}
if !options.debug {
@@ -568,25 +493,33 @@ impl Rust {
args.push(target);
}
match Command::new(&runner)
.args(&["build", "--features=custom-protocol"])
.args(args)
.env("STATIC_VCRUNTIME", "true")
.piped()
{
Ok(status) => {
if status.success() {
Ok(())
} else {
Err(anyhow::anyhow!(
"Result of `{} build` operation was unsuccessful",
runner
))
}
}
let mut build_cmd = Command::new(&runner);
build_cmd
.env(
"CARGO_TERM_PROGRESS_WIDTH",
terminal::stderr_width()
.map(|width| {
if cfg!(windows) {
std::cmp::min(60, width)
} else {
width
}
})
.unwrap_or(if cfg!(windows) { 60 } else { 80 })
.to_string(),
)
.env("CARGO_TERM_PROGRESS_WHEN", "always");
build_cmd.arg("build").arg("--color").arg("always");
build_cmd.args(args);
build_cmd.stdout(os_pipe::dup_stdout()?);
build_cmd.stderr(Stdio::piped());
let build_child = match SharedChild::spawn(&mut build_cmd) {
Ok(c) => c,
Err(e) => {
if e.kind() == ErrorKind::NotFound {
Err(anyhow::anyhow!(
return Err(anyhow::anyhow!(
"`{}` command not found.{}",
runner,
if runner == "cargo" {
@@ -594,12 +527,62 @@ impl Rust {
} else {
""
}
))
));
} else {
Err(e.into())
return Err(e.into());
}
}
}
};
let build_child = Arc::new(build_child);
let build_child_stderr = build_child.take_stderr().unwrap();
let mut stderr = BufReader::new(build_child_stderr);
let stderr_lines = Arc::new(Mutex::new(Vec::new()));
let stderr_lines_ = stderr_lines.clone();
std::thread::spawn(move || {
let mut buf = Vec::new();
let mut lines = stderr_lines_.lock().unwrap();
let mut io_stderr = std::io::stderr();
loop {
buf.clear();
match tauri_utils::io::read_line(&mut stderr, &mut buf) {
Ok(s) if s == 0 => break,
_ => (),
}
let _ = io_stderr.write_all(&buf);
if !buf.ends_with(&[b'\r']) {
let _ = io_stderr.write_all(b"\n");
}
lines.push(String::from_utf8_lossy(&buf).into_owned());
}
});
let build_child_ = build_child.clone();
std::thread::spawn(move || {
let status = build_child_.wait().expect("failed to wait on build");
if status.success() {
on_exit(status, ExitReason::NormalExit);
} else {
let is_cargo_compile_error = stderr_lines
.lock()
.unwrap()
.last()
.map(|l| l.contains("could not compile"))
.unwrap_or_default();
stderr_lines.lock().unwrap().clear();
on_exit(
status,
if status.code() == Some(101) && is_cargo_compile_error {
ExitReason::CompilationFailed
} else {
ExitReason::NormalExit
},
);
}
});
Ok(build_child)
}
}
@@ -1123,7 +1106,7 @@ fn tauri_config_to_bundle_settings(
})
}
fn rename_app(bin_path: PathBuf, product_name: Option<&str>) -> crate::Result<PathBuf> {
fn rename_app(bin_path: &Path, product_name: Option<&str>) -> crate::Result<PathBuf> {
if let Some(product_name) = product_name {
#[cfg(target_os = "linux")]
let product_name = product_name.to_kebab_case();
@@ -1143,7 +1126,7 @@ fn rename_app(bin_path: PathBuf, product_name: Option<&str>) -> crate::Result<Pa
})?;
Ok(product_path)
} else {
Ok(bin_path)
Ok(bin_path.to_path_buf())
}
}