mirror of
https://github.com/tauri-apps/tauri.git
synced 2024-12-14 21:21:44 +03:00
refactor(cli): move watch and manifest logic to the Rust impl (#4571)
This commit is contained in:
parent
d4cdf80781
commit
573b4c0b3c
@ -7,7 +7,6 @@ use crate::{
|
||||
app_paths::{app_dir, tauri_dir},
|
||||
command_env,
|
||||
config::{get as get_config, AppUrl, WindowUrl},
|
||||
manifest::rewrite_manifest,
|
||||
updater_signature::sign_file_from_env_variables,
|
||||
},
|
||||
interface::{AppInterface, AppSettings, Interface},
|
||||
@ -55,7 +54,7 @@ pub struct Options {
|
||||
}
|
||||
|
||||
pub fn command(mut options: Options) -> Result<()> {
|
||||
let merge_config = if let Some(config) = &options.config {
|
||||
options.config = if let Some(config) = &options.config {
|
||||
Some(if config.starts_with('{') {
|
||||
config.to_string()
|
||||
} else {
|
||||
@ -68,9 +67,7 @@ pub fn command(mut options: Options) -> Result<()> {
|
||||
let tauri_path = tauri_dir();
|
||||
set_current_dir(&tauri_path).with_context(|| "failed to change current working directory")?;
|
||||
|
||||
let config = get_config(merge_config.as_deref())?;
|
||||
|
||||
let manifest = rewrite_manifest(config.clone())?;
|
||||
let config = get_config(options.config.as_deref())?;
|
||||
|
||||
let config_guard = config.lock().unwrap();
|
||||
let config_ = config_guard.as_ref().unwrap();
|
||||
@ -210,7 +207,7 @@ pub fn command(mut options: Options) -> Result<()> {
|
||||
}
|
||||
|
||||
let settings = app_settings
|
||||
.get_bundler_settings(&options.into(), &manifest, config_, out_dir, package_types)
|
||||
.get_bundler_settings(&options.into(), config_, out_dir, package_types)
|
||||
.with_context(|| "failed to build bundler settings")?;
|
||||
|
||||
// set env vars used by the bundler
|
||||
|
@ -6,33 +6,26 @@ use crate::{
|
||||
helpers::{
|
||||
app_paths::{app_dir, tauri_dir},
|
||||
command_env,
|
||||
config::{get as get_config, reload as reload_config, AppUrl, ConfigHandle, WindowUrl},
|
||||
manifest::{rewrite_manifest, Manifest},
|
||||
config::{get as get_config, AppUrl, WindowUrl},
|
||||
},
|
||||
interface::{AppInterface, DevProcess, ExitReason, Interface},
|
||||
interface::{AppInterface, ExitReason, Interface},
|
||||
Result,
|
||||
};
|
||||
use clap::Parser;
|
||||
|
||||
use anyhow::Context;
|
||||
use log::{error, info, warn};
|
||||
use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
|
||||
use once_cell::sync::OnceCell;
|
||||
use shared_child::SharedChild;
|
||||
|
||||
use std::{
|
||||
env::set_current_dir,
|
||||
ffi::OsStr,
|
||||
fs::FileType,
|
||||
io::Write,
|
||||
path::{Path, PathBuf},
|
||||
process::{exit, Command, ExitStatus, Stdio},
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
mpsc::channel,
|
||||
Arc, Mutex,
|
||||
},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
static BEFORE_DEV: OnceCell<Mutex<Arc<SharedChild>>> = OnceCell::new();
|
||||
@ -41,7 +34,7 @@ static KILL_BEFORE_DEV_FLAG: OnceCell<AtomicBool> = OnceCell::new();
|
||||
#[cfg(unix)]
|
||||
const KILL_CHILDREN_SCRIPT: &[u8] = include_bytes!("../scripts/kill-children.sh");
|
||||
|
||||
const TAURI_DEV_WATCHER_GITIGNORE: &[u8] = include_bytes!("../tauri-dev-watcher.gitignore");
|
||||
pub const TAURI_DEV_WATCHER_GITIGNORE: &[u8] = include_bytes!("../tauri-dev-watcher.gitignore");
|
||||
|
||||
#[derive(Debug, Clone, Parser)]
|
||||
#[clap(about = "Tauri dev", trailing_var_arg(true))]
|
||||
@ -60,7 +53,7 @@ pub struct Options {
|
||||
exit_on_panic: bool,
|
||||
/// JSON string or path to JSON file to merge with tauri.conf.json
|
||||
#[clap(short, long)]
|
||||
config: Option<String>,
|
||||
pub config: Option<String>,
|
||||
/// Run the code in release mode
|
||||
#[clap(long = "release")]
|
||||
pub release_mode: bool,
|
||||
@ -80,7 +73,7 @@ pub fn command(options: Options) -> Result<()> {
|
||||
|
||||
fn command_internal(mut options: Options) -> Result<()> {
|
||||
let tauri_path = tauri_dir();
|
||||
let merge_config = if let Some(config) = &options.config {
|
||||
options.config = if let Some(config) = &options.config {
|
||||
Some(if config.starts_with('{') {
|
||||
config.to_string()
|
||||
} else {
|
||||
@ -92,7 +85,7 @@ fn command_internal(mut options: Options) -> Result<()> {
|
||||
|
||||
set_current_dir(&tauri_path).with_context(|| "failed to change current working directory")?;
|
||||
|
||||
let config = get_config(merge_config.as_deref())?;
|
||||
let config = get_config(options.config.as_deref())?;
|
||||
|
||||
if let Some(before_dev) = &config
|
||||
.lock()
|
||||
@ -167,19 +160,6 @@ fn command_internal(mut options: Options) -> Result<()> {
|
||||
.clone();
|
||||
}
|
||||
|
||||
let manifest = {
|
||||
let (tx, rx) = channel();
|
||||
let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap();
|
||||
watcher.watch(tauri_path.join("Cargo.toml"), RecursiveMode::Recursive)?;
|
||||
let manifest = rewrite_manifest(config.clone())?;
|
||||
loop {
|
||||
if let Ok(DebouncedEvent::NoticeWrite(_)) = rx.recv() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
manifest
|
||||
};
|
||||
|
||||
let mut cargo_features = config
|
||||
.lock()
|
||||
.unwrap()
|
||||
@ -255,29 +235,9 @@ fn command_internal(mut options: Options) -> Result<()> {
|
||||
let mut interface = AppInterface::new(config.lock().unwrap().as_ref().unwrap())?;
|
||||
|
||||
let exit_on_panic = options.exit_on_panic;
|
||||
let process = interface.dev(options.clone().into(), &manifest, move |status, reason| {
|
||||
interface.dev(options.into(), move |status, reason| {
|
||||
on_dev_exit(status, reason, exit_on_panic)
|
||||
})?;
|
||||
let shared_process = Arc::new(Mutex::new(process));
|
||||
|
||||
if let Err(e) = watch(
|
||||
interface,
|
||||
shared_process.clone(),
|
||||
tauri_path,
|
||||
merge_config,
|
||||
config,
|
||||
options,
|
||||
manifest,
|
||||
) {
|
||||
shared_process
|
||||
.lock()
|
||||
.unwrap()
|
||||
.kill()
|
||||
.with_context(|| "failed to kill app process")?;
|
||||
Err(e)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn on_dev_exit(status: ExitStatus, reason: ExitReason, exit_on_panic: bool) {
|
||||
@ -309,92 +269,6 @@ fn check_for_updates() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn lookup<F: FnMut(FileType, PathBuf)>(dir: &Path, mut f: F) {
|
||||
let mut default_gitignore = std::env::temp_dir();
|
||||
default_gitignore.push(".tauri-dev");
|
||||
let _ = std::fs::create_dir_all(&default_gitignore);
|
||||
default_gitignore.push(".gitignore");
|
||||
if !default_gitignore.exists() {
|
||||
if let Ok(mut file) = std::fs::File::create(default_gitignore.clone()) {
|
||||
let _ = file.write_all(TAURI_DEV_WATCHER_GITIGNORE);
|
||||
}
|
||||
}
|
||||
|
||||
let mut builder = ignore::WalkBuilder::new(dir);
|
||||
let _ = builder.add_ignore(default_gitignore);
|
||||
if let Ok(ignore_file) = std::env::var("TAURI_DEV_WATCHER_IGNORE_FILE") {
|
||||
builder.add_ignore(ignore_file);
|
||||
}
|
||||
builder.require_git(false).ignore(false).max_depth(Some(1));
|
||||
|
||||
for entry in builder.build().flatten() {
|
||||
f(entry.file_type().unwrap(), dir.join(entry.path()));
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn watch<P: DevProcess, I: Interface<Dev = P>>(
|
||||
mut interface: I,
|
||||
process: Arc<Mutex<P>>,
|
||||
tauri_path: PathBuf,
|
||||
merge_config: Option<String>,
|
||||
config: ConfigHandle,
|
||||
options: Options,
|
||||
mut manifest: Manifest,
|
||||
) -> Result<()> {
|
||||
let (tx, rx) = channel();
|
||||
|
||||
let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap();
|
||||
lookup(&tauri_path, |file_type, path| {
|
||||
if path != tauri_path {
|
||||
let _ = watcher.watch(
|
||||
path,
|
||||
if file_type.is_dir() {
|
||||
RecursiveMode::Recursive
|
||||
} else {
|
||||
RecursiveMode::NonRecursive
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
let exit_on_panic = options.exit_on_panic;
|
||||
|
||||
loop {
|
||||
if let Ok(event) = rx.recv() {
|
||||
let event_path = match event {
|
||||
DebouncedEvent::Create(path) => Some(path),
|
||||
DebouncedEvent::Remove(path) => Some(path),
|
||||
DebouncedEvent::Rename(_, dest) => Some(dest),
|
||||
DebouncedEvent::Write(path) => Some(path),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(event_path) = event_path {
|
||||
if event_path.file_name() == Some(OsStr::new("tauri.conf.json")) {
|
||||
reload_config(merge_config.as_deref())?;
|
||||
manifest = rewrite_manifest(config.clone())?;
|
||||
} else {
|
||||
// When tauri.conf.json is changed, rewrite_manifest will be called
|
||||
// which will trigger the watcher again
|
||||
// So the app should only be started when a file other than tauri.conf.json is changed
|
||||
let mut p = process.lock().unwrap();
|
||||
p.kill().with_context(|| "failed to kill app process")?;
|
||||
// wait for the process to exit
|
||||
loop {
|
||||
if let Ok(Some(_)) = p.try_wait() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*p = interface.dev(options.clone().into(), &manifest, move |status, reason| {
|
||||
on_dev_exit(status, reason, exit_on_panic)
|
||||
})?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn kill_before_dev_process() {
|
||||
if let Some(child) = BEFORE_DEV.get() {
|
||||
let child = child.lock().unwrap();
|
||||
|
@ -102,7 +102,6 @@ pub fn get(merge_config: Option<&str>) -> crate::Result<ConfigHandle> {
|
||||
get_internal(merge_config, false)
|
||||
}
|
||||
|
||||
pub fn reload(merge_config: Option<&str>) -> crate::Result<()> {
|
||||
get_internal(merge_config, true)?;
|
||||
Ok(())
|
||||
pub fn reload(merge_config: Option<&str>) -> crate::Result<ConfigHandle> {
|
||||
get_internal(merge_config, true)
|
||||
}
|
||||
|
@ -5,7 +5,6 @@
|
||||
pub mod app_paths;
|
||||
pub mod config;
|
||||
pub mod framework;
|
||||
pub mod manifest;
|
||||
pub mod template;
|
||||
pub mod updater_signature;
|
||||
|
||||
|
@ -9,7 +9,7 @@ use std::{
|
||||
process::ExitStatus,
|
||||
};
|
||||
|
||||
use crate::helpers::{config::Config, manifest::Manifest};
|
||||
use crate::helpers::config::Config;
|
||||
use tauri_bundler::bundle::{PackageType, Settings, SettingsBuilder};
|
||||
|
||||
pub use rust::{Options, Rust as AppInterface};
|
||||
@ -19,7 +19,6 @@ pub trait AppSettings {
|
||||
fn get_bundle_settings(
|
||||
&self,
|
||||
config: &Config,
|
||||
manifest: &Manifest,
|
||||
features: &[String],
|
||||
) -> crate::Result<tauri_bundler::BundleSettings>;
|
||||
fn app_binary_path(&self, options: &Options) -> crate::Result<PathBuf>;
|
||||
@ -32,7 +31,6 @@ pub trait AppSettings {
|
||||
fn get_bundler_settings(
|
||||
&self,
|
||||
options: &Options,
|
||||
manifest: &Manifest,
|
||||
config: &Config,
|
||||
out_dir: &Path,
|
||||
package_types: Option<Vec<PackageType>>,
|
||||
@ -51,7 +49,7 @@ pub trait AppSettings {
|
||||
|
||||
let mut settings_builder = SettingsBuilder::new()
|
||||
.package_settings(self.get_package_settings())
|
||||
.bundle_settings(self.get_bundle_settings(config, manifest, &enabled_features)?)
|
||||
.bundle_settings(self.get_bundle_settings(config, &enabled_features)?)
|
||||
.binaries(self.get_binaries(config, &target)?)
|
||||
.project_out_directory(out_dir)
|
||||
.target(target);
|
||||
@ -64,11 +62,6 @@ pub trait AppSettings {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait DevProcess {
|
||||
fn kill(&self) -> std::io::Result<()>;
|
||||
fn try_wait(&self) -> std::io::Result<Option<ExitStatus>>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ExitReason {
|
||||
/// Killed manually.
|
||||
@ -81,15 +74,13 @@ pub enum ExitReason {
|
||||
|
||||
pub trait Interface: Sized {
|
||||
type AppSettings: AppSettings;
|
||||
type Dev: DevProcess;
|
||||
|
||||
fn new(config: &Config) -> crate::Result<Self>;
|
||||
fn app_settings(&self) -> &Self::AppSettings;
|
||||
fn build(&mut self, options: Options) -> crate::Result<()>;
|
||||
fn dev<F: FnOnce(ExitStatus, ExitReason) + Send + 'static>(
|
||||
fn dev<F: Fn(ExitStatus, ExitReason) + Send + Sync + 'static>(
|
||||
&mut self,
|
||||
options: Options,
|
||||
manifest: &Manifest,
|
||||
on_exit: F,
|
||||
) -> crate::Result<Self::Dev>;
|
||||
) -> crate::Result<()>;
|
||||
}
|
||||
|
@ -3,21 +3,25 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use std::{
|
||||
fs::{rename, File},
|
||||
ffi::OsStr,
|
||||
fs::{rename, File, FileType},
|
||||
io::{BufReader, ErrorKind, Read, Write},
|
||||
path::{Path, PathBuf},
|
||||
process::{Command, ExitStatus, Stdio},
|
||||
str::FromStr,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
mpsc::channel,
|
||||
Arc, Mutex,
|
||||
},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::Context;
|
||||
#[cfg(target_os = "linux")]
|
||||
use heck::ToKebabCase;
|
||||
use log::warn;
|
||||
use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
|
||||
use serde::Deserialize;
|
||||
use shared_child::SharedChild;
|
||||
use tauri_bundler::{
|
||||
@ -25,16 +29,18 @@ use tauri_bundler::{
|
||||
UpdaterSettings, WindowsSettings,
|
||||
};
|
||||
|
||||
use super::{AppSettings, DevProcess, ExitReason, Interface};
|
||||
use super::{AppSettings, ExitReason, Interface};
|
||||
use crate::{
|
||||
helpers::{
|
||||
app_paths::tauri_dir,
|
||||
config::{wix_settings, Config},
|
||||
manifest::Manifest,
|
||||
config::{reload as reload_config, wix_settings, Config},
|
||||
},
|
||||
CommandExt,
|
||||
};
|
||||
|
||||
mod manifest;
|
||||
use manifest::{rewrite_manifest, Manifest};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Options {
|
||||
pub runner: Option<String>,
|
||||
@ -42,6 +48,7 @@ pub struct Options {
|
||||
pub target: Option<String>,
|
||||
pub features: Option<Vec<String>>,
|
||||
pub args: Vec<String>,
|
||||
pub config: Option<String>,
|
||||
}
|
||||
|
||||
impl From<crate::build::Options> for Options {
|
||||
@ -52,6 +59,7 @@ impl From<crate::build::Options> for Options {
|
||||
target: options.target,
|
||||
features: options.features,
|
||||
args: options.args,
|
||||
config: options.config,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -64,6 +72,7 @@ impl From<crate::dev::Options> for Options {
|
||||
target: options.target,
|
||||
features: options.features,
|
||||
args: options.args,
|
||||
config: options.config,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -74,7 +83,7 @@ pub struct DevChild {
|
||||
app_child: Arc<Mutex<Option<Arc<SharedChild>>>>,
|
||||
}
|
||||
|
||||
impl DevProcess for DevChild {
|
||||
impl DevChild {
|
||||
fn kill(&self) -> std::io::Result<()> {
|
||||
if let Some(child) = &*self.app_child.lock().unwrap() {
|
||||
child.kill()?;
|
||||
@ -109,11 +118,22 @@ pub struct Rust {
|
||||
|
||||
impl Interface for Rust {
|
||||
type AppSettings = RustAppSettings;
|
||||
type Dev = DevChild;
|
||||
|
||||
fn new(config: &Config) -> crate::Result<Self> {
|
||||
let manifest = {
|
||||
let (tx, rx) = channel();
|
||||
let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap();
|
||||
watcher.watch(tauri_dir().join("Cargo.toml"), RecursiveMode::Recursive)?;
|
||||
let manifest = rewrite_manifest(config)?;
|
||||
loop {
|
||||
if let Ok(DebouncedEvent::NoticeWrite(_)) = rx.recv() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
manifest
|
||||
};
|
||||
Ok(Self {
|
||||
app_settings: RustAppSettings::new(config)?,
|
||||
app_settings: RustAppSettings::new(config, manifest)?,
|
||||
config_features: config.build.features.clone().unwrap_or_default(),
|
||||
product_name: config.package.product_name.clone(),
|
||||
available_targets: None,
|
||||
@ -172,12 +192,87 @@ impl Interface for Rust {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn dev<F: FnOnce(ExitStatus, ExitReason) + Send + 'static>(
|
||||
fn dev<F: Fn(ExitStatus, ExitReason) + Send + Sync + 'static>(
|
||||
&mut self,
|
||||
options: Options,
|
||||
manifest: &Manifest,
|
||||
on_exit: F,
|
||||
) -> crate::Result<Self::Dev> {
|
||||
) -> crate::Result<()> {
|
||||
let on_exit = Arc::new(on_exit);
|
||||
|
||||
let on_exit_ = on_exit.clone();
|
||||
let child = self.run_dev(options.clone(), move |status, reason| {
|
||||
on_exit_(status, reason)
|
||||
})?;
|
||||
|
||||
self.run_dev_watcher(child, options, on_exit)
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup<F: FnMut(FileType, PathBuf)>(dir: &Path, mut f: F) {
|
||||
let mut default_gitignore = std::env::temp_dir();
|
||||
default_gitignore.push(".tauri-dev");
|
||||
let _ = std::fs::create_dir_all(&default_gitignore);
|
||||
default_gitignore.push(".gitignore");
|
||||
if !default_gitignore.exists() {
|
||||
if let Ok(mut file) = std::fs::File::create(default_gitignore.clone()) {
|
||||
let _ = file.write_all(crate::dev::TAURI_DEV_WATCHER_GITIGNORE);
|
||||
}
|
||||
}
|
||||
|
||||
let mut builder = ignore::WalkBuilder::new(dir);
|
||||
let _ = builder.add_ignore(default_gitignore);
|
||||
if let Ok(ignore_file) = std::env::var("TAURI_DEV_WATCHER_IGNORE_FILE") {
|
||||
builder.add_ignore(ignore_file);
|
||||
}
|
||||
builder.require_git(false).ignore(false).max_depth(Some(1));
|
||||
|
||||
for entry in builder.build().flatten() {
|
||||
f(entry.file_type().unwrap(), dir.join(entry.path()));
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
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();
|
||||
|
||||
@ -207,7 +302,7 @@ impl Interface for Rust {
|
||||
build_cmd.arg("build").arg("--color").arg("always");
|
||||
|
||||
if !options.args.contains(&"--no-default-features".into()) {
|
||||
let manifest_features = manifest.features();
|
||||
let manifest_features = self.app_settings.manifest.features();
|
||||
let enable_features: Vec<String> = manifest_features
|
||||
.get("default")
|
||||
.cloned()
|
||||
@ -356,43 +451,66 @@ impl Interface for Rust {
|
||||
app_child,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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 run_dev_watcher<F: Fn(ExitStatus, ExitReason) + Send + Sync + 'static>(
|
||||
&mut self,
|
||||
child: DevChild,
|
||||
options: Options,
|
||||
on_exit: Arc<F>,
|
||||
) -> crate::Result<()> {
|
||||
let process = Arc::new(Mutex::new(child));
|
||||
let (tx, rx) = channel();
|
||||
let tauri_path = tauri_dir();
|
||||
|
||||
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(", ")
|
||||
);
|
||||
let mut watcher = watcher(tx, Duration::from_secs(1)).unwrap();
|
||||
lookup(&tauri_path, |file_type, path| {
|
||||
if path != tauri_path {
|
||||
let _ = watcher.watch(
|
||||
path,
|
||||
if file_type.is_dir() {
|
||||
RecursiveMode::Recursive
|
||||
} else {
|
||||
RecursiveMode::NonRecursive
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
loop {
|
||||
let on_exit = on_exit.clone();
|
||||
if let Ok(event) = rx.recv() {
|
||||
let event_path = match event {
|
||||
DebouncedEvent::Create(path) => Some(path),
|
||||
DebouncedEvent::Remove(path) => Some(path),
|
||||
DebouncedEvent::Rename(_, dest) => Some(dest),
|
||||
DebouncedEvent::Write(path) => Some(path),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(event_path) = event_path {
|
||||
if event_path.file_name() == Some(OsStr::new("tauri.conf.json")) {
|
||||
let config = reload_config(options.config.as_deref())?;
|
||||
self.app_settings.manifest =
|
||||
rewrite_manifest(config.lock().unwrap().as_ref().unwrap())?;
|
||||
} else {
|
||||
// When tauri.conf.json is changed, rewrite_manifest will be called
|
||||
// which will trigger the watcher again
|
||||
// So the app should only be started when a file other than tauri.conf.json is changed
|
||||
let mut p = process.lock().unwrap();
|
||||
p.kill().with_context(|| "failed to kill app process")?;
|
||||
// wait for the process to exit
|
||||
loop {
|
||||
if let Ok(Some(_)) = p.try_wait() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*p = self.run_dev(options.clone(), move |status, reason| {
|
||||
on_exit(status, reason)
|
||||
})?;
|
||||
}
|
||||
}
|
||||
}
|
||||
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 build_app(&mut self, options: Options) -> crate::Result<()> {
|
||||
@ -533,6 +651,7 @@ struct CargoConfig {
|
||||
}
|
||||
|
||||
pub struct RustAppSettings {
|
||||
manifest: Manifest,
|
||||
cargo_settings: CargoSettings,
|
||||
cargo_package_settings: CargoPackageSettings,
|
||||
package_settings: PackageSettings,
|
||||
@ -546,11 +665,10 @@ impl AppSettings for RustAppSettings {
|
||||
fn get_bundle_settings(
|
||||
&self,
|
||||
config: &Config,
|
||||
manifest: &Manifest,
|
||||
features: &[String],
|
||||
) -> crate::Result<BundleSettings> {
|
||||
tauri_config_to_bundle_settings(
|
||||
manifest,
|
||||
&self.manifest,
|
||||
features,
|
||||
config.tauri.bundle.clone(),
|
||||
config.tauri.system_tray.clone(),
|
||||
@ -690,7 +808,7 @@ impl AppSettings for RustAppSettings {
|
||||
}
|
||||
|
||||
impl RustAppSettings {
|
||||
pub fn new(config: &Config) -> crate::Result<Self> {
|
||||
pub fn new(config: &Config, manifest: Manifest) -> crate::Result<Self> {
|
||||
let cargo_settings =
|
||||
CargoSettings::load(&tauri_dir()).with_context(|| "failed to load cargo settings")?;
|
||||
let cargo_package_settings = match &cargo_settings.package {
|
||||
@ -725,6 +843,7 @@ impl RustAppSettings {
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
manifest,
|
||||
cargo_settings,
|
||||
cargo_package_settings,
|
||||
package_settings,
|
||||
|
@ -2,9 +2,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use super::{
|
||||
use crate::helpers::{
|
||||
app_paths::tauri_dir,
|
||||
config::{ConfigHandle, PatternKind},
|
||||
config::{Config, PatternKind},
|
||||
};
|
||||
|
||||
use anyhow::Context;
|
||||
@ -184,13 +184,10 @@ fn write_features(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rewrite_manifest(config: ConfigHandle) -> crate::Result<Manifest> {
|
||||
pub fn rewrite_manifest(config: &Config) -> crate::Result<Manifest> {
|
||||
let manifest_path = tauri_dir().join("Cargo.toml");
|
||||
let mut manifest = read_manifest(&manifest_path)?;
|
||||
|
||||
let config_guard = config.lock().unwrap();
|
||||
let config = config_guard.as_ref().unwrap();
|
||||
|
||||
let mut tauri_build_features = HashSet::new();
|
||||
if let PatternKind::Isolation { .. } = config.tauri.pattern {
|
||||
tauri_build_features.insert("isolation".to_string());
|
||||
@ -209,7 +206,7 @@ pub fn rewrite_manifest(config: ConfigHandle) -> crate::Result<Manifest> {
|
||||
|
||||
let mut tauri_features =
|
||||
HashSet::from_iter(config.tauri.features().into_iter().map(|f| f.to_string()));
|
||||
let cli_managed_tauri_features = super::config::TauriConfig::all_features();
|
||||
let cli_managed_tauri_features = crate::helpers::config::TauriConfig::all_features();
|
||||
let res = match write_features(
|
||||
manifest
|
||||
.as_table_mut()
|
Loading…
Reference in New Issue
Block a user