11use std:: collections:: HashMap ;
22
3- use anyhow:: Context as _;
3+ use anyhow:: { Context as _, anyhow } ;
44use camino_tempfile:: tempdir;
55use clap:: Parser ;
66use futures:: { StreamExt , stream:: FuturesOrdered } ;
@@ -9,10 +9,7 @@ use icp::{
99 fs:: read,
1010} ;
1111
12- use crate :: {
13- commands:: Context ,
14- progress:: { ProgressManager , ScriptProgressHandler } ,
15- } ;
12+ use crate :: { commands:: Context , progress:: ProgressManager } ;
1613
1714#[ derive( Parser , Debug ) ]
1815pub struct Cmd {
@@ -37,6 +34,9 @@ pub enum CommandError {
3734 #[ error( "failed to store build artifact" ) ]
3835 ArtifactStore ,
3936
37+ #[ error( "failed to join build output" ) ]
38+ JoinError ( #[ from] tokio:: task:: JoinError ) ,
39+
4040 #[ error( transparent) ]
4141 Unexpected ( #[ from] anyhow:: Error ) ,
4242}
@@ -77,83 +77,105 @@ pub async fn exec(ctx: &Context, cmd: Cmd) -> Result<(), CommandError> {
7777 // Iterate through each resolved canister and trigger its build process.
7878 for ( _, ( canister_path, c) ) in cs {
7979 // Create progress bar with standard configuration
80- let pb = progress_manager. create_progress_bar ( & c. name ) ;
80+ let mut pb = progress_manager. create_multi_step_progress_bar ( & c. name , "Build" ) ;
8181
8282 // Create an async closure that handles the build process for this specific canister
83- let build_fn = {
83+ let fut = {
8484 let c = c. clone ( ) ;
85- let pb = pb. clone ( ) ;
8685
8786 async move {
88- // Create a temporary directory for build artifacts
89- let build_dir =
90- tempdir ( ) . context ( "failed to create a temporary build directory" ) ?;
91-
92- // Prepare a path for our output wasm
93- let wasm_output_path = build_dir. path ( ) . join ( "out.wasm" ) ;
94-
95- let step_count = c. build . steps . len ( ) ;
96- for ( i, step) in c. build . steps . iter ( ) . enumerate ( ) {
97- // Indicate to user the current step being executed
98- let current_step = i + 1 ;
99- let pb_hdr = format ! ( "Building: {step} {current_step} of {step_count}" ) ;
100-
101- let script_handler = ScriptProgressHandler :: new ( pb. clone ( ) , pb_hdr. clone ( ) ) ;
102-
103- // Setup script progress handling and receiver join handle
104- let ( tx, rx) = script_handler. setup_output_handler ( ) ;
105-
106- // Perform build step
107- ctx. builder
108- . build (
109- step, // step
110- & Params {
111- path : canister_path. to_owned ( ) ,
112- output : wasm_output_path. to_owned ( ) ,
113- } ,
114- Some ( tx) ,
115- )
116- . await ?;
117-
118- // Ensure background receiver drains all messages
119- let _ = rx. await ;
87+ // Define the build logic
88+ let build_result = async {
89+ // Create a temporary directory for build artifacts
90+ let build_dir =
91+ tempdir ( ) . context ( "failed to create a temporary build directory" ) ?;
92+
93+ // Prepare a path for our output wasm
94+ let wasm_output_path = build_dir. path ( ) . join ( "out.wasm" ) ;
95+
96+ let step_count = c. build . steps . len ( ) ;
97+ for ( i, step) in c. build . steps . iter ( ) . enumerate ( ) {
98+ // Indicate to user the current step being executed
99+ let current_step = i + 1 ;
100+ let pb_hdr = format ! ( "\n Building: {step} {current_step} of {step_count}" ) ;
101+ let tx = pb. begin_step ( pb_hdr) ;
102+
103+ // Perform build step
104+ let build_result = ctx
105+ . builder
106+ . build (
107+ step, // step
108+ & Params {
109+ path : canister_path. to_owned ( ) ,
110+ output : wasm_output_path. to_owned ( ) ,
111+ } ,
112+ Some ( tx) ,
113+ )
114+ . await ;
115+
116+ // Ensure background receiver drains all messages
117+ pb. end_step ( ) . await ;
118+
119+ if let Err ( e) = build_result {
120+ return Err ( CommandError :: Build ( e) ) ;
121+ }
122+ }
123+
124+ // Verify a file exists in the wasm output path
125+ if !wasm_output_path. exists ( ) {
126+ return Err ( CommandError :: MissingOutput ) ;
127+ }
128+
129+ // Load wasm output
130+ let wasm = read ( & wasm_output_path) . context ( CommandError :: ReadOutput ) ?;
131+
132+ // TODO(or.ricon): Verify wasm output is valid wasm (consider using wasmparser)
133+
134+ // Save the wasm artifact
135+ ctx. artifacts
136+ . save ( & c. name , & wasm)
137+ . context ( CommandError :: ArtifactStore ) ?;
138+
139+ Ok :: < _ , CommandError > ( ( ) )
120140 }
121-
122- // Verify a file exists in the wasm output path
123- if !wasm_output_path. exists ( ) {
124- return Err ( CommandError :: MissingOutput ) ;
141+ . await ;
142+
143+ // Execute with progress tracking for final state
144+ let result = ProgressManager :: execute_with_progress (
145+ & pb,
146+ async { build_result } ,
147+ || "Built successfully" . to_string ( ) ,
148+ |err| format ! ( "Failed to build canister: {err}" ) ,
149+ )
150+ . await ;
151+
152+ // After progress bar is finished, dump the output if build failed
153+ if let Err ( e) = & result {
154+ pb. dump_output ( ctx) ;
155+ let _ = ctx
156+ . term
157+ . write_line ( & format ! ( "Failed to build canister: {e}" ) ) ;
125158 }
126159
127- // Load wasm output
128- let wasm = read ( & wasm_output_path) . context ( CommandError :: ReadOutput ) ?;
129-
130- // TODO(or.ricon): Verify wasm output is valid wasm (consider using wasmparser)
131-
132- // Save the wasm artifact
133- ctx. artifacts
134- . save ( & c. name , & wasm)
135- . context ( CommandError :: ArtifactStore ) ?;
136-
137- Ok :: < _ , CommandError > ( ( ) )
160+ result
138161 }
139162 } ;
140163
141- futs. push_back ( async move {
142- // Execute the build function with progress tracking
143- ProgressManager :: execute_with_progress (
144- pb,
145- build_fn,
146- || "Built successfully" . to_string ( ) ,
147- |err| format ! ( "Failed to build canister: {err}" ) ,
148- )
149- . await
150- } ) ;
164+ futs. push_back ( fut) ;
151165 }
152166
153167 // Consume the set of futures and abort if an error occurs
168+ let mut found_error = false ;
154169 while let Some ( res) = futs. next ( ) . await {
155- // TODO(or.ricon): Handle canister build failures
156- res?;
170+ if res. is_err ( ) {
171+ found_error = true ;
172+ }
173+ }
174+
175+ if found_error {
176+ return Err ( CommandError :: Unexpected ( anyhow ! (
177+ "One or more canisters failed to build"
178+ ) ) ) ;
157179 }
158180
159181 Ok ( ( ) )
0 commit comments