@@ -3,6 +3,7 @@ use regex::Regex;
3
3
use serde:: { Deserialize , Serialize } ;
4
4
use std:: collections:: { HashMap , HashSet } ;
5
5
use std:: fs;
6
+ use std:: io:: ErrorKind ;
6
7
use std:: path:: { Component , Path , PathBuf } ;
7
8
use std:: process:: Command ;
8
9
use tauri:: { AppHandle , Manager , Window } ;
@@ -28,13 +29,22 @@ pub async fn install_sdk_operation(
28
29
window : Window ,
29
30
xcode_path : String ,
30
31
toolchain_path : String ,
32
+ is_dir : bool ,
31
33
) -> Result < ( ) , String > {
32
34
let op = Operation :: new ( "install_sdk" . to_string ( ) , & window) ;
33
35
op. start ( "create_stage" ) ?;
34
36
let work_dir = op
35
37
. fail_if_err ( "create_stage" , linux_temp_dir ( ) ) ?
36
38
. join ( "DarwinSDKBuild" ) ;
37
- let res = install_sdk_internal ( app, xcode_path, toolchain_path, work_dir. clone ( ) , & op) . await ;
39
+ let res = install_sdk_internal (
40
+ app,
41
+ xcode_path,
42
+ toolchain_path,
43
+ work_dir. clone ( ) ,
44
+ is_dir,
45
+ & op,
46
+ )
47
+ . await ;
38
48
op. start ( "cleanup" ) ?;
39
49
let cleanup_result = if work_dir. exists ( ) {
40
50
remove_dir_all ( & work_dir)
@@ -71,9 +81,10 @@ async fn install_sdk_internal(
71
81
xcode_path : String ,
72
82
toolchain_path : String ,
73
83
work_dir : PathBuf ,
84
+ is_dir : bool ,
74
85
op : & Operation < ' _ > ,
75
86
) -> Result < ( ) , String > {
76
- if xcode_path. is_empty ( ) || !xcode_path. ends_with ( ".xip" ) {
87
+ if xcode_path. is_empty ( ) || ( !xcode_path. ends_with ( ".xip" ) && !is_dir ) {
77
88
return op. fail ( "create_stage" , "Xcode not found" . to_string ( ) ) ;
78
89
}
79
90
if toolchain_path. is_empty ( ) {
@@ -114,7 +125,7 @@ async fn install_sdk_internal(
114
125
op. move_on ( "create_stage" , "install_toolset" ) ?;
115
126
op. fail_if_err ( "install_toolset" , install_toolset ( & output_dir) . await ) ?;
116
127
op. complete ( "install_toolset" ) ?;
117
- let dev = install_developer ( & app, & output_dir, & xcode_path, op) . await ?;
128
+ let dev = install_developer ( & app, & output_dir, & xcode_path, is_dir , op) . await ?;
118
129
op. start ( "write_metadata" ) ?;
119
130
120
131
let iphone_os_sdk = sdk ( & dev, "iPhoneOS" ) ?;
@@ -307,72 +318,78 @@ async fn install_developer(
307
318
app : & AppHandle ,
308
319
output_path : & PathBuf ,
309
320
xcode_path : & str ,
321
+ is_dir : bool ,
310
322
op : & Operation < ' _ > ,
311
323
) -> Result < PathBuf , String > {
312
324
op. start ( "extract_xip" ) ?;
313
- let dev_stage = output_path. join ( "DeveloperStage" ) ;
314
- op. fail_if_err_map ( "extract_xip" , fs:: create_dir_all ( & dev_stage) , |e| {
315
- format ! ( "Failed to create DeveloperStage directory: {}" , e)
316
- } ) ?;
317
325
318
- let unxip_path = op . fail_if_err_map (
319
- "extract_xip" ,
320
- app . path ( )
321
- . resolve ( "unxip ", tauri :: path :: BaseDirectory :: Resource ) ,
322
- |e| format ! ( "Failed to resolve unxip path : {}" , e) ,
323
- ) ?;
326
+ let dev_stage = output_path . join ( "DeveloperStage" ) ;
327
+ let mut app_path = PathBuf :: from ( xcode_path ) ;
328
+ if !is_dir {
329
+ op . fail_if_err_map ( "extract_xip ", fs :: create_dir_all ( & dev_stage ) , |e| {
330
+ format ! ( "Failed to create DeveloperStage directory : {}" , e)
331
+ } ) ?;
324
332
325
- #[ cfg( target_os = "windows" ) ]
326
- let status = Command :: new ( "wsl" )
327
- . arg ( "bash" )
328
- . arg ( "-c" )
329
- . arg ( format ! (
330
- "{} {} {}" ,
331
- windows_to_wsl_path( & unxip_path. to_string_lossy( ) ) ?,
332
- windows_to_wsl_path( & xcode_path) ?,
333
- windows_to_wsl_path( & dev_stage. to_string_lossy( ) ) ?
334
- ) )
335
- . creation_flags ( CREATE_NO_WINDOW )
336
- . output ( ) ;
337
- #[ cfg( not( target_os = "windows" ) ) ]
338
- let status = Command :: new ( unxip_path)
339
- . current_dir ( & dev_stage)
340
- . arg ( xcode_path)
341
- . output ( ) ;
342
- if let Err ( e) = status {
343
- return op. fail ( "extract_xip" , format ! ( "Failed to run unxip: {}" , e) ) ;
344
- }
345
- let status = status. unwrap ( ) ;
346
- if !status. status . success ( ) {
347
- return op. fail (
333
+ let unxip_path = op. fail_if_err_map (
348
334
"extract_xip" ,
349
- format ! (
350
- "{}\n Process exited with code {}" ,
351
- String :: from_utf8_lossy( & status. stderr. trim_ascii( ) ) ,
352
- status. status. code( ) . unwrap_or( 0 )
353
- ) ,
354
- ) ;
355
- }
335
+ app. path ( )
336
+ . resolve ( "unxip" , tauri:: path:: BaseDirectory :: Resource ) ,
337
+ |e| format ! ( "Failed to resolve unxip path: {}" , e) ,
338
+ ) ?;
339
+
340
+ #[ cfg( target_os = "windows" ) ]
341
+ let status = Command :: new ( "wsl" )
342
+ . arg ( "bash" )
343
+ . arg ( "-c" )
344
+ . arg ( format ! (
345
+ "{} {} {}" ,
346
+ windows_to_wsl_path( & unxip_path. to_string_lossy( ) ) ?,
347
+ windows_to_wsl_path( & xcode_path) ?,
348
+ windows_to_wsl_path( & dev_stage. to_string_lossy( ) ) ?
349
+ ) )
350
+ . creation_flags ( CREATE_NO_WINDOW )
351
+ . output ( ) ;
352
+ #[ cfg( not( target_os = "windows" ) ) ]
353
+ let status = Command :: new ( unxip_path)
354
+ . current_dir ( & dev_stage)
355
+ . arg ( xcode_path)
356
+ . output ( ) ;
357
+ if let Err ( e) = status {
358
+ return op. fail ( "extract_xip" , format ! ( "Failed to run unxip: {}" , e) ) ;
359
+ }
360
+ let status = status. unwrap ( ) ;
361
+ if !status. status . success ( ) {
362
+ return op. fail (
363
+ "extract_xip" ,
364
+ format ! (
365
+ "{}\n Process exited with code {}" ,
366
+ String :: from_utf8_lossy( & status. stderr. trim_ascii( ) ) ,
367
+ status. status. code( ) . unwrap_or( 0 )
368
+ ) ,
369
+ ) ;
370
+ }
356
371
357
- let app_dirs = op
358
- . fail_if_err_map ( "extract_xip" , fs:: read_dir ( & dev_stage) , |e| {
359
- format ! ( "Failed to read DeveloperStage directory: {}" , e)
360
- } ) ?
361
- . filter_map ( Result :: ok)
362
- . filter ( |entry| entry. path ( ) . extension ( ) . map_or ( false , |ext| ext == "app" ) )
363
- . collect :: < Vec < _ > > ( ) ;
364
- if app_dirs. len ( ) != 1 {
365
- return op. fail (
366
- "extract_xip" ,
367
- format ! (
368
- "Expected one .app in DeveloperStage, found {}" ,
369
- app_dirs. len( )
370
- ) ,
371
- ) ;
372
+ let app_dirs = op
373
+ . fail_if_err_map ( "extract_xip" , fs:: read_dir ( & dev_stage) , |e| {
374
+ format ! ( "Failed to read DeveloperStage directory: {}" , e)
375
+ } ) ?
376
+ . filter_map ( Result :: ok)
377
+ . filter ( |entry| entry. path ( ) . extension ( ) . map_or ( false , |ext| ext == "app" ) )
378
+ . collect :: < Vec < _ > > ( ) ;
379
+ if app_dirs. len ( ) != 1 {
380
+ return op. fail (
381
+ "extract_xip" ,
382
+ format ! (
383
+ "Expected one .app in DeveloperStage, found {}" ,
384
+ app_dirs. len( )
385
+ ) ,
386
+ ) ;
387
+ }
388
+
389
+ app_path = app_dirs[ 0 ] . path ( ) ;
372
390
}
373
391
374
392
op. move_on ( "extract_xip" , "copy_files" ) ?;
375
- let app_path = app_dirs[ 0 ] . path ( ) ;
376
393
let dev = output_path. join ( "Developer" ) ;
377
394
op. fail_if_err_map ( "copy_files" , fs:: create_dir_all ( & dev) , |e| {
378
395
format ! ( "Failed to create Developer directory: {}" , e)
@@ -390,9 +407,11 @@ async fn install_developer(
390
407
"copy_files" ,
391
408
copy_developer ( & contents_developer, & dev, Path :: new ( "Contents/Developer" ) ) ,
392
409
) ?;
393
- op. fail_if_err_map ( "copy_files" , remove_dir_all ( & dev_stage) , |e| {
394
- format ! ( "Failed to remove DeveloperStage directory: {}" , e)
395
- } ) ?;
410
+ if dev_stage. exists ( ) {
411
+ op. fail_if_err_map ( "copy_files" , remove_dir_all ( & dev_stage) , |e| {
412
+ format ! ( "Failed to remove DeveloperStage directory: {}" , e)
413
+ } ) ?;
414
+ }
396
415
397
416
for platform in [ "iPhoneOS" , "MacOSX" , "iPhoneSimulator" ] {
398
417
let lib = "../../../../../Library" ;
@@ -485,7 +504,17 @@ fn copy_developer(src: &Path, dst: &Path, rel: &Path) -> Result<(), String> {
485
504
fs:: create_dir_all ( parent)
486
505
. map_err ( |e| format ! ( "Failed to create parent dir: {}" , e) ) ?;
487
506
}
488
- fs:: rename ( & src_path, & dst_path) . map_err ( |e| format ! ( "Failed to copy file: {}" , e) ) ?;
507
+ match fs:: rename ( & src_path, & dst_path) {
508
+ Ok ( _) => { }
509
+ Err ( e) => {
510
+ if e. kind ( ) == ErrorKind :: CrossesDevices {
511
+ fs:: copy ( & src_path, & dst_path)
512
+ . map_err ( |e2| format ! ( "Failed to copy file across devices: {}" , e2) ) ?;
513
+ } else {
514
+ return Err ( format ! ( "Failed to move file: {}" , e) ) ;
515
+ }
516
+ }
517
+ }
489
518
}
490
519
}
491
520
Ok ( ( ) )
0 commit comments