Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions FULL_HELP_DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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 <META>` — Add key-value to contract meta (adds the meta to the `contractmetav0` custom section)
- `--optimize` — Optimize the generated wasm

## `stellar contract extend`

Expand Down Expand Up @@ -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 <WASM>`

Expand Down Expand Up @@ -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 <SOURCE_ACCOUNT> --wasm <WASM>`

Expand Down Expand Up @@ -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 <WASM>...`

Expand Down
95 changes: 83 additions & 12 deletions cmd/soroban-cli/src/commands/contract/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
///
Expand All @@ -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)]
Expand All @@ -41,12 +46,15 @@ pub struct Cmd {
/// If omitted, all packages that build for crate-type cdylib are built.
#[arg(long)]
pub package: Option<String>,

/// 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<String>,

/// Build with the all features activated
#[arg(
long,
Expand All @@ -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
Expand All @@ -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<std::path::PathBuf>,

/// 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> {
Expand All @@ -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";
Expand Down Expand Up @@ -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)
Expand All @@ -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<u8> = 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);
}
}

Expand Down Expand Up @@ -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<u8>,
optimized_wasm_bytes: Vec<u8>,
) {
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)
Expand All @@ -363,6 +434,7 @@ impl Cmd {
.map(|export| export.name)
.sorted()
.collect();

if export_names.is_empty() {
print.blankln("Exported Functions: None found");
} else {
Expand All @@ -371,9 +443,8 @@ impl Cmd {
print.blankln(format!(" • {name}"));
}
}
print.checkln("Build Complete");

Ok(())
print.checkln("Build Complete\n");
}
}

Expand Down
8 changes: 4 additions & 4 deletions cmd/soroban-cli/src/commands/contract/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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?,
Expand Down
Loading