refactor: copy resources and sidecars on the Cargo build script (#3357)

This commit is contained in:
Lucas Fernandes Nogueira 2022-02-08 13:13:21 -03:00 committed by GitHub
parent 4a10e88c4b
commit 5eb72c24de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 263 additions and 186 deletions

View File

@ -208,7 +208,8 @@
},
"tauri-bundler": {
"path": "./tooling/bundler",
"manager": "rust"
"manager": "rust",
"dependencies": ["tauri-utils"]
},
"tauri-utils": {
"path": "./core/tauri-utils",
@ -268,7 +269,7 @@
"cli.rs": {
"path": "./tooling/cli.rs",
"manager": "rust",
"dependencies": ["tauri-bundler"],
"dependencies": ["tauri-bundler", "tauri-utils"],
"postversion": "node ../../.scripts/covector/generate-cli-doc.js && node ../../.scripts/covector/generate-config-doc.js && cargo check"
},
"create-tauri-app": {

View File

@ -0,0 +1,8 @@
---
"cli.rs": patch
"tauri-bundler": patch
"tauri-utils": patch
"tauri-build": patch
---
Move the copying of resources and sidecars from `cli.rs` to `tauri-build` so using the Cargo CLI directly processes the files for the application execution in development.

View File

@ -20,7 +20,7 @@ rustdoc-args = [ "--cfg", "doc_cfg" ]
anyhow = "1"
quote = { version = "1", optional = true }
tauri-codegen = { version = "1.0.0-beta.4", path = "../tauri-codegen", optional = true }
tauri-utils = { version = "1.0.0-beta.0", path = "../tauri-utils", features = [ "build" ] }
tauri-utils = { version = "1.0.0-beta.0", path = "../tauri-utils", features = [ "build", "resources" ] }
cargo_toml = "0.10"
serde_json = "1"

View File

@ -5,6 +5,7 @@
#![cfg_attr(doc_cfg, feature(doc_cfg))]
pub use anyhow::Result;
use tauri_utils::resources::{external_binaries, resource_relpath, ResourcePaths};
use std::path::{Path, PathBuf};
@ -15,6 +16,44 @@ mod codegen;
#[cfg_attr(doc_cfg, doc(cfg(feature = "codegen")))]
pub use codegen::context::CodegenContext;
fn copy_file(from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<()> {
let from = from.as_ref();
let to = to.as_ref();
if !from.exists() {
return Err(anyhow::anyhow!("{:?} does not exist", from));
}
if !from.is_file() {
return Err(anyhow::anyhow!("{:?} is not a file", from));
}
std::fs::copy(from, to)?;
Ok(())
}
fn copy_binaries<'a>(binaries: ResourcePaths<'a>, target_triple: &str, path: &Path) -> Result<()> {
for src in binaries {
let src = src?;
let dest = path.join(
src
.file_name()
.expect("failed to extract external binary filename")
.to_string_lossy()
.replace(&format!("-{}", target_triple), ""),
);
copy_file(&src, &dest)?;
}
Ok(())
}
/// Copies resources to a path.
fn copy_resources(resources: ResourcePaths<'_>, path: &Path) -> Result<()> {
for src in resources {
let src = src?;
let dest = path.join(resource_relpath(&src));
copy_file(&src, &dest)?;
}
Ok(())
}
/// Attributes used on Windows.
#[allow(dead_code)]
#[derive(Debug)]
@ -176,6 +215,28 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
}
}
let target_triple = std::env::var("TARGET").unwrap();
let out_dir = std::env::var("OUT_DIR").unwrap();
// TODO: far from ideal, but there's no other way to get the target dir, see <https://github.com/rust-lang/cargo/issues/5457>
let target_dir = Path::new(&out_dir)
.parent()
.unwrap()
.parent()
.unwrap()
.parent()
.unwrap();
if let Some(paths) = config.tauri.bundle.external_bin {
copy_binaries(
ResourcePaths::new(external_binaries(&paths, &target_triple).as_slice(), true),
&target_triple,
target_dir,
)?;
}
if let Some(paths) = config.tauri.bundle.resources {
copy_resources(ResourcePaths::new(paths.as_slice(), true), target_dir)?;
}
#[cfg(windows)]
{
use anyhow::Context;

View File

@ -31,6 +31,8 @@ serialize-to-javascript = { git = "https://github.com/chippers/serialize-to-java
ctor = "0.1"
json5 = { version = "0.4", optional = true }
json-patch = "0.2"
glob = { version = "0.3.0", optional = true }
walkdir = { version = "2", optional = true }
[target."cfg(target_os = \"linux\")".dependencies]
heck = "0.4"
@ -42,3 +44,4 @@ schema = ["schemars"]
isolation = [ "aes-gcm", "ring", "once_cell" ]
process-relaunch-dangerous-allow-symlink-macos = []
config-json5 = [ "json5" ]
resources = [ "glob", "walkdir" ]

View File

@ -9,6 +9,9 @@ pub mod assets;
pub mod config;
pub mod html;
pub mod platform;
/// Prepare application resources and sidecars.
#[cfg(feature = "resources")]
pub mod resources;
/// Application pattern.
pub mod pattern;
@ -123,4 +126,24 @@ pub enum Error {
/// Invalid pattern.
#[error("invalid pattern `{0}`. Expected either `brownfield` or `isolation`.")]
InvalidPattern(String),
/// Invalid glob pattern.
#[cfg(feature = "resources")]
#[error("{0}")]
GlobPattern(#[from] glob::PatternError),
/// Failed to use glob pattern.
#[cfg(feature = "resources")]
#[error("`{0}`")]
Glob(#[from] glob::GlobError),
/// Glob pattern did not find any results.
#[cfg(feature = "resources")]
#[error("path matching {0} not found.")]
GlobPathNotFound(String),
/// Error walking directory.
#[cfg(feature = "resources")]
#[error("{0}")]
WalkdirError(#[from] walkdir::Error),
/// Not allowed to walk dir.
#[cfg(feature = "resources")]
#[error("could not walk directory `{0}`, try changing `allow_walk` to true on the `ResourcePaths` constructor.")]
NotAllowedToWalkDir(std::path::PathBuf),
}

View File

@ -0,0 +1,126 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use std::path::{Component, Path, PathBuf};
/// Given a path (absolute or relative) to a resource file, returns the
/// relative path from the bundle resources directory where that resource
/// should be stored.
pub fn resource_relpath(path: &Path) -> PathBuf {
let mut dest = PathBuf::new();
for component in path.components() {
match component {
Component::Prefix(_) => {}
Component::RootDir => dest.push("_root_"),
Component::CurDir => {}
Component::ParentDir => dest.push("_up_"),
Component::Normal(string) => dest.push(string),
}
}
dest
}
/// Parses the external binaries to bundle, adding the target triple suffix to each of them.
pub fn external_binaries(external_binaries: &[String], target_triple: &str) -> Vec<String> {
let mut paths = Vec::new();
for curr_path in external_binaries {
paths.push(format!(
"{}-{}{}",
curr_path,
target_triple,
if cfg!(windows) { ".exe" } else { "" }
));
}
paths
}
/// A helper to iterate through resources.
pub struct ResourcePaths<'a> {
/// the patterns to iterate.
pattern_iter: std::slice::Iter<'a, String>,
/// the glob iterator if the path from the current iteration is a glob pattern.
glob_iter: Option<glob::Paths>,
/// the walkdir iterator if the path from the current iteration is a directory.
walk_iter: Option<walkdir::IntoIter>,
/// whether the resource paths allows directories or not.
allow_walk: bool,
/// the pattern of the current iteration.
current_pattern: Option<String>,
/// whether the current pattern is valid or not.
current_pattern_is_valid: bool,
}
impl<'a> ResourcePaths<'a> {
/// Creates a new ResourcePaths from a slice of patterns to iterate
pub fn new(patterns: &'a [String], allow_walk: bool) -> ResourcePaths<'a> {
ResourcePaths {
pattern_iter: patterns.iter(),
glob_iter: None,
walk_iter: None,
allow_walk,
current_pattern: None,
current_pattern_is_valid: false,
}
}
}
impl<'a> Iterator for ResourcePaths<'a> {
type Item = crate::Result<PathBuf>;
fn next(&mut self) -> Option<crate::Result<PathBuf>> {
loop {
if let Some(ref mut walk_entries) = self.walk_iter {
if let Some(entry) = walk_entries.next() {
let entry = match entry {
Ok(entry) => entry,
Err(error) => return Some(Err(crate::Error::from(error))),
};
let path = entry.path();
if path.is_dir() {
continue;
}
self.current_pattern_is_valid = true;
return Some(Ok(path.to_path_buf()));
}
}
self.walk_iter = None;
if let Some(ref mut glob_paths) = self.glob_iter {
if let Some(glob_result) = glob_paths.next() {
let path = match glob_result {
Ok(path) => path,
Err(error) => return Some(Err(error.into())),
};
if path.is_dir() {
if self.allow_walk {
let walk = walkdir::WalkDir::new(path);
self.walk_iter = Some(walk.into_iter());
continue;
} else {
return Some(Err(crate::Error::NotAllowedToWalkDir(path)));
}
}
self.current_pattern_is_valid = true;
return Some(Ok(path));
} else if let Some(current_path) = &self.current_pattern {
if !self.current_pattern_is_valid {
self.glob_iter = None;
return Some(Err(crate::Error::GlobPathNotFound(current_path.clone())));
}
}
}
self.glob_iter = None;
if let Some(pattern) = self.pattern_iter.next() {
self.current_pattern = Some(pattern.to_string());
self.current_pattern_is_valid = false;
let glob = match glob::glob(pattern) {
Ok(glob) => glob,
Err(error) => return Some(Err(error.into())),
};
self.glob_iter = Some(glob);
continue;
}
return None;
}
}
}

View File

@ -2715,6 +2715,7 @@ name = "tauri-utils"
version = "1.0.0-beta.3"
dependencies = [
"ctor",
"glob",
"heck 0.4.0",
"html5ever",
"json-patch",
@ -2728,6 +2729,7 @@ dependencies = [
"serialize-to-javascript",
"thiserror",
"url",
"walkdir",
"zstd",
]
@ -3076,9 +3078,9 @@ dependencies = [
[[package]]
name = "webview2-com"
version = "0.10.0"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "381febeeb86214fd262941a2b26322c33c38bf8b565b0ddfd4a7a8d4003053a9"
checksum = "1975ce3573344c099935fe3903f1708dac69efe8539f1efee3ae54d8f9315fbb"
dependencies = [
"webview2-com-macros",
"webview2-com-sys",
@ -3099,9 +3101,9 @@ dependencies = [
[[package]]
name = "webview2-com-sys"
version = "0.10.0"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2e3542bb16fe61e951f87c9146571344f1e773327498efa65704a4cff472662"
checksum = "9a746838a94b7391f707209a246e3436d81d1e71832126a65a897d3ee5511040"
dependencies = [
"regex",
"serde",
@ -3240,9 +3242,9 @@ dependencies = [
[[package]]
name = "wry"
version = "0.13.0"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4875fbbfc2c63f6c57c4ef84f678b1b57e3b8795443add7fbd02f3e8017e30"
checksum = "194b2750d8fe10fef189af5e2ca09e56cb8c5458a365d2b32842b024351f58c9"
dependencies = [
"cocoa",
"core-graphics",

View File

@ -22,8 +22,8 @@ exclude = [
]
[dependencies]
tauri-utils = { version = "1.0.0-beta.3", path = "../../core/tauri-utils", features = [ "resources" ] }
ar = "0.9.0"
glob = "0.3.0"
icns = "0.3"
image = "0.23.14"
libflate = "1.1"
@ -48,6 +48,7 @@ bitness = "0.4"
winreg = "0.10"
sha2 = "0.10"
hex = "0.4"
glob = "0.3"
[target."cfg(target_os = \"macos\")".dependencies]
time = { version = "0.3", features = ["formatting"] }

View File

@ -7,7 +7,7 @@ use std::{
ffi::OsStr,
fs::{self, File},
io::{self, BufWriter, Write},
path::{Component, Path, PathBuf},
path::Path,
process::{Command, Stdio},
};
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
@ -133,23 +133,6 @@ pub fn copy_dir(from: &Path, to: &Path) -> crate::Result<()> {
Ok(())
}
/// Given a path (absolute or relative) to a resource file, returns the
/// relative path from the bundle resources directory where that resource
/// should be stored.
pub fn resource_relpath(path: &Path) -> PathBuf {
let mut dest = PathBuf::new();
for component in path.components() {
match component {
Component::Prefix(_) => {}
Component::RootDir => dest.push("_root_"),
Component::CurDir => {}
Component::ParentDir => dest.push("_up_"),
Component::Normal(string) => dest.push(string),
}
}
dest
}
/// Prints a message to stderr, in the same format that `cargo` uses,
/// indicating that we are creating a bundle with the given filename.
pub fn print_bundling(filename: &str) -> crate::Result<()> {
@ -233,8 +216,9 @@ pub fn execute_with_verbosity(cmd: &mut Command, settings: &Settings) -> crate::
#[cfg(test)]
mod tests {
use super::{create_file, is_retina, resource_relpath};
use super::{create_file, is_retina};
use std::{io::Write, path::PathBuf};
use tauri_utils::resources::resource_relpath;
#[test]
fn create_file_with_parent_dirs() {

View File

@ -45,7 +45,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
for src in settings.resource_files() {
let src = src?;
let dest = bundle_dir.join(common::resource_relpath(&src));
let dest = bundle_dir.join(tauri_utils::resources::resource_relpath(&src));
common::copy_file(&src, &dest)
.with_context(|| format!("Failed to copy resource file {:?}", src))?;
}

View File

@ -4,6 +4,7 @@
use super::category::AppCategory;
use crate::bundle::{common, platform::target_triple};
use tauri_utils::resources::{external_binaries, ResourcePaths};
use std::{
collections::HashMap,
@ -466,7 +467,6 @@ impl SettingsBuilder {
} else {
target_triple()?
};
let bundle_settings = parse_external_bin(&target, self.bundle_settings)?;
Ok(Settings {
package: self.package_settings.expect("package settings is required"),
@ -476,7 +476,14 @@ impl SettingsBuilder {
.project_out_directory
.expect("out directory is required"),
binaries: self.binaries,
bundle_settings,
bundle_settings: BundleSettings {
external_bin: self
.bundle_settings
.external_bin
.as_ref()
.map(|bins| external_binaries(bins, &target)),
..self.bundle_settings
},
target,
})
}
@ -636,7 +643,7 @@ impl Settings {
pub fn copy_resources(&self, path: &Path) -> crate::Result<()> {
for src in self.resource_files() {
let src = src?;
let dest = path.join(common::resource_relpath(&src));
let dest = path.join(tauri_utils::resources::resource_relpath(&src));
common::copy_file(&src, &dest)?;
}
Ok(())
@ -717,124 +724,3 @@ impl Settings {
}
}
}
/// Parses the external binaries to bundle, adding the target triple suffix to each of them.
fn parse_external_bin(
target_triple: &str,
bundle_settings: BundleSettings,
) -> crate::Result<BundleSettings> {
let mut win_paths = Vec::new();
let external_bin = match bundle_settings.external_bin {
Some(paths) => {
for curr_path in paths.iter() {
win_paths.push(format!(
"{}-{}{}",
curr_path,
target_triple,
if cfg!(windows) { ".exe" } else { "" }
));
}
Some(win_paths)
}
None => Some(vec![]),
};
Ok(BundleSettings {
external_bin,
..bundle_settings
})
}
/// A helper to iterate through resources.
pub struct ResourcePaths<'a> {
/// the patterns to iterate.
pattern_iter: std::slice::Iter<'a, String>,
/// the glob iterator if the path from the current iteration is a glob pattern.
glob_iter: Option<glob::Paths>,
/// the walkdir iterator if the path from the current iteration is a directory.
walk_iter: Option<walkdir::IntoIter>,
/// whether the resource paths allows directories or not.
allow_walk: bool,
/// the pattern of the current iteration.
current_pattern: Option<String>,
/// whether the current pattern is valid or not.
current_pattern_is_valid: bool,
}
impl<'a> ResourcePaths<'a> {
/// Creates a new ResourcePaths from a slice of patterns to iterate
fn new(patterns: &'a [String], allow_walk: bool) -> ResourcePaths<'a> {
ResourcePaths {
pattern_iter: patterns.iter(),
glob_iter: None,
walk_iter: None,
allow_walk,
current_pattern: None,
current_pattern_is_valid: false,
}
}
}
impl<'a> Iterator for ResourcePaths<'a> {
type Item = crate::Result<PathBuf>;
fn next(&mut self) -> Option<crate::Result<PathBuf>> {
loop {
if let Some(ref mut walk_entries) = self.walk_iter {
if let Some(entry) = walk_entries.next() {
let entry = match entry {
Ok(entry) => entry,
Err(error) => return Some(Err(crate::Error::from(error))),
};
let path = entry.path();
if path.is_dir() {
continue;
}
self.current_pattern_is_valid = true;
return Some(Ok(path.to_path_buf()));
}
}
self.walk_iter = None;
if let Some(ref mut glob_paths) = self.glob_iter {
if let Some(glob_result) = glob_paths.next() {
let path = match glob_result {
Ok(path) => path,
Err(error) => return Some(Err(crate::Error::from(error))),
};
if path.is_dir() {
if self.allow_walk {
let walk = walkdir::WalkDir::new(path);
self.walk_iter = Some(walk.into_iter());
continue;
} else {
let msg = format!("{:?} is a directory", path);
return Some(Err(crate::Error::GenericError(msg)));
}
}
self.current_pattern_is_valid = true;
return Some(Ok(path));
} else if let Some(current_path) = &self.current_pattern {
if !self.current_pattern_is_valid {
self.glob_iter = None;
return Some(Err(crate::Error::GenericError(format!(
"Path matching '{}' not found",
current_path
))));
}
}
}
self.glob_iter = None;
if let Some(pattern) = self.pattern_iter.next() {
self.current_pattern = Some(pattern.to_string());
self.current_pattern_is_valid = false;
let glob = match glob::glob(pattern) {
Ok(glob) => glob,
Err(error) => return Some(Err(crate::Error::from(error))),
};
self.glob_iter = Some(glob);
continue;
}
return None;
}
}
}

View File

@ -9,15 +9,12 @@ use thiserror::Error as DeriveError;
#[derive(Debug, DeriveError)]
#[non_exhaustive]
pub enum Error {
/// Error running tauri_utils API.
#[error("{0}")]
Resource(#[from] tauri_utils::Error),
/// Bundler error.
#[error("{0}")]
BundlerError(#[from] anyhow::Error),
/// Failed to use glob pattern.
#[error("`{0}`")]
GlobError(#[from] glob::GlobError),
/// Invalid glob pattern.
#[error("`{0}`")]
GlobPatternError(#[from] glob::PatternError),
/// I/O error.
#[error("`{0}`")]
IoError(#[from] io::Error),
@ -57,6 +54,14 @@ pub enum Error {
#[cfg(windows)]
#[error("`{0}`")]
HttpError(#[from] attohttpc::Error),
/// Invalid glob pattern.
#[cfg(windows)]
#[error("{0}")]
GlobPattern(#[from] glob::PatternError),
/// Failed to use glob pattern.
#[cfg(windows)]
#[error("`{0}`")]
Glob(#[from] glob::GlobError),
/// Failed to validate downloaded file hash.
#[error("hash mismatch of downloaded file")]
HashError,

View File

@ -2380,7 +2380,6 @@ dependencies = [
"attohttpc",
"bitness",
"dirs-next",
"glob",
"handlebars",
"heck",
"hex",
@ -2394,6 +2393,7 @@ dependencies = [
"sha2",
"strsim",
"tar",
"tauri-utils",
"tempfile",
"termcolor",
"thiserror",
@ -2453,6 +2453,7 @@ version = "1.0.0-beta.3"
dependencies = [
"aes-gcm",
"ctor",
"glob",
"heck",
"html5ever",
"json-patch",
@ -2468,6 +2469,7 @@ dependencies = [
"serialize-to-javascript",
"thiserror",
"url",
"walkdir",
]
[[package]]

View File

@ -299,9 +299,6 @@ pub fn command(options: Options) -> Result<()> {
)
.with_context(|| "failed to build bundler settings")?;
settings.copy_resources(&out_dir)?;
settings.copy_binaries(&out_dir)?;
let bundles = bundle_project(settings).with_context(|| "failed to bundle project")?;
// If updater is active

View File

@ -74,28 +74,6 @@ pub fn command(options: Options) -> Result<()> {
};
let config = get_config(merge_config.as_deref())?;
let (settings, out_dir) = {
let config_guard = config.lock().unwrap();
let config_ = config_guard.as_ref().unwrap();
let app_settings = crate::interface::rust::AppSettings::new(config_)?;
let out_dir = app_settings
.get_out_dir(options.target.clone(), true)
.with_context(|| "failed to get project out directory")?;
let settings = crate::interface::get_bundler_settings(
app_settings,
options.target.clone(),
&Default::default(),
config_,
&out_dir,
false,
None,
)
.with_context(|| "failed to build bundler settings")?;
(settings, out_dir)
};
settings.copy_resources(&out_dir)?;
settings.copy_binaries(&out_dir)?;
if let Some(before_dev) = &config
.lock()
.unwrap()