Pārlūkot izejas kodu

fix(core): Incorrect resource_dir when app run from new style target/PLATFORM+ARCH/(debug|release) dir (#8852)

* Fix resource_dir when app run from new target/someting/(debug|release) dir

* Update core/tauri-utils/src/platform.rs

Co-authored-by: Amr Bashir <amr.bashir2015@gmail.com>

* change file, update logic, add tests

* lint

* fix tests

---------

Co-authored-by: Amr Bashir <amr.bashir2015@gmail.com>
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
Sam Willis 1 gadu atpakaļ
vecāks
revīzija
28fb036ce4

+ 5 - 0
.changes/enhance-resource-dir-resolution.md

@@ -0,0 +1,5 @@
+---
+"tauri-utils": patch:enhance
+---
+
+Enhance resource directory resolution on development.

+ 8 - 1
core/tauri-config-schema/schema.json

@@ -1095,7 +1095,14 @@
           ]
         },
         "windows": {
-          "description": "List of windows that uses this capability. Can be a glob pattern.",
+          "description": "List of windows that uses this capability. Can be a glob pattern.\n\nOn multiwebview windows, prefer [`Self::webviews`] for a fine grained access control.",
+          "type": "array",
+          "items": {
+            "type": "string"
+          }
+        },
+        "webviews": {
+          "description": "List of webviews that uses this capability. Can be a glob pattern.\n\nThis is only required when using on multiwebview contexts, by default all child webviews of a window that matches [`Self::windows`] are linked.",
           "type": "array",
           "items": {
             "type": "string"

+ 84 - 7
core/tauri-utils/src/platform.rs

@@ -6,7 +6,7 @@
 
 use std::{
   fmt::Display,
-  path::{PathBuf, MAIN_SEPARATOR},
+  path::{Path, PathBuf, MAIN_SEPARATOR},
 };
 
 use serde::{Deserialize, Serialize};
@@ -221,6 +221,28 @@ pub fn target_triple() -> crate::Result<String> {
   Ok(format!("{arch}-{os}"))
 }
 
+#[cfg(not(test))]
+fn is_cargo_output_directory(path: &Path) -> bool {
+  path.join(".cargo-lock").exists()
+}
+
+#[cfg(test)]
+const CARGO_OUTPUT_DIRECTORIES: &[&str] = &["debug", "release", "custom-profile"];
+
+#[cfg(test)]
+fn is_cargo_output_directory(path: &Path) -> bool {
+  let last_component = path
+    .components()
+    .last()
+    .unwrap()
+    .as_os_str()
+    .to_str()
+    .unwrap();
+  CARGO_OUTPUT_DIRECTORIES
+    .iter()
+    .any(|dirname| &last_component == dirname)
+}
+
 /// Computes the resource directory of the current environment.
 ///
 /// On Windows, it's the path to the executable.
@@ -233,17 +255,32 @@ pub fn target_triple() -> crate::Result<String> {
 /// `${exe_dir}/../lib/${exe_name}`.
 ///
 /// On MacOS, it's `${exe_dir}../Resources` (inside .app).
-#[allow(unused_variables)]
 pub fn resource_dir(package_info: &PackageInfo, env: &Env) -> crate::Result<PathBuf> {
   let exe = current_exe()?;
-  let exe_dir = exe.parent().expect("failed to get exe directory");
+  resource_dir_from(exe, package_info, env)
+}
+
+#[allow(unused_variables)]
+fn resource_dir_from<P: AsRef<Path>>(
+  exe: P,
+  package_info: &PackageInfo,
+  env: &Env,
+) -> crate::Result<PathBuf> {
+  let exe_dir = exe.as_ref().parent().expect("failed to get exe directory");
   let curr_dir = exe_dir.display().to_string();
 
-  if curr_dir.ends_with(format!("{MAIN_SEPARATOR}target{MAIN_SEPARATOR}debug").as_str())
-    || curr_dir.ends_with(format!("{MAIN_SEPARATOR}target{MAIN_SEPARATOR}release").as_str())
-    || cfg!(target_os = "windows")
+  let parts: Vec<&str> = curr_dir.split(MAIN_SEPARATOR).collect();
+  let len = parts.len();
+
+  // Check if running from the Cargo output directory, which means it's an executable in a development machine
+  // We check if the binary is inside a `target` folder which can be either `target/$profile` or `target/$triple/$profile`
+  // and see if there's a .cargo-lock file along the executable
+  // This ensures the check is safer so it doesn't affect apps in production
+  // Windows also includes the resources in the executable folder so we check that too
+  if cfg!(target_os = "windows")
+    || ((len >= 2 && parts[len - 2] == "target") || (len >= 3 && parts[len - 3] == "target"))
+      && is_cargo_output_directory(exe_dir)
   {
-    // running from the out dir or windows
     return Ok(exe_dir.to_path_buf());
   }
 
@@ -306,3 +343,43 @@ mod build {
     }
   }
 }
+
+#[cfg(test)]
+mod tests {
+  use std::path::PathBuf;
+
+  use crate::{Env, PackageInfo};
+
+  #[test]
+  fn resolve_resource_dir() {
+    let package_info = PackageInfo {
+      name: "MyApp".into(),
+      version: "1.0.0".parse().unwrap(),
+      authors: "",
+      description: "",
+      crate_name: "",
+    };
+    let env = Env::default();
+
+    let path = PathBuf::from("/path/to/target/aarch64-apple-darwin/debug/app");
+    let resource_dir = super::resource_dir_from(&path, &package_info, &env).unwrap();
+    assert_eq!(resource_dir, path.parent().unwrap());
+
+    let path = PathBuf::from("/path/to/target/custom-profile/app");
+    let resource_dir = super::resource_dir_from(&path, &package_info, &env).unwrap();
+    assert_eq!(resource_dir, path.parent().unwrap());
+
+    let path = PathBuf::from("/path/to/target/release/app");
+    let resource_dir = super::resource_dir_from(&path, &package_info, &env).unwrap();
+    assert_eq!(resource_dir, path.parent().unwrap());
+
+    let path = PathBuf::from("/path/to/target/unknown-profile/app");
+    let resource_dir = super::resource_dir_from(&path, &package_info, &env);
+    #[cfg(target_os = "macos")]
+    assert!(resource_dir.is_err());
+    #[cfg(target_os = "linux")]
+    assert_eq!(resource_dir.unwrap(), PathBuf::from("/usr/lib/my-app"));
+    #[cfg(windows)]
+    assert_eq!(resource_dir.unwrap(), path.parent().unwrap());
+  }
+}

+ 8 - 1
tooling/cli/schema.json

@@ -1095,7 +1095,14 @@
           ]
         },
         "windows": {
-          "description": "List of windows that uses this capability. Can be a glob pattern.",
+          "description": "List of windows that uses this capability. Can be a glob pattern.\n\nOn multiwebview windows, prefer [`Self::webviews`] for a fine grained access control.",
+          "type": "array",
+          "items": {
+            "type": "string"
+          }
+        },
+        "webviews": {
+          "description": "List of webviews that uses this capability. Can be a glob pattern.\n\nThis is only required when using on multiwebview contexts, by default all child webviews of a window that matches [`Self::windows`] are linked.",
           "type": "array",
           "items": {
             "type": "string"