Skip to content

Commit 8991129

Browse files
feat(bundler): codesign nested code on macos (#8259)
* feat(bundler): codesign nested code on macos * chore: update changelog tag * typo * also sign stuff in the Libraries folder tested this for spacedrive, which has a bunch of dylib inside the libraries folder * Update .changes/mac-bundler-nested-code-sign.md [skip ci] --------- Co-authored-by: Lucas Nogueira <lucas@tauri.app>
1 parent 8f8729d commit 8991129

File tree

2 files changed

+110
-16
lines changed

2 files changed

+110
-16
lines changed
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"tauri-cli": patch:feat
3+
"tauri-bundler": patch:feat
4+
---
5+
6+
On macOS, support for signing nested .dylib, .app, .xpc and .framework under predefined directories inside the bundled frameworks ("MacOS", "Frameworks", "Plugins", "Helpers", "XPCServices" and "Libraries").

tooling/bundler/src/bundle/macos/app.rs

+104-16
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ use std::{
3939
process::Command,
4040
};
4141

42+
const NESTED_CODE_FOLDER: [&str; 6] = [
43+
"MacOS",
44+
"Frameworks",
45+
"Plugins",
46+
"Helpers",
47+
"XPCServices",
48+
"Libraries",
49+
];
50+
4251
/// Bundles the project.
4352
/// Returns a vector of PathBuf that shows where the .app was created.
4453
pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
@@ -77,18 +86,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
7786

7887
let framework_paths = copy_frameworks_to_bundle(&bundle_directory, settings)
7988
.with_context(|| "Failed to bundle frameworks")?;
80-
sign_paths.extend(
81-
framework_paths
82-
.into_iter()
83-
.filter(|p| {
84-
let ext = p.extension();
85-
ext == Some(OsStr::new("framework")) || ext == Some(OsStr::new("dylib"))
86-
})
87-
.map(|path| SignTarget {
88-
path,
89-
is_an_executable: false,
90-
}),
91-
);
89+
sign_paths.extend(framework_paths);
9290

9391
settings.copy_resources(&resources_dir)?;
9492

@@ -141,7 +139,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
141139

142140
fn remove_extra_attr(app_bundle_path: &Path) -> crate::Result<()> {
143141
Command::new("xattr")
144-
.arg("-cr")
142+
.arg("-crs")
145143
.arg(app_bundle_path)
146144
.output_ok()
147145
.context("failed to remove extra attributes from app bundle")?;
@@ -265,7 +263,7 @@ fn copy_framework_from(dest_dir: &Path, framework: &str, src_dir: &Path) -> crat
265263
fn copy_frameworks_to_bundle(
266264
bundle_directory: &Path,
267265
settings: &Settings,
268-
) -> crate::Result<Vec<PathBuf>> {
266+
) -> crate::Result<Vec<SignTarget>> {
269267
let mut paths = Vec::new();
270268

271269
let frameworks = settings
@@ -288,7 +286,7 @@ fn copy_frameworks_to_bundle(
288286
.expect("Couldn't get framework filename");
289287
let dest_path = dest_dir.join(src_name);
290288
common::copy_dir(&src_path, &dest_path)?;
291-
paths.push(dest_path);
289+
add_framework_sign_path(&src_path, &dest_path, &mut paths);
292290
continue;
293291
} else if framework.ends_with(".dylib") {
294292
let src_path = PathBuf::from(framework);
@@ -301,7 +299,10 @@ fn copy_frameworks_to_bundle(
301299
let src_name = src_path.file_name().expect("Couldn't get library filename");
302300
let dest_path = dest_dir.join(src_name);
303301
common::copy_file(&src_path, &dest_path)?;
304-
paths.push(dest_path);
302+
paths.push(SignTarget {
303+
path: dest_path,
304+
is_an_executable: false,
305+
});
305306
continue;
306307
} else if framework.contains('/') {
307308
return Err(crate::Error::GenericError(format!(
@@ -330,3 +331,90 @@ fn copy_frameworks_to_bundle(
330331
}
331332
Ok(paths)
332333
}
334+
335+
/// Recursively add framework's sign paths.
336+
/// If the framework has multiple versions, it will sign "Current" version by default.
337+
fn add_framework_sign_path(
338+
framework_root: &Path,
339+
dest_path: &Path,
340+
sign_paths: &mut Vec<SignTarget>,
341+
) {
342+
if framework_root.join("Versions/Current").exists() {
343+
add_nested_code_sign_path(
344+
&framework_root.join("Versions/Current"),
345+
&dest_path.join("Versions/Current"),
346+
sign_paths,
347+
);
348+
} else {
349+
add_nested_code_sign_path(framework_root, dest_path, sign_paths);
350+
}
351+
sign_paths.push(SignTarget {
352+
path: dest_path.into(),
353+
is_an_executable: false,
354+
});
355+
}
356+
357+
/// Recursively add executable bundle's sign path (.xpc, .app).
358+
fn add_executable_bundle_sign_path(
359+
bundle_root: &Path,
360+
dest_path: &Path,
361+
sign_paths: &mut Vec<SignTarget>,
362+
) {
363+
if bundle_root.join("Contents").exists() {
364+
add_nested_code_sign_path(
365+
&bundle_root.join("Contents"),
366+
&dest_path.join("Contents"),
367+
sign_paths,
368+
);
369+
} else {
370+
add_nested_code_sign_path(bundle_root, dest_path, sign_paths);
371+
}
372+
sign_paths.push(SignTarget {
373+
path: dest_path.into(),
374+
is_an_executable: true,
375+
});
376+
}
377+
378+
fn add_nested_code_sign_path(src_path: &Path, dest_path: &Path, sign_paths: &mut Vec<SignTarget>) {
379+
for folder_name in NESTED_CODE_FOLDER.iter() {
380+
let src_folder_path = src_path.join(folder_name);
381+
let dest_folder_path = dest_path.join(folder_name);
382+
383+
if src_folder_path.exists() {
384+
for entry in walkdir::WalkDir::new(src_folder_path)
385+
.min_depth(1)
386+
.max_depth(1)
387+
.into_iter()
388+
.filter_map(|e| e.ok())
389+
{
390+
if entry.path_is_symlink() || entry.file_name().to_string_lossy().starts_with('.') {
391+
continue;
392+
}
393+
394+
let dest_path = dest_folder_path.join(entry.file_name());
395+
let ext = entry.path().extension();
396+
if entry.path().is_dir() {
397+
// Bundles, like .app, .framework, .xpc
398+
if ext == Some(OsStr::new("framework")) {
399+
add_framework_sign_path(&entry.clone().into_path(), &dest_path, sign_paths);
400+
} else if ext == Some(OsStr::new("xpc")) || ext == Some(OsStr::new("app")) {
401+
add_executable_bundle_sign_path(&entry.clone().into_path(), &dest_path, sign_paths);
402+
}
403+
} else if entry.path().is_file() {
404+
// Binaries, like .dylib, Mach-O executables
405+
if ext == Some(OsStr::new("dylib")) {
406+
sign_paths.push(SignTarget {
407+
path: dest_path,
408+
is_an_executable: false,
409+
});
410+
} else if ext.is_none() {
411+
sign_paths.push(SignTarget {
412+
path: dest_path,
413+
is_an_executable: true,
414+
});
415+
}
416+
}
417+
}
418+
}
419+
}
420+
}

0 commit comments

Comments
 (0)