@@ -17,7 +17,11 @@ use std::{
1717} ;
1818use stellar_xdr:: curr:: { Limited , Limits , ScMetaEntry , ScMetaV0 , StringM , WriteXdr } ;
1919
20- use crate :: { commands:: global, print:: Print } ;
20+ use crate :: {
21+ commands:: { contract:: optimize, global} ,
22+ print:: Print ,
23+ wasm,
24+ } ;
2125
2226/// Build a contract from source
2327///
@@ -32,6 +36,7 @@ use crate::{commands::global, print::Print};
3236/// To view the commands that will be executed, without executing them, use the
3337/// --print-commands-only option.
3438#[ derive( Parser , Debug , Clone ) ]
39+ #[ allow( clippy:: struct_excessive_bools) ]
3540pub struct Cmd {
3641 /// Path to Cargo.toml
3742 #[ arg( long) ]
@@ -41,12 +46,15 @@ pub struct Cmd {
4146 /// If omitted, all packages that build for crate-type cdylib are built.
4247 #[ arg( long) ]
4348 pub package : Option < String > ,
49+
4450 /// Build with the specified profile
4551 #[ arg( long, default_value = "release" ) ]
4652 pub profile : String ,
53+
4754 /// Build with the list of features activated, space or comma separated
4855 #[ arg( long, help_heading = "Features" ) ]
4956 pub features : Option < String > ,
57+
5058 /// Build with the all features activated
5159 #[ arg(
5260 long,
@@ -55,9 +63,11 @@ pub struct Cmd {
5563 help_heading = "Features"
5664 ) ]
5765 pub all_features : bool ,
66+
5867 /// Build with the default feature not activated
5968 #[ arg( long, help_heading = "Features" ) ]
6069 pub no_default_features : bool ,
70+
6171 /// Directory to copy wasm files to
6272 ///
6373 /// If provided, wasm files can be found in the cargo target directory, and
@@ -66,12 +76,18 @@ pub struct Cmd {
6676 /// If ommitted, wasm files are written only to the cargo target directory.
6777 #[ arg( long) ]
6878 pub out_dir : Option < std:: path:: PathBuf > ,
79+
6980 /// Print commands to build without executing them
7081 #[ arg( long, conflicts_with = "out_dir" , help_heading = "Other" ) ]
7182 pub print_commands_only : bool ,
83+
7284 /// Add key-value to contract meta (adds the meta to the `contractmetav0` custom section)
7385 #[ arg( long, num_args=1 , value_parser=parse_meta_arg, action=clap:: ArgAction :: Append , help_heading = "Metadata" ) ]
7486 pub meta : Vec < ( String , String ) > ,
87+
88+ /// Optimize the generated wasm.
89+ #[ arg( long) ]
90+ pub optimize : bool ,
7591}
7692
7793fn parse_meta_arg ( s : & str ) -> Result < ( String , String ) , Error > {
@@ -89,34 +105,54 @@ fn parse_meta_arg(s: &str) -> Result<(String, String), Error> {
89105pub enum Error {
90106 #[ error( transparent) ]
91107 Metadata ( #[ from] cargo_metadata:: Error ) ,
108+
92109 #[ error( transparent) ]
93110 CargoCmd ( io:: Error ) ,
111+
94112 #[ error( "exit status {0}" ) ]
95113 Exit ( ExitStatus ) ,
114+
96115 #[ error( "package {package} not found" ) ]
97116 PackageNotFound { package : String } ,
117+
98118 #[ error( "finding absolute path of Cargo.toml: {0}" ) ]
99119 AbsolutePath ( io:: Error ) ,
120+
100121 #[ error( "creating out directory: {0}" ) ]
101122 CreatingOutDir ( io:: Error ) ,
123+
102124 #[ error( "deleting existing artifact: {0}" ) ]
103125 DeletingArtifact ( io:: Error ) ,
126+
104127 #[ error( "copying wasm file: {0}" ) ]
105128 CopyingWasmFile ( io:: Error ) ,
129+
106130 #[ error( "getting the current directory: {0}" ) ]
107131 GettingCurrentDir ( io:: Error ) ,
132+
108133 #[ error( "retreiving CARGO_HOME: {0}" ) ]
109134 CargoHome ( io:: Error ) ,
135+
110136 #[ error( "reading wasm file: {0}" ) ]
111137 ReadingWasmFile ( io:: Error ) ,
138+
112139 #[ error( "writing wasm file: {0}" ) ]
113140 WritingWasmFile ( io:: Error ) ,
141+
114142 #[ error( "invalid meta entry: {0}" ) ]
115143 MetaArg ( String ) ,
144+
116145 #[ error( "use rust 1.81 or 1.84+ to build contracts (got {0})" ) ]
117146 RustVersion ( String ) ,
147+
118148 #[ error( transparent) ]
119149 Xdr ( #[ from] stellar_xdr:: curr:: Error ) ,
150+
151+ #[ error( transparent) ]
152+ Optimize ( #[ from] optimize:: Error ) ,
153+
154+ #[ error( transparent) ]
155+ Wasm ( #[ from] wasm:: Error ) ,
120156}
121157
122158const WASM_TARGET : & str = "wasm32v1-none" ;
@@ -202,7 +238,8 @@ impl Cmd {
202238 return Err ( Error :: Exit ( status) ) ;
203239 }
204240
205- let file = format ! ( "{}.wasm" , p. name. replace( '-' , "_" ) ) ;
241+ let wasm_name = p. name . replace ( '-' , "_" ) ;
242+ let file = format ! ( "{wasm_name}.wasm" ) ;
206243 let target_file_path = Path :: new ( target_dir)
207244 . join ( & wasm_target)
208245 . join ( & self . profile )
@@ -219,7 +256,20 @@ impl Cmd {
219256 target_file_path
220257 } ;
221258
222- Self :: print_build_summary ( & print, & final_path) ?;
259+ let wasm_bytes = fs:: read ( & final_path) . map_err ( Error :: ReadingWasmFile ) ?;
260+ let mut optimized_wasm_bytes: Vec < u8 > = Vec :: new ( ) ;
261+
262+ if self . optimize {
263+ let mut path = final_path. clone ( ) ;
264+ path. set_extension ( "optimized.wasm" ) ;
265+ optimize:: optimize ( true , vec ! [ final_path. clone( ) ] , Some ( path. clone ( ) ) ) ?;
266+ optimized_wasm_bytes = fs:: read ( & path) . map_err ( Error :: ReadingWasmFile ) ?;
267+
268+ fs:: remove_file ( & final_path) . map_err ( Error :: DeletingArtifact ) ?;
269+ fs:: rename ( & path, & final_path) . map_err ( Error :: CopyingWasmFile ) ?;
270+ }
271+
272+ Self :: print_build_summary ( & print, & final_path, wasm_bytes, optimized_wasm_bytes) ;
223273 }
224274 }
225275
@@ -332,20 +382,41 @@ impl Cmd {
332382 Ok ( buffer)
333383 }
334384
335- fn print_build_summary ( print : & Print , target_file_path : & PathBuf ) -> Result < ( ) , Error > {
385+ fn print_build_summary (
386+ print : & Print ,
387+ path : & Path ,
388+ wasm_bytes : Vec < u8 > ,
389+ optimized_wasm_bytes : Vec < u8 > ,
390+ ) {
336391 print. infoln ( "Build Summary:" ) ;
337- let rel_target_file_path = target_file_path
392+
393+ let rel_path = path
338394 . strip_prefix ( env:: current_dir ( ) . unwrap ( ) )
339- . unwrap_or ( target_file_path) ;
340- print. blankln ( format ! ( "Wasm File: {}" , rel_target_file_path. display( ) ) ) ;
395+ . unwrap_or ( path) ;
396+
397+ let size = wasm_bytes. len ( ) ;
398+ let optimized_size = optimized_wasm_bytes. len ( ) ;
399+
400+ let size_description = if optimized_size > 0 {
401+ format ! ( "{optimized_size} bytes optimized (original size was {size} bytes)" )
402+ } else {
403+ format ! ( "{size} bytes" )
404+ } ;
341405
342- let wasm_bytes = fs:: read ( target_file_path) . map_err ( Error :: ReadingWasmFile ) ?;
406+ let bytes = if optimized_size > 0 {
407+ & optimized_wasm_bytes
408+ } else {
409+ & wasm_bytes
410+ } ;
343411
344412 print. blankln ( format ! (
345- "Wasm Hash : {} " ,
346- hex :: encode ( Sha256 :: digest ( & wasm_bytes ) )
413+ "Wasm File : {path} ({size_description}) " ,
414+ path = rel_path . display ( )
347415 ) ) ;
348416
417+ print. blankln ( format ! ( "Wasm Hash: {}" , hex:: encode( Sha256 :: digest( bytes) ) ) ) ;
418+ print. blankln ( format ! ( "Wasm Size: {size_description}" ) ) ;
419+
349420 let parser = wasmparser:: Parser :: new ( 0 ) ;
350421 let export_names: Vec < & str > = parser
351422 . parse_all ( & wasm_bytes)
@@ -363,6 +434,7 @@ impl Cmd {
363434 . map ( |export| export. name )
364435 . sorted ( )
365436 . collect ( ) ;
437+
366438 if export_names. is_empty ( ) {
367439 print. blankln ( "Exported Functions: None found" ) ;
368440 } else {
@@ -371,9 +443,8 @@ impl Cmd {
371443 print. blankln ( format ! ( " • {name}" ) ) ;
372444 }
373445 }
374- print. checkln ( "Build Complete" ) ;
375446
376- Ok ( ( ) )
447+ print . checkln ( "Build Complete \n " ) ;
377448 }
378449}
379450
0 commit comments