Newer
Older
// Copyright 2018-2021 Parity Technologies (UK) Ltd.
// This file is part of cargo-contract.
// cargo-contract is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// cargo-contract is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with cargo-contract. If not, see <http://www.gnu.org/licenses/>.
Andrew Jones
committed
use crate::{
maybe_println, util, validate_wasm,
workspace::{Manifest, ManifestPath, Profile, Workspace},
BuildArtifacts, BuildResult, OptimizationPasses, OptimizationResult, UnstableFlags,
UnstableOptions, Verbosity, VerbosityFlags,
Andrew Jones
committed
};
use anyhow::{Context, Result};
use parity_wasm::elements::{External, MemoryType, Module, Section};
use regex::Regex;
use std::{
convert::TryFrom,
ffi::OsStr,
fs::metadata,
path::{Path, PathBuf},
process::Command,
str,
};
/// 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<PathBuf>,
/// Which build artifacts to generate.
///
/// - `all`: Generate the Wasm, the metadata and a bundled `<name>.contract` file.
///
/// - `code-only`: Only the Wasm is created, generation of metadata and a bundled
/// `<name>.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,
/// Number of optimization passes, passed as an argument to wasm-opt.
///
/// - `0`: execute no optimization passes
///
/// - `1`: execute 1 optimization pass (quick & useful opts, useful for iteration builds)
///
/// - `2`, execute 2 optimization passes (most opts, generally gets most perf)
///
/// - `3`, execute 3 optimization passes (spends potentially a lot of time optimizing)
///
/// - `4`, execute 4 optimization passes (also flatten the IR, which can take a lot more time and memory
/// but is useful on more nested / complex / less-optimized input)
///
/// - `s`, execute default optimization passes, focusing on code size
///
/// - `z`, execute default optimization passes, super-focusing on code size
///
/// - The default value is `3`
///
/// - It is possible to define the number of optimization passes in the
/// `[package.metadata.contract]` of your `Cargo.toml` as e.g. `optimization-passes = "3"`.
/// The CLI argument always takes precedence over the profile value.
#[structopt(long = "optimization-passes")]
optimization_passes: Option<OptimizationPasses>,
}
impl BuildCommand {
pub fn exec(&self) -> Result<BuildResult> {
let manifest_path = ManifestPath::try_from(self.manifest_path.as_ref())?;
let unstable_flags: UnstableFlags =
TryFrom::<&UnstableOptions>::try_from(&self.unstable_options)?;
let verbosity = TryFrom::<&VerbosityFlags>::try_from(&self.verbosity)?;
// The CLI flag `optimization-passes` overwrites optimization passes which are
// potentially defined in the `Cargo.toml` profile.
let optimization_passes = match self.optimization_passes {
Some(opt_passes) => opt_passes,
None => {
let mut manifest = Manifest::new(manifest_path.clone())?;
match manifest.get_profile_optimization_passes() {
// if no setting is found, neither on the cli nor in the profile,
// then we use the default
None => OptimizationPasses::default(),
Some(opt_passes) => opt_passes,
}
}
};
execute(
&manifest_path,
verbosity,
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<PathBuf>,
#[structopt(flatten)]
verbosity: VerbosityFlags,
#[structopt(flatten)]
unstable_options: UnstableOptions,
}
impl CheckCommand {
pub fn exec(&self) -> Result<BuildResult> {
let manifest_path = ManifestPath::try_from(self.manifest_path.as_ref())?;
let unstable_flags: UnstableFlags =
TryFrom::<&UnstableOptions>::try_from(&self.unstable_options)?;
let verbosity: Verbosity = TryFrom::<&VerbosityFlags>::try_from(&self.verbosity)?;
execute(
&manifest_path,
verbosity,
BuildArtifacts::CheckOnly,
unstable_flags,
OptimizationPasses::Zero,
/// Executes the supplied cargo command on 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)
/// to build the standard library with [`panic_immediate_abort`](https://github.com/johnthagen/min-sized-rust#remove-panic-string-formatting-with-panic_immediate_abort)
/// which reduces the size of the Wasm binary by not including panic strings and formatting code.
///
/// # Cargo.toml optimizations
///
/// The original Cargo.toml will be amended to remove the `rlib` crate type in order to minimize
/// the final Wasm binary size.
///
/// Preferred default `[profile.release]` settings will be added if they are missing, existing
/// user-defined settings will be preserved.
///
/// To disable this and use the original `Cargo.toml` as is then pass the `-Z original_manifest` flag.
crate_metadata: &CrateMetadata,
) -> Result<()> {
Andrew Jones
committed
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.
Andrew Jones
committed
std::env::set_var(
"RUSTFLAGS",
"-C link-arg=-z -C link-arg=stack-size=65536 -C link-arg=--import-memory",
);
let cargo_build = |manifest_path: &ManifestPath| {
let target_dir = &crate_metadata.target_directory;
let args = [
"--target=wasm32-unknown-unknown",
"-Zbuild-std",
"-Zbuild-std-features=panic_immediate_abort",
"--no-default-features",
"--release",
&format!("--target-dir={}", target_dir.to_string_lossy()),
];
util::invoke_cargo(command, &args, manifest_path.directory(), verbosity)?;
Andrew Jones
committed
Ok(())
};
if unstable_flags.original_manifest {
"{} {}",
"warning:".yellow().bold(),
"with 'original-manifest' enabled, the contract binary may not be of optimal size."
.bold()
);
cargo_build(&crate_metadata.manifest_path)?;
} else {
Workspace::new(&crate_metadata.cargo_meta, &crate_metadata.root_package.id)?
.with_root_package_manifest(|manifest| {
manifest
.with_removed_crate_type("rlib")?
.with_profile_release_defaults(Profile::default_contract_release())?;
Ok(())
})?
Andrew Jones
committed
// clear RUSTFLAGS
std::env::remove_var("RUSTFLAGS");
Andrew Jones
committed
Ok(())
}
/// Ensures the wasm memory import of a given module has the maximum number of pages.
///
/// Iterates over the import section, finds the memory import entry if any and adjusts the maximum
/// limit.
fn ensure_maximum_memory_pages(module: &mut Module, maximum_allowed_pages: u32) -> Result<()> {
let mem_ty = module
.import_section_mut()
.and_then(|section| {
.find_map(|entry| match entry.external_mut() {
External::Memory(ref mut mem_ty) => Some(mem_ty),
_ => None,
.context("Memory import is not found. Is --import-memory specified in the linker args")?;
if let Some(requested_maximum) = mem_ty.limits().maximum() {
// The module already has maximum, check if it is within the limit bail out.
if requested_maximum > maximum_allowed_pages {
anyhow::bail!(
"The wasm module requires {} pages. The maximum allowed number of pages is {}",
requested_maximum,
maximum_allowed_pages,
);
}
} else {
let initial = mem_ty.limits().initial();
*mem_ty = MemoryType::new(initial, Some(MAX_MEMORY_PAGES));
}
Ok(())
}
/// Strips all custom sections.
///
/// Presently all custom sections are not required so they can be stripped safely.
fn strip_custom_sections(module: &mut Module) {
module.sections_mut().retain(|section| {
!matches!(
section,
Section::Custom(_) | Section::Name(_) | Section::Reloc(_)
)
});
}
/// Performs required post-processing steps on the wasm artifact.
fn post_process_wasm(crate_metadata: &CrateMetadata) -> Result<()> {
// Deserialize wasm module from a file.
Andrew Jones
committed
let mut module =
parity_wasm::deserialize_file(&crate_metadata.original_wasm).context(format!(
"Loading original wasm file '{}'",
crate_metadata.original_wasm.display()
))?;
// Perform optimization.
//
// In practice only tree-shaking is performed, i.e transitively removing all symbols that are
// NOT used by the specified entrypoints.
if pwasm_utils::optimize(&mut module, ["call", "deploy"].to_vec()).is_err() {
anyhow::bail!("Optimizer failed");
}
ensure_maximum_memory_pages(&mut module, MAX_MEMORY_PAGES)?;
strip_custom_sections(&mut module);
validate_wasm::validate_import_section(&module)?;
Michael Müller
committed
debug_assert!(
!module.clone().to_bytes().unwrap().is_empty(),
"resulting wasm size of post processing must be > 0"
);
parity_wasm::serialize_to_file(&crate_metadata.dest_wasm, module)?;
Ok(())
}
Michael Müller
committed
/// Attempts to perform optional wasm optimization using `binaryen`.
///
/// 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,
optimization_passes: OptimizationPasses,
) -> Result<OptimizationResult> {
Michael Müller
committed
let mut dest_optimized = crate_metadata.dest_wasm.clone();
dest_optimized.set_file_name(format!("{}-opt.wasm", crate_metadata.package_name));
let _ = do_optimization(
crate_metadata.dest_wasm.as_os_str(),
&dest_optimized.as_os_str(),
Michael Müller
committed
)?;
if !dest_optimized.exists() {
return Err(anyhow::anyhow!(
"Optimization failed, optimized wasm output file `{}` not found.",
dest_optimized.display()
));
}
Andrew Jones
committed
let original_size = metadata(&crate_metadata.dest_wasm)?.len() as f64 / 1000.0;
Michael Müller
committed
let optimized_size = metadata(&dest_optimized)?.len() as f64 / 1000.0;
// overwrite existing destination wasm file with the optimised version
Michael Müller
committed
std::fs::rename(&dest_optimized, &crate_metadata.dest_wasm)?;
Michael Müller
committed
Ok(OptimizationResult {
dest_wasm: crate_metadata.dest_wasm.clone(),
Michael Müller
committed
original_size,
optimized_size,
})
/// Optimizes the Wasm supplied as `crate_metadata.dest_wasm` using
/// the `wasm-opt` binary.
///
/// The supplied `optimization_level` denotes the number of optimization passes,
/// resulting in potentially a lot of time spent optimizing.
///
Michael Müller
committed
/// If successful, the optimized wasm is written to `dest_optimized`.
fn do_optimization(
Michael Müller
committed
dest_wasm: &OsStr,
dest_optimized: &OsStr,
optimization_level: OptimizationPasses,
Michael Müller
committed
) -> Result<()> {
// check `wasm-opt` is installed
let which = which::which("wasm-opt");
if which.is_err() {
anyhow::bail!(
"wasm-opt not found! Make sure the binary is in your PATH environment.\n\
We use this tool to optimize the size of your contract's Wasm binary.\n\n\
wasm-opt is part of the binaryen package. You can find detailed\n\
installation instructions on https://github.com/WebAssembly/binaryen#tools.\n\n\
There are ready-to-install packages for many platforms:\n\
* Debian/Ubuntu: apt-get install binaryen\n\
* Homebrew: brew install binaryen\n\
* Arch Linux: pacman -S binaryen\n\
* Windows: binary releases at https://github.com/WebAssembly/binaryen/releases"
.to_string()
.bright_yellow()
let wasm_opt_path = which
.as_ref()
.expect("we just checked if which returned an err; qed")
.as_path();
log::info!("Path to wasm-opt executable: {}", wasm_opt_path.display());
let _ = check_wasm_opt_version_compatibility(wasm_opt_path)?;
log::info!(
"Optimization level passed to wasm-opt: {}",
optimization_level
);
let output = Command::new(wasm_opt_path)
Michael Müller
committed
.arg(dest_wasm)
.arg(format!("-O{}", optimization_level))
Michael Müller
committed
.arg(dest_optimized)
// the memory in our module is imported, `wasm-opt` needs to be told that
// the memory is initialized to zeroes, otherwise it won't run the
// memory-packing pre-pass.
.arg("--zero-filled-memory")
.output()
.map_err(|err| {
anyhow::anyhow!(
"Executing {} failed with {:?}",
wasm_opt_path.display(),
err
)
})?;
if !output.status.success() {
let err = str::from_utf8(&output.stderr)
.expect("Cannot convert stderr output of wasm-opt to string")
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
"The wasm-opt optimization failed.\n\n\
The error which wasm-opt returned was: \n{}",
err
);
}
Ok(())
}
/// Checks if the wasm-opt binary under `wasm_opt_path` returns a version
/// compatible with `cargo-contract`.
///
/// Currently this must be a version >= 99.
fn check_wasm_opt_version_compatibility(wasm_opt_path: &Path) -> Result<()> {
let cmd = Command::new(wasm_opt_path)
.arg("--version")
.output()
.map_err(|err| {
anyhow::anyhow!(
"Executing `{:?} --version` failed with {:?}",
wasm_opt_path.display(),
err
)
})?;
if !cmd.status.success() {
let err = str::from_utf8(&cmd.stderr)
.expect("Cannot convert stderr output of wasm-opt to string")
.trim();
anyhow::bail!(
"Getting version information from wasm-opt failed.\n\
The error which wasm-opt returned was: \n{}",
err
);
// ```sh
// $ wasm-opt --version
// wasm-opt version 99 (version_99-79-gc12cc3f50)
// ```
let github_note = "\n\n\
If you tried installing from your system package manager the best\n\
way forward is to download a recent binary release directly:\n\n\
https://github.com/WebAssembly/binaryen/releases\n\n\
Make sure that the `wasm-opt` file from that release is in your `PATH`.";
let version_stdout = str::from_utf8(&cmd.stdout)
.expect("Cannot convert stdout output of wasm-opt to string")
.trim();
let re = Regex::new(r"wasm-opt version (\d+)").expect("invalid regex");
let captures = re.captures(version_stdout).ok_or_else(|| {
anyhow::anyhow!(
"Unable to extract version information from '{}'.\n\
Your wasm-opt version is most probably too old. Make sure you use a version >= 99.{}",
version_stdout,
github_note,
)
})?;
let version_number: u32 = captures
.get(1) // first capture group is at index 1
.ok_or_else(|| {
anyhow::anyhow!(
"Unable to extract version number from '{:?}'",
version_stdout
)
})?
.as_str()
.parse()
.map_err(|err| {
anyhow::anyhow!(
"Parsing version number failed with '{:?}' for '{:?}'",
err,
version_stdout
)
})?;
log::info!(
"The wasm-opt version output is '{}', which was parsed to '{}'",
version_stdout,
version_number
);
if version_number < 99 {
anyhow::bail!(
"Your wasm-opt version is {}, but we require a version >= 99.{}",
version_number,
github_note,
);
}
Michael Müller
committed
Ok(())
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
/// Asserts that the contract's dependencies are compatible to the ones used in ink!.
///
/// This function utilizes `cargo tree`, which takes semver into consideration.
///
/// Hence this function only returns an `Err` if it is a proper mismatch according
/// to semantic versioning. This means that either:
/// - the major version mismatches, differences in the minor/patch version
/// are not considered incompatible.
/// - or if the version starts with zero (i.e. `0.y.z`) a mismatch in the minor
/// version is already considered incompatible.
fn assert_compatible_ink_dependencies(
manifest_path: &ManifestPath,
verbosity: Verbosity,
) -> Result<()> {
for dependency in ["parity-scale-codec", "scale-info"].iter() {
let args = ["-i", dependency, "--duplicates"];
let _ = util::invoke_cargo("tree", &args, manifest_path.directory(), verbosity).map_err(
|_| {
anyhow::anyhow!(
"Mismatching versions of `{}` were found!\n\
Please ensure that your contract and your ink! dependencies use a compatible \
version of this package.",
dependency
)
},
)?;
}
Ok(())
}
/// 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.
build_artifact: BuildArtifacts,
unstable_flags: UnstableFlags,
optimization_passes: OptimizationPasses,
let crate_metadata = CrateMetadata::collect(&manifest_path)?;
assert_compatible_ink_dependencies(&manifest_path, verbosity)?;
let build = || -> Result<OptimizationResult> {
maybe_println!(
Michael Müller
committed
verbosity,
" {} {}",
format!("[1/{}]", build_artifact.steps()).bold(),
"Building cargo project".bright_green().bold()
);
exec_cargo_for_wasm_target(&crate_metadata, "build", verbosity, &unstable_flags)?;
maybe_println!(
" {} {}",
format!("[2/{}]", build_artifact.steps()).bold(),
"Post processing wasm file".bright_green().bold()
);
post_process_wasm(&crate_metadata)?;
Michael Müller
committed
maybe_println!(
verbosity,
" {} {}",
format!("[3/{}]", build_artifact.steps()).bold(),
"Optimizing wasm file".bright_green().bold()
);
let optimization_result = optimize_wasm(&crate_metadata, optimization_passes)?;
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
Ok(optimization_result)
};
let (opt_result, metadata_result) = match build_artifact {
BuildArtifacts::CheckOnly => {
exec_cargo_for_wasm_target(&crate_metadata, "check", verbosity, &unstable_flags)?;
(None, None)
}
BuildArtifacts::CodeOnly => {
let optimization_result = build()?;
(Some(optimization_result), None)
}
BuildArtifacts::All => {
let optimization_result = build()?;
let metadata_result = super::metadata::execute(
&crate_metadata,
optimization_result.dest_wasm.as_path(),
verbosity,
build_artifact.steps(),
&unstable_flags,
)?;
(Some(optimization_result), Some(metadata_result))
}
};
let dest_wasm = opt_result.as_ref().map(|r| r.dest_wasm.clone());
Ok(BuildResult {
dest_wasm,
metadata_result,
target_directory: crate_metadata.target_directory,
optimization_result: opt_result,
build_artifact,
#[cfg(feature = "test-ci-only")]
mod tests_ci_only {
use super::{assert_compatible_ink_dependencies, check_wasm_opt_version_compatibility};
cmd::BuildCommand,
util::tests::{with_new_contract_project, with_tmp_dir},
workspace::Manifest,
BuildArtifacts, ManifestPath, OptimizationPasses, UnstableFlags, UnstableOptions,
Verbosity, VerbosityFlags,
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
use std::{
io::Write,
path::{Path, PathBuf},
};
/// Modifies the `Cargo.toml` under the supplied `cargo_toml_path` by
/// setting `optimization-passes` in `[package.metadata.contract]` to `passes`.
fn write_optimization_passes_into_manifest(
cargo_toml_path: &PathBuf,
passes: OptimizationPasses,
) {
let manifest_path =
ManifestPath::new(cargo_toml_path).expect("manifest path creation failed");
let mut manifest = Manifest::new(manifest_path.clone()).expect("manifest creation failed");
manifest
.set_profile_optimization_passes(passes)
.expect("setting `optimization-passes` in profile failed");
manifest
.write(&manifest_path)
.expect("writing manifest failed");
}
/// Creates an executable `wasm-opt-mocked` file which outputs
/// "wasm-opt version `version`".
///
/// Returns the path to this file.
///
/// Currently works only on `unix`.
#[cfg(unix)]
fn mock_wasm_opt_version(tmp_dir: &Path, version: &str) -> PathBuf {
let path = tmp_dir.join("wasm-opt-mocked");
{
let mut file = std::fs::File::create(&path).unwrap();
let version = format!("#!/bin/sh\necho \"wasm-opt version {}\"", version);
file.write_all(version.as_bytes())
.expect("writing wasm-opt-mocked failed");
}
std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o777))
.expect("setting permissions failed");
path
}
let res = super::execute(
Michael Müller
committed
&manifest_path,
Michael Müller
committed
UnstableFlags::default(),
Michael Müller
committed
)
.expect("build failed");
// our ci has set `CARGO_TARGET_DIR` to cache artifacts.
// this dir does not include `/target/` as a path, hence
// we can't match for e.g. `foo_project/target/ink`.
//
// we also can't match for `/ink` here, since this would match
// for `/ink` being the root path.
assert!(res.target_directory.ends_with("ink"));
assert!(
res.metadata_result.is_none(),
"CodeOnly should not generate the metadata"
);
let optimized_size = res.optimization_result.unwrap().optimized_size;
assert!(optimized_size > 0.0);
// our optimized contract template should always be below 3k.
#[test]
fn check_must_not_output_contract_artifacts_in_project_dir() {
// given
let project_dir = manifest_path.directory().expect("directory must exist");
// when
super::execute(
&manifest_path,
BuildArtifacts::CheckOnly,
UnstableFlags::default(),
)
.expect("build failed");
// then
assert!(
!project_dir.join("target/ink/new_project.contract").exists(),
"found contract artifact in project directory!"
);
assert!(
!project_dir.join("target/ink/new_project.wasm").exists(),
"found wasm artifact in project directory!"
);
Ok(())
})
}
#[test]
fn optimization_passes_from_cli_must_take_precedence_over_profile() {
write_optimization_passes_into_manifest(
&manifest_path.clone().into(),
OptimizationPasses::Three,
);
let cmd = BuildCommand {
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
build_artifact: BuildArtifacts::All,
verbosity: VerbosityFlags::default(),
unstable_options: UnstableOptions::default(),
// we choose zero optimization passes as the "cli" parameter
optimization_passes: Some(OptimizationPasses::Zero),
};
// when
let res = cmd.exec().expect("build failed");
let optimization = res
.optimization_result
.expect("no optimization result available");
// then
// we have to truncate here to account for a possible small delta
// in the floating point numbers
let optimized_size = optimization.optimized_size.trunc();
let original_size = optimization.original_size.trunc();
assert!(
optimized_size == original_size,
"The optimized size {:?} differs from the original size {:?}",
optimized_size,
original_size
);
Ok(())
})
}
#[test]
fn optimization_passes_from_profile_must_be_used() {
write_optimization_passes_into_manifest(
&manifest_path.clone().into(),
OptimizationPasses::Three,
);
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
build_artifact: BuildArtifacts::All,
verbosity: VerbosityFlags::default(),
unstable_options: UnstableOptions::default(),
// we choose no optimization passes as the "cli" parameter
optimization_passes: None,
};
// when
let res = cmd.exec().expect("build failed");
let optimization = res
.optimization_result
.expect("no optimization result available");
// then
// we have to truncate here to account for a possible small delta
// in the floating point numbers
let optimized_size = optimization.optimized_size.trunc();
let original_size = optimization.original_size.trunc();
assert!(
optimized_size < original_size,
"The optimized size DOES NOT {:?} differ from the original size {:?}",
optimized_size,
original_size
);
Ok(())
})
}
#[test]
fn project_template_dependencies_must_be_ink_compatible() {
// when
let res = assert_compatible_ink_dependencies(&manifest_path, Verbosity::Default);
// then
assert!(res.is_ok());
Ok(())
})
}
#[test]
fn detect_mismatching_parity_scale_codec_dependencies() {
// at the time of writing this test ink! already uses `parity-scale-codec`
// in a version > 2, hence 1 is an incompatible version.
let mut manifest = Manifest::new(manifest_path.clone())?;
manifest
.set_dependency_version("scale", "1.0.0")
.expect("setting `scale` version failed");
manifest
.write(&manifest_path)
.expect("writing manifest failed");
// when
let res = assert_compatible_ink_dependencies(&manifest_path, Verbosity::Default);
// then
assert!(res.is_err());
Ok(())
})
}
#[test]
fn incompatible_wasm_opt_version_must_be_detected_if_built_from_repo() {
with_tmp_dir(|path| {
// given
let path = mock_wasm_opt_version(path, "98 (version_13-79-gc12cc3f50)");
// when
let res = check_wasm_opt_version_compatibility(&path);
// then
assert!(res.is_err());
assert!(format!("{:?}", res)
.starts_with("Err(Your wasm-opt version is 98, but we require a version >= 99."));
Ok(())
})
}
#[test]
fn compatible_wasm_opt_version_must_be_detected_if_built_from_repo() {
with_tmp_dir(|path| {
// given
let path = mock_wasm_opt_version(path, "99 (version_99-79-gc12cc3f50");
// when
let res = check_wasm_opt_version_compatibility(&path);
// then
assert!(res.is_ok());
Ok(())
})
}
#[test]
fn incompatible_wasm_opt_version_must_be_detected_if_installed_as_package() {
with_tmp_dir(|path| {
// given
let path = mock_wasm_opt_version(path, "98");
// when
let res = check_wasm_opt_version_compatibility(&path);
// then
assert!(res.is_err());
assert!(format!("{:?}", res)
.starts_with("Err(Your wasm-opt version is 98, but we require a version >= 99."));
#[test]
fn compatible_wasm_opt_version_must_be_detected_if_installed_as_package() {
with_tmp_dir(|path| {
// given
let path = mock_wasm_opt_version(path, "99");
// when
let res = check_wasm_opt_version_compatibility(&path);
// then
assert!(res.is_ok());
Ok(())
})
}