@@ -39,6 +39,15 @@ use std::{
39
39
process:: Command ,
40
40
} ;
41
41
42
+ const NESTED_CODE_FOLDER : [ & str ; 6 ] = [
43
+ "MacOS" ,
44
+ "Frameworks" ,
45
+ "Plugins" ,
46
+ "Helpers" ,
47
+ "XPCServices" ,
48
+ "Libraries" ,
49
+ ] ;
50
+
42
51
/// Bundles the project.
43
52
/// Returns a vector of PathBuf that shows where the .app was created.
44
53
pub fn bundle_project ( settings : & Settings ) -> crate :: Result < Vec < PathBuf > > {
@@ -77,18 +86,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
77
86
78
87
let framework_paths = copy_frameworks_to_bundle ( & bundle_directory, settings)
79
88
. 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) ;
92
90
93
91
settings. copy_resources ( & resources_dir) ?;
94
92
@@ -141,7 +139,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<PathBuf>> {
141
139
142
140
fn remove_extra_attr ( app_bundle_path : & Path ) -> crate :: Result < ( ) > {
143
141
Command :: new ( "xattr" )
144
- . arg ( "-cr " )
142
+ . arg ( "-crs " )
145
143
. arg ( app_bundle_path)
146
144
. output_ok ( )
147
145
. 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
265
263
fn copy_frameworks_to_bundle (
266
264
bundle_directory : & Path ,
267
265
settings : & Settings ,
268
- ) -> crate :: Result < Vec < PathBuf > > {
266
+ ) -> crate :: Result < Vec < SignTarget > > {
269
267
let mut paths = Vec :: new ( ) ;
270
268
271
269
let frameworks = settings
@@ -288,7 +286,7 @@ fn copy_frameworks_to_bundle(
288
286
. expect ( "Couldn't get framework filename" ) ;
289
287
let dest_path = dest_dir. join ( src_name) ;
290
288
common:: copy_dir ( & src_path, & dest_path) ?;
291
- paths . push ( dest_path) ;
289
+ add_framework_sign_path ( & src_path , & dest_path, & mut paths ) ;
292
290
continue ;
293
291
} else if framework. ends_with ( ".dylib" ) {
294
292
let src_path = PathBuf :: from ( framework) ;
@@ -301,7 +299,10 @@ fn copy_frameworks_to_bundle(
301
299
let src_name = src_path. file_name ( ) . expect ( "Couldn't get library filename" ) ;
302
300
let dest_path = dest_dir. join ( src_name) ;
303
301
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
+ } ) ;
305
306
continue ;
306
307
} else if framework. contains ( '/' ) {
307
308
return Err ( crate :: Error :: GenericError ( format ! (
@@ -330,3 +331,90 @@ fn copy_frameworks_to_bundle(
330
331
}
331
332
Ok ( paths)
332
333
}
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