mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-01-31 00:35:19 +01:00
* feat(cli): add mobile run commands, closes #13196 * headers * debug by default * fix android env * implement watcher * clippy * skip ipa build
This commit is contained in:
committed by
GitHub
parent
006d592837
commit
75082cc5b3
6
.changes/mobile-run.md
Normal file
6
.changes/mobile-run.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@tauri-apps/cli": minor:feat
|
||||
"tauri-cli": minor:feat
|
||||
---
|
||||
|
||||
Added `ios run` and `android run` commands to run the app in production mode.
|
||||
@@ -14,7 +14,7 @@ use std::{
|
||||
use crate::{error::Context, helpers::config::Config};
|
||||
use tauri_bundler::bundle::{PackageType, Settings, SettingsBuilder};
|
||||
|
||||
pub use rust::{MobileOptions, Options, Rust as AppInterface};
|
||||
pub use rust::{MobileOptions, Options, Rust as AppInterface, WatcherOptions};
|
||||
|
||||
pub trait DevProcess {
|
||||
fn kill(&self) -> std::io::Result<()>;
|
||||
@@ -113,4 +113,9 @@ pub trait Interface: Sized {
|
||||
options: MobileOptions,
|
||||
runner: R,
|
||||
) -> crate::Result<()>;
|
||||
fn watch<R: Fn() -> crate::Result<Box<dyn DevProcess + Send>>>(
|
||||
&mut self,
|
||||
options: WatcherOptions,
|
||||
runner: R,
|
||||
) -> crate::Result<()>;
|
||||
}
|
||||
|
||||
@@ -115,6 +115,12 @@ pub struct MobileOptions {
|
||||
pub additional_watch_folders: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WatcherOptions {
|
||||
pub config: Vec<ConfigValue>,
|
||||
pub additional_watch_folders: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RustupTarget {
|
||||
name: String,
|
||||
@@ -245,12 +251,26 @@ impl Interface for Rust {
|
||||
runner(options)?;
|
||||
Ok(())
|
||||
} else {
|
||||
let merge_configs = options.config.iter().map(|c| &c.0).collect::<Vec<_>>();
|
||||
let run = Arc::new(|_rust: &mut Rust| runner(options.clone()));
|
||||
self.run_dev_watcher(&options.additional_watch_folders, &merge_configs, run)
|
||||
self.watch(
|
||||
WatcherOptions {
|
||||
config: options.config.clone(),
|
||||
additional_watch_folders: options.additional_watch_folders.clone(),
|
||||
},
|
||||
move || runner(options.clone()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn watch<R: Fn() -> crate::Result<Box<dyn DevProcess + Send>>>(
|
||||
&mut self,
|
||||
options: WatcherOptions,
|
||||
runner: R,
|
||||
) -> crate::Result<()> {
|
||||
let merge_configs = options.config.iter().map(|c| &c.0).collect::<Vec<_>>();
|
||||
let run = Arc::new(|_rust: &mut Rust| runner());
|
||||
self.run_dev_watcher(&options.additional_watch_folders, &merge_configs, run)
|
||||
}
|
||||
|
||||
fn env(&self) -> HashMap<&str, String> {
|
||||
let mut env = HashMap::new();
|
||||
env.insert(
|
||||
|
||||
@@ -15,7 +15,7 @@ use crate::{
|
||||
flock,
|
||||
},
|
||||
interface::{AppInterface, Interface, Options as InterfaceOptions},
|
||||
mobile::{write_options, CliOptions},
|
||||
mobile::{write_options, CliOptions, TargetDevice},
|
||||
ConfigValue, Error, Result,
|
||||
};
|
||||
use clap::{ArgAction, Parser};
|
||||
@@ -63,10 +63,10 @@ pub struct Options {
|
||||
pub split_per_abi: bool,
|
||||
/// Build APKs.
|
||||
#[clap(long)]
|
||||
pub apk: bool,
|
||||
pub apk: Option<bool>,
|
||||
/// Build AABs.
|
||||
#[clap(long)]
|
||||
pub aab: bool,
|
||||
pub aab: Option<bool>,
|
||||
/// Open Android Studio
|
||||
#[clap(short, long)]
|
||||
pub open: bool,
|
||||
@@ -83,6 +83,9 @@ pub struct Options {
|
||||
/// Only use this when you are sure the mismatch is incorrectly detected as version mismatched Tauri packages can lead to unknown behavior.
|
||||
#[clap(long)]
|
||||
pub ignore_version_mismatches: bool,
|
||||
/// Target device of this build
|
||||
#[clap(skip)]
|
||||
pub target_device: Option<TargetDevice>,
|
||||
}
|
||||
|
||||
impl From<Options> for BuildOptions {
|
||||
@@ -104,7 +107,15 @@ impl From<Options> for BuildOptions {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> {
|
||||
pub struct BuiltApplication {
|
||||
pub config: AndroidConfig,
|
||||
pub interface: AppInterface,
|
||||
// prevent drop
|
||||
#[allow(dead_code)]
|
||||
options_handle: OptionsHandle,
|
||||
}
|
||||
|
||||
pub fn command(options: Options, noise_level: NoiseLevel) -> Result<BuiltApplication> {
|
||||
crate::helpers::app_paths::resolve();
|
||||
|
||||
delete_codegen_vars();
|
||||
@@ -188,8 +199,8 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> {
|
||||
.context("failed to build Android app")?;
|
||||
|
||||
let open = options.open;
|
||||
let _handle = run_build(
|
||||
interface,
|
||||
let options_handle = run_build(
|
||||
&interface,
|
||||
options,
|
||||
build_options,
|
||||
tauri_config,
|
||||
@@ -203,12 +214,16 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> {
|
||||
open_and_wait(&config, &env);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(BuiltApplication {
|
||||
config,
|
||||
interface,
|
||||
options_handle,
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn run_build(
|
||||
interface: AppInterface,
|
||||
interface: &AppInterface,
|
||||
mut options: Options,
|
||||
build_options: BuildOptions,
|
||||
tauri_config: ConfigHandle,
|
||||
@@ -217,10 +232,10 @@ fn run_build(
|
||||
env: &mut Env,
|
||||
noise_level: NoiseLevel,
|
||||
) -> Result<OptionsHandle> {
|
||||
if !(options.apk || options.aab) {
|
||||
if !(options.apk.is_some() || options.aab.is_some()) {
|
||||
// if the user didn't specify the format to build, we'll do both
|
||||
options.apk = true;
|
||||
options.aab = true;
|
||||
options.apk = Some(true);
|
||||
options.aab = Some(true);
|
||||
}
|
||||
|
||||
let interface_options = InterfaceOptions {
|
||||
@@ -241,13 +256,13 @@ fn run_build(
|
||||
noise_level,
|
||||
vars: Default::default(),
|
||||
config: build_options.config,
|
||||
target_device: None,
|
||||
target_device: options.target_device.clone(),
|
||||
};
|
||||
let handle = write_options(tauri_config.lock().unwrap().as_ref().unwrap(), cli_options)?;
|
||||
|
||||
inject_resources(config, tauri_config.lock().unwrap().as_ref().unwrap())?;
|
||||
|
||||
let apk_outputs = if options.apk {
|
||||
let apk_outputs = if options.apk.unwrap_or_default() {
|
||||
apk::build(
|
||||
config,
|
||||
env,
|
||||
@@ -261,7 +276,7 @@ fn run_build(
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
let aab_outputs = if options.aab {
|
||||
let aab_outputs = if options.aab.unwrap_or_default() {
|
||||
aab::build(
|
||||
config,
|
||||
env,
|
||||
@@ -275,8 +290,12 @@ fn run_build(
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
log_finished(apk_outputs, "APK");
|
||||
log_finished(aab_outputs, "AAB");
|
||||
if !apk_outputs.is_empty() {
|
||||
log_finished(apk_outputs, "APK");
|
||||
}
|
||||
if !aab_outputs.is_empty() {
|
||||
log_finished(aab_outputs, "AAB");
|
||||
}
|
||||
|
||||
Ok(handle)
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ mod android_studio_script;
|
||||
mod build;
|
||||
mod dev;
|
||||
pub(crate) mod project;
|
||||
mod run;
|
||||
|
||||
const NDK_VERSION: &str = "29.0.13846066";
|
||||
const SDK_VERSION: u8 = 36;
|
||||
@@ -96,6 +97,7 @@ enum Commands {
|
||||
Init(InitOptions),
|
||||
Dev(dev::Options),
|
||||
Build(build::Options),
|
||||
Run(run::Options),
|
||||
#[clap(hide(true))]
|
||||
AndroidStudioScript(android_studio_script::Options),
|
||||
}
|
||||
@@ -114,7 +116,8 @@ pub fn command(cli: Cli, verbosity: u8) -> Result<()> {
|
||||
)?
|
||||
}
|
||||
Commands::Dev(options) => dev::command(options, noise_level)?,
|
||||
Commands::Build(options) => build::command(options, noise_level)?,
|
||||
Commands::Build(options) => build::command(options, noise_level).map(|_| ())?,
|
||||
Commands::Run(options) => run::command(options, noise_level)?,
|
||||
Commands::AndroidStudioScript(options) => android_studio_script::command(options)?,
|
||||
}
|
||||
|
||||
|
||||
152
crates/tauri-cli/src/mobile/android/run.rs
Normal file
152
crates/tauri-cli/src/mobile/android/run.rs
Normal file
@@ -0,0 +1,152 @@
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use cargo_mobile2::{
|
||||
android::target::Target,
|
||||
opts::{FilterLevel, NoiseLevel, Profile},
|
||||
target::TargetTrait,
|
||||
};
|
||||
use clap::{ArgAction, Parser};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::{configure_cargo, device_prompt, env};
|
||||
use crate::{
|
||||
error::Context,
|
||||
interface::{DevProcess, Interface, WatcherOptions},
|
||||
mobile::{DevChild, TargetDevice},
|
||||
ConfigValue, Result,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Parser)]
|
||||
#[clap(
|
||||
about = "Run your app in production mode on Android",
|
||||
long_about = "Run your app in production mode on Android. It makes use of the `build.frontendDist` property from your `tauri.conf.json` file. It also runs your `build.beforeBuildCommand` which usually builds your frontend into `build.frontendDist`."
|
||||
)]
|
||||
pub struct Options {
|
||||
/// Run the app in release mode
|
||||
#[clap(short, long)]
|
||||
pub release: bool,
|
||||
/// List of cargo features to activate
|
||||
#[clap(short, long, action = ArgAction::Append, num_args(0..))]
|
||||
pub features: Option<Vec<String>>,
|
||||
/// JSON strings or paths to JSON, JSON5 or TOML files to merge with the default configuration file
|
||||
///
|
||||
/// Configurations are merged in the order they are provided, which means a particular value overwrites previous values when a config key-value pair conflicts.
|
||||
///
|
||||
/// Note that a platform-specific file is looked up and merged with the default file by default
|
||||
/// (tauri.macos.conf.json, tauri.linux.conf.json, tauri.windows.conf.json, tauri.android.conf.json and tauri.ios.conf.json)
|
||||
/// but you can use this for more specific use cases such as different build flavors.
|
||||
#[clap(short, long)]
|
||||
pub config: Vec<ConfigValue>,
|
||||
/// Disable the file watcher
|
||||
#[clap(long)]
|
||||
pub no_watch: bool,
|
||||
/// Additional paths to watch for changes.
|
||||
#[clap(long)]
|
||||
pub additional_watch_folders: Vec<PathBuf>,
|
||||
/// Open Android Studio
|
||||
#[clap(short, long)]
|
||||
pub open: bool,
|
||||
/// Runs on the given device name
|
||||
pub device: Option<String>,
|
||||
/// Command line arguments passed to the runner.
|
||||
/// Use `--` to explicitly mark the start of the arguments.
|
||||
/// e.g. `tauri android build -- [runnerArgs]`.
|
||||
#[clap(last(true))]
|
||||
pub args: Vec<String>,
|
||||
/// Do not error out if a version mismatch is detected on a Tauri package.
|
||||
///
|
||||
/// Only use this when you are sure the mismatch is incorrectly detected as version mismatched Tauri packages can lead to unknown behavior.
|
||||
#[clap(long)]
|
||||
pub ignore_version_mismatches: bool,
|
||||
}
|
||||
|
||||
pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> {
|
||||
let mut env = env(false)?;
|
||||
|
||||
let device = if options.open {
|
||||
None
|
||||
} else {
|
||||
match device_prompt(&env, options.device.as_deref()) {
|
||||
Ok(d) => Some(d),
|
||||
Err(e) => {
|
||||
log::error!("{e}");
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut built_application = super::build::command(
|
||||
super::build::Options {
|
||||
debug: !options.release,
|
||||
targets: device.as_ref().map(|d| {
|
||||
vec![Target::all()
|
||||
.iter()
|
||||
.find(|(_key, t)| t.arch == d.target().arch)
|
||||
.map(|(key, _t)| key.to_string())
|
||||
.expect("Target not found")]
|
||||
}),
|
||||
features: options.features,
|
||||
config: options.config.clone(),
|
||||
split_per_abi: true,
|
||||
apk: Some(false),
|
||||
aab: Some(false),
|
||||
open: options.open,
|
||||
ci: false,
|
||||
args: options.args,
|
||||
ignore_version_mismatches: options.ignore_version_mismatches,
|
||||
target_device: device.as_ref().map(|d| TargetDevice {
|
||||
id: d.serial_no().to_string(),
|
||||
name: d.name().to_string(),
|
||||
}),
|
||||
},
|
||||
noise_level,
|
||||
)?;
|
||||
|
||||
configure_cargo(&mut env, &built_application.config)?;
|
||||
|
||||
// options.open is handled by the build command
|
||||
// so all we need to do here is run the app on the selected device
|
||||
if let Some(device) = device {
|
||||
let config = built_application.config.clone();
|
||||
let release = options.release;
|
||||
let runner = move || {
|
||||
device
|
||||
.run(
|
||||
&config,
|
||||
&env,
|
||||
noise_level,
|
||||
if !release {
|
||||
Profile::Debug
|
||||
} else {
|
||||
Profile::Release
|
||||
},
|
||||
Some(match noise_level {
|
||||
NoiseLevel::Polite => FilterLevel::Info,
|
||||
NoiseLevel::LoudAndProud => FilterLevel::Debug,
|
||||
NoiseLevel::FranklyQuitePedantic => FilterLevel::Verbose,
|
||||
}),
|
||||
false,
|
||||
false,
|
||||
".MainActivity".into(),
|
||||
)
|
||||
.map(|c| Box::new(DevChild::new(c)) as Box<dyn DevProcess + Send>)
|
||||
.context("failed to run Android app")
|
||||
};
|
||||
|
||||
if options.no_watch {
|
||||
runner()?;
|
||||
} else {
|
||||
built_application.interface.watch(
|
||||
WatcherOptions {
|
||||
config: options.config,
|
||||
additional_watch_folders: options.additional_watch_folders,
|
||||
},
|
||||
runner,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -17,7 +17,7 @@ use crate::{
|
||||
plist::merge_plist,
|
||||
},
|
||||
interface::{AppInterface, Interface, Options as InterfaceOptions},
|
||||
mobile::{ios::ensure_ios_runtime_installed, write_options, CliOptions},
|
||||
mobile::{ios::ensure_ios_runtime_installed, write_options, CliOptions, TargetDevice},
|
||||
ConfigValue, Error, Result,
|
||||
};
|
||||
use clap::{ArgAction, Parser, ValueEnum};
|
||||
@@ -57,7 +57,7 @@ pub struct Options {
|
||||
default_value = Target::DEFAULT_KEY,
|
||||
value_parser(clap::builder::PossibleValuesParser::new(Target::name_list()))
|
||||
)]
|
||||
pub targets: Vec<String>,
|
||||
pub targets: Option<Vec<String>>,
|
||||
/// List of cargo features to activate
|
||||
#[clap(short, long, action = ArgAction::Append, num_args(0..))]
|
||||
pub features: Option<Vec<String>>,
|
||||
@@ -94,6 +94,9 @@ pub struct Options {
|
||||
/// Only use this when you are sure the mismatch is incorrectly detected as version mismatched Tauri packages can lead to unknown behavior.
|
||||
#[clap(long)]
|
||||
pub ignore_version_mismatches: bool,
|
||||
/// Target device of this build
|
||||
#[clap(skip)]
|
||||
pub target_device: Option<TargetDevice>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, ValueEnum)]
|
||||
@@ -156,7 +159,15 @@ impl From<Options> for BuildOptions {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> {
|
||||
pub struct BuiltApplication {
|
||||
pub config: AppleConfig,
|
||||
pub interface: AppInterface,
|
||||
// prevent drop
|
||||
#[allow(dead_code)]
|
||||
options_handle: OptionsHandle,
|
||||
}
|
||||
|
||||
pub fn command(options: Options, noise_level: NoiseLevel) -> Result<BuiltApplication> {
|
||||
crate::helpers::app_paths::resolve();
|
||||
|
||||
let mut build_options: BuildOptions = options.clone().into();
|
||||
@@ -165,7 +176,8 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> {
|
||||
.get(
|
||||
options
|
||||
.targets
|
||||
.first()
|
||||
.as_ref()
|
||||
.and_then(|t| t.first())
|
||||
.map(|t| t.as_str())
|
||||
.unwrap_or(Target::DEFAULT_KEY),
|
||||
)
|
||||
@@ -318,8 +330,8 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> {
|
||||
};
|
||||
|
||||
let open = options.open;
|
||||
let _handle = run_build(
|
||||
interface,
|
||||
let options_handle = run_build(
|
||||
&interface,
|
||||
options,
|
||||
build_options,
|
||||
tauri_config,
|
||||
@@ -332,12 +344,16 @@ pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> {
|
||||
open_and_wait(&config, &env);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(BuiltApplication {
|
||||
config,
|
||||
interface,
|
||||
options_handle,
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn run_build(
|
||||
interface: AppInterface,
|
||||
interface: &AppInterface,
|
||||
options: Options,
|
||||
mut build_options: BuildOptions,
|
||||
tauri_config: ConfigHandle,
|
||||
@@ -351,7 +367,7 @@ fn run_build(
|
||||
Profile::Release
|
||||
};
|
||||
|
||||
crate::build::setup(&interface, &mut build_options, tauri_config.clone(), true)?;
|
||||
crate::build::setup(interface, &mut build_options, tauri_config.clone(), true)?;
|
||||
|
||||
let app_settings = interface.app_settings();
|
||||
let out_dir = app_settings.out_dir(&InterfaceOptions {
|
||||
@@ -369,7 +385,7 @@ fn run_build(
|
||||
noise_level,
|
||||
vars: Default::default(),
|
||||
config: build_options.config.clone(),
|
||||
target_device: None,
|
||||
target_device: options.target_device.clone(),
|
||||
};
|
||||
let handle = write_options(tauri_config.lock().unwrap().as_ref().unwrap(), cli_options)?;
|
||||
|
||||
@@ -379,9 +395,15 @@ fn run_build(
|
||||
|
||||
let mut out_files = Vec::new();
|
||||
|
||||
let force_skip_target_fallback = options.targets.as_ref().is_some_and(|t| t.is_empty());
|
||||
|
||||
call_for_targets_with_fallback(
|
||||
options.targets.iter(),
|
||||
&detect_target_ok,
|
||||
options.targets.unwrap_or_default().iter(),
|
||||
if force_skip_target_fallback {
|
||||
&|_| None
|
||||
} else {
|
||||
&detect_target_ok
|
||||
},
|
||||
env,
|
||||
|target: &Target| -> Result<()> {
|
||||
let mut app_version = config.bundle_version().to_string();
|
||||
@@ -506,7 +528,9 @@ fn run_build(
|
||||
)
|
||||
.map_err(|e: TargetInvalid| Error::GenericError(e.to_string()))??;
|
||||
|
||||
log_finished(out_files, "iOS Bundle");
|
||||
if !out_files.is_empty() {
|
||||
log_finished(out_files, "iOS Bundle");
|
||||
}
|
||||
|
||||
Ok(handle)
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ use std::{
|
||||
mod build;
|
||||
mod dev;
|
||||
pub(crate) mod project;
|
||||
mod run;
|
||||
mod xcode_script;
|
||||
|
||||
pub const APPLE_DEVELOPMENT_TEAM_ENV_VAR_NAME: &str = "APPLE_DEVELOPMENT_TEAM";
|
||||
@@ -95,6 +96,7 @@ enum Commands {
|
||||
Init(InitOptions),
|
||||
Dev(dev::Options),
|
||||
Build(build::Options),
|
||||
Run(run::Options),
|
||||
#[clap(hide(true))]
|
||||
XcodeScript(xcode_script::Options),
|
||||
}
|
||||
@@ -113,7 +115,8 @@ pub fn command(cli: Cli, verbosity: u8) -> Result<()> {
|
||||
)?
|
||||
}
|
||||
Commands::Dev(options) => dev::command(options, noise_level)?,
|
||||
Commands::Build(options) => build::command(options, noise_level)?,
|
||||
Commands::Build(options) => build::command(options, noise_level).map(|_| ())?,
|
||||
Commands::Run(options) => run::command(options, noise_level)?,
|
||||
Commands::XcodeScript(options) => xcode_script::command(options)?,
|
||||
}
|
||||
|
||||
|
||||
130
crates/tauri-cli/src/mobile/ios/run.rs
Normal file
130
crates/tauri-cli/src/mobile/ios/run.rs
Normal file
@@ -0,0 +1,130 @@
|
||||
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use cargo_mobile2::opts::{NoiseLevel, Profile};
|
||||
use clap::{ArgAction, Parser};
|
||||
|
||||
use super::{device_prompt, env};
|
||||
use crate::{
|
||||
error::Context,
|
||||
interface::{DevProcess, Interface, WatcherOptions},
|
||||
mobile::{DevChild, TargetDevice},
|
||||
ConfigValue, Result,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Parser)]
|
||||
#[clap(
|
||||
about = "Run your app in production mode on iOS",
|
||||
long_about = "Run your app in production mode on iOS. It makes use of the `build.frontendDist` property from your `tauri.conf.json` file. It also runs your `build.beforeBuildCommand` which usually builds your frontend into `build.frontendDist`."
|
||||
)]
|
||||
pub struct Options {
|
||||
/// Run the app in release mode
|
||||
#[clap(short, long)]
|
||||
pub release: bool,
|
||||
/// List of cargo features to activate
|
||||
#[clap(short, long, action = ArgAction::Append, num_args(0..))]
|
||||
pub features: Option<Vec<String>>,
|
||||
/// JSON strings or paths to JSON, JSON5 or TOML files to merge with the default configuration file
|
||||
///
|
||||
/// Configurations are merged in the order they are provided, which means a particular value overwrites previous values when a config key-value pair conflicts.
|
||||
///
|
||||
/// Note that a platform-specific file is looked up and merged with the default file by default
|
||||
/// (tauri.macos.conf.json, tauri.linux.conf.json, tauri.windows.conf.json, tauri.android.conf.json and tauri.ios.conf.json)
|
||||
/// but you can use this for more specific use cases such as different build flavors.
|
||||
#[clap(short, long)]
|
||||
pub config: Vec<ConfigValue>,
|
||||
/// Disable the file watcher
|
||||
#[clap(long)]
|
||||
pub no_watch: bool,
|
||||
/// Additional paths to watch for changes.
|
||||
#[clap(long)]
|
||||
pub additional_watch_folders: Vec<PathBuf>,
|
||||
/// Open Xcode
|
||||
#[clap(short, long)]
|
||||
pub open: bool,
|
||||
/// Runs on the given device name
|
||||
pub device: Option<String>,
|
||||
/// Command line arguments passed to the runner.
|
||||
/// Use `--` to explicitly mark the start of the arguments.
|
||||
/// e.g. `tauri android build -- [runnerArgs]`.
|
||||
#[clap(last(true))]
|
||||
pub args: Vec<String>,
|
||||
/// Do not error out if a version mismatch is detected on a Tauri package.
|
||||
///
|
||||
/// Only use this when you are sure the mismatch is incorrectly detected as version mismatched Tauri packages can lead to unknown behavior.
|
||||
#[clap(long)]
|
||||
pub ignore_version_mismatches: bool,
|
||||
}
|
||||
|
||||
pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> {
|
||||
let env = env().context("failed to load iOS environment")?;
|
||||
let device = if options.open {
|
||||
None
|
||||
} else {
|
||||
match device_prompt(&env, options.device.as_deref()) {
|
||||
Ok(d) => Some(d),
|
||||
Err(e) => {
|
||||
log::error!("{e}");
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut built_application = super::build::command(
|
||||
super::build::Options {
|
||||
debug: !options.release,
|
||||
targets: Some(vec![]), /* skips IPA build since there's no target */
|
||||
features: None,
|
||||
config: options.config.clone(),
|
||||
build_number: None,
|
||||
open: options.open,
|
||||
ci: false,
|
||||
export_method: None,
|
||||
args: options.args,
|
||||
ignore_version_mismatches: options.ignore_version_mismatches,
|
||||
target_device: device.as_ref().map(|d| TargetDevice {
|
||||
id: d.id().to_string(),
|
||||
name: d.name().to_string(),
|
||||
}),
|
||||
},
|
||||
noise_level,
|
||||
)?;
|
||||
|
||||
// options.open is handled by the build command
|
||||
// so all we need to do here is run the app on the selected device
|
||||
if let Some(device) = device {
|
||||
let runner = move || {
|
||||
device
|
||||
.run(
|
||||
&built_application.config,
|
||||
&env,
|
||||
noise_level,
|
||||
false, // do not quit on app exit
|
||||
if !options.release {
|
||||
Profile::Debug
|
||||
} else {
|
||||
Profile::Release
|
||||
},
|
||||
)
|
||||
.map(|c| Box::new(DevChild::new(c)) as Box<dyn DevProcess + Send>)
|
||||
.context("failed to run iOS app")
|
||||
};
|
||||
|
||||
if options.no_watch {
|
||||
runner()?;
|
||||
} else {
|
||||
built_application.interface.watch(
|
||||
WatcherOptions {
|
||||
config: options.config,
|
||||
additional_watch_folders: options.additional_watch_folders,
|
||||
},
|
||||
runner,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user