|
@@ -39,6 +39,15 @@ use std::{
|
|
|
process::Command,
|
|
|
};
|
|
|
|
|
|
+const NESTED_CODE_FOLDER: [&str; 6] = [
|
|
|
+ "MacOS",
|
|
|
+ "Frameworks",
|
|
|
+ "Plugins",
|
|
|
+ "Helpers",
|
|
|
+ "XPCServices",
|
|
|
+ "Libraries",
|
|
|
+];
|
|
|
+
|
|
|
/// Bundles the project.
|
|
|
/// Returns a vector of PathBuf that shows where the .app was created.
|
|
|
pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
|
|
@@ -77,18 +86,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
|
|
|
|
|
|
let framework_paths = copy_frameworks_to_bundle(&bundle_directory, settings)
|
|
|
.with_context(|| "Failed to bundle frameworks")?;
|
|
|
- sign_paths.extend(
|
|
|
- framework_paths
|
|
|
- .into_iter()
|
|
|
- .filter(|p| {
|
|
|
- let ext = p.extension();
|
|
|
- ext == Some(OsStr::new("framework")) || ext == Some(OsStr::new("dylib"))
|
|
|
- })
|
|
|
- .map(|path| SignTarget {
|
|
|
- path,
|
|
|
- is_an_executable: false,
|
|
|
- }),
|
|
|
- );
|
|
|
+ sign_paths.extend(framework_paths);
|
|
|
|
|
|
settings.copy_resources(&resources_dir)?;
|
|
|
|
|
@@ -141,7 +139,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
|
|
|
|
|
|
fn remove_extra_attr(app_bundle_path: &Path) -> crate::Result<()> {
|
|
|
Command::new("xattr")
|
|
|
- .arg("-cr")
|
|
|
+ .arg("-crs")
|
|
|
.arg(app_bundle_path)
|
|
|
.output_ok()
|
|
|
.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
|
|
|
fn copy_frameworks_to_bundle(
|
|
|
bundle_directory: &Path,
|
|
|
settings: &Settings,
|
|
|
-) -> crate::Result<Vec<PathBuf>> {
|
|
|
+) -> crate::Result<Vec<SignTarget>> {
|
|
|
let mut paths = Vec::new();
|
|
|
|
|
|
let frameworks = settings
|
|
@@ -288,7 +286,7 @@ fn copy_frameworks_to_bundle(
|
|
|
.expect("Couldn't get framework filename");
|
|
|
let dest_path = dest_dir.join(src_name);
|
|
|
common::copy_dir(&src_path, &dest_path)?;
|
|
|
- paths.push(dest_path);
|
|
|
+ add_framework_sign_path(&src_path, &dest_path, &mut paths);
|
|
|
continue;
|
|
|
} else if framework.ends_with(".dylib") {
|
|
|
let src_path = PathBuf::from(framework);
|
|
@@ -301,7 +299,10 @@ fn copy_frameworks_to_bundle(
|
|
|
let src_name = src_path.file_name().expect("Couldn't get library filename");
|
|
|
let dest_path = dest_dir.join(src_name);
|
|
|
common::copy_file(&src_path, &dest_path)?;
|
|
|
- paths.push(dest_path);
|
|
|
+ paths.push(SignTarget {
|
|
|
+ path: dest_path,
|
|
|
+ is_an_executable: false,
|
|
|
+ });
|
|
|
continue;
|
|
|
} else if framework.contains('/') {
|
|
|
return Err(crate::Error::GenericError(format!(
|
|
@@ -330,3 +331,90 @@ fn copy_frameworks_to_bundle(
|
|
|
}
|
|
|
Ok(paths)
|
|
|
}
|
|
|
+
|
|
|
+/// Recursively add framework's sign paths.
|
|
|
+/// If the framework has multiple versions, it will sign "Current" version by default.
|
|
|
+fn add_framework_sign_path(
|
|
|
+ framework_root: &Path,
|
|
|
+ dest_path: &Path,
|
|
|
+ sign_paths: &mut Vec<SignTarget>,
|
|
|
+) {
|
|
|
+ if framework_root.join("Versions/Current").exists() {
|
|
|
+ add_nested_code_sign_path(
|
|
|
+ &framework_root.join("Versions/Current"),
|
|
|
+ &dest_path.join("Versions/Current"),
|
|
|
+ sign_paths,
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ add_nested_code_sign_path(framework_root, dest_path, sign_paths);
|
|
|
+ }
|
|
|
+ sign_paths.push(SignTarget {
|
|
|
+ path: dest_path.into(),
|
|
|
+ is_an_executable: false,
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+/// Recursively add executable bundle's sign path (.xpc, .app).
|
|
|
+fn add_executable_bundle_sign_path(
|
|
|
+ bundle_root: &Path,
|
|
|
+ dest_path: &Path,
|
|
|
+ sign_paths: &mut Vec<SignTarget>,
|
|
|
+) {
|
|
|
+ if bundle_root.join("Contents").exists() {
|
|
|
+ add_nested_code_sign_path(
|
|
|
+ &bundle_root.join("Contents"),
|
|
|
+ &dest_path.join("Contents"),
|
|
|
+ sign_paths,
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ add_nested_code_sign_path(bundle_root, dest_path, sign_paths);
|
|
|
+ }
|
|
|
+ sign_paths.push(SignTarget {
|
|
|
+ path: dest_path.into(),
|
|
|
+ is_an_executable: true,
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+fn add_nested_code_sign_path(src_path: &Path, dest_path: &Path, sign_paths: &mut Vec<SignTarget>) {
|
|
|
+ for folder_name in NESTED_CODE_FOLDER.iter() {
|
|
|
+ let src_folder_path = src_path.join(folder_name);
|
|
|
+ let dest_folder_path = dest_path.join(folder_name);
|
|
|
+
|
|
|
+ if src_folder_path.exists() {
|
|
|
+ for entry in walkdir::WalkDir::new(src_folder_path)
|
|
|
+ .min_depth(1)
|
|
|
+ .max_depth(1)
|
|
|
+ .into_iter()
|
|
|
+ .filter_map(|e| e.ok())
|
|
|
+ {
|
|
|
+ if entry.path_is_symlink() || entry.file_name().to_string_lossy().starts_with('.') {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ let dest_path = dest_folder_path.join(entry.file_name());
|
|
|
+ let ext = entry.path().extension();
|
|
|
+ if entry.path().is_dir() {
|
|
|
+ // Bundles, like .app, .framework, .xpc
|
|
|
+ if ext == Some(OsStr::new("framework")) {
|
|
|
+ add_framework_sign_path(&entry.clone().into_path(), &dest_path, sign_paths);
|
|
|
+ } else if ext == Some(OsStr::new("xpc")) || ext == Some(OsStr::new("app")) {
|
|
|
+ add_executable_bundle_sign_path(&entry.clone().into_path(), &dest_path, sign_paths);
|
|
|
+ }
|
|
|
+ } else if entry.path().is_file() {
|
|
|
+ // Binaries, like .dylib, Mach-O executables
|
|
|
+ if ext == Some(OsStr::new("dylib")) {
|
|
|
+ sign_paths.push(SignTarget {
|
|
|
+ path: dest_path,
|
|
|
+ is_an_executable: false,
|
|
|
+ });
|
|
|
+ } else if ext.is_none() {
|
|
|
+ sign_paths.push(SignTarget {
|
|
|
+ path: dest_path,
|
|
|
+ is_an_executable: true,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|