diff --git a/src/cmd/build.rs b/src/cmd/build.rs
index d2e9f7f056a04624e4fe70b65af8726254aea2ab..0435cfe2532f97f44248ae030adc4c7f067ece76 100644
--- a/src/cmd/build.rs
+++ b/src/cmd/build.rs
@@ -15,6 +15,7 @@
// along with cargo-contract. If not, see .
use std::{
+ convert::TryFrom,
fs::{metadata, File},
io::{Read, Write},
path::PathBuf,
@@ -24,15 +25,89 @@ use crate::{
crate_metadata::CrateMetadata,
util,
workspace::{ManifestPath, Profile, Workspace},
- GenerateArtifacts, GenerationResult, OptimizationResult, UnstableFlags, Verbosity,
+ BuildArtifacts, BuildResult, UnstableFlags, UnstableOptions, VerbosityFlags,
};
+use crate::{OptimizationResult, Verbosity};
use anyhow::{Context, Result};
use colored::Colorize;
use parity_wasm::elements::{External, MemoryType, Module, Section};
+use structopt::StructOpt;
/// This is the maximum number of pages available for a contract to allocate.
const MAX_MEMORY_PAGES: u32 = 16;
+/// Executes build of the smart-contract which produces a wasm binary that is ready for deploying.
+///
+/// It does so by invoking `cargo build` and then post processing the final binary.
+#[derive(Debug, StructOpt)]
+#[structopt(name = "build")]
+pub struct BuildCommand {
+ /// Path to the Cargo.toml of the contract to build
+ #[structopt(long, parse(from_os_str))]
+ manifest_path: Option,
+ /// Which build artifacts to generate.
+ ///
+ /// - `all`: Generate the Wasm, the metadata and a bundled `.contract` file.
+ ///
+ /// - `code-only`: Only the Wasm is created, generation of metadata and a bundled
+ /// `.contract` file is skipped.
+ #[structopt(
+ long = "generate",
+ default_value = "all",
+ value_name = "all | code-only",
+ verbatim_doc_comment
+ )]
+ build_artifact: BuildArtifacts,
+ #[structopt(flatten)]
+ verbosity: VerbosityFlags,
+ #[structopt(flatten)]
+ unstable_options: UnstableOptions,
+}
+
+impl BuildCommand {
+ pub fn exec(&self) -> Result {
+ let manifest_path = ManifestPath::try_from(self.manifest_path.as_ref())?;
+ let unstable_flags: UnstableFlags =
+ TryFrom::<&UnstableOptions>::try_from(&self.unstable_options)?;
+ let verbosity: Option = TryFrom::<&VerbosityFlags>::try_from(&self.verbosity)?;
+ execute(
+ &manifest_path,
+ verbosity,
+ true,
+ self.build_artifact,
+ unstable_flags,
+ )
+ }
+}
+
+#[derive(Debug, StructOpt)]
+#[structopt(name = "check")]
+pub struct CheckCommand {
+ /// Path to the Cargo.toml of the contract to build
+ #[structopt(long, parse(from_os_str))]
+ manifest_path: Option,
+ #[structopt(flatten)]
+ verbosity: VerbosityFlags,
+ #[structopt(flatten)]
+ unstable_options: UnstableOptions,
+}
+
+impl CheckCommand {
+ pub fn exec(&self) -> Result {
+ let manifest_path = ManifestPath::try_from(self.manifest_path.as_ref())?;
+ let unstable_flags: UnstableFlags =
+ TryFrom::<&UnstableOptions>::try_from(&self.unstable_options)?;
+ let verbosity: Option = TryFrom::<&VerbosityFlags>::try_from(&self.verbosity)?;
+ execute(
+ &manifest_path,
+ verbosity,
+ false,
+ BuildArtifacts::CheckOnly,
+ unstable_flags,
+ )
+ }
+}
+
/// Builds the project in the specified directory, defaults to the current directory.
///
/// Uses the unstable cargo feature [`build-std`](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std)
@@ -51,7 +126,7 @@ const MAX_MEMORY_PAGES: u32 = 16;
fn build_cargo_project(
crate_metadata: &CrateMetadata,
verbosity: Option,
- unstable_options: UnstableFlags,
+ unstable_flags: UnstableFlags,
) -> Result<()> {
util::assert_channel()?;
@@ -80,7 +155,7 @@ fn build_cargo_project(
Ok(())
};
- if unstable_options.original_manifest {
+ if unstable_flags.original_manifest {
println!(
"{} {}",
"warning:".yellow().bold(),
@@ -218,39 +293,34 @@ fn optimize_wasm(crate_metadata: &CrateMetadata) -> Result {
/// Executes build of the smart-contract which produces a wasm binary that is ready for deploying.
///
/// It does so by invoking `cargo build` and then post processing the final binary.
-///
-/// # Note
-///
-/// Collects the contract crate's metadata using the supplied manifest (`Cargo.toml`) path. Use
-/// [`execute_build_with_metadata`] if an instance is already available.
-pub(crate) fn execute(
+fn execute(
manifest_path: &ManifestPath,
verbosity: Option,
optimize_contract: bool,
- build_artifact: GenerateArtifacts,
- unstable_options: UnstableFlags,
-) -> Result {
+ build_artifact: BuildArtifacts,
+ unstable_flags: UnstableFlags,
+) -> Result {
let crate_metadata = CrateMetadata::collect(manifest_path)?;
- if build_artifact == GenerateArtifacts::CodeOnly {
+ if build_artifact == BuildArtifacts::CodeOnly || build_artifact == BuildArtifacts::CheckOnly {
let (maybe_dest_wasm, maybe_optimization_result) = execute_with_crate_metadata(
&crate_metadata,
verbosity,
optimize_contract,
build_artifact,
- unstable_options,
+ unstable_flags,
)?;
- let res = GenerationResult {
+ let res = BuildResult {
dest_wasm: maybe_dest_wasm,
dest_metadata: None,
dest_bundle: None,
target_directory: crate_metadata.cargo_meta.target_directory,
optimization_result: maybe_optimization_result,
+ build_artifact,
};
return Ok(res);
}
- let res =
- super::metadata::execute(&manifest_path, verbosity, build_artifact, unstable_options)?;
+ let res = super::metadata::execute(&manifest_path, verbosity, build_artifact, unstable_flags)?;
Ok(res)
}
@@ -267,15 +337,15 @@ pub(crate) fn execute_with_crate_metadata(
crate_metadata: &CrateMetadata,
verbosity: Option,
optimize_contract: bool,
- build_artifact: GenerateArtifacts,
- unstable_options: UnstableFlags,
+ build_artifact: BuildArtifacts,
+ unstable_flags: UnstableFlags,
) -> Result<(Option, Option)> {
println!(
" {} {}",
format!("[1/{}]", build_artifact.steps()).bold(),
"Building cargo project".bright_green().bold()
);
- build_cargo_project(&crate_metadata, verbosity, unstable_options)?;
+ build_cargo_project(&crate_metadata, verbosity, unstable_flags)?;
println!(
" {} {}",
format!("[2/{}]", build_artifact.steps()).bold(),
@@ -300,7 +370,7 @@ pub(crate) fn execute_with_crate_metadata(
#[cfg(feature = "test-ci-only")]
#[cfg(test)]
mod tests {
- use crate::{cmd, util::tests::with_tmp_dir, GenerateArtifacts, ManifestPath, UnstableFlags};
+ use crate::{cmd, util::tests::with_tmp_dir, BuildArtifacts, ManifestPath, UnstableFlags};
#[test]
fn build_template() {
@@ -312,7 +382,7 @@ mod tests {
&manifest_path,
None,
true,
- GenerateArtifacts::All,
+ BuildArtifacts::All,
UnstableFlags::default(),
)
.expect("build failed");
diff --git a/src/cmd/metadata.rs b/src/cmd/metadata.rs
index 32b8677a56f48103f80b82d585113f08de79b9b8..d93565adf6e3d2997ec7dd91da81c01af4f3c34b 100644
--- a/src/cmd/metadata.rs
+++ b/src/cmd/metadata.rs
@@ -18,7 +18,7 @@ use crate::{
crate_metadata::CrateMetadata,
util,
workspace::{ManifestPath, Workspace},
- GenerateArtifacts, GenerationResult, OptimizationResult, UnstableFlags, Verbosity,
+ BuildArtifacts, BuildResult, OptimizationResult, UnstableFlags, Verbosity,
};
use anyhow::Result;
@@ -38,7 +38,7 @@ const METADATA_FILE: &str = "metadata.json";
struct GenerateMetadataCommand {
crate_metadata: CrateMetadata,
verbosity: Option,
- build_artifact: GenerateArtifacts,
+ build_artifact: BuildArtifacts,
unstable_options: UnstableFlags,
}
@@ -52,7 +52,7 @@ struct ExtendedMetadataResult {
}
impl GenerateMetadataCommand {
- pub fn exec(&self) -> Result {
+ pub fn exec(&self) -> Result {
util::assert_channel()?;
let cargo_meta = &self.crate_metadata.cargo_meta;
@@ -104,7 +104,7 @@ impl GenerateMetadataCommand {
current_progress += 1;
}
- if self.build_artifact == GenerateArtifacts::All {
+ if self.build_artifact == BuildArtifacts::All {
println!(
" {} {}",
format!("[{}/{}]", current_progress, self.build_artifact.steps()).bold(),
@@ -131,17 +131,18 @@ impl GenerateMetadataCommand {
.using_temp(generate_metadata)?;
}
- let dest_bundle = if self.build_artifact == GenerateArtifacts::All {
+ let dest_bundle = if self.build_artifact == BuildArtifacts::All {
Some(out_path_bundle)
} else {
None
};
- Ok(GenerationResult {
+ Ok(BuildResult {
dest_metadata: Some(out_path_metadata),
dest_wasm,
dest_bundle,
optimization_result,
target_directory,
+ build_artifact: self.build_artifact,
})
}
@@ -167,7 +168,7 @@ impl GenerateMetadataCommand {
let source = {
let lang = SourceLanguage::new(Language::Ink, ink_version.clone());
let compiler = SourceCompiler::new(Compiler::RustC, rust_version);
- let maybe_wasm = if self.build_artifact == GenerateArtifacts::All {
+ let maybe_wasm = if self.build_artifact == BuildArtifacts::All {
let wasm = fs::read(&self.crate_metadata.dest_wasm)?;
// The Wasm which we read must have the same hash as `source.hash`
debug_assert!({
@@ -258,9 +259,9 @@ fn blake2_hash(code: &[u8]) -> CodeHash {
pub(crate) fn execute(
manifest_path: &ManifestPath,
verbosity: Option,
- build_artifact: GenerateArtifacts,
+ build_artifact: BuildArtifacts,
unstable_options: UnstableFlags,
-) -> Result {
+) -> Result {
let crate_metadata = CrateMetadata::collect(manifest_path)?;
let res = GenerateMetadataCommand {
crate_metadata,
@@ -277,7 +278,7 @@ pub(crate) fn execute(
mod tests {
use crate::cmd::metadata::blake2_hash;
use crate::{
- cmd, crate_metadata::CrateMetadata, util::tests::with_tmp_dir, GenerateArtifacts,
+ cmd, crate_metadata::CrateMetadata, util::tests::with_tmp_dir, BuildArtifacts,
ManifestPath, UnstableFlags,
};
use contract_metadata::*;
@@ -375,7 +376,7 @@ mod tests {
let dest_bundle = cmd::metadata::execute(
&test_manifest.manifest_path,
None,
- GenerateArtifacts::All,
+ BuildArtifacts::All,
UnstableFlags::default(),
)?
.dest_bundle
diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs
index 6a7191d6d10eb59dedc4eceed4b61a2124a7fc29..01483a48deafd8aaa7fa7b3e5cc6ddad41869c1b 100644
--- a/src/cmd/mod.rs
+++ b/src/cmd/mod.rs
@@ -22,5 +22,6 @@ mod instantiate;
pub mod metadata;
pub mod new;
+pub(crate) use self::build::{BuildCommand, CheckCommand};
#[cfg(feature = "extrinsics")]
pub(crate) use self::{deploy::execute_deploy, instantiate::execute_instantiate};
diff --git a/src/main.rs b/src/main.rs
index c5f6823187e190f7732245fea1ead538c473b8e5..6fed88ddee63af6b147eb35e10e17137e5c547a5 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -21,12 +21,11 @@ mod workspace;
use self::workspace::ManifestPath;
+use crate::cmd::{BuildCommand, CheckCommand};
+
#[cfg(feature = "extrinsics")]
use sp_core::{crypto::Pair, sr25519, H256};
-use std::{
- convert::{TryFrom, TryInto},
- path::PathBuf,
-};
+use std::{convert::TryFrom, path::PathBuf};
#[cfg(feature = "extrinsics")]
use subxt::PairSigner;
@@ -93,8 +92,8 @@ impl ExtrinsicOpts {
}
}
-#[derive(Debug, StructOpt)]
-struct VerbosityFlags {
+#[derive(Clone, Debug, StructOpt)]
+pub struct VerbosityFlags {
#[structopt(long)]
quiet: bool,
#[structopt(long)]
@@ -120,7 +119,7 @@ impl TryFrom<&VerbosityFlags> for Option {
}
}
-#[derive(Debug, StructOpt)]
+#[derive(Clone, Debug, StructOpt)]
struct UnstableOptions {
/// Use the original manifest (Cargo.toml), do not modify for build optimizations
#[structopt(long = "unstable-options", short = "Z", number_of_values = 1)]
@@ -154,39 +153,77 @@ impl TryFrom<&UnstableOptions> for UnstableFlags {
/// Describes which artifacts to generate
#[derive(Copy, Clone, Eq, PartialEq, Debug, StructOpt)]
#[structopt(name = "build-artifacts")]
-pub enum GenerateArtifacts {
+pub enum BuildArtifacts {
/// Generate the Wasm, the metadata and a bundled `.contract` file
#[structopt(name = "all")]
All,
/// Only the Wasm is created, generation of metadata and a bundled `.contract` file is skipped
#[structopt(name = "code-only")]
CodeOnly,
+ CheckOnly,
}
-impl GenerateArtifacts {
+impl BuildArtifacts {
/// Returns the number of steps required to complete a build artifact.
/// Used as output on the cli.
pub fn steps(&self) -> usize {
match self {
- GenerateArtifacts::All => 5,
- GenerateArtifacts::CodeOnly => 3,
+ BuildArtifacts::All => 5,
+ BuildArtifacts::CodeOnly => 3,
+ BuildArtifacts::CheckOnly => 2,
+ }
+ }
+}
+
+impl std::str::FromStr for BuildArtifacts {
+ type Err = String;
+ fn from_str(artifact: &str) -> Result {
+ match artifact {
+ "all" => Ok(BuildArtifacts::All),
+ "code-only" => Ok(BuildArtifacts::CodeOnly),
+ _ => Err("Could not parse build artifact".to_string()),
}
}
+}
- pub fn display(&self, result: &GenerationResult) -> String {
- let optimization = GenerationResult::display_optimization(result);
+/// Result of the metadata generation process.
+pub struct BuildResult {
+ /// Path to the resulting metadata file.
+ pub dest_metadata: Option,
+ /// Path to the resulting Wasm file.
+ pub dest_wasm: Option,
+ /// Path to the bundled file.
+ pub dest_bundle: Option,
+ /// Path to the directory where output files are written to.
+ pub target_directory: PathBuf,
+ /// If existent the result of the optimization.
+ pub optimization_result: Option,
+ /// Which build artifacts were generated.
+ pub build_artifact: BuildArtifacts,
+}
+
+/// Result of the optimization process.
+pub struct OptimizationResult {
+ /// The original Wasm size.
+ pub original_size: f64,
+ /// The Wasm size after optimizations have been applied.
+ pub optimized_size: f64,
+}
+
+impl BuildResult {
+ pub fn display(&self) -> String {
+ let optimization = self.display_optimization();
let size_diff = format!(
"\nOriginal wasm size: {}, Optimized: {}\n\n",
format!("{:.1}K", optimization.0).bold(),
format!("{:.1}K", optimization.1).bold(),
);
- if self == &GenerateArtifacts::CodeOnly {
+ if self.build_artifact == BuildArtifacts::CodeOnly {
let out = format!(
"{}Your contract's code is ready. You can find it here:\n{}",
size_diff,
- result
- .dest_wasm
+ self.dest_wasm
.as_ref()
.expect("wasm path must exist")
.display()
@@ -199,80 +236,37 @@ impl GenerateArtifacts {
let mut out = format!(
"{}Your contract artifacts are ready. You can find them in:\n{}\n\n",
size_diff,
- result.target_directory.display().to_string().bold(),
+ self.target_directory.display().to_string().bold(),
);
- if let Some(dest_bundle) = result.dest_bundle.as_ref() {
+ if let Some(dest_bundle) = self.dest_bundle.as_ref() {
let bundle = format!(
" - {} (code + metadata)\n",
- GenerationResult::display(&dest_bundle).bold()
+ util::base_name(&dest_bundle).bold()
);
out.push_str(&bundle);
}
- if let Some(dest_wasm) = result.dest_wasm.as_ref() {
+ if let Some(dest_wasm) = self.dest_wasm.as_ref() {
let wasm = format!(
" - {} (the contract's code)\n",
- GenerationResult::display(&dest_wasm).bold()
+ util::base_name(&dest_wasm).bold()
);
out.push_str(&wasm);
}
- if let Some(dest_metadata) = result.dest_metadata.as_ref() {
+ if let Some(dest_metadata) = self.dest_metadata.as_ref() {
let metadata = format!(
" - {} (the contract's metadata)",
- GenerationResult::display(&dest_metadata).bold()
+ util::base_name(&dest_metadata).bold()
);
out.push_str(&metadata);
}
out
}
-}
-
-impl std::str::FromStr for GenerateArtifacts {
- type Err = String;
- fn from_str(artifact: &str) -> Result {
- match artifact {
- "all" => Ok(GenerateArtifacts::All),
- "code-only" => Ok(GenerateArtifacts::CodeOnly),
- _ => Err("Could not parse build artifact".to_string()),
- }
- }
-}
-
-/// Result of the metadata generation process.
-pub struct GenerationResult {
- /// Path to the resulting metadata file.
- pub dest_metadata: Option,
- /// Path to the resulting Wasm file.
- pub dest_wasm: Option,
- /// Path to the bundled file.
- pub dest_bundle: Option,
- /// Path to the directory where output files are written to.
- pub target_directory: PathBuf,
- /// If existent the result of the optimization.
- pub optimization_result: Option,
-}
-
-/// Result of the optimization process.
-pub struct OptimizationResult {
- /// The original Wasm size.
- pub original_size: f64,
- /// The Wasm size after optimizations have been applied.
- pub optimized_size: f64,
-}
-
-impl GenerationResult {
- /// Returns the base name of the path.
- pub fn display(path: &PathBuf) -> &str {
- path.file_name()
- .expect("file name must exist")
- .to_str()
- .expect("must be valid utf-8")
- }
/// Returns a tuple of `(original_size, optimized_size)`.
///
/// Panics if no optimization result is available.
- pub fn display_optimization(res: &GenerationResult) -> (f64, f64) {
- let optimization = res
+ fn display_optimization(&self) -> (f64, f64) {
+ let optimization = self
.optimization_result
.as_ref()
.expect("optimization result must exist");
@@ -293,42 +287,13 @@ enum Command {
},
/// Compiles the contract, generates metadata, bundles both together in a `.contract` file
#[structopt(name = "build")]
- Build {
- /// Path to the Cargo.toml of the contract to build
- #[structopt(long, parse(from_os_str))]
- manifest_path: Option,
- /// Which build artifacts to generate.
- ///
- /// - `all`: Generate the Wasm, the metadata and a bundled `.contract` file.
- ///
- /// - `code-only`: Only the Wasm is created, generation of metadata and a bundled
- /// `.contract` file is skipped.
- #[structopt(
- long = "generate",
- default_value = "all",
- value_name = "all | code-only",
- verbatim_doc_comment
- )]
- build_artifact: GenerateArtifacts,
- #[structopt(flatten)]
- verbosity: VerbosityFlags,
- #[structopt(flatten)]
- unstable_options: UnstableOptions,
- },
+ Build(BuildCommand),
/// Command has been deprecated, use `cargo contract build` instead
#[structopt(name = "generate-metadata")]
GenerateMetadata {},
/// Check that the code builds as Wasm; does not output any build artifact to the top level `target/` directory
#[structopt(name = "check")]
- Check {
- /// Path to the Cargo.toml of the contract to build
- #[structopt(long, parse(from_os_str))]
- manifest_path: Option,
- #[structopt(flatten)]
- verbosity: VerbosityFlags,
- #[structopt(flatten)]
- unstable_options: UnstableOptions,
- },
+ Check(CheckCommand),
/// Test the smart contract off-chain
#[structopt(name = "test")]
Test {},
@@ -394,37 +359,16 @@ fn main() {
fn exec(cmd: Command) -> Result {
match &cmd {
Command::New { name, target_dir } => cmd::new::execute(name, target_dir.as_ref()),
- Command::Build {
- manifest_path,
- verbosity,
- build_artifact,
- unstable_options,
- } => {
- let manifest_path = ManifestPath::try_from(manifest_path.as_ref())?;
- let result = cmd::build::execute(
- &manifest_path,
- verbosity.try_into()?,
- true,
- *build_artifact,
- unstable_options.try_into()?,
- )?;
-
- Ok(build_artifact.display(&result))
+ Command::Build(build) => {
+ let result = build.exec()?;
+ Ok(result.display())
}
- Command::Check {
- manifest_path,
- verbosity,
- unstable_options,
- } => {
- let manifest_path = ManifestPath::try_from(manifest_path.as_ref())?;
- let res = cmd::build::execute(
- &manifest_path,
- verbosity.try_into()?,
- false,
- GenerateArtifacts::CodeOnly,
- unstable_options.try_into()?,
- )?;
- assert!(res.dest_wasm.is_none(), "no dest_wasm should exist");
+ Command::Check(check) => {
+ let res = check.exec()?;
+ assert!(
+ res.dest_wasm.is_none(),
+ "no dest_wasm must be on the generation result"
+ );
Ok("\nYour contract's code was built successfully.".to_string())
}
Command::GenerateMetadata {} => Err(anyhow::anyhow!(
diff --git a/src/util.rs b/src/util.rs
index 84b2b89488d57e4cfbb68a4e4500e7c4b2b01004..f8deaa40a2f5510dbb1ba4d1bbc3b33b93b863b7 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -17,6 +17,7 @@
use crate::Verbosity;
use anyhow::{Context, Result};
use rustc_version::Channel;
+use std::path::PathBuf;
use std::{ffi::OsStr, path::Path, process::Command};
/// Check whether the current rust channel is valid: `nightly` is recommended.
@@ -84,6 +85,14 @@ where
}
}
+/// Returns the base name of the path.
+pub(crate) fn base_name(path: &PathBuf) -> &str {
+ path.file_name()
+ .expect("file name must exist")
+ .to_str()
+ .expect("must be valid utf-8")
+}
+
#[cfg(test)]
pub mod tests {
use std::path::Path;