Skip to content

Commit 67c2ef0

Browse files
committed
produce optimized wasm in addition to regular
1 parent 889b447 commit 67c2ef0

1 file changed

Lines changed: 80 additions & 72 deletions

File tree

  • cmd/soroban-cli/src/commands/contract

cmd/soroban-cli/src/commands/contract/build.rs

Lines changed: 80 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use crate::{commands::global, print::Print};
3131
/// To view the commands that will be executed, without executing them, use the
3232
/// --print-commands-only option.
3333
#[derive(Parser, Debug, Clone)]
34+
#[allow(clippy::struct_excessive_bools)]
3435
pub struct Cmd {
3536
/// Path to Cargo.toml
3637
#[arg(long)]
@@ -46,25 +47,6 @@ pub struct Cmd {
4647
/// Build with the list of features activated, space or comma separated
4748
#[arg(long, help_heading = "Features")]
4849
pub features: Option<String>,
49-
#[clap(flatten)]
50-
pub feature_flags: FeatureFlags,
51-
#[clap(flatten)]
52-
pub build_options: BuildOptions,
53-
/// Directory to copy wasm files to
54-
///
55-
/// If provided, wasm files can be found in the cargo target directory, and
56-
/// the specified directory.
57-
///
58-
/// If ommitted, wasm files are written only to the cargo target directory.
59-
#[arg(long)]
60-
pub out_dir: Option<std::path::PathBuf>,
61-
/// Add key-value to contract meta (adds the meta to the `contractmetav0` custom section)
62-
#[arg(long, num_args=1, value_parser=parse_meta_arg, action=clap::ArgAction::Append, help_heading = "Metadata")]
63-
pub meta: Vec<(String, String)>,
64-
}
65-
66-
#[derive(Debug, Clone, Parser)]
67-
pub struct FeatureFlags {
6850
/// Build with the all features activated
6951
#[arg(
7052
long,
@@ -76,15 +58,23 @@ pub struct FeatureFlags {
7658
/// Build with the default feature not activated
7759
#[arg(long, help_heading = "Features")]
7860
pub no_default_features: bool,
79-
}
80-
#[derive(Debug, Clone, Parser)]
81-
pub struct BuildOptions {
8261
/// Additionally optimize the built WASM file(s)
8362
#[arg(long, help_heading = "Other")]
8463
pub optimize: bool,
64+
/// Directory to copy wasm files to
65+
///
66+
/// If provided, wasm files can be found in the cargo target directory, and
67+
/// the specified directory.
68+
///
69+
/// If ommitted, wasm files are written only to the cargo target directory.
70+
#[arg(long)]
71+
pub out_dir: Option<std::path::PathBuf>,
8572
/// Print commands to build without executing them
8673
#[arg(long, conflicts_with = "out_dir", help_heading = "Other")]
8774
pub print_commands_only: bool,
75+
/// Add key-value to contract meta (adds the meta to the `contractmetav0` custom section)
76+
#[arg(long, num_args=1, value_parser=parse_meta_arg, action=clap::ArgAction::Append, help_heading = "Metadata")]
77+
pub meta: Vec<(String, String)>,
8878
}
8979

9080
fn parse_meta_arg(s: &str) -> Result<(String, String), Error> {
@@ -171,10 +161,10 @@ impl Cmd {
171161
} else {
172162
cmd.arg(format!("--profile={}", self.profile));
173163
}
174-
if self.feature_flags.all_features {
164+
if self.all_features {
175165
cmd.arg("--all-features");
176166
}
177-
if self.feature_flags.no_default_features {
167+
if self.no_default_features {
178168
cmd.arg("--no-default-features");
179169
}
180170
if let Some(features) = self.features() {
@@ -206,7 +196,7 @@ impl Cmd {
206196
);
207197
let cmd_str = cmd_str_parts.join(" ");
208198

209-
if self.build_options.print_commands_only {
199+
if self.print_commands_only {
210200
println!("{cmd_str}");
211201
} else {
212202
print.infoln(cmd_str);
@@ -247,24 +237,20 @@ impl Cmd {
247237
};
248238

249239
#[cfg(feature = "opt")]
250-
let mut cost_savings = None;
240+
let mut optimized_path = None;
251241
#[cfg(not(feature = "opt"))]
252-
let cost_savings = None;
242+
let optimized_path = None;
253243

254-
if self.build_options.optimize {
244+
if self.optimize {
255245
#[cfg(feature = "opt")]
256246
{
257247
use crate::wasm::Args as WasmArgs;
258-
let orig_size = fs::metadata(&final_path).map(|m| m.len()).unwrap_or(0);
259-
let tmp_optimized = final_path.with_extension("optimized.wasm");
248+
let optimized_file_path = final_path.with_extension("optimized.wasm");
260249
let wasm_args = WasmArgs {
261250
wasm: final_path.clone(),
262251
};
263-
wasm_args.optimize(&tmp_optimized)?;
264-
let opt_size = fs::metadata(&tmp_optimized).map(|m| m.len()).unwrap_or(0);
265-
// Overwrite original file with optimized artifact:
266-
fs::rename(&tmp_optimized, &final_path).map_err(Error::WritingWasmFile)?;
267-
cost_savings = Some((orig_size, opt_size));
252+
wasm_args.optimize(&optimized_file_path)?;
253+
optimized_path = Some(optimized_file_path);
268254
}
269255
#[cfg(not(feature = "opt"))]
270256
{
@@ -274,12 +260,7 @@ impl Cmd {
274260
}
275261
}
276262

277-
Self::print_build_summary(
278-
print,
279-
&final_path,
280-
self.build_options.optimize,
281-
cost_savings,
282-
)?;
263+
Self::print_build_summary(print, &final_path, optimized_path.as_ref())?;
283264

284265
Ok(())
285266
}
@@ -385,26 +366,40 @@ impl Cmd {
385366
fn print_build_summary(
386367
print: &Print,
387368
target_file_path: &PathBuf,
388-
optimized: bool,
389-
cost_savings: Option<(u64, u64)>,
369+
optimized_path: Option<&PathBuf>,
390370
) -> Result<(), Error> {
391-
if optimized {
371+
if optimized_path.is_some() {
392372
print.infoln("Build Summary (Optimized):");
393373
} else {
394374
print.infoln("Build Summary:");
395375
}
396376

397-
Self::print_file_summary(print, "Wasm File", target_file_path)?;
377+
Self::print_file_summary(print, "Wasm File", target_file_path, false, None)?;
378+
379+
if let Some(optimized_path) = optimized_path {
380+
let orig_size = fs::metadata(target_file_path).map(|m| m.len()).unwrap_or(0);
381+
let opt_size = fs::metadata(optimized_path).map(|m| m.len()).unwrap_or(0);
398382

399-
if let Some((before, after)) = cost_savings {
400-
print.blankln(format!("Optimized: {before} → {after} bytes"));
383+
Self::print_file_summary(
384+
print,
385+
"Optimized Wasm File",
386+
optimized_path,
387+
true,
388+
Some((orig_size, opt_size)),
389+
)?;
401390
}
402391

403392
print.checkln("Build Complete");
404393
Ok(())
405394
}
406395

407-
fn print_file_summary(print: &Print, label: &str, file_path: &PathBuf) -> Result<(), Error> {
396+
fn print_file_summary(
397+
print: &Print,
398+
label: &str,
399+
file_path: &PathBuf,
400+
show_size: bool,
401+
size_savings: Option<(u64, u64)>,
402+
) -> Result<(), Error> {
408403
let rel_file_path = file_path
409404
.strip_prefix(env::current_dir().unwrap())
410405
.unwrap_or(file_path);
@@ -416,31 +411,44 @@ impl Cmd {
416411
"Wasm Hash: {}",
417412
hex::encode(Sha256::digest(&wasm_bytes))
418413
));
419-
print.blankln(format!("Wasm Size: {} bytes", wasm_bytes.len()));
420-
421-
let parser = wasmparser::Parser::new(0);
422-
let export_names: Vec<&str> = parser
423-
.parse_all(&wasm_bytes)
424-
.filter_map(Result::ok)
425-
.filter_map(|payload| {
426-
if let wasmparser::Payload::ExportSection(exports) = payload {
427-
Some(exports)
428-
} else {
429-
None
414+
415+
if show_size {
416+
if let Some((orig_size, opt_size)) = size_savings {
417+
let savings = orig_size - opt_size;
418+
print.blankln(format!(
419+
"Wasm Size: {opt_size} bytes (reduced by {savings} bytes)",
420+
));
421+
} else {
422+
print.blankln(format!("Wasm Size: {} bytes", wasm_bytes.len()));
423+
}
424+
}
425+
426+
// Only show exported functions for non-optimized files
427+
if !show_size || size_savings.is_none() {
428+
let parser = wasmparser::Parser::new(0);
429+
let export_names: Vec<&str> = parser
430+
.parse_all(&wasm_bytes)
431+
.filter_map(Result::ok)
432+
.filter_map(|payload| {
433+
if let wasmparser::Payload::ExportSection(exports) = payload {
434+
Some(exports)
435+
} else {
436+
None
437+
}
438+
})
439+
.flatten()
440+
.filter_map(Result::ok)
441+
.filter(|export| matches!(export.kind, wasmparser::ExternalKind::Func))
442+
.map(|export| export.name)
443+
.sorted()
444+
.collect();
445+
if export_names.is_empty() {
446+
print.blankln("Exported Functions: None found");
447+
} else {
448+
print.blankln(format!("Exported Functions: {} found", export_names.len()));
449+
for name in export_names {
450+
print.blankln(format!(" • {name}"));
430451
}
431-
})
432-
.flatten()
433-
.filter_map(Result::ok)
434-
.filter(|export| matches!(export.kind, wasmparser::ExternalKind::Func))
435-
.map(|export| export.name)
436-
.sorted()
437-
.collect();
438-
if export_names.is_empty() {
439-
print.blankln("Exported Functions: None found");
440-
} else {
441-
print.blankln(format!("Exported Functions: {} found", export_names.len()));
442-
for name in export_names {
443-
print.blankln(format!(" • {name}"));
444452
}
445453
}
446454

0 commit comments

Comments
 (0)