Skip to content

Commit 5eb72c2

Browse files
authored
refactor: copy resources and sidecars on the Cargo build script (#3357)
1 parent 4a10e88 commit 5eb72c2

File tree

16 files changed

+263
-186
lines changed

16 files changed

+263
-186
lines changed

.changes/config.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,8 @@
208208
},
209209
"tauri-bundler": {
210210
"path": "./tooling/bundler",
211-
"manager": "rust"
211+
"manager": "rust",
212+
"dependencies": ["tauri-utils"]
212213
},
213214
"tauri-utils": {
214215
"path": "./core/tauri-utils",
@@ -268,7 +269,7 @@
268269
"cli.rs": {
269270
"path": "./tooling/cli.rs",
270271
"manager": "rust",
271-
"dependencies": ["tauri-bundler"],
272+
"dependencies": ["tauri-bundler", "tauri-utils"],
272273
"postversion": "node ../../.scripts/covector/generate-cli-doc.js && node ../../.scripts/covector/generate-config-doc.js && cargo check"
273274
},
274275
"create-tauri-app": {
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"cli.rs": patch
3+
"tauri-bundler": patch
4+
"tauri-utils": patch
5+
"tauri-build": patch
6+
---
7+
8+
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.

core/tauri-build/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ rustdoc-args = [ "--cfg", "doc_cfg" ]
2020
anyhow = "1"
2121
quote = { version = "1", optional = true }
2222
tauri-codegen = { version = "1.0.0-beta.4", path = "../tauri-codegen", optional = true }
23-
tauri-utils = { version = "1.0.0-beta.0", path = "../tauri-utils", features = [ "build" ] }
23+
tauri-utils = { version = "1.0.0-beta.0", path = "../tauri-utils", features = [ "build", "resources" ] }
2424
cargo_toml = "0.10"
2525
serde_json = "1"
2626

core/tauri-build/src/lib.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#![cfg_attr(doc_cfg, feature(doc_cfg))]
66

77
pub use anyhow::Result;
8+
use tauri_utils::resources::{external_binaries, resource_relpath, ResourcePaths};
89

910
use std::path::{Path, PathBuf};
1011

@@ -15,6 +16,44 @@ mod codegen;
1516
#[cfg_attr(doc_cfg, doc(cfg(feature = "codegen")))]
1617
pub use codegen::context::CodegenContext;
1718

19+
fn copy_file(from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<()> {
20+
let from = from.as_ref();
21+
let to = to.as_ref();
22+
if !from.exists() {
23+
return Err(anyhow::anyhow!("{:?} does not exist", from));
24+
}
25+
if !from.is_file() {
26+
return Err(anyhow::anyhow!("{:?} is not a file", from));
27+
}
28+
std::fs::copy(from, to)?;
29+
Ok(())
30+
}
31+
32+
fn copy_binaries<'a>(binaries: ResourcePaths<'a>, target_triple: &str, path: &Path) -> Result<()> {
33+
for src in binaries {
34+
let src = src?;
35+
let dest = path.join(
36+
src
37+
.file_name()
38+
.expect("failed to extract external binary filename")
39+
.to_string_lossy()
40+
.replace(&format!("-{}", target_triple), ""),
41+
);
42+
copy_file(&src, &dest)?;
43+
}
44+
Ok(())
45+
}
46+
47+
/// Copies resources to a path.
48+
fn copy_resources(resources: ResourcePaths<'_>, path: &Path) -> Result<()> {
49+
for src in resources {
50+
let src = src?;
51+
let dest = path.join(resource_relpath(&src));
52+
copy_file(&src, &dest)?;
53+
}
54+
Ok(())
55+
}
56+
1857
/// Attributes used on Windows.
1958
#[allow(dead_code)]
2059
#[derive(Debug)]
@@ -176,6 +215,28 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
176215
}
177216
}
178217

218+
let target_triple = std::env::var("TARGET").unwrap();
219+
let out_dir = std::env::var("OUT_DIR").unwrap();
220+
// TODO: far from ideal, but there's no other way to get the target dir, see <https://github.com/rust-lang/cargo/issues/5457>
221+
let target_dir = Path::new(&out_dir)
222+
.parent()
223+
.unwrap()
224+
.parent()
225+
.unwrap()
226+
.parent()
227+
.unwrap();
228+
229+
if let Some(paths) = config.tauri.bundle.external_bin {
230+
copy_binaries(
231+
ResourcePaths::new(external_binaries(&paths, &target_triple).as_slice(), true),
232+
&target_triple,
233+
target_dir,
234+
)?;
235+
}
236+
if let Some(paths) = config.tauri.bundle.resources {
237+
copy_resources(ResourcePaths::new(paths.as_slice(), true), target_dir)?;
238+
}
239+
179240
#[cfg(windows)]
180241
{
181242
use anyhow::Context;

core/tauri-utils/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ serialize-to-javascript = { git = "https://github.com/chippers/serialize-to-java
3131
ctor = "0.1"
3232
json5 = { version = "0.4", optional = true }
3333
json-patch = "0.2"
34+
glob = { version = "0.3.0", optional = true }
35+
walkdir = { version = "2", optional = true }
3436

3537
[target."cfg(target_os = \"linux\")".dependencies]
3638
heck = "0.4"
@@ -42,3 +44,4 @@ schema = ["schemars"]
4244
isolation = [ "aes-gcm", "ring", "once_cell" ]
4345
process-relaunch-dangerous-allow-symlink-macos = []
4446
config-json5 = [ "json5" ]
47+
resources = [ "glob", "walkdir" ]

core/tauri-utils/src/lib.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ pub mod assets;
99
pub mod config;
1010
pub mod html;
1111
pub mod platform;
12+
/// Prepare application resources and sidecars.
13+
#[cfg(feature = "resources")]
14+
pub mod resources;
1215

1316
/// Application pattern.
1417
pub mod pattern;
@@ -123,4 +126,24 @@ pub enum Error {
123126
/// Invalid pattern.
124127
#[error("invalid pattern `{0}`. Expected either `brownfield` or `isolation`.")]
125128
InvalidPattern(String),
129+
/// Invalid glob pattern.
130+
#[cfg(feature = "resources")]
131+
#[error("{0}")]
132+
GlobPattern(#[from] glob::PatternError),
133+
/// Failed to use glob pattern.
134+
#[cfg(feature = "resources")]
135+
#[error("`{0}`")]
136+
Glob(#[from] glob::GlobError),
137+
/// Glob pattern did not find any results.
138+
#[cfg(feature = "resources")]
139+
#[error("path matching {0} not found.")]
140+
GlobPathNotFound(String),
141+
/// Error walking directory.
142+
#[cfg(feature = "resources")]
143+
#[error("{0}")]
144+
WalkdirError(#[from] walkdir::Error),
145+
/// Not allowed to walk dir.
146+
#[cfg(feature = "resources")]
147+
#[error("could not walk directory `{0}`, try changing `allow_walk` to true on the `ResourcePaths` constructor.")]
148+
NotAllowedToWalkDir(std::path::PathBuf),
126149
}

core/tauri-utils/src/resources.rs

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
2+
// SPDX-License-Identifier: Apache-2.0
3+
// SPDX-License-Identifier: MIT
4+
5+
use std::path::{Component, Path, PathBuf};
6+
7+
/// Given a path (absolute or relative) to a resource file, returns the
8+
/// relative path from the bundle resources directory where that resource
9+
/// should be stored.
10+
pub fn resource_relpath(path: &Path) -> PathBuf {
11+
let mut dest = PathBuf::new();
12+
for component in path.components() {
13+
match component {
14+
Component::Prefix(_) => {}
15+
Component::RootDir => dest.push("_root_"),
16+
Component::CurDir => {}
17+
Component::ParentDir => dest.push("_up_"),
18+
Component::Normal(string) => dest.push(string),
19+
}
20+
}
21+
dest
22+
}
23+
24+
/// Parses the external binaries to bundle, adding the target triple suffix to each of them.
25+
pub fn external_binaries(external_binaries: &[String], target_triple: &str) -> Vec<String> {
26+
let mut paths = Vec::new();
27+
for curr_path in external_binaries {
28+
paths.push(format!(
29+
"{}-{}{}",
30+
curr_path,
31+
target_triple,
32+
if cfg!(windows) { ".exe" } else { "" }
33+
));
34+
}
35+
paths
36+
}
37+
38+
/// A helper to iterate through resources.
39+
pub struct ResourcePaths<'a> {
40+
/// the patterns to iterate.
41+
pattern_iter: std::slice::Iter<'a, String>,
42+
/// the glob iterator if the path from the current iteration is a glob pattern.
43+
glob_iter: Option<glob::Paths>,
44+
/// the walkdir iterator if the path from the current iteration is a directory.
45+
walk_iter: Option<walkdir::IntoIter>,
46+
/// whether the resource paths allows directories or not.
47+
allow_walk: bool,
48+
/// the pattern of the current iteration.
49+
current_pattern: Option<String>,
50+
/// whether the current pattern is valid or not.
51+
current_pattern_is_valid: bool,
52+
}
53+
54+
impl<'a> ResourcePaths<'a> {
55+
/// Creates a new ResourcePaths from a slice of patterns to iterate
56+
pub fn new(patterns: &'a [String], allow_walk: bool) -> ResourcePaths<'a> {
57+
ResourcePaths {
58+
pattern_iter: patterns.iter(),
59+
glob_iter: None,
60+
walk_iter: None,
61+
allow_walk,
62+
current_pattern: None,
63+
current_pattern_is_valid: false,
64+
}
65+
}
66+
}
67+
68+
impl<'a> Iterator for ResourcePaths<'a> {
69+
type Item = crate::Result<PathBuf>;
70+
71+
fn next(&mut self) -> Option<crate::Result<PathBuf>> {
72+
loop {
73+
if let Some(ref mut walk_entries) = self.walk_iter {
74+
if let Some(entry) = walk_entries.next() {
75+
let entry = match entry {
76+
Ok(entry) => entry,
77+
Err(error) => return Some(Err(crate::Error::from(error))),
78+
};
79+
let path = entry.path();
80+
if path.is_dir() {
81+
continue;
82+
}
83+
self.current_pattern_is_valid = true;
84+
return Some(Ok(path.to_path_buf()));
85+
}
86+
}
87+
self.walk_iter = None;
88+
if let Some(ref mut glob_paths) = self.glob_iter {
89+
if let Some(glob_result) = glob_paths.next() {
90+
let path = match glob_result {
91+
Ok(path) => path,
92+
Err(error) => return Some(Err(error.into())),
93+
};
94+
if path.is_dir() {
95+
if self.allow_walk {
96+
let walk = walkdir::WalkDir::new(path);
97+
self.walk_iter = Some(walk.into_iter());
98+
continue;
99+
} else {
100+
return Some(Err(crate::Error::NotAllowedToWalkDir(path)));
101+
}
102+
}
103+
self.current_pattern_is_valid = true;
104+
return Some(Ok(path));
105+
} else if let Some(current_path) = &self.current_pattern {
106+
if !self.current_pattern_is_valid {
107+
self.glob_iter = None;
108+
return Some(Err(crate::Error::GlobPathNotFound(current_path.clone())));
109+
}
110+
}
111+
}
112+
self.glob_iter = None;
113+
if let Some(pattern) = self.pattern_iter.next() {
114+
self.current_pattern = Some(pattern.to_string());
115+
self.current_pattern_is_valid = false;
116+
let glob = match glob::glob(pattern) {
117+
Ok(glob) => glob,
118+
Err(error) => return Some(Err(error.into())),
119+
};
120+
self.glob_iter = Some(glob);
121+
continue;
122+
}
123+
return None;
124+
}
125+
}
126+
}

examples/sidecar/src-tauri/Cargo.lock

Lines changed: 8 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tooling/bundler/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ exclude = [
2222
]
2323

2424
[dependencies]
25+
tauri-utils = { version = "1.0.0-beta.3", path = "../../core/tauri-utils", features = [ "resources" ] }
2526
ar = "0.9.0"
26-
glob = "0.3.0"
2727
icns = "0.3"
2828
image = "0.23.14"
2929
libflate = "1.1"
@@ -48,6 +48,7 @@ bitness = "0.4"
4848
winreg = "0.10"
4949
sha2 = "0.10"
5050
hex = "0.4"
51+
glob = "0.3"
5152

5253
[target."cfg(target_os = \"macos\")".dependencies]
5354
time = { version = "0.3", features = ["formatting"] }

0 commit comments

Comments
 (0)