diff --git a/src/cmd/build.rs b/src/cmd/build.rs index 39c628dda0c303f0afe1533a849a19f1482749cd..866d192f32709ef69a8044acb70759ce47e34162 100644 --- a/src/cmd/build.rs +++ b/src/cmd/build.rs @@ -62,6 +62,9 @@ pub struct BuildCommand { verbosity: VerbosityFlags, #[structopt(flatten)] unstable_options: UnstableOptions, + /// Emits debug info into wasm file + #[structopt(long, short)] + debug: bool, } impl BuildCommand { @@ -76,6 +79,7 @@ impl BuildCommand { true, self.build_artifact, unstable_flags, + self.debug, ) } } @@ -104,6 +108,7 @@ impl CheckCommand { false, BuildArtifacts::CheckOnly, unstable_flags, + false, ) } } @@ -127,15 +132,18 @@ fn build_cargo_project( crate_metadata: &CrateMetadata, verbosity: Option, unstable_flags: UnstableFlags, + debug: bool, ) -> Result<()> { util::assert_channel()?; // set linker args via RUSTFLAGS. // Currently will override user defined RUSTFLAGS from .cargo/config. See https://github.com/paritytech/cargo-contract/issues/98. - std::env::set_var( - "RUSTFLAGS", - "-C link-arg=-z -C link-arg=stack-size=65536 -C link-arg=--import-memory", - ); + let mut flags = + "-C link-arg=-z -C link-arg=stack-size=65536 -C link-arg=--import-memory".to_string(); + if debug { + flags.push_str(" -C opt-level=1"); + } + std::env::set_var("RUSTFLAGS", flags); let cargo_build = |manifest_path: &ManifestPath| { let target_dir = &crate_metadata.target_directory; @@ -176,7 +184,6 @@ fn build_cargo_project( // clear RUSTFLAGS std::env::remove_var("RUSTFLAGS"); - Ok(()) } @@ -218,17 +225,17 @@ fn ensure_maximum_memory_pages(module: &mut Module, maximum_allowed_pages: u32) /// Strips all custom sections. /// /// Presently all custom sections are not required so they can be stripped safely. -fn strip_custom_sections(module: &mut Module) { +fn strip_custom_sections(module: &mut Module, debug: bool) { module.sections_mut().retain(|section| match section { Section::Custom(_) => false, - Section::Name(_) => false, + Section::Name(_) => debug, Section::Reloc(_) => false, _ => true, }); } /// Performs required post-processing steps on the wasm artifact. -fn post_process_wasm(crate_metadata: &CrateMetadata) -> Result<()> { +fn post_process_wasm(crate_metadata: &CrateMetadata, debug: bool) -> Result<()> { // Deserialize wasm module from a file. let mut module = parity_wasm::deserialize_file(&crate_metadata.original_wasm).context(format!( @@ -244,7 +251,7 @@ fn post_process_wasm(crate_metadata: &CrateMetadata) -> Result<()> { anyhow::bail!("Optimizer failed"); } ensure_maximum_memory_pages(&mut module, MAX_MEMORY_PAGES)?; - strip_custom_sections(&mut module); + strip_custom_sections(&mut module, debug); parity_wasm::serialize_to_file(&crate_metadata.dest_wasm, module)?; Ok(()) @@ -254,7 +261,10 @@ fn post_process_wasm(crate_metadata: &CrateMetadata) -> Result<()> { /// /// The intention is to reduce the size of bloated wasm binaries as a result of missing /// optimizations (or bugs?) between Rust and Wasm. -fn optimize_wasm(crate_metadata: &CrateMetadata) -> Result { +fn optimize_wasm( + crate_metadata: &CrateMetadata, + debug_info: bool, +) -> Result<(OptimizationResult, Option)> { let mut optimized = crate_metadata.dest_wasm.clone(); optimized.set_file_name(format!("{}-opt.wasm", crate_metadata.package_name)); @@ -264,7 +274,7 @@ fn optimize_wasm(crate_metadata: &CrateMetadata) -> Result { // the default shrink_level: 1, // the default - debug_info: false, + debug_info, }; let mut dest_wasm_file = File::open(crate_metadata.dest_wasm.as_os_str())?; @@ -282,12 +292,28 @@ fn optimize_wasm(crate_metadata: &CrateMetadata) -> Result { let original_size = metadata(&crate_metadata.dest_wasm)?.len() as f64 / 1000.0; let optimized_size = metadata(&optimized)?.len() as f64 / 1000.0; + // move debug source wasm file to `*.src.wasm` + let mut maybe_debug_wasm = None; + if debug_info { + let debug_wasm = PathBuf::from( + &crate_metadata + .dest_wasm + .to_string_lossy() + .replace(".wasm", ".src.wasm"), + ); + std::fs::rename(&crate_metadata.dest_wasm, &debug_wasm)?; + maybe_debug_wasm = Some(debug_wasm); + } + // overwrite existing destination wasm file with the optimised version std::fs::rename(&optimized, &crate_metadata.dest_wasm)?; - Ok(OptimizationResult { - original_size, - optimized_size, - }) + Ok(( + OptimizationResult { + original_size, + optimized_size, + }, + maybe_debug_wasm, + )) } /// Executes build of the smart-contract which produces a wasm binary that is ready for deploying. @@ -299,18 +325,22 @@ fn execute( optimize_contract: bool, build_artifact: BuildArtifacts, unstable_flags: UnstableFlags, + debug: bool, ) -> Result { let crate_metadata = CrateMetadata::collect(manifest_path)?; 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_flags, - )?; + let (maybe_dest_wasm, maybe_dest_debug_wasm, maybe_optimization_result) = + execute_with_crate_metadata( + &crate_metadata, + verbosity, + optimize_contract, + build_artifact, + unstable_flags, + debug, + )?; let res = BuildResult { dest_wasm: maybe_dest_wasm, + maybe_dest_debug_wasm, dest_metadata: None, dest_bundle: None, target_directory: crate_metadata.target_directory, @@ -320,7 +350,13 @@ fn execute( return Ok(res); } - let res = super::metadata::execute(&manifest_path, verbosity, build_artifact, unstable_flags)?; + let res = super::metadata::execute( + &manifest_path, + verbosity, + build_artifact, + unstable_flags, + debug, + )?; Ok(res) } @@ -339,30 +375,32 @@ pub(crate) fn execute_with_crate_metadata( optimize_contract: bool, build_artifact: BuildArtifacts, unstable_flags: UnstableFlags, -) -> Result<(Option, Option)> { + debug: bool, +) -> Result<(Option, Option, Option)> { println!( " {} {}", format!("[1/{}]", build_artifact.steps()).bold(), "Building cargo project".bright_green().bold() ); - build_cargo_project(&crate_metadata, verbosity, unstable_flags)?; + build_cargo_project(&crate_metadata, verbosity, unstable_flags, debug)?; println!( " {} {}", format!("[2/{}]", build_artifact.steps()).bold(), "Post processing wasm file".bright_green().bold() ); - post_process_wasm(&crate_metadata)?; + post_process_wasm(&crate_metadata, debug)?; if !optimize_contract { - return Ok((None, None)); + return Ok((None, None, None)); } println!( " {} {}", format!("[3/{}]", build_artifact.steps()).bold(), "Optimizing wasm file".bright_green().bold() ); - let optimization_result = optimize_wasm(&crate_metadata)?; + let (optimization_result, maybe_dest_debug_wasm) = optimize_wasm(&crate_metadata, debug)?; Ok(( Some(crate_metadata.dest_wasm.clone()), + maybe_dest_debug_wasm, Some(optimization_result), )) } diff --git a/src/cmd/metadata.rs b/src/cmd/metadata.rs index 8ca48262d2df889cac8af9bd83c7674aa5dd39c2..d853dfe357b224f994884d0b90ba7c55c1b5943e 100644 --- a/src/cmd/metadata.rs +++ b/src/cmd/metadata.rs @@ -40,11 +40,13 @@ struct GenerateMetadataCommand { verbosity: Option, build_artifact: BuildArtifacts, unstable_options: UnstableFlags, + debug: bool, } /// Result of generating the extended contract project metadata struct ExtendedMetadataResult { dest_wasm: Option, + maybe_dest_debug_wasm: Option, source: Source, contract: Contract, user: Option, @@ -64,6 +66,7 @@ impl GenerateMetadataCommand { // build the extended contract project metadata let ExtendedMetadataResult { dest_wasm, + maybe_dest_debug_wasm, source, contract, user, @@ -140,6 +143,7 @@ impl GenerateMetadataCommand { Ok(BuildResult { dest_metadata: Some(out_path_metadata), dest_wasm, + maybe_dest_debug_wasm, dest_bundle, optimization_result, target_directory, @@ -165,7 +169,7 @@ impl GenerateMetadataCommand { .transpose()?; let homepage = self.crate_metadata.homepage.clone(); let license = contract_package.license.clone(); - let (dest_wasm, hash, optimization_result) = self.wasm_hash()?; + let (dest_wasm, maybe_dest_debug_wasm, hash, optimization_result) = self.wasm_hash()?; let source = { let lang = SourceLanguage::new(Language::Ink, ink_version.clone()); let compiler = SourceCompiler::new(Compiler::RustC, rust_version); @@ -219,6 +223,7 @@ impl GenerateMetadataCommand { Ok(ExtendedMetadataResult { dest_wasm: Some(dest_wasm), + maybe_dest_debug_wasm, source, contract, user, @@ -229,19 +234,26 @@ impl GenerateMetadataCommand { /// Compile the contract and then hash the resulting Wasm. /// /// Return a tuple of `(dest_wasm, hash, optimization_result)`. - fn wasm_hash(&self) -> Result<(PathBuf, CodeHash, OptimizationResult)> { - let (maybe_dest_wasm, maybe_optimization_res) = super::build::execute_with_crate_metadata( - &self.crate_metadata, - self.verbosity, - true, // for the hash we always use the optimized version of the contract - self.build_artifact, - self.unstable_options.clone(), - )?; + fn wasm_hash(&self) -> Result<(PathBuf, Option, CodeHash, OptimizationResult)> { + let (maybe_dest_wasm, maybe_dest_debug_wasm, maybe_optimization_res) = + super::build::execute_with_crate_metadata( + &self.crate_metadata, + self.verbosity, + true, // for the hash we always use the optimized version of the contract + self.build_artifact, + self.unstable_options.clone(), + self.debug, + )?; let wasm = fs::read(&self.crate_metadata.dest_wasm)?; let dest_wasm = maybe_dest_wasm.expect("dest wasm must exist"); let optimization_res = maybe_optimization_res.expect("optimization result must exist"); - Ok((dest_wasm, blake2_hash(wasm.as_slice()), optimization_res)) + Ok(( + dest_wasm, + maybe_dest_debug_wasm, + blake2_hash(wasm.as_slice()), + optimization_res, + )) } } @@ -262,6 +274,7 @@ pub(crate) fn execute( verbosity: Option, build_artifact: BuildArtifacts, unstable_options: UnstableFlags, + debug: bool, ) -> Result { let crate_metadata = CrateMetadata::collect(manifest_path)?; let res = GenerateMetadataCommand { @@ -269,6 +282,7 @@ pub(crate) fn execute( verbosity, build_artifact, unstable_options, + debug, } .exec()?; Ok(res) diff --git a/src/crate_metadata.rs b/src/crate_metadata.rs index 1e57e950e14cc4815925d7dda555e7c5614e7eef..8cbc2f55da0bf9f9b7cab10a5b9fbabb5c4b7670 100644 --- a/src/crate_metadata.rs +++ b/src/crate_metadata.rs @@ -50,7 +50,7 @@ impl CrateMetadata { // Normalize the package name. let package_name = root_package.name.replace("-", "_"); - // {target_dir}/wasm32-unknown-unknown/release/{package_name}.wasm + // {target_dir}/wasm32-unknown-unknown/{debug/release}/{package_name}.wasm let mut original_wasm = target_directory.clone(); original_wasm.push("wasm32-unknown-unknown"); original_wasm.push("release"); diff --git a/src/main.rs b/src/main.rs index 6619d5802dcf3aba7f68f86de58d2b380ba629ed..8e2930750d27842e9fe214235c6a8acc45943442 100644 --- a/src/main.rs +++ b/src/main.rs @@ -192,6 +192,8 @@ pub struct BuildResult { pub dest_metadata: Option, /// Path to the resulting Wasm file. pub dest_wasm: Option, + /// Path to the debug Wasm file. + pub maybe_dest_debug_wasm: Option, /// Path to the bundled file. pub dest_bundle: Option, /// Path to the directory where output files are written to. @@ -252,6 +254,13 @@ impl BuildResult { ); out.push_str(&wasm); } + if let Some(dest_debug_wasm) = self.maybe_dest_debug_wasm.as_ref() { + let wasm = format!( + " - {} (the contract's debug code)\n", + util::base_name(&dest_debug_wasm).bold() + ); + out.push_str(&wasm); + } if let Some(dest_metadata) = self.dest_metadata.as_ref() { let metadata = format!( " - {} (the contract's metadata)",