diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md
index e15af8691c..41be49507b 100644
--- a/FULL_HELP_DOCS.md
+++ b/FULL_HELP_DOCS.md
@@ -89,11 +89,11 @@ Tools for smart contract developers
- `id` — Generate the contract id for a given contract or asset
- `info` — Access info about contracts
- `init` — Initialize a Soroban contract project
-- `inspect` — (Deprecated in favor of `contract info` subcommand) Inspect a WASM file listing contract functions, meta, etc
+- `inspect` — (Deprecated, use `contract info`) Inspect a WASM file listing contract functions, meta, etc
- `upload` — Install a WASM file to the ledger without creating a contract instance
-- `install` — (Deprecated in favor of `contract upload` subcommand) Install a WASM file to the ledger without creating a contract instance
+- `install` — (Deprecated, use `contract upload`) Install a WASM file to the ledger without creating a contract instance
- `invoke` — Invoke a contract function
-- `optimize` — Optimize a WASM file
+- `optimize` — (Deprecated, use `build --optimize`) Optimize a WASM file
- `read` — Print the current value of a contract-data ledger entry
- `restore` — Restore an evicted value for a contract-data legder entry
@@ -357,6 +357,7 @@ To view the commands that will be executed, without executing them, use the --pr
- `--print-commands-only` — Print commands to build without executing them
- `--meta ` — Add key-value to contract meta (adds the meta to the `contractmetav0` custom section)
+- `--optimize` — Optimize the generated wasm
## `stellar contract extend`
@@ -661,7 +662,7 @@ This command will create a Cargo workspace project and add a sample Stellar cont
## `stellar contract inspect`
-(Deprecated in favor of `contract info` subcommand) Inspect a WASM file listing contract functions, meta, etc
+(Deprecated, use `contract info`) Inspect a WASM file listing contract functions, meta, etc
**Usage:** `stellar contract inspect [OPTIONS] --wasm `
@@ -713,7 +714,7 @@ Install a WASM file to the ledger without creating a contract instance
## `stellar contract install`
-(Deprecated in favor of `contract upload` subcommand) Install a WASM file to the ledger without creating a contract instance
+(Deprecated, use `contract upload`) Install a WASM file to the ledger without creating a contract instance
**Usage:** `stellar contract install [OPTIONS] --source-account --wasm `
@@ -789,7 +790,7 @@ stellar contract invoke ... -- --help
## `stellar contract optimize`
-Optimize a WASM file
+(Deprecated, use `build --optimize`) Optimize a WASM file
**Usage:** `stellar contract optimize [OPTIONS] --wasm ...`
diff --git a/cmd/soroban-cli/src/commands/contract/build.rs b/cmd/soroban-cli/src/commands/contract/build.rs
index 7dbb296781..440eef9220 100644
--- a/cmd/soroban-cli/src/commands/contract/build.rs
+++ b/cmd/soroban-cli/src/commands/contract/build.rs
@@ -17,7 +17,11 @@ use std::{
};
use stellar_xdr::curr::{Limited, Limits, ScMetaEntry, ScMetaV0, StringM, WriteXdr};
-use crate::{commands::global, print::Print};
+use crate::{
+ commands::{contract::optimize, global},
+ print::Print,
+ wasm,
+};
/// Build a contract from source
///
@@ -32,6 +36,7 @@ use crate::{commands::global, print::Print};
/// To view the commands that will be executed, without executing them, use the
/// --print-commands-only option.
#[derive(Parser, Debug, Clone)]
+#[allow(clippy::struct_excessive_bools)]
pub struct Cmd {
/// Path to Cargo.toml
#[arg(long)]
@@ -41,12 +46,15 @@ pub struct Cmd {
/// If omitted, all packages that build for crate-type cdylib are built.
#[arg(long)]
pub package: Option,
+
/// Build with the specified profile
#[arg(long, default_value = "release")]
pub profile: String,
+
/// Build with the list of features activated, space or comma separated
#[arg(long, help_heading = "Features")]
pub features: Option,
+
/// Build with the all features activated
#[arg(
long,
@@ -55,9 +63,11 @@ pub struct Cmd {
help_heading = "Features"
)]
pub all_features: bool,
+
/// Build with the default feature not activated
#[arg(long, help_heading = "Features")]
pub no_default_features: bool,
+
/// Directory to copy wasm files to
///
/// If provided, wasm files can be found in the cargo target directory, and
@@ -66,12 +76,18 @@ pub struct Cmd {
/// If ommitted, wasm files are written only to the cargo target directory.
#[arg(long)]
pub out_dir: Option,
+
/// Print commands to build without executing them
#[arg(long, conflicts_with = "out_dir", help_heading = "Other")]
pub print_commands_only: bool,
+
/// Add key-value to contract meta (adds the meta to the `contractmetav0` custom section)
#[arg(long, num_args=1, value_parser=parse_meta_arg, action=clap::ArgAction::Append, help_heading = "Metadata")]
pub meta: Vec<(String, String)>,
+
+ /// Optimize the generated wasm.
+ #[arg(long)]
+ pub optimize: bool,
}
fn parse_meta_arg(s: &str) -> Result<(String, String), Error> {
@@ -89,34 +105,54 @@ fn parse_meta_arg(s: &str) -> Result<(String, String), Error> {
pub enum Error {
#[error(transparent)]
Metadata(#[from] cargo_metadata::Error),
+
#[error(transparent)]
CargoCmd(io::Error),
+
#[error("exit status {0}")]
Exit(ExitStatus),
+
#[error("package {package} not found")]
PackageNotFound { package: String },
+
#[error("finding absolute path of Cargo.toml: {0}")]
AbsolutePath(io::Error),
+
#[error("creating out directory: {0}")]
CreatingOutDir(io::Error),
+
#[error("deleting existing artifact: {0}")]
DeletingArtifact(io::Error),
+
#[error("copying wasm file: {0}")]
CopyingWasmFile(io::Error),
+
#[error("getting the current directory: {0}")]
GettingCurrentDir(io::Error),
+
#[error("retreiving CARGO_HOME: {0}")]
CargoHome(io::Error),
+
#[error("reading wasm file: {0}")]
ReadingWasmFile(io::Error),
+
#[error("writing wasm file: {0}")]
WritingWasmFile(io::Error),
+
#[error("invalid meta entry: {0}")]
MetaArg(String),
+
#[error("use rust 1.81 or 1.84+ to build contracts (got {0})")]
RustVersion(String),
+
#[error(transparent)]
Xdr(#[from] stellar_xdr::curr::Error),
+
+ #[error(transparent)]
+ Optimize(#[from] optimize::Error),
+
+ #[error(transparent)]
+ Wasm(#[from] wasm::Error),
}
const WASM_TARGET: &str = "wasm32v1-none";
@@ -202,7 +238,8 @@ impl Cmd {
return Err(Error::Exit(status));
}
- let file = format!("{}.wasm", p.name.replace('-', "_"));
+ let wasm_name = p.name.replace('-', "_");
+ let file = format!("{wasm_name}.wasm");
let target_file_path = Path::new(target_dir)
.join(&wasm_target)
.join(&self.profile)
@@ -219,7 +256,20 @@ impl Cmd {
target_file_path
};
- Self::print_build_summary(&print, &final_path)?;
+ let wasm_bytes = fs::read(&final_path).map_err(Error::ReadingWasmFile)?;
+ let mut optimized_wasm_bytes: Vec = Vec::new();
+
+ if self.optimize {
+ let mut path = final_path.clone();
+ path.set_extension("optimized.wasm");
+ optimize::optimize(true, vec![final_path.clone()], Some(path.clone()))?;
+ optimized_wasm_bytes = fs::read(&path).map_err(Error::ReadingWasmFile)?;
+
+ fs::remove_file(&final_path).map_err(Error::DeletingArtifact)?;
+ fs::rename(&path, &final_path).map_err(Error::CopyingWasmFile)?;
+ }
+
+ Self::print_build_summary(&print, &final_path, wasm_bytes, optimized_wasm_bytes);
}
}
@@ -332,20 +382,41 @@ impl Cmd {
Ok(buffer)
}
- fn print_build_summary(print: &Print, target_file_path: &PathBuf) -> Result<(), Error> {
+ fn print_build_summary(
+ print: &Print,
+ path: &Path,
+ wasm_bytes: Vec,
+ optimized_wasm_bytes: Vec,
+ ) {
print.infoln("Build Summary:");
- let rel_target_file_path = target_file_path
+
+ let rel_path = path
.strip_prefix(env::current_dir().unwrap())
- .unwrap_or(target_file_path);
- print.blankln(format!("Wasm File: {}", rel_target_file_path.display()));
+ .unwrap_or(path);
+
+ let size = wasm_bytes.len();
+ let optimized_size = optimized_wasm_bytes.len();
+
+ let size_description = if optimized_size > 0 {
+ format!("{optimized_size} bytes optimized (original size was {size} bytes)")
+ } else {
+ format!("{size} bytes")
+ };
- let wasm_bytes = fs::read(target_file_path).map_err(Error::ReadingWasmFile)?;
+ let bytes = if optimized_size > 0 {
+ &optimized_wasm_bytes
+ } else {
+ &wasm_bytes
+ };
print.blankln(format!(
- "Wasm Hash: {}",
- hex::encode(Sha256::digest(&wasm_bytes))
+ "Wasm File: {path} ({size_description})",
+ path = rel_path.display()
));
+ print.blankln(format!("Wasm Hash: {}", hex::encode(Sha256::digest(bytes))));
+ print.blankln(format!("Wasm Size: {size_description}"));
+
let parser = wasmparser::Parser::new(0);
let export_names: Vec<&str> = parser
.parse_all(&wasm_bytes)
@@ -363,6 +434,7 @@ impl Cmd {
.map(|export| export.name)
.sorted()
.collect();
+
if export_names.is_empty() {
print.blankln("Exported Functions: None found");
} else {
@@ -371,9 +443,8 @@ impl Cmd {
print.blankln(format!(" • {name}"));
}
}
- print.checkln("Build Complete");
- Ok(())
+ print.checkln("Build Complete\n");
}
}
diff --git a/cmd/soroban-cli/src/commands/contract/mod.rs b/cmd/soroban-cli/src/commands/contract/mod.rs
index dd642755ad..0c2bd968ae 100644
--- a/cmd/soroban-cli/src/commands/contract/mod.rs
+++ b/cmd/soroban-cli/src/commands/contract/mod.rs
@@ -61,14 +61,14 @@ pub enum Cmd {
/// be overwritten unless `--overwrite` is passed.
Init(init::Cmd),
- /// (Deprecated in favor of `contract info` subcommand) Inspect a WASM file listing contract functions, meta, etc
+ /// (Deprecated, use `contract info`) Inspect a WASM file listing contract functions, meta, etc
#[command(display_order = 100)]
Inspect(inspect::Cmd),
/// Install a WASM file to the ledger without creating a contract instance
Upload(upload::Cmd),
- /// (Deprecated in favor of `contract upload` subcommand) Install a WASM file to the ledger without creating a contract instance
+ /// (Deprecated, use `contract upload`) Install a WASM file to the ledger without creating a contract instance
Install(upload::Cmd),
/// Invoke a contract function
@@ -81,7 +81,7 @@ pub enum Cmd {
/// stellar contract invoke ... -- --help
Invoke(invoke::Cmd),
- /// Optimize a WASM file
+ /// (Deprecated, use `build --optimize`) Optimize a WASM file
Optimize(optimize::Cmd),
/// Print the current value of a contract-data ledger entry
@@ -165,7 +165,7 @@ impl Cmd {
}
Cmd::Upload(upload) => upload.run(global_args).await?,
Cmd::Invoke(invoke) => invoke.run(global_args).await?,
- Cmd::Optimize(optimize) => optimize.run()?,
+ Cmd::Optimize(optimize) => optimize.run(global_args)?,
Cmd::Fetch(fetch) => fetch.run().await?,
Cmd::Read(read) => read.run().await?,
Cmd::Restore(restore) => restore.run().await?,
diff --git a/cmd/soroban-cli/src/commands/contract/optimize.rs b/cmd/soroban-cli/src/commands/contract/optimize.rs
index e8d21cda44..5e2310afa1 100644
--- a/cmd/soroban-cli/src/commands/contract/optimize.rs
+++ b/cmd/soroban-cli/src/commands/contract/optimize.rs
@@ -3,6 +3,8 @@ use std::{fmt::Debug, path::PathBuf};
#[cfg(feature = "additional-libs")]
use wasm_opt::{Feature, OptimizationError, OptimizationOptions};
+#[cfg(feature = "additional-libs")]
+use crate::commands::global;
use crate::wasm;
#[derive(Parser, Debug, Clone)]
@@ -36,60 +38,77 @@ pub enum Error {
impl Cmd {
#[cfg(not(feature = "additional-libs"))]
- pub fn run(&self) -> Result<(), Error> {
+ pub fn run(&self, _global_args: &global::Args) -> Result<(), Error> {
Err(Error::Install)
}
#[cfg(feature = "additional-libs")]
- pub fn run(&self) -> Result<(), Error> {
- if self.wasm.len() > 1 && self.wasm_out.is_some() {
- return Err(Error::MultipleFilesOutput);
- }
+ pub fn run(&self, global_args: &global::Args) -> Result<(), Error> {
+ use crate::print::Print;
+
+ let print = Print::new(global_args.quiet);
+ print
+ .warnln("`stellar contract optimize` is deprecated and will be removed in the future. Use `stellar contract build --optimize` instead.");
+
+ optimize(false, self.wasm.clone(), self.wasm_out.clone())
+ }
+}
+
+#[cfg(feature = "additional-libs")]
+pub fn optimize(
+ quiet: bool,
+ wasm: Vec,
+ wasm_out: Option,
+) -> Result<(), Error> {
+ if wasm.len() > 1 && wasm_out.is_some() {
+ return Err(Error::MultipleFilesOutput);
+ }
- for wasm_path in &self.wasm {
- let wasm_arg = wasm::Args {
- wasm: wasm_path.into(),
- };
- let wasm_size = wasm_arg.len()?;
+ for wasm_path in &wasm {
+ let wasm_arg = wasm::Args {
+ wasm: wasm_path.into(),
+ };
+ if !quiet {
println!(
- "Reading: {} ({} bytes)",
- wasm_arg.wasm.to_string_lossy(),
- wasm_size
+ "Reading: {path} ({wasm_size} bytes)",
+ path = wasm_arg.wasm.to_string_lossy(),
+ wasm_size = wasm_arg.len()?
);
+ }
- let wasm_out = self.wasm_out.clone().unwrap_or_else(|| {
- let mut wasm_out = wasm_arg.wasm.clone();
- wasm_out.set_extension("optimized.wasm");
- wasm_out
- });
-
- let mut options = OptimizationOptions::new_optimize_for_size_aggressively();
- options.converge = true;
-
- // Explicitly set to MVP + sign-ext + mutable-globals, which happens to
- // also be the default featureset, but just to be extra clear we set it
- // explicitly.
- //
- // Formerly Soroban supported only the MVP feature set, but Rust 1.70 as
- // well as Clang generate code with sign-ext + mutable-globals enabled,
- // so Soroban has taken a change to support them also.
- options.mvp_features_only();
- options.enable_feature(Feature::MutableGlobals);
- options.enable_feature(Feature::SignExt);
-
- options
- .run(&wasm_arg.wasm, &wasm_out)
- .map_err(Error::OptimizationError)?;
-
- let wasm_out_size = wasm::len(&wasm_out)?;
+ let wasm_out = wasm_out.clone().unwrap_or_else(|| {
+ let mut wasm_out = wasm_arg.wasm.clone();
+ wasm_out.set_extension("optimized.wasm");
+ wasm_out
+ });
+
+ let mut options = OptimizationOptions::new_optimize_for_size_aggressively();
+ options.converge = true;
+
+ // Explicitly set to MVP + sign-ext + mutable-globals, which happens to
+ // also be the default featureset, but just to be extra clear we set it
+ // explicitly.
+ //
+ // Formerly Soroban supported only the MVP feature set, but Rust 1.70 as
+ // well as Clang generate code with sign-ext + mutable-globals enabled,
+ // so Soroban has taken a change to support them also.
+ options.mvp_features_only();
+ options.enable_feature(Feature::MutableGlobals);
+ options.enable_feature(Feature::SignExt);
+
+ options
+ .run(&wasm_arg.wasm, &wasm_out)
+ .map_err(Error::OptimizationError)?;
+
+ if !quiet {
println!(
- "Optimized: {} ({} bytes)",
- wasm_out.to_string_lossy(),
- wasm_out_size
+ "Optimized: {path} ({size} bytes)",
+ path = wasm_out.to_string_lossy(),
+ size = wasm::len(&wasm_out)?
);
}
-
- Ok(())
}
+
+ Ok(())
}
diff --git a/cmd/soroban-cli/src/print.rs b/cmd/soroban-cli/src/print.rs
index eeea11ebb0..c947832919 100644
--- a/cmd/soroban-cli/src/print.rs
+++ b/cmd/soroban-cli/src/print.rs
@@ -116,6 +116,6 @@ create_print_functions!(exclaim, exclaimln, "❗️");
create_print_functions!(arrow, arrowln, "➡️");
create_print_functions!(log, logln, "📔");
create_print_functions!(event, eventln, "📅");
-create_print_functions!(blank, blankln, " ");
+create_print_functions!(blank, blankln, " ");
create_print_functions!(gear, gearln, "⚙️");
create_print_functions!(dir, dirln, "📁");
diff --git a/licenses.rb b/licenses.rb
deleted file mode 100644
index 4cc6def02a..0000000000
--- a/licenses.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-require "json"
-
-metadata = JSON.parse(`cargo metadata --format-version 1 --frozen`)
-packages = metadata["packages"]
-puts JSON.pretty_generate(packages)