|
@@ -4,10 +4,10 @@
|
|
|
|
|
|
use std::{
|
|
|
ffi::OsStr,
|
|
|
- fs::{rename, File, FileType},
|
|
|
- io::{BufReader, ErrorKind, Read, Write},
|
|
|
+ fs::{File, FileType},
|
|
|
+ io::{Read, Write},
|
|
|
path::{Path, PathBuf},
|
|
|
- process::{Command, ExitStatus, Stdio},
|
|
|
+ process::ExitStatus,
|
|
|
str::FromStr,
|
|
|
sync::{
|
|
|
atomic::{AtomicBool, Ordering},
|
|
@@ -31,14 +31,12 @@ use tauri_bundler::{
|
|
|
};
|
|
|
|
|
|
use super::{AppSettings, ExitReason, Interface};
|
|
|
-use crate::{
|
|
|
- helpers::{
|
|
|
- app_paths::tauri_dir,
|
|
|
- config::{reload as reload_config, wix_settings, Config},
|
|
|
- },
|
|
|
- CommandExt,
|
|
|
+use crate::helpers::{
|
|
|
+ app_paths::tauri_dir,
|
|
|
+ config::{reload as reload_config, wix_settings, Config},
|
|
|
};
|
|
|
|
|
|
+mod desktop;
|
|
|
mod manifest;
|
|
|
use manifest::{rewrite_manifest, Manifest};
|
|
|
|
|
@@ -108,7 +106,7 @@ impl DevChild {
|
|
|
}
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
-struct Target {
|
|
|
+pub struct Target {
|
|
|
name: String,
|
|
|
installed: bool,
|
|
|
}
|
|
@@ -159,59 +157,17 @@ impl Interface for Rust {
|
|
|
}
|
|
|
|
|
|
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());
|
|
|
-
|
|
|
- if !std::env::var("STATIC_VCRUNTIME").map_or(false, |v| v == "false") {
|
|
|
- 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")?;
|
|
|
-
|
|
|
- let mut lipo_cmd = Command::new("lipo");
|
|
|
- lipo_cmd
|
|
|
- .arg("-create")
|
|
|
- .arg("-output")
|
|
|
- .arg(out_dir.join(&bin_name));
|
|
|
- for triple in ["aarch64-apple-darwin", "x86_64-apple-darwin"] {
|
|
|
- let mut options = options.clone();
|
|
|
- options.target.replace(triple.into());
|
|
|
-
|
|
|
- let triple_out_dir = self
|
|
|
- .app_settings
|
|
|
- .out_dir(Some(triple.into()), options.debug)
|
|
|
- .with_context(|| format!("failed to get {} out dir", triple))?;
|
|
|
- self
|
|
|
- .build_production_app(options)
|
|
|
- .with_context(|| format!("failed to build {} binary", triple))?;
|
|
|
-
|
|
|
- lipo_cmd.arg(triple_out_dir.join(&bin_name));
|
|
|
- }
|
|
|
-
|
|
|
- let lipo_status = lipo_cmd.output_ok()?.status;
|
|
|
- if !lipo_status.success() {
|
|
|
- return Err(anyhow::anyhow!(format!(
|
|
|
- "Result of `lipo` command was unsuccessful: {}. (Is `lipo` installed?)",
|
|
|
- lipo_status
|
|
|
- )));
|
|
|
- }
|
|
|
- } else {
|
|
|
- self
|
|
|
- .build_production_app(options)
|
|
|
- .with_context(|| "failed to build app")?;
|
|
|
- }
|
|
|
-
|
|
|
- rename_app(&bin_path, self.product_name.as_deref())?;
|
|
|
-
|
|
|
+ desktop::build(
|
|
|
+ options,
|
|
|
+ &self.app_settings,
|
|
|
+ self.product_name.clone(),
|
|
|
+ &mut self.available_targets,
|
|
|
+ self.config_features.clone(),
|
|
|
+ )?;
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
@@ -268,50 +224,11 @@ fn lookup<F: FnMut(FileType, PathBuf)>(dir: &Path, mut f: F) {
|
|
|
}
|
|
|
|
|
|
impl Rust {
|
|
|
- fn fetch_available_targets(&mut self) {
|
|
|
- if let Ok(output) = Command::new("rustup").args(["target", "list"]).output() {
|
|
|
- let stdout = String::from_utf8_lossy(&output.stdout).into_owned();
|
|
|
- self.available_targets.replace(
|
|
|
- stdout
|
|
|
- .split('\n')
|
|
|
- .map(|t| {
|
|
|
- let mut s = t.split(' ');
|
|
|
- let name = s.next().unwrap().to_string();
|
|
|
- let installed = s.next().map(|v| v == "(installed)").unwrap_or_default();
|
|
|
- Target { name, installed }
|
|
|
- })
|
|
|
- .filter(|t| !t.name.is_empty())
|
|
|
- .collect(),
|
|
|
- );
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- fn validate_target(&self, target: &str) -> crate::Result<()> {
|
|
|
- if let Some(available_targets) = &self.available_targets {
|
|
|
- if let Some(target) = available_targets.iter().find(|t| t.name == target) {
|
|
|
- if !target.installed {
|
|
|
- anyhow::bail!(
|
|
|
- "Target {target} is not installed (installed targets: {installed}). Please run `rustup target add {target}`.",
|
|
|
- target = target.name,
|
|
|
- installed = available_targets.iter().filter(|t| t.installed).map(|t| t.name.as_str()).collect::<Vec<&str>>().join(", ")
|
|
|
- );
|
|
|
- }
|
|
|
- }
|
|
|
- if !available_targets.iter().any(|t| t.name == target) {
|
|
|
- anyhow::bail!("Target {target} does not exist. Please run `rustup target list` to see the available targets.", target = target);
|
|
|
- }
|
|
|
- }
|
|
|
- Ok(())
|
|
|
- }
|
|
|
-
|
|
|
fn run_dev<F: Fn(ExitStatus, ExitReason) + Send + Sync + 'static>(
|
|
|
&mut self,
|
|
|
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();
|
|
|
-
|
|
|
if !options.args.contains(&"--no-default-features".into()) {
|
|
|
let manifest_features = self.app_settings.manifest.features();
|
|
|
let enable_features: Vec<String> = manifest_features
|
|
@@ -350,51 +267,15 @@ impl Rust {
|
|
|
}
|
|
|
options.args = args;
|
|
|
|
|
|
- let manually_killed_app = Arc::new(AtomicBool::default());
|
|
|
- let manually_killed_app_ = manually_killed_app.clone();
|
|
|
- let app_child = Arc::new(Mutex::new(None));
|
|
|
- let app_child_ = app_child.clone();
|
|
|
-
|
|
|
- let build_child = self.build_dev_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 mut app = Command::new(bin_path);
|
|
|
- app.stdout(os_pipe::dup_stdout().unwrap());
|
|
|
- app.stderr(os_pipe::dup_stderr().unwrap());
|
|
|
- app.args(run_args);
|
|
|
- let app_child = Arc::new(SharedChild::spawn(&mut app).unwrap());
|
|
|
- let app_child_t = app_child.clone();
|
|
|
- std::thread::spawn(move || {
|
|
|
- let status = app_child_t.wait().expect("failed to wait on app");
|
|
|
- on_exit(
|
|
|
- status,
|
|
|
- if manually_killed_app_.load(Ordering::Relaxed) {
|
|
|
- ExitReason::TriggeredKill
|
|
|
- } else {
|
|
|
- ExitReason::NormalExit
|
|
|
- },
|
|
|
- );
|
|
|
- });
|
|
|
-
|
|
|
- app_child_.lock().unwrap().replace(app_child);
|
|
|
- } else {
|
|
|
- on_exit(
|
|
|
- status,
|
|
|
- if manually_killed_app_.load(Ordering::Relaxed) {
|
|
|
- ExitReason::TriggeredKill
|
|
|
- } else {
|
|
|
- reason
|
|
|
- },
|
|
|
- );
|
|
|
- }
|
|
|
- })?;
|
|
|
-
|
|
|
- Ok(DevChild {
|
|
|
- manually_killed_app,
|
|
|
- build_child,
|
|
|
- app_child,
|
|
|
- })
|
|
|
+ desktop::run_dev(
|
|
|
+ options,
|
|
|
+ run_args,
|
|
|
+ &mut self.available_targets,
|
|
|
+ self.config_features.clone(),
|
|
|
+ &self.app_settings,
|
|
|
+ self.product_name.clone(),
|
|
|
+ on_exit,
|
|
|
+ )
|
|
|
}
|
|
|
|
|
|
fn run_dev_watcher<F: Fn(ExitStatus, ExitReason) + Send + Sync + 'static>(
|
|
@@ -480,158 +361,6 @@ impl Rust {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- fn build_production_app(&mut self, options: Options) -> crate::Result<()> {
|
|
|
- let mut build_cmd = self.build_command(options)?;
|
|
|
- let runner = build_cmd.get_program().to_string_lossy().into_owned();
|
|
|
- match build_cmd.piped() {
|
|
|
- Ok(status) if status.success() => Ok(()),
|
|
|
- Ok(_) => Err(anyhow::anyhow!("failed to build app")),
|
|
|
- Err(e) if e.kind() == ErrorKind::NotFound => 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 {
|
|
|
- ""
|
|
|
- }
|
|
|
- )),
|
|
|
- Err(e) => Err(e.into()),
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- fn build_dev_app<F: FnOnce(ExitStatus, ExitReason) + Send + 'static>(
|
|
|
- &mut self,
|
|
|
- options: Options,
|
|
|
- on_exit: F,
|
|
|
- ) -> crate::Result<Arc<SharedChild>> {
|
|
|
- let mut build_cmd = self.build_command(options)?;
|
|
|
- let runner = build_cmd.get_program().to_string_lossy().into_owned();
|
|
|
- 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("--color");
|
|
|
- build_cmd.arg("always");
|
|
|
-
|
|
|
- build_cmd.stdout(os_pipe::dup_stdout()?);
|
|
|
- build_cmd.stderr(Stdio::piped());
|
|
|
-
|
|
|
- let build_child = match SharedChild::spawn(&mut build_cmd) {
|
|
|
- Ok(c) => Ok(c),
|
|
|
- Err(e) if e.kind() == ErrorKind::NotFound => 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 {
|
|
|
- ""
|
|
|
- }
|
|
|
- )),
|
|
|
- Err(e) => 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)
|
|
|
- }
|
|
|
-
|
|
|
- fn build_command(&mut self, options: Options) -> crate::Result<Command> {
|
|
|
- let runner = options.runner.unwrap_or_else(|| "cargo".into());
|
|
|
-
|
|
|
- if let Some(target) = &options.target {
|
|
|
- if self.available_targets.is_none() {
|
|
|
- self.fetch_available_targets();
|
|
|
- }
|
|
|
- self.validate_target(target)?;
|
|
|
- }
|
|
|
-
|
|
|
- let mut args = Vec::new();
|
|
|
- if !options.args.is_empty() {
|
|
|
- args.extend(options.args);
|
|
|
- }
|
|
|
-
|
|
|
- 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 {
|
|
|
- args.push("--release".into());
|
|
|
- }
|
|
|
-
|
|
|
- if let Some(target) = options.target {
|
|
|
- args.push("--target".into());
|
|
|
- args.push(target);
|
|
|
- }
|
|
|
-
|
|
|
- let mut build_cmd = Command::new(&runner);
|
|
|
- build_cmd.arg("build");
|
|
|
- build_cmd.args(args);
|
|
|
-
|
|
|
- Ok(build_cmd)
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
/// The `workspace` section of the app configuration (read from Cargo.toml).
|
|
@@ -1153,106 +882,3 @@ fn tauri_config_to_bundle_settings(
|
|
|
..Default::default()
|
|
|
})
|
|
|
}
|
|
|
-
|
|
|
-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();
|
|
|
-
|
|
|
- let product_path = bin_path
|
|
|
- .parent()
|
|
|
- .unwrap()
|
|
|
- .join(&product_name)
|
|
|
- .with_extension(bin_path.extension().unwrap_or_default());
|
|
|
-
|
|
|
- rename(&bin_path, &product_path).with_context(|| {
|
|
|
- format!(
|
|
|
- "failed to rename `{}` to `{}`",
|
|
|
- bin_path.display(),
|
|
|
- product_path.display(),
|
|
|
- )
|
|
|
- })?;
|
|
|
- Ok(product_path)
|
|
|
- } else {
|
|
|
- Ok(bin_path.to_path_buf())
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// taken from https://github.com/rust-lang/cargo/blob/78b10d4e611ab0721fc3aeaf0edd5dd8f4fdc372/src/cargo/core/shell.rs#L514
|
|
|
-#[cfg(unix)]
|
|
|
-mod terminal {
|
|
|
- use std::mem;
|
|
|
-
|
|
|
- pub fn stderr_width() -> Option<usize> {
|
|
|
- unsafe {
|
|
|
- let mut winsize: libc::winsize = mem::zeroed();
|
|
|
- // The .into() here is needed for FreeBSD which defines TIOCGWINSZ
|
|
|
- // as c_uint but ioctl wants c_ulong.
|
|
|
- #[allow(clippy::useless_conversion)]
|
|
|
- if libc::ioctl(libc::STDERR_FILENO, libc::TIOCGWINSZ.into(), &mut winsize) < 0 {
|
|
|
- return None;
|
|
|
- }
|
|
|
- if winsize.ws_col > 0 {
|
|
|
- Some(winsize.ws_col as usize)
|
|
|
- } else {
|
|
|
- None
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// taken from https://github.com/rust-lang/cargo/blob/78b10d4e611ab0721fc3aeaf0edd5dd8f4fdc372/src/cargo/core/shell.rs#L543
|
|
|
-#[cfg(windows)]
|
|
|
-mod terminal {
|
|
|
- use std::{cmp, mem, ptr};
|
|
|
- use winapi::um::fileapi::*;
|
|
|
- use winapi::um::handleapi::*;
|
|
|
- use winapi::um::processenv::*;
|
|
|
- use winapi::um::winbase::*;
|
|
|
- use winapi::um::wincon::*;
|
|
|
- use winapi::um::winnt::*;
|
|
|
-
|
|
|
- pub fn stderr_width() -> Option<usize> {
|
|
|
- unsafe {
|
|
|
- let stdout = GetStdHandle(STD_ERROR_HANDLE);
|
|
|
- let mut csbi: CONSOLE_SCREEN_BUFFER_INFO = mem::zeroed();
|
|
|
- if GetConsoleScreenBufferInfo(stdout, &mut csbi) != 0 {
|
|
|
- return Some((csbi.srWindow.Right - csbi.srWindow.Left) as usize);
|
|
|
- }
|
|
|
-
|
|
|
- // On mintty/msys/cygwin based terminals, the above fails with
|
|
|
- // INVALID_HANDLE_VALUE. Use an alternate method which works
|
|
|
- // in that case as well.
|
|
|
- let h = CreateFileA(
|
|
|
- "CONOUT$\0".as_ptr() as *const CHAR,
|
|
|
- GENERIC_READ | GENERIC_WRITE,
|
|
|
- FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
|
- ptr::null_mut(),
|
|
|
- OPEN_EXISTING,
|
|
|
- 0,
|
|
|
- ptr::null_mut(),
|
|
|
- );
|
|
|
- if h == INVALID_HANDLE_VALUE {
|
|
|
- return None;
|
|
|
- }
|
|
|
-
|
|
|
- let mut csbi: CONSOLE_SCREEN_BUFFER_INFO = mem::zeroed();
|
|
|
- let rc = GetConsoleScreenBufferInfo(h, &mut csbi);
|
|
|
- CloseHandle(h);
|
|
|
- if rc != 0 {
|
|
|
- let width = (csbi.srWindow.Right - csbi.srWindow.Left) as usize;
|
|
|
- // Unfortunately cygwin/mintty does not set the size of the
|
|
|
- // backing console to match the actual window size. This
|
|
|
- // always reports a size of 80 or 120 (not sure what
|
|
|
- // determines that). Use a conservative max of 60 which should
|
|
|
- // work in most circumstances. ConEmu does some magic to
|
|
|
- // resize the console correctly, but there's no reasonable way
|
|
|
- // to detect which kind of terminal we are running in, or if
|
|
|
- // GetConsoleScreenBufferInfo returns accurate information.
|
|
|
- return Some(cmp::min(60, width));
|
|
|
- }
|
|
|
-
|
|
|
- None
|
|
|
- }
|
|
|
- }
|
|
|
-}
|