|
4 | 4 |
|
5 | 5 | #![cfg_attr(doc_cfg, feature(doc_cfg))] |
6 | 6 |
|
| 7 | +use anyhow::Context; |
7 | 8 | pub use anyhow::Result; |
8 | 9 | use cargo_toml::Manifest; |
9 | 10 | use heck::AsShoutySnakeCase; |
@@ -80,6 +81,113 @@ fn copy_resources(resources: ResourcePaths<'_>, path: &Path) -> Result<()> { |
80 | 81 | Ok(()) |
81 | 82 | } |
82 | 83 |
|
| 84 | +#[cfg(unix)] |
| 85 | +fn symlink_dir(src: &Path, dst: &Path) -> std::io::Result<()> { |
| 86 | + std::os::unix::fs::symlink(src, dst) |
| 87 | +} |
| 88 | + |
| 89 | +/// Makes a symbolic link to a directory. |
| 90 | +#[cfg(windows)] |
| 91 | +fn symlink_dir(src: &Path, dst: &Path) -> std::io::Result<()> { |
| 92 | + std::os::windows::fs::symlink_dir(src, dst) |
| 93 | +} |
| 94 | + |
| 95 | +/// Makes a symbolic link to a file. |
| 96 | +#[cfg(unix)] |
| 97 | +fn symlink_file(src: &Path, dst: &Path) -> std::io::Result<()> { |
| 98 | + std::os::unix::fs::symlink(src, dst) |
| 99 | +} |
| 100 | + |
| 101 | +/// Makes a symbolic link to a file. |
| 102 | +#[cfg(windows)] |
| 103 | +fn symlink_file(src: &Path, dst: &Path) -> std::io::Result<()> { |
| 104 | + std::os::windows::fs::symlink_file(src, dst) |
| 105 | +} |
| 106 | + |
| 107 | +fn copy_dir(from: &Path, to: &Path) -> Result<()> { |
| 108 | + for entry in walkdir::WalkDir::new(from) { |
| 109 | + let entry = entry?; |
| 110 | + debug_assert!(entry.path().starts_with(from)); |
| 111 | + let rel_path = entry.path().strip_prefix(from)?; |
| 112 | + let dest_path = to.join(rel_path); |
| 113 | + if entry.file_type().is_symlink() { |
| 114 | + let target = std::fs::read_link(entry.path())?; |
| 115 | + if entry.path().is_dir() { |
| 116 | + symlink_dir(&target, &dest_path)?; |
| 117 | + } else { |
| 118 | + symlink_file(&target, &dest_path)?; |
| 119 | + } |
| 120 | + } else if entry.file_type().is_dir() { |
| 121 | + std::fs::create_dir(dest_path)?; |
| 122 | + } else { |
| 123 | + std::fs::copy(entry.path(), dest_path)?; |
| 124 | + } |
| 125 | + } |
| 126 | + Ok(()) |
| 127 | +} |
| 128 | + |
| 129 | +// Copies the framework under `{src_dir}/{framework}.framework` to `{dest_dir}/{framework}.framework`. |
| 130 | +fn copy_framework_from(src_dir: &Path, framework: &str, dest_dir: &Path) -> Result<bool> { |
| 131 | + let src_name = format!("{}.framework", framework); |
| 132 | + let src_path = src_dir.join(&src_name); |
| 133 | + if src_path.exists() { |
| 134 | + copy_dir(&src_path, &dest_dir.join(&src_name))?; |
| 135 | + Ok(true) |
| 136 | + } else { |
| 137 | + Ok(false) |
| 138 | + } |
| 139 | +} |
| 140 | + |
| 141 | +// Copies the macOS application bundle frameworks to the target folder |
| 142 | +fn copy_frameworks(dest_dir: &Path, frameworks: &[String]) -> Result<()> { |
| 143 | + std::fs::create_dir_all(dest_dir).with_context(|| { |
| 144 | + format!( |
| 145 | + "Failed to create frameworks output directory at {:?}", |
| 146 | + dest_dir |
| 147 | + ) |
| 148 | + })?; |
| 149 | + for framework in frameworks.iter() { |
| 150 | + if framework.ends_with(".framework") { |
| 151 | + let src_path = PathBuf::from(framework); |
| 152 | + let src_name = src_path |
| 153 | + .file_name() |
| 154 | + .expect("Couldn't get framework filename"); |
| 155 | + let dest_path = dest_dir.join(src_name); |
| 156 | + copy_dir(&src_path, &dest_path)?; |
| 157 | + continue; |
| 158 | + } else if framework.ends_with(".dylib") { |
| 159 | + let src_path = PathBuf::from(framework); |
| 160 | + if !src_path.exists() { |
| 161 | + return Err(anyhow::anyhow!("Library not found: {}", framework)); |
| 162 | + } |
| 163 | + let src_name = src_path.file_name().expect("Couldn't get library filename"); |
| 164 | + let dest_path = dest_dir.join(src_name); |
| 165 | + copy_file(&src_path, &dest_path)?; |
| 166 | + continue; |
| 167 | + } else if framework.contains('/') { |
| 168 | + return Err(anyhow::anyhow!( |
| 169 | + "Framework path should have .framework extension: {}", |
| 170 | + framework |
| 171 | + )); |
| 172 | + } |
| 173 | + if let Some(home_dir) = dirs_next::home_dir() { |
| 174 | + if copy_framework_from(&home_dir.join("Library/Frameworks/"), framework, dest_dir)? { |
| 175 | + continue; |
| 176 | + } |
| 177 | + } |
| 178 | + if copy_framework_from(&PathBuf::from("/Library/Frameworks/"), framework, dest_dir)? |
| 179 | + || copy_framework_from( |
| 180 | + &PathBuf::from("/Network/Library/Frameworks/"), |
| 181 | + framework, |
| 182 | + dest_dir, |
| 183 | + )? |
| 184 | + { |
| 185 | + continue; |
| 186 | + } |
| 187 | + } |
| 188 | + Ok(()) |
| 189 | +} |
| 190 | + |
83 | 191 | // checks if the given Cargo feature is enabled. |
84 | 192 | fn has_feature(feature: &str) -> bool { |
85 | 193 | // when a feature is enabled, Cargo sets the `CARGO_FEATURE_<name` env var to 1 |
@@ -370,13 +478,26 @@ pub fn try_build(attributes: Attributes) -> Result<()> { |
370 | 478 | } |
371 | 479 |
|
372 | 480 | if target_triple.contains("darwin") { |
| 481 | + if let Some(frameworks) = &config.tauri.bundle.macos.frameworks { |
| 482 | + if !frameworks.is_empty() { |
| 483 | + let frameworks_dir = target_dir.parent().unwrap().join("Frameworks"); |
| 484 | + let _ = std::fs::remove_dir_all(&frameworks_dir); |
| 485 | + // copy frameworks to the root `target` folder (instead of `target/debug` for instance) |
| 486 | + // because the rpath is set to `@executable_path/../Frameworks`. |
| 487 | + copy_frameworks(&frameworks_dir, frameworks)?; |
| 488 | + |
| 489 | + // If we have frameworks, we need to set the @rpath |
| 490 | + // https://github.com/tauri-apps/tauri/issues/7710 |
| 491 | + println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path/../Frameworks"); |
| 492 | + } |
| 493 | + } |
| 494 | + |
373 | 495 | if let Some(version) = &config.tauri.bundle.macos.minimum_system_version { |
374 | 496 | println!("cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET={version}"); |
375 | 497 | } |
376 | 498 | } |
377 | 499 |
|
378 | 500 | if target_triple.contains("windows") { |
379 | | - use anyhow::Context; |
380 | 501 | use semver::Version; |
381 | 502 | use tauri_winres::{VersionInfo, WindowsResource}; |
382 | 503 |
|
|
0 commit comments