|
| 1 | +// Copyright 2019-2024 Tauri Programme within The Commons Conservancy |
| 2 | +// SPDX-License-Identifier: Apache-2.0 |
| 3 | +// SPDX-License-Identifier: MIT |
| 4 | + |
| 5 | +use cargo_mobile2::{ |
| 6 | + android::target::Target, |
| 7 | + opts::{FilterLevel, NoiseLevel, Profile}, |
| 8 | + target::TargetTrait, |
| 9 | +}; |
| 10 | +use clap::{ArgAction, Parser}; |
| 11 | +use std::path::PathBuf; |
| 12 | + |
| 13 | +use super::{configure_cargo, device_prompt, env}; |
| 14 | +use crate::{ |
| 15 | + error::Context, |
| 16 | + interface::{DevProcess, Interface, WatcherOptions}, |
| 17 | + mobile::{DevChild, TargetDevice}, |
| 18 | + ConfigValue, Result, |
| 19 | +}; |
| 20 | + |
| 21 | +#[derive(Debug, Clone, Parser)] |
| 22 | +#[clap( |
| 23 | + about = "Run your app in production mode on Android", |
| 24 | + 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`." |
| 25 | +)] |
| 26 | +pub struct Options { |
| 27 | + /// Run the app in release mode |
| 28 | + #[clap(short, long)] |
| 29 | + pub release: bool, |
| 30 | + /// List of cargo features to activate |
| 31 | + #[clap(short, long, action = ArgAction::Append, num_args(0..))] |
| 32 | + pub features: Option<Vec<String>>, |
| 33 | + /// JSON strings or paths to JSON, JSON5 or TOML files to merge with the default configuration file |
| 34 | + /// |
| 35 | + /// Configurations are merged in the order they are provided, which means a particular value overwrites previous values when a config key-value pair conflicts. |
| 36 | + /// |
| 37 | + /// Note that a platform-specific file is looked up and merged with the default file by default |
| 38 | + /// (tauri.macos.conf.json, tauri.linux.conf.json, tauri.windows.conf.json, tauri.android.conf.json and tauri.ios.conf.json) |
| 39 | + /// but you can use this for more specific use cases such as different build flavors. |
| 40 | + #[clap(short, long)] |
| 41 | + pub config: Vec<ConfigValue>, |
| 42 | + /// Disable the file watcher |
| 43 | + #[clap(long)] |
| 44 | + pub no_watch: bool, |
| 45 | + /// Additional paths to watch for changes. |
| 46 | + #[clap(long)] |
| 47 | + pub additional_watch_folders: Vec<PathBuf>, |
| 48 | + /// Open Android Studio |
| 49 | + #[clap(short, long)] |
| 50 | + pub open: bool, |
| 51 | + /// Runs on the given device name |
| 52 | + pub device: Option<String>, |
| 53 | + /// Command line arguments passed to the runner. |
| 54 | + /// Use `--` to explicitly mark the start of the arguments. |
| 55 | + /// e.g. `tauri android build -- [runnerArgs]`. |
| 56 | + #[clap(last(true))] |
| 57 | + pub args: Vec<String>, |
| 58 | + /// Do not error out if a version mismatch is detected on a Tauri package. |
| 59 | + /// |
| 60 | + /// Only use this when you are sure the mismatch is incorrectly detected as version mismatched Tauri packages can lead to unknown behavior. |
| 61 | + #[clap(long)] |
| 62 | + pub ignore_version_mismatches: bool, |
| 63 | +} |
| 64 | + |
| 65 | +pub fn command(options: Options, noise_level: NoiseLevel) -> Result<()> { |
| 66 | + let mut env = env(false)?; |
| 67 | + |
| 68 | + let device = if options.open { |
| 69 | + None |
| 70 | + } else { |
| 71 | + match device_prompt(&env, options.device.as_deref()) { |
| 72 | + Ok(d) => Some(d), |
| 73 | + Err(e) => { |
| 74 | + log::error!("{e}"); |
| 75 | + None |
| 76 | + } |
| 77 | + } |
| 78 | + }; |
| 79 | + |
| 80 | + let mut built_application = super::build::command( |
| 81 | + super::build::Options { |
| 82 | + debug: !options.release, |
| 83 | + targets: device.as_ref().map(|d| { |
| 84 | + vec![Target::all() |
| 85 | + .iter() |
| 86 | + .find(|(_key, t)| t.arch == d.target().arch) |
| 87 | + .map(|(key, _t)| key.to_string()) |
| 88 | + .expect("Target not found")] |
| 89 | + }), |
| 90 | + features: options.features, |
| 91 | + config: options.config.clone(), |
| 92 | + split_per_abi: true, |
| 93 | + apk: Some(false), |
| 94 | + aab: Some(false), |
| 95 | + open: options.open, |
| 96 | + ci: false, |
| 97 | + args: options.args, |
| 98 | + ignore_version_mismatches: options.ignore_version_mismatches, |
| 99 | + target_device: device.as_ref().map(|d| TargetDevice { |
| 100 | + id: d.serial_no().to_string(), |
| 101 | + name: d.name().to_string(), |
| 102 | + }), |
| 103 | + }, |
| 104 | + noise_level, |
| 105 | + )?; |
| 106 | + |
| 107 | + configure_cargo(&mut env, &built_application.config)?; |
| 108 | + |
| 109 | + // options.open is handled by the build command |
| 110 | + // so all we need to do here is run the app on the selected device |
| 111 | + if let Some(device) = device { |
| 112 | + let config = built_application.config.clone(); |
| 113 | + let release = options.release; |
| 114 | + let runner = move || { |
| 115 | + device |
| 116 | + .run( |
| 117 | + &config, |
| 118 | + &env, |
| 119 | + noise_level, |
| 120 | + if !release { |
| 121 | + Profile::Debug |
| 122 | + } else { |
| 123 | + Profile::Release |
| 124 | + }, |
| 125 | + Some(match noise_level { |
| 126 | + NoiseLevel::Polite => FilterLevel::Info, |
| 127 | + NoiseLevel::LoudAndProud => FilterLevel::Debug, |
| 128 | + NoiseLevel::FranklyQuitePedantic => FilterLevel::Verbose, |
| 129 | + }), |
| 130 | + false, |
| 131 | + false, |
| 132 | + ".MainActivity".into(), |
| 133 | + ) |
| 134 | + .map(|c| Box::new(DevChild::new(c)) as Box<dyn DevProcess + Send>) |
| 135 | + .context("failed to run Android app") |
| 136 | + }; |
| 137 | + |
| 138 | + if options.no_watch { |
| 139 | + runner()?; |
| 140 | + } else { |
| 141 | + built_application.interface.watch( |
| 142 | + WatcherOptions { |
| 143 | + config: options.config, |
| 144 | + additional_watch_folders: options.additional_watch_folders, |
| 145 | + }, |
| 146 | + runner, |
| 147 | + )?; |
| 148 | + } |
| 149 | + } |
| 150 | + |
| 151 | + Ok(()) |
| 152 | +} |
0 commit comments