diff --git a/.changes/config.json b/.changes/config.json index 77134069f..3d2f4e37d 100644 --- a/.changes/config.json +++ b/.changes/config.json @@ -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": { diff --git a/.changes/refactor-resources-sidecar-copying.md b/.changes/refactor-resources-sidecar-copying.md new file mode 100644 index 000000000..b20407f19 --- /dev/null +++ b/.changes/refactor-resources-sidecar-copying.md @@ -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. diff --git a/core/tauri-build/Cargo.toml b/core/tauri-build/Cargo.toml index 6ed4b8fc7..f97227c6b 100644 --- a/core/tauri-build/Cargo.toml +++ b/core/tauri-build/Cargo.toml @@ -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" diff --git a/core/tauri-build/src/lib.rs b/core/tauri-build/src/lib.rs index 6830f833a..9f720a12a 100644 --- a/core/tauri-build/src/lib.rs +++ b/core/tauri-build/src/lib.rs @@ -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, to: impl AsRef) -> 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 + 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; diff --git a/core/tauri-utils/Cargo.toml b/core/tauri-utils/Cargo.toml index 5b942a578..8ecaaa195 100644 --- a/core/tauri-utils/Cargo.toml +++ b/core/tauri-utils/Cargo.toml @@ -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" ] diff --git a/core/tauri-utils/src/lib.rs b/core/tauri-utils/src/lib.rs index 37fa8d808..8ff4e534c 100644 --- a/core/tauri-utils/src/lib.rs +++ b/core/tauri-utils/src/lib.rs @@ -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), } diff --git a/core/tauri-utils/src/resources.rs b/core/tauri-utils/src/resources.rs new file mode 100644 index 000000000..4ae0219ae --- /dev/null +++ b/core/tauri-utils/src/resources.rs @@ -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 { + 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, + /// the walkdir iterator if the path from the current iteration is a directory. + walk_iter: Option, + /// whether the resource paths allows directories or not. + allow_walk: bool, + /// the pattern of the current iteration. + current_pattern: Option, + /// 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; + + fn next(&mut self) -> Option> { + 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; + } + } +} diff --git a/examples/sidecar/src-tauri/Cargo.lock b/examples/sidecar/src-tauri/Cargo.lock index b3b354170..13abe6678 100644 --- a/examples/sidecar/src-tauri/Cargo.lock +++ b/examples/sidecar/src-tauri/Cargo.lock @@ -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", diff --git a/tooling/bundler/Cargo.toml b/tooling/bundler/Cargo.toml index 0a3f86c4d..d276f75db 100644 --- a/tooling/bundler/Cargo.toml +++ b/tooling/bundler/Cargo.toml @@ -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"] } diff --git a/tooling/bundler/src/bundle/common.rs b/tooling/bundler/src/bundle/common.rs index aa3266349..c75bad590 100644 --- a/tooling/bundler/src/bundle/common.rs +++ b/tooling/bundler/src/bundle/common.rs @@ -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() { diff --git a/tooling/bundler/src/bundle/macos/ios.rs b/tooling/bundler/src/bundle/macos/ios.rs index 8a0850bc2..5b7b792d2 100644 --- a/tooling/bundler/src/bundle/macos/ios.rs +++ b/tooling/bundler/src/bundle/macos/ios.rs @@ -45,7 +45,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result> { 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))?; } diff --git a/tooling/bundler/src/bundle/settings.rs b/tooling/bundler/src/bundle/settings.rs index 2befbd83d..62306472e 100644 --- a/tooling/bundler/src/bundle/settings.rs +++ b/tooling/bundler/src/bundle/settings.rs @@ -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 { - 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, - /// the walkdir iterator if the path from the current iteration is a directory. - walk_iter: Option, - /// whether the resource paths allows directories or not. - allow_walk: bool, - /// the pattern of the current iteration. - current_pattern: Option, - /// 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; - - fn next(&mut self) -> Option> { - 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; - } - } -} diff --git a/tooling/bundler/src/error.rs b/tooling/bundler/src/error.rs index 3b29300f2..7fed9989c 100644 --- a/tooling/bundler/src/error.rs +++ b/tooling/bundler/src/error.rs @@ -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, diff --git a/tooling/cli.rs/Cargo.lock b/tooling/cli.rs/Cargo.lock index bfab746be..b4c83403c 100644 --- a/tooling/cli.rs/Cargo.lock +++ b/tooling/cli.rs/Cargo.lock @@ -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]] diff --git a/tooling/cli.rs/src/build.rs b/tooling/cli.rs/src/build.rs index 6ac46cd7a..1fb902225 100644 --- a/tooling/cli.rs/src/build.rs +++ b/tooling/cli.rs/src/build.rs @@ -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 diff --git a/tooling/cli.rs/src/dev.rs b/tooling/cli.rs/src/dev.rs index 032610ad9..057517556 100644 --- a/tooling/cli.rs/src/dev.rs +++ b/tooling/cli.rs/src/dev.rs @@ -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()