@@ -6,26 +6,44 @@ use std::fs;
66use std:: os:: unix:: fs:: symlink;
77use std:: path:: { Component , Path , PathBuf } ;
88use std:: process:: Command ;
9- use tauri:: { AppHandle , Manager } ;
9+ use tauri:: { AppHandle , Manager , Window } ;
1010
1111use crate :: builder:: swift:: { swift_bin, validate_toolchain} ;
12+ use crate :: operation:: Operation ;
1213
1314const DARWIN_TOOLS_VERSION : & str = "1.0.1" ;
1415
1516#[ tauri:: command]
1617pub async fn install_sdk_operation (
1718 app : AppHandle ,
19+ window : Window ,
1820 xcode_path : String ,
1921 toolchain_path : String ,
2022) -> Result < ( ) , String > {
23+ let op = Operation :: new ( "install_sdk" . to_string ( ) , & window) ;
2124 let work_dir = std:: env:: temp_dir ( ) . join ( "DarwinSDKBuild" ) ;
22- let res = install_sdk_internal ( app, xcode_path, toolchain_path, work_dir. clone ( ) ) . await ;
25+ let res = install_sdk_internal ( app, xcode_path, toolchain_path, work_dir. clone ( ) , & op) . await ;
26+ op. start ( "cleanup" ) ?;
2327 let cleanup_result = if work_dir. exists ( ) {
2428 fs:: remove_dir_all ( & work_dir)
2529 } else {
2630 Ok ( ( ) )
2731 } ;
28- match ( res, cleanup_result) {
32+
33+ let cleanup_result_for_match = cleanup_result
34+ . as_ref ( )
35+ . map ( |_| ( ) )
36+ . map_err ( |e| format ! ( "{}" , e) ) ;
37+
38+ let cleanup_result = op. fail_if_err_map ( "cleanup" , cleanup_result, |e| {
39+ format ! ( "Failed to remove temp dir: {}" , e)
40+ } ) ;
41+
42+ if cleanup_result. is_ok ( ) {
43+ op. complete ( "cleanup" ) ?;
44+ }
45+
46+ match ( res, cleanup_result_for_match) {
2947 ( Err ( main_err) , Err ( cleanup_err) ) => Err ( format ! (
3048 "{main_err} (additionally, failed to clean up temp dir: {cleanup_err})"
3149 ) ) ,
@@ -41,7 +59,19 @@ async fn install_sdk_internal(
4159 xcode_path : String ,
4260 toolchain_path : String ,
4361 work_dir : PathBuf ,
62+ op : & Operation < ' _ > ,
4463) -> Result < ( ) , String > {
64+ op. start ( "create_stage" ) ?;
65+ if xcode_path. is_empty ( ) || !xcode_path. ends_with ( ".xip" ) {
66+ return op. fail ( "create_stage" , "Xcode not found" . to_string ( ) ) ;
67+ }
68+ if toolchain_path. is_empty ( ) {
69+ return op. fail ( "create_stage" , "Toolchain not found" . to_string ( ) ) ;
70+ }
71+ if !validate_toolchain ( & toolchain_path) {
72+ return op. fail ( "create_stage" , "Invalid toolchain path" . to_string ( ) ) ;
73+ }
74+
4575 let swift_bin = swift_bin ( & toolchain_path) ?;
4676 let output = std:: process:: Command :: new ( swift_bin)
4777 . arg ( "sdk" )
@@ -50,32 +80,31 @@ async fn install_sdk_internal(
5080 . output ( ) ;
5181 if let Ok ( output) = output {
5282 if !output. status . success ( ) && output. status . code ( ) != Some ( 1 ) {
53- return Err ( format ! (
54- "Failed to remove existing darwin SDK: {}" ,
55- String :: from_utf8_lossy( & output. stderr)
56- ) ) ;
83+ return op. fail (
84+ "create_stage" ,
85+ format ! (
86+ "Failed to remove existing darwin SDK: {}" ,
87+ String :: from_utf8_lossy( & output. stderr)
88+ ) ,
89+ ) ;
5790 }
5891 }
59- if xcode_path. is_empty ( ) || !xcode_path. ends_with ( ".xip" ) {
60- return Err ( "Xcode not found" . to_string ( ) ) ;
61- }
62- if toolchain_path. is_empty ( ) {
63- return Err ( "Toolchain not found" . to_string ( ) ) ;
64- }
65- if !validate_toolchain ( & toolchain_path) {
66- return Err ( "Invalid toolchain path" . to_string ( ) ) ;
67- }
92+
6893 let output_dir = work_dir. join ( "darwin.artifactbundle" ) ;
6994 if output_dir. exists ( ) {
70- fs:: remove_dir_all ( & output_dir)
71- . map_err ( |e| format ! ( "Failed to remove existing output directory: {}" , e) ) ?;
95+ op. fail_if_err_map ( "create_stage" , fs:: remove_dir_all ( & output_dir) , |e| {
96+ format ! ( "Failed to remove existing output directory: {}" , e)
97+ } ) ?;
7298 }
73- fs:: create_dir_all ( & output_dir)
74- . map_err ( |e| format ! ( "Failed to create output directory: {}" , e) ) ?;
75-
76- install_toolset ( & output_dir) . await ?;
99+ op. fail_if_err_map ( "create_stage" , fs:: create_dir_all ( & output_dir) , |e| {
100+ format ! ( "Failed to create output directory: {}" , e)
101+ } ) ?;
77102
78- let dev = install_developer ( & app, & output_dir, & xcode_path) . await ?;
103+ op. move_on ( "create_stage" , "install_toolset" ) ?;
104+ op. fail_if_err ( "install_toolset" , install_toolset ( & output_dir) . await ) ?;
105+ op. complete ( "install_toolset" ) ?;
106+ let dev = install_developer ( & app, & output_dir, & xcode_path, op) . await ?;
107+ op. start ( "write_metadata" ) ?;
79108
80109 let iphone_os_sdk = sdk ( & dev, "iPhoneOS" ) ?;
81110 let mac_os_sdk = sdk ( & dev, "MacOSX" ) ?;
@@ -98,8 +127,11 @@ async fn install_sdk_internal(
98127 }
99128 }
100129 " ;
101- fs:: write ( output_dir. join ( "info.json" ) , info)
102- . map_err ( |e| format ! ( "Failed to write info.json: {}" , e) ) ?;
130+ op. fail_if_err_map (
131+ "write_metadata" ,
132+ fs:: write ( output_dir. join ( "info.json" ) , info) ,
133+ |e| format ! ( "Failed to write info.json: {}" , e) ,
134+ ) ?;
103135
104136 let toolset = "
105137 {
@@ -115,8 +147,11 @@ async fn install_sdk_internal(
115147 }
116148 }
117149 " ;
118- fs:: write ( output_dir. join ( "toolset.json" ) , toolset)
119- . map_err ( |e| format ! ( "Failed to write toolset.json: {}" , e) ) ?;
150+ op. fail_if_err_map (
151+ "write_metadata" ,
152+ fs:: write ( output_dir. join ( "toolset.json" ) , toolset) ,
153+ |e| format ! ( "Failed to write toolset.json: {}" , e) ,
154+ ) ?;
120155
121156 let sdk_def = SDKDefinition {
122157 schema_version : "4.0" . to_string ( ) ,
@@ -145,35 +180,56 @@ async fn install_sdk_internal(
145180 } ;
146181
147182 let sdk_def_path = output_dir. join ( "swift-sdk.json" ) ;
148- fs:: write (
149- sdk_def_path,
150- serde_json:: to_string_pretty ( & sdk_def)
151- . map_err ( |e| format ! ( "Failed to serialize SDKDefinition: {}" , e) ) ?,
152- )
153- . map_err ( |e| format ! ( "Failed to write swift-sdk.json: {}" , e) ) ?;
183+ op. fail_if_err_map (
184+ "write_metadata" ,
185+ fs:: write (
186+ sdk_def_path,
187+ op. fail_if_err_map (
188+ "write_metadata" ,
189+ serde_json:: to_string_pretty ( & sdk_def) ,
190+ |e| format ! ( "Failed to serialize SDKDefinition: {}" , e) ,
191+ ) ?,
192+ ) ,
193+ |e| format ! ( "Failed to write swift-sdk.json: {}" , e) ,
194+ ) ?;
195+
154196 let sdk_version_path = output_dir. join ( "darwin-sdk-version.txt" ) ;
155- fs:: write ( & sdk_version_path, "develop" )
156- . map_err ( |e| format ! ( "Failed to write darwin-sdk-version.txt: {}" , e) ) ?;
197+ op. fail_if_err_map (
198+ "write_metadata" ,
199+ fs:: write ( & sdk_version_path, "develop" ) ,
200+ |e| format ! ( "Failed to write darwin-sdk-version.txt: {}" , e) ,
201+ ) ?;
202+ op. move_on ( "write_metadata" , "install_sdk" ) ?;
157203
158204 let path = PathBuf :: from ( toolchain_path) ;
159205 let swift_path = path. join ( "usr" ) . join ( "bin" ) . join ( "swift" ) ;
160206 if !swift_path. exists ( ) || !swift_path. is_file ( ) {
161- return Err ( "Swift binary not found in toolchain" . to_string ( ) ) ;
207+ return op. fail (
208+ "install_sdk" ,
209+ "Swift binary not found in toolchain" . to_string ( ) ,
210+ ) ;
162211 }
163212
164- let output = std:: process:: Command :: new ( swift_path)
165- . arg ( "sdk" )
166- . arg ( "install" )
167- . arg ( output_dir. to_string_lossy ( ) . to_string ( ) )
168- . output ( )
169- . map_err ( |e| format ! ( "Failed to execute swift command: {}" , e) ) ?;
213+ let output = op. fail_if_err_map (
214+ "install_sdk" ,
215+ std:: process:: Command :: new ( swift_path)
216+ . arg ( "sdk" )
217+ . arg ( "install" )
218+ . arg ( output_dir. to_string_lossy ( ) . to_string ( ) )
219+ . output ( ) ,
220+ |e| format ! ( "Failed to execute swift command: {}" , e) ,
221+ ) ?;
170222
171223 if !output. status . success ( ) {
172- return Err ( format ! (
173- "Swift command failed: {}" ,
174- String :: from_utf8_lossy( & output. stderr)
175- ) ) ;
224+ return op. fail (
225+ "install_sdk" ,
226+ format ! (
227+ "Swift command failed: {}" ,
228+ String :: from_utf8_lossy( & output. stderr)
229+ ) ,
230+ ) ;
176231 }
232+ op. complete ( "install_sdk" ) ?;
177233
178234 Ok ( ( ) )
179235}
@@ -236,51 +292,79 @@ async fn install_developer(
236292 app : & AppHandle ,
237293 output_path : & PathBuf ,
238294 xcode_path : & str ,
295+ op : & Operation < ' _ > ,
239296) -> Result < PathBuf , String > {
297+ op. start ( "extract_xip" ) ?;
240298 let dev_stage = output_path. join ( "DeveloperStage" ) ;
241- fs:: create_dir_all ( & dev_stage)
242- . map_err ( |e| format ! ( "Failed to create DeveloperStage directory: {}" , e) ) ?;
299+ op. fail_if_err_map ( "extract_xip" , fs:: create_dir_all ( & dev_stage) , |e| {
300+ format ! ( "Failed to create DeveloperStage directory: {}" , e)
301+ } ) ?;
243302
244- let unxip_path = app
245- . path ( )
246- . resolve ( "unxip" , tauri:: path:: BaseDirectory :: Resource )
247- . map_err ( |e| format ! ( "Failed to resolve unxip path: {}" , e) ) ?;
303+ let unxip_path = op. fail_if_err_map (
304+ "extract_xip" ,
305+ app. path ( )
306+ . resolve ( "unxip" , tauri:: path:: BaseDirectory :: Resource ) ,
307+ |e| format ! ( "Failed to resolve unxip path: {}" , e) ,
308+ ) ?;
248309
249310 let status = Command :: new ( unxip_path)
250311 . current_dir ( & dev_stage)
251312 . arg ( xcode_path)
252- . status ( ) ;
313+ . output ( ) ;
253314 if let Err ( e) = status {
254- return Err ( format ! ( "Failed to run unxip: {}" , e) ) ;
315+ return op . fail ( "extract_xip" , format ! ( "Failed to run unxip: {}" , e) ) ;
255316 }
256- if !status. unwrap ( ) . success ( ) {
257- return Err ( "Failed to unxip Xcode" . to_string ( ) ) ;
317+ let status = status. unwrap ( ) ;
318+ if !status. status . success ( ) {
319+ return op. fail (
320+ "extract_xip" ,
321+ format ! (
322+ "{}\n Process exited with code {}" ,
323+ String :: from_utf8_lossy( & status. stderr. trim_ascii( ) ) ,
324+ status. status. code( ) . unwrap_or( 0 )
325+ ) ,
326+ ) ;
258327 }
259328
260- let app_dirs = fs:: read_dir ( & dev_stage)
261- . map_err ( |e| format ! ( "Failed to read DeveloperStage directory: {}" , e) ) ?
329+ let app_dirs = op
330+ . fail_if_err_map ( "extract_xip" , fs:: read_dir ( & dev_stage) , |e| {
331+ format ! ( "Failed to read DeveloperStage directory: {}" , e)
332+ } ) ?
262333 . filter_map ( Result :: ok)
263334 . filter ( |entry| entry. path ( ) . extension ( ) . map_or ( false , |ext| ext == "app" ) )
264335 . collect :: < Vec < _ > > ( ) ;
265336 if app_dirs. len ( ) != 1 {
266- return Err ( format ! (
267- "Expected one .app in DeveloperStage, found {}" ,
268- app_dirs. len( )
269- ) ) ;
337+ return op. fail (
338+ "extract_xip" ,
339+ format ! (
340+ "Expected one .app in DeveloperStage, found {}" ,
341+ app_dirs. len( )
342+ ) ,
343+ ) ;
270344 }
271345
346+ op. move_on ( "extract_xip" , "copy_files" ) ?;
272347 let app_path = app_dirs[ 0 ] . path ( ) ;
273348 let dev = output_path. join ( "Developer" ) ;
274- fs:: create_dir_all ( & dev) . map_err ( |e| format ! ( "Failed to create Developer directory: {}" , e) ) ?;
349+ op. fail_if_err_map ( "copy_files" , fs:: create_dir_all ( & dev) , |e| {
350+ format ! ( "Failed to create Developer directory: {}" , e)
351+ } ) ?;
275352
276353 let contents_developer = app_path. join ( "Contents/Developer" ) ;
277354 if !contents_developer. exists ( ) {
278- return Err ( "Contents/Developer not found in .app" . to_string ( ) ) ;
355+ return op. fail (
356+ "copy_files" ,
357+ "Contents/Developer not found in .app" . to_string ( ) ,
358+ ) ;
279359 }
280- copy_developer ( & contents_developer, & dev, Path :: new ( "Contents/Developer" ) )
281- . map_err ( |e| format ! ( "Failed to copy Developer: {}" , e) ) ?;
282- fs:: remove_dir_all ( & dev_stage)
283- . map_err ( |e| format ! ( "Failed to remove DeveloperStage directory: {}" , e) ) ?;
360+
361+ op. fail_if_err (
362+ "copy_files" ,
363+ copy_developer ( & contents_developer, & dev, Path :: new ( "Contents/Developer" ) ) ,
364+ ) ?;
365+ op. fail_if_err_map ( "copy_files" , fs:: remove_dir_all ( & dev_stage) , |e| {
366+ format ! ( "Failed to remove DeveloperStage directory: {}" , e)
367+ } ) ?;
284368
285369 for platform in [ "iPhoneOS" , "MacOSX" , "iPhoneSimulator" ] {
286370 let lib = "../../../../../Library" ;
@@ -310,7 +394,7 @@ async fn install_developer(
310394
311395 for ( name, target) in & links {
312396 let link_path = dest. join ( name) ;
313- symlink ( target, & link_path) . map_err ( |e| {
397+ op . fail_if_err_map ( "copy_files" , symlink ( target, & link_path) , |e| {
314398 format ! (
315399 "Failed to create symlink {:?} -> {:?}: {}" ,
316400 link_path, target, e
@@ -319,6 +403,8 @@ async fn install_developer(
319403 }
320404 }
321405
406+ op. complete ( "copy_files" ) ?;
407+
322408 Ok ( dev)
323409}
324410
0 commit comments