@@ -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.
4453pub 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
142140fn 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
265263fn 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