diff --git a/crates/config/schema.json b/crates/config/schema.json index a3abf15e..3d475eaa 100644 --- a/crates/config/schema.json +++ b/crates/config/schema.json @@ -175,7 +175,7 @@ } }, "resources": { - "description": "The app's resources to package. This a list of either a path to a resource (with optional glob pattern) or an object of `src` and `target` paths.\n\n## Format-specific:\n\n- **[PackageFormat::Nsis] / [PackageFormat::Wix]**: The resources are placed next to the executable in the root of the packager. - **[PackageFormat::Deb]**: The resources are placed in `usr/lib` of the package.", + "description": "The app's resources to package. This a list of either a glob pattern, path to a file, path to a directory or an object of `src` and `target` paths. In the case of using an object, the `src` could be either a glob pattern, path to a file, path to a directory, and the `target` is a path inside the final resources folder in the installed package.\n\n## Format-specific:\n\n- **[PackageFormat::Nsis] / [PackageFormat::Wix]**: The resources are placed next to the executable in the root of the packager. - **[PackageFormat::Deb]**: The resources are placed in `usr/lib` of the package.", "type": [ "array", "null" diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 1146b1e0..efa7cac0 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -454,7 +454,7 @@ pub enum Resource { /// Supports glob patterns Single(String), Mapped { - src: PathBuf, + src: String, target: PathBuf, }, } @@ -548,8 +548,10 @@ pub struct Config { /// the file associations #[serde(alias = "file-associations", alias = "file_associations")] pub file_associations: Option>, - /// The app's resources to package. This a list of either a path to a resource (with optional glob pattern) - /// or an object of `src` and `target` paths. + /// The app's resources to package. This a list of either a glob pattern, path to a file, path to a directory + /// or an object of `src` and `target` paths. In the case of using an object, + /// the `src` could be either a glob pattern, path to a file, path to a directory, + /// and the `target` is a path inside the final resources folder in the installed package. /// /// ## Format-specific: /// diff --git a/crates/packager/schema.json b/crates/packager/schema.json index a3abf15e..3d475eaa 100644 --- a/crates/packager/schema.json +++ b/crates/packager/schema.json @@ -175,7 +175,7 @@ } }, "resources": { - "description": "The app's resources to package. This a list of either a path to a resource (with optional glob pattern) or an object of `src` and `target` paths.\n\n## Format-specific:\n\n- **[PackageFormat::Nsis] / [PackageFormat::Wix]**: The resources are placed next to the executable in the root of the packager. - **[PackageFormat::Deb]**: The resources are placed in `usr/lib` of the package.", + "description": "The app's resources to package. This a list of either a glob pattern, path to a file, path to a directory or an object of `src` and `target` paths. In the case of using an object, the `src` could be either a glob pattern, path to a file, path to a directory, and the `target` is a path inside the final resources folder in the installed package.\n\n## Format-specific:\n\n- **[PackageFormat::Nsis] / [PackageFormat::Wix]**: The resources are placed next to the executable in the root of the packager. - **[PackageFormat::Deb]**: The resources are placed in `usr/lib` of the package.", "type": [ "array", "null" diff --git a/crates/packager/src/config.rs b/crates/packager/src/config.rs index b1b9bcac..d0be420a 100644 --- a/crates/packager/src/config.rs +++ b/crates/packager/src/config.rs @@ -97,6 +97,8 @@ impl ConfigExt for Config { pub(crate) trait ConfigExtInternal { fn main_binary(&self) -> crate::Result<&Binary>; fn main_binary_name(&self) -> crate::Result<&String>; + fn resources_from_dir(src_dir: &PathBuf, target_dir: &Path) -> crate::Result>; + fn resources_from_glob(glob: &str) -> crate::Result>; fn resources(&self) -> crate::Result>; fn find_ico(&self) -> Option; fn copy_resources(&self, path: &Path) -> crate::Result<()>; @@ -119,58 +121,66 @@ impl ConfigExtInternal for Config { .ok_or_else(|| crate::Error::MainBinaryNotFound) } - fn resources(&self) -> crate::Result> { - if let Some(resources) = &self.resources { - let cwd = std::env::current_dir()?; - let cwd = dunce::simplified(&cwd); - - #[inline] - fn create_resources_from_dir( - src: &PathBuf, - target: &Path, - ) -> crate::Result> { - let mut out = Vec::new(); - for entry in walkdir::WalkDir::new(src) { - let entry = entry?; - let path = entry.path(); - if path.is_file() { - let relative = path.relative_to(src)?.to_path(""); - let resource = IResource { - src: dunce::canonicalize(path)?, - target: target.join(relative), - }; - out.push(resource); - } - } - Ok(out) + #[inline] + fn resources_from_dir(src_dir: &PathBuf, target_dir: &Path) -> crate::Result> { + let mut out = Vec::new(); + for entry in walkdir::WalkDir::new(src_dir) { + let entry = entry?; + let path = entry.path(); + if path.is_file() { + let relative = path.relative_to(src_dir)?.to_path(""); + let resource = IResource { + src: dunce::canonicalize(path)?, + target: target_dir.join(relative), + }; + out.push(resource); } + } + Ok(out) + } + + #[inline] + fn resources_from_glob(glob: &str) -> crate::Result> { + let mut out = Vec::new(); + for src in glob::glob(glob).unwrap() { + let src = dunce::canonicalize(src?)?; + let target = PathBuf::from(src.file_name().unwrap()); + out.push(IResource { src, target }) + } + Ok(out) + } + fn resources(&self) -> crate::Result> { + if let Some(resources) = &self.resources { let mut out = Vec::new(); for r in resources { match r { Resource::Single(src) => { - let src_p = PathBuf::from(src); - if src_p.is_dir() { - out.extend(create_resources_from_dir(&src_p, &src_p)?); + let src_dir = PathBuf::from(src); + if src_dir.is_dir() { + let target_dir = Path::new(src_dir.file_name().unwrap()); + out.extend(Self::resources_from_dir(&src_dir, target_dir)?); } else { - for src in glob::glob(src).unwrap() { - let src = src?; - let src = dunce::canonicalize(src)?; - let target = src.relative_to(cwd)?; - out.push(IResource { - src, - target: target.to_path(""), - }) - } + out.extend(Self::resources_from_glob(&src)?); } } Resource::Mapped { src, target } => { - let src = dunce::canonicalize(src)?; - let target = PathBuf::from(target); - if src.is_file() { - out.push(IResource { src, target }) - } else if src.is_dir() { - out.extend(create_resources_from_dir(&src, &target)?); + let src_path = PathBuf::from(src); + let target_dir = sanitize_path(target); + if src_path.is_dir() { + out.extend(Self::resources_from_dir(&src_path, &target_dir)?); + } else if src_path.is_file() { + out.push(IResource { + src: dunce::canonicalize(src_path)?, + target: sanitize_path(target), + }); + } else { + let globbed_res = Self::resources_from_glob(&src)?; + let retargetd_res = globbed_res.into_iter().map(|mut r| { + r.target = target_dir.join(r.target); + r + }); + out.extend(retargetd_res); } } } @@ -224,3 +234,14 @@ impl ConfigExtInternal for Config { Ok(()) } } + +fn sanitize_path>(path: P) -> PathBuf { + let mut dest = PathBuf::new(); + for c in path.as_ref().components() { + match c { + std::path::Component::Normal(s) => dest.push(s), + _ => {} + } + } + dest +} diff --git a/crates/packager/src/nsis/installer.nsi b/crates/packager/src/nsis/installer.nsi index 9c8c921d..7a0345b1 100644 --- a/crates/packager/src/nsis/installer.nsi +++ b/crates/packager/src/nsis/installer.nsi @@ -468,10 +468,14 @@ Section Install ${GetSize} "$INSTDIR" "/M=${MAINBINARYNAME}.exe /S=0B" $0 $1 $2 IntOp $AppSize $AppSize + $0 + ; Create resources directory structure + {{#each resources_dirs}} + CreateDirectory "$INSTDIR\\{{this}}" + {{/each}} + ; Copy resources {{#each resources}} - CreateDirectory "$INSTDIR\\{{this.[0]}}" - File /a "/oname={{this.[1]}}" "{{@key}}" + File /a "/oname={{this.[1]}}" "{{this.[0]}}" ${GetSize} "$INSTDIR" "/M={{this.[1]}} /S=0B" $0 $1 $2 IntOp $AppSize $AppSize + $0 {{/each}} diff --git a/crates/packager/src/nsis/mod.rs b/crates/packager/src/nsis/mod.rs index 5d721ae3..9bfebf39 100644 --- a/crates/packager/src/nsis/mod.rs +++ b/crates/packager/src/nsis/mod.rs @@ -1,5 +1,5 @@ use std::{ - collections::{BTreeMap, HashMap}, + collections::{BTreeMap, BTreeSet, HashMap}, path::{Path, PathBuf}, process::Command, }; @@ -42,25 +42,21 @@ const NSIS_REQUIRED_FILES: &[&str] = &[ "Plugins/x86-unicode/nsis_tauri_utils.dll", ]; -/// BTreeMap -type ResourcesMap = BTreeMap; -fn generate_resource_data(config: &Config) -> crate::Result { - let mut resources_map = ResourcesMap::new(); - for resource in config.resources()? { - resources_map.insert( - resource.src, - ( - resource - .target - .parent() - .map(|p| p.to_string_lossy().to_string()) - .unwrap_or_default(), - resource.target, - ), +fn generate_resource_data( + config: &Config, +) -> crate::Result<(BTreeSet, Vec<(PathBuf, PathBuf)>)> { + let mut directories = BTreeSet::new(); + let mut resources_map = Vec::new(); + for r in config.resources()? { + directories.insert( + r.target + .parent() + .unwrap_or_else(|| Path::new("")) + .to_path_buf(), ); + resources_map.push((r.src, r.target)) } - - Ok(resources_map) + Ok((directories, resources_map)) } /// BTreeMap @@ -84,14 +80,12 @@ fn generate_binaries_data(config: &Config) -> crate::Result { for bin in &config.binaries { if !bin.main { let bin_path = config.binary_path(bin); - binaries.insert( - bin_path.clone(), - bin_path - .file_name() - .expect("failed to extract binary filename") - .to_string_lossy() - .to_string(), - ); + let dest_filename = bin_path + .file_name() + .expect("failed to extract binary filename") + .to_string_lossy() + .to_string(); + binaries.insert(bin_path, dest_filename); } } @@ -390,7 +384,8 @@ fn build_nsis_app_installer( let out_file = "nsis-output.exe"; data.insert("out_file", to_json(out_file)); - let resources = generate_resource_data(config)?; + let (resources_dirs, resources) = generate_resource_data(config)?; + data.insert("resources_dirs", to_json(resources_dirs)); data.insert("resources", to_json(resources)); let binaries = generate_binaries_data(config)?; diff --git a/crates/packager/src/wix/mod.rs b/crates/packager/src/wix/mod.rs index 679b433a..1fbe03e8 100644 --- a/crates/packager/src/wix/mod.rs +++ b/crates/packager/src/wix/mod.rs @@ -318,36 +318,6 @@ fn generate_resource_data(config: &Config) -> crate::Result { directory_entry.add_file(resource_entry); } - let mut dlls = Vec::new(); - - for dll in glob::glob( - config - .out_dir() - .join("*.dll") - .to_string_lossy() - .to_string() - .as_str(), - )? { - let path = dll?; - dlls.push(ResourceFile { - id: format!("I{}", Uuid::new_v4().as_simple()), - guid: Uuid::new_v4().to_string(), - path: dunce::simplified(&path).to_path_buf(), - }); - } - - if !dlls.is_empty() { - resources_map.insert( - "".to_string(), - ResourceDirectory { - path: "".to_string(), - name: "".to_string(), - directories: vec![], - files: dlls, - }, - ); - } - Ok(resources_map) } @@ -564,7 +534,7 @@ fn build_wix_app_installer( data.insert("resources", to_json(resources_wix_string)); data.insert("resource_file_ids", to_json(files_ids)); - // TODO: how to choose to include merge modules + // TODO: how to include merge modules data.insert( "app_exe_source", diff --git a/examples/tauri/Cargo.toml b/examples/tauri/Cargo.toml index 371fc339..928714cd 100644 --- a/examples/tauri/Cargo.toml +++ b/examples/tauri/Cargo.toml @@ -9,10 +9,12 @@ product-name = "Tauri example" identifier = "com.tauri.example" resources = [ "Cargo.toml", - "README.md", + "../../README.md", "icons/*", "src", - { src = "src-ui", target = "public" }, + { src = "src-ui/assets/tauri.svg", target = "path/to/tauri.svg" }, + { src = "src-ui", target = "path/to/src-ui" }, + { src = "src-ui/assets/*", target = "public" }, ] icons = [ "icons/32x32.png",