Files
tauri/tooling/cli/src/lib.rs
Lucas Fernandes Nogueira a50f24b2bd Merge remote-tracking branch 'origin/dev' into next (#7067)
Co-authored-by: wusyong <wusyong@users.noreply.github.com>
Co-authored-by: Fabian-Lars <fabianlars@fabianlars.de>
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
Co-authored-by: Simon Hyll <hyllsimon@gmail.com>
Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.app>
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.studio>
Co-authored-by: Amr Bashir <amr.bashir2015@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: chip <chip@chip.sh>
Co-authored-by: Raphii <iam@raphii.co>
Co-authored-by: Ronie Martinez <ronmarti18@gmail.com>
Co-authored-by: hanaTsuk1 <101488209+hanaTsuk1@users.noreply.github.com>
Co-authored-by: nathan-fall <39990940+nathan-fall@users.noreply.github.com>
Co-authored-by: Akshay <nerdy@peppe.rs>
Co-authored-by: KurikoMoe <kurikomoe@gmail.com>
Co-authored-by: Guilherme Oenning <me@goenning.net>
Co-authored-by: Pierre Cashon <biaocy91@gmail.com>
Co-authored-by: Jack Wills <32690432+mrjackwills@users.noreply.github.com>
Co-authored-by: Amirhossein Akhlaghpour <m9.akhlaghpoor@gmail.com>
Co-authored-by: Risto Stevcev <me@risto.codes>
Co-authored-by: Soumt <rltks1305@naver.com>
Co-authored-by: yutotnh <57719497+yutotnh@users.noreply.github.com>
Co-authored-by: Gökçe Merdun <agmmnn@gmail.com>
Co-authored-by: Nathanael Rea <Nathan@NathanaelRea.com>
Co-authored-by: Usman Rajab <usman.rajab@gmail.com>
Co-authored-by: Francis The Basilisk <36006338+snorkysnark@users.noreply.github.com>
Co-authored-by: Lej77 <31554212+Lej77@users.noreply.github.com>
Co-authored-by: Tomáš Diblík <dibla.tomas@post.cz>
Co-authored-by: Jonas Kruckenberg <iterpre@protonmail.com>
Co-authored-by: Pascal Sommer <Pascal-So@users.noreply.github.com>
Co-authored-by: Bo <bertonzh@gmail.com>
Co-authored-by: Kevin Yue <k3vinyue@gmail.com>
fixed grammar and typos (#6937)
Fix api.js docs pipeline with updated typedoc dependencies (#6945)
closes #6887 (#6922)
fix(core): Fix `WindowBuilder::on_navigation` handler never registerd, closes #6865 (#6921)
fix(core): Fix `WindowBuilder::on_navigation` handler never registerd, closes #6865
fix broken symlinks in license files (#6336)
fix(cli): fix cli connection timeout to dev server (fix #6045) (#6046)
fix(bundler): ensure that there are no duplicate extension arguments when bundling on Windows, fixes #6103 (#6917)
fix(bundler): ensure that there are no duplicate extension arguments during bundling on Windows (fix #6103)
closes #5491 (#6408)
fix(nsis): prefill $INSTDIR with previous install path and respect `/D` flag, closes #6928 (#6935)
fix(nsis): prefill $INSTDIR with previous install path and respect `/D` flag, closes #6928
fix(updater): emit `UPTODATE` when server responds with 204, closes #6934 (#6970)
fix(core): unpin all dependencies, closes #6944 (#6966)
fix(bundler): Add new lang_file option in persian variant. (#6972)
fix(core/ipc): access url through webview native object, closes #6889 (#6976)
fix(core): remove trailing slash in http scope url, closes #5208 (#6974)
fix(core): remove trailing slash in http scope url, closes #5208
fix(cli): find correct binary when `--profile` is used, closes #6954 (#6979)
fix(cli): find correct binary when `--profile` is used, closes #6954
closes #6955 (#6987)
closes #6955
closes #6158 (#6969)
closes #6158
fix(cli): improve vs build tools detection (#6982)
fix: updated appimage script to follow symlinks for /usr/lib* (fix: #6992) (#6996)
fix(cli): correctly remove Cargo features (#7013)
Fix typo (#7012)
fix(cli): revert metadata.json field rename from #6795 (#7029)
closes #6732 (#6736)
fix: add missing file properties on Windows, closes #6676 (#6693)
fix(cli.js): detect node-20 binary (#6667)
fix version-or-publish workflow (#7031)
fix(cli/devserver): inject autoreload into HTML only, closes #6997 (#7032)
fix(bundler/nsis): write installer templates UTF16LE encoded, closes #7036 (#7040)
fix(bundler/nsis): write installer templates UTF16LE encoded, closes #7036
fix(core): rewrite `asset` protocol streaming, closes #6375 (#6390)
closes #5939 (#5960)
fix(core): use `safe_block_on` (#7047)
closes #6859 (#6933)
closes #6955 (#6998)
fix(core): populate webview_attrs from config, closes #6794 (#6797)
closes #5176 (#5180)
fix: sound for notifications on windows (fix #6652) (#6680)
close native window's buttons, closes #2353 (#6665)
fix(bundler/nsis): calculate accurate app size, closes #7056 (#7057)
fix(tests): only download update when it is available (#7061)
closes #6706 (#6712)
fix(doc): correct the doc of `content_protected()` (#7065)
closes #6472 (#6530)
fix(macros): use full path to Result to avoid issues with type aliases (#7071)
2023-05-29 21:29:24 -03:00

311 lines
8.6 KiB
Rust

// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
pub use anyhow::Result;
mod build;
mod completions;
mod dev;
mod helpers;
mod icon;
mod info;
mod init;
mod interface;
mod migrate;
mod mobile;
mod plugin;
mod signer;
use clap::{ArgAction, CommandFactory, FromArgMatches, Parser, Subcommand, ValueEnum};
use env_logger::fmt::Color;
use env_logger::Builder;
use log::{debug, log_enabled, Level};
use serde::Deserialize;
use std::io::{BufReader, Write};
use std::process::{exit, Command, ExitStatus, Output, Stdio};
use std::{
ffi::OsString,
fmt::Display,
sync::{Arc, Mutex},
};
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
pub enum RunMode {
Desktop,
#[cfg(target_os = "macos")]
Ios,
Android,
}
impl Display for RunMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Desktop => "desktop",
#[cfg(target_os = "macos")]
Self::Ios => "iOS",
Self::Android => "android",
}
)
}
}
#[derive(Deserialize)]
pub struct VersionMetadata {
tauri: String,
#[serde(rename = "tauri-build")]
tauri_build: String,
}
#[derive(Deserialize)]
pub struct PackageJson {
name: Option<String>,
version: Option<String>,
product_name: Option<String>,
}
#[derive(Parser)]
#[clap(
author,
version,
about,
bin_name("cargo-tauri"),
subcommand_required(true),
arg_required_else_help(true),
propagate_version(true),
no_binary_name(true)
)]
pub(crate) struct Cli {
/// Enables verbose logging
#[clap(short, long, global = true, action = ArgAction::Count)]
verbose: u8,
#[clap(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Build(build::Options),
Dev(dev::Options),
Icon(icon::Options),
Info(info::Options),
Init(init::Options),
Plugin(plugin::Cli),
Signer(signer::Cli),
Completions(completions::Options),
Android(mobile::android::Cli),
#[cfg(target_os = "macos")]
Ios(mobile::ios::Cli),
/// Migrate from v1 to v2
Migrate,
}
fn format_error<I: CommandFactory>(err: clap::Error) -> clap::Error {
let mut app = I::command();
err.format(&mut app)
}
/// Run the Tauri CLI with the passed arguments, exiting if an error occurs.
///
/// The passed arguments should have the binary argument(s) stripped out before being passed.
///
/// e.g.
/// 1. `tauri-cli 1 2 3` -> `1 2 3`
/// 2. `cargo tauri 1 2 3` -> `1 2 3`
/// 3. `node tauri.js 1 2 3` -> `1 2 3`
///
/// The passed `bin_name` parameter should be how you want the help messages to display the command.
/// This defaults to `cargo-tauri`, but should be set to how the program was called, such as
/// `cargo tauri`.
pub fn run<I, A>(args: I, bin_name: Option<String>)
where
I: IntoIterator<Item = A>,
A: Into<OsString> + Clone,
{
if let Err(e) = try_run(args, bin_name) {
log::error!("{:#}", e);
exit(1);
}
}
/// Run the Tauri CLI with the passed arguments.
///
/// It is similar to [`run`], but instead of exiting on an error, it returns a result.
pub fn try_run<I, A>(args: I, bin_name: Option<String>) -> Result<()>
where
I: IntoIterator<Item = A>,
A: Into<OsString> + Clone,
{
let cli = match bin_name {
Some(bin_name) => Cli::command().bin_name(bin_name),
None => Cli::command(),
};
let cli_ = cli.clone();
let matches = cli.get_matches_from(args);
let res = Cli::from_arg_matches(&matches).map_err(format_error::<Cli>);
let cli = match res {
Ok(s) => s,
Err(e) => e.exit(),
};
let mut builder = Builder::from_default_env();
let init_res = builder
.format_indent(Some(12))
.filter(None, verbosity_level(cli.verbose).to_level_filter())
.format(|f, record| {
let mut is_command_output = false;
if let Some(action) = record.key_values().get("action".into()) {
let action = action.to_str().unwrap();
is_command_output = action == "stdout" || action == "stderr";
if !is_command_output {
let mut action_style = f.style();
action_style.set_color(Color::Green).set_bold(true);
write!(f, "{:>12} ", action_style.value(action))?;
}
} else {
let mut level_style = f.default_level_style(record.level());
level_style.set_bold(true);
write!(
f,
"{:>12} ",
level_style.value(prettyprint_level(record.level()))
)?;
}
if !is_command_output && log_enabled!(Level::Debug) {
let mut target_style = f.style();
target_style.set_color(Color::Black);
write!(f, "[{}] ", target_style.value(record.target()))?;
}
writeln!(f, "{}", record.args())
})
.try_init();
if let Err(err) = init_res {
eprintln!("Failed to attach logger: {err}");
}
match cli.command {
Commands::Build(options) => build::command(options, cli.verbose)?,
Commands::Dev(options) => dev::command(options)?,
Commands::Icon(options) => icon::command(options)?,
Commands::Info(options) => info::command(options)?,
Commands::Init(options) => init::command(options)?,
Commands::Plugin(cli) => plugin::command(cli)?,
Commands::Signer(cli) => signer::command(cli)?,
Commands::Completions(options) => completions::command(options, cli_)?,
Commands::Android(c) => mobile::android::command(c, cli.verbose)?,
#[cfg(target_os = "macos")]
Commands::Ios(c) => mobile::ios::command(c, cli.verbose)?,
Commands::Migrate => migrate::command()?,
}
Ok(())
}
/// This maps the occurrence of `--verbose` flags to the correct log level
fn verbosity_level(num: u8) -> Level {
match num {
0 => Level::Info,
1 => Level::Debug,
2.. => Level::Trace,
}
}
/// The default string representation for `Level` is all uppercaps which doesn't mix well with the other printed actions.
fn prettyprint_level(lvl: Level) -> &'static str {
match lvl {
Level::Error => "Error",
Level::Warn => "Warn",
Level::Info => "Info",
Level::Debug => "Debug",
Level::Trace => "Trace",
}
}
pub trait CommandExt {
// The `pipe` function sets the stdout and stderr to properly
// show the command output in the Node.js wrapper.
fn piped(&mut self) -> std::io::Result<ExitStatus>;
fn output_ok(&mut self) -> crate::Result<Output>;
}
impl CommandExt for Command {
fn piped(&mut self) -> std::io::Result<ExitStatus> {
self.stdout(os_pipe::dup_stdout()?);
self.stderr(os_pipe::dup_stderr()?);
let program = self.get_program().to_string_lossy().into_owned();
debug!(action = "Running"; "Command `{} {}`", program, self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{acc} {arg}")));
self.status().map_err(Into::into)
}
fn output_ok(&mut self) -> crate::Result<Output> {
let program = self.get_program().to_string_lossy().into_owned();
debug!(action = "Running"; "Command `{} {}`", program, self.get_args().map(|arg| arg.to_string_lossy()).fold(String::new(), |acc, arg| format!("{acc} {arg}")));
self.stdout(Stdio::piped());
self.stderr(Stdio::piped());
let mut child = self.spawn()?;
let mut stdout = child.stdout.take().map(BufReader::new).unwrap();
let stdout_lines = Arc::new(Mutex::new(Vec::new()));
let stdout_lines_ = stdout_lines.clone();
std::thread::spawn(move || {
let mut buf = Vec::new();
let mut lines = stdout_lines_.lock().unwrap();
loop {
buf.clear();
match tauri_utils::io::read_line(&mut stdout, &mut buf) {
Ok(s) if s == 0 => break,
_ => (),
}
debug!(action = "stdout"; "{}", String::from_utf8_lossy(&buf));
lines.extend(buf.clone());
lines.push(b'\n');
}
});
let mut stderr = child.stderr.take().map(BufReader::new).unwrap();
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();
loop {
buf.clear();
match tauri_utils::io::read_line(&mut stderr, &mut buf) {
Ok(s) if s == 0 => break,
_ => (),
}
debug!(action = "stderr"; "{}", String::from_utf8_lossy(&buf));
lines.extend(buf.clone());
lines.push(b'\n');
}
});
let status = child.wait()?;
let output = Output {
status,
stdout: std::mem::take(&mut *stdout_lines.lock().unwrap()),
stderr: std::mem::take(&mut *stderr_lines.lock().unwrap()),
};
if output.status.success() {
Ok(output)
} else {
Err(anyhow::anyhow!("failed to run {}", program))
}
}
}