From b1a4d6559890d8d7449eb834fb65440230a9f9fa Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Fri, 17 Nov 2023 18:08:32 +0700 Subject: [PATCH 01/32] feat(build): Always execute dylint-based lints dylint-based ink! linting rules become mandatory, because we can check ink! compilation errors with them (see https://github.com/paritytech/ink/issues/1976) --- crates/build/src/lib.rs | 42 +++++++++++++------------ crates/build/src/tests.rs | 26 +++++++-------- crates/cargo-contract/src/cmd/build.rs | 10 +++--- crates/cargo-contract/src/cmd/verify.rs | 2 +- 4 files changed, 41 insertions(+), 39 deletions(-) diff --git a/crates/build/src/lib.rs b/crates/build/src/lib.rs index add3b2103..496a7c45a 100644 --- a/crates/build/src/lib.rs +++ b/crates/build/src/lib.rs @@ -125,7 +125,7 @@ pub struct ExecuteArgs { pub unstable_flags: UnstableFlags, pub optimization_passes: Option, pub keep_debug_symbols: bool, - pub dylint: bool, + pub extra_lints: bool, pub output_type: OutputType, pub skip_wasm_validation: bool, pub target: Target, @@ -145,7 +145,7 @@ impl Default for ExecuteArgs { unstable_flags: Default::default(), optimization_passes: Default::default(), keep_debug_symbols: Default::default(), - dylint: Default::default(), + extra_lints: Default::default(), output_type: Default::default(), skip_wasm_validation: Default::default(), target: Default::default(), @@ -497,13 +497,13 @@ fn invoke_cargo_and_scan_for_error(cargo: duct::Expression) -> Result<()> { Ok(()) } -/// Run linting steps which include `clippy` (mandatory) + `dylint` (optional). +/// Run linting that involves two steps: `clippy` and `dylint`. Both are mandatory as +/// they're part of the compilation process and implement security-critical features. fn lint( - dylint: bool, + extra_lints: bool, crate_metadata: &CrateMetadata, verbosity: &Verbosity, ) -> Result<()> { - // mandatory: Always run clippy. verbose_eprintln!( verbosity, " {} {}", @@ -512,16 +512,14 @@ fn lint( ); exec_cargo_clippy(crate_metadata, *verbosity)?; - // optional: Dylint only on demand (for now). - if dylint { - verbose_eprintln!( - verbosity, - " {} {}", - "[==]".bold(), - "Checking ink! linting rules".bright_green().bold() - ); - exec_cargo_dylint(crate_metadata, *verbosity)?; - } + verbose_eprintln!( + verbosity, + " {} {}", + "[==]".bold(), + "Checking ink! linting rules".bright_green().bold() + ); + exec_cargo_dylint(extra_lints, crate_metadata, *verbosity)?; + Ok(()) } @@ -549,7 +547,11 @@ fn exec_cargo_clippy(crate_metadata: &CrateMetadata, verbosity: Verbosity) -> Re /// /// We create a temporary folder, extract the linting driver there and run /// `cargo dylint` with it. -fn exec_cargo_dylint(crate_metadata: &CrateMetadata, verbosity: Verbosity) -> Result<()> { +fn exec_cargo_dylint( + _extra_lints: bool, + crate_metadata: &CrateMetadata, + verbosity: Verbosity, +) -> Result<()> { check_dylint_requirements(crate_metadata.manifest_path.directory())?; // `dylint` is verbose by default, it doesn't have a `--verbose` argument, @@ -792,7 +794,7 @@ pub fn execute(args: ExecuteArgs) -> Result { build_artifact, unstable_flags, optimization_passes, - dylint, + extra_lints, output_type, target, .. @@ -837,7 +839,7 @@ pub fn execute(args: ExecuteArgs) -> Result { let (opt_result, metadata_result, dest_wasm) = match build_artifact { BuildArtifacts::CheckOnly => { // Check basically means only running our linter without building. - lint(*dylint, &crate_metadata, verbosity)?; + lint(*extra_lints, &crate_metadata, verbosity)?; (None, None, None) } BuildArtifacts::CodeOnly => { @@ -911,7 +913,7 @@ fn local_build( network, unstable_flags, keep_debug_symbols, - dylint, + extra_lints, skip_wasm_validation, target, max_memory_pages, @@ -920,7 +922,7 @@ fn local_build( // We always want to lint first so we don't suppress any warnings when a build is // skipped because of a matching fingerprint. - lint(*dylint, crate_metadata, verbosity)?; + lint(*extra_lints, crate_metadata, verbosity)?; let pre_fingerprint = Fingerprint::new(crate_metadata)?; diff --git a/crates/build/src/tests.rs b/crates/build/src/tests.rs index 40a16e3c8..022a87ad7 100644 --- a/crates/build/src/tests.rs +++ b/crates/build/src/tests.rs @@ -87,7 +87,7 @@ fn build_code_only(manifest_path: &ManifestPath) -> Result<()> { manifest_path: manifest_path.clone(), build_mode: BuildMode::Release, build_artifact: BuildArtifacts::CodeOnly, - dylint: false, + extra_lints: false, ..Default::default() }; @@ -127,7 +127,7 @@ fn check_must_not_output_contract_artifacts_in_project_dir( let args = ExecuteArgs { manifest_path: manifest_path.clone(), build_artifact: BuildArtifacts::CheckOnly, - dylint: false, + extra_lints: false, ..Default::default() }; @@ -164,7 +164,7 @@ fn optimization_passes_from_cli_must_take_precedence_over_profile( unstable_flags: Default::default(), optimization_passes: Some(OptimizationPasses::Zero), keep_debug_symbols: false, - dylint: false, + extra_lints: false, output_type: OutputType::Json, skip_wasm_validation: false, target: Default::default(), @@ -207,7 +207,7 @@ fn optimization_passes_from_profile_must_be_used( // no optimization passes specified. optimization_passes: None, keep_debug_symbols: false, - dylint: false, + extra_lints: false, output_type: OutputType::Json, skip_wasm_validation: false, target: Default::default(), @@ -237,7 +237,7 @@ fn building_template_in_debug_mode_must_work(manifest_path: &ManifestPath) -> Re let args = ExecuteArgs { manifest_path: manifest_path.clone(), build_mode: BuildMode::Debug, - dylint: false, + extra_lints: false, ..Default::default() }; @@ -256,7 +256,7 @@ fn building_template_in_release_mode_must_work( let args = ExecuteArgs { manifest_path: manifest_path.clone(), build_mode: BuildMode::Release, - dylint: false, + extra_lints: false, ..Default::default() }; @@ -286,7 +286,7 @@ fn building_contract_with_source_file_in_subfolder_must_work( let args = ExecuteArgs { manifest_path: manifest_path.clone(), build_artifact: BuildArtifacts::CheckOnly, - dylint: false, + extra_lints: false, ..Default::default() }; @@ -312,7 +312,7 @@ fn building_contract_with_build_rs_must_work(manifest_path: &ManifestPath) -> Re let args = ExecuteArgs { manifest_path: manifest_path.clone(), build_artifact: BuildArtifacts::CheckOnly, - dylint: false, + extra_lints: false, ..Default::default() }; @@ -330,7 +330,7 @@ fn keep_debug_symbols_in_debug_mode(manifest_path: &ManifestPath) -> Result<()> build_mode: BuildMode::Debug, build_artifact: BuildArtifacts::CodeOnly, keep_debug_symbols: true, - dylint: false, + extra_lints: false, ..Default::default() }; @@ -348,7 +348,7 @@ fn keep_debug_symbols_in_release_mode(manifest_path: &ManifestPath) -> Result<() build_mode: BuildMode::Release, build_artifact: BuildArtifacts::CodeOnly, keep_debug_symbols: true, - dylint: false, + extra_lints: false, ..Default::default() }; @@ -365,7 +365,7 @@ fn build_with_json_output_works(manifest_path: &ManifestPath) -> Result<()> { let args = ExecuteArgs { manifest_path: manifest_path.clone(), output_type: OutputType::Json, - dylint: false, + extra_lints: false, ..Default::default() }; @@ -395,7 +395,7 @@ fn missing_cargo_dylint_installation_must_be_detected( // when let args = ExecuteArgs { manifest_path: manifest_path.clone(), - dylint: true, + extra_lints: true, ..Default::default() }; let res = super::execute(args).map(|_| ()).unwrap_err(); @@ -438,7 +438,7 @@ fn generates_metadata(manifest_path: &ManifestPath) -> Result<()> { fs::write(final_contract_wasm_path, "TEST FINAL WASM BLOB").unwrap(); let mut args = ExecuteArgs { - dylint: false, + extra_lints: false, ..Default::default() }; args.manifest_path = manifest_path.clone(); diff --git a/crates/cargo-contract/src/cmd/build.rs b/crates/cargo-contract/src/cmd/build.rs index d0a86915d..f26cfb994 100644 --- a/crates/cargo-contract/src/cmd/build.rs +++ b/crates/cargo-contract/src/cmd/build.rs @@ -58,10 +58,10 @@ pub struct BuildCommand { /// Build offline #[clap(long = "offline")] build_offline: bool, - /// Performs ink! linting checks during the build process. + /// Performs extra ink! linting checks during the build process. /// - /// This only applies to ink! custom lints of which are there none at the moment. - /// Basic clippy lints which we are deem important are run anyways. + /// This only adds extra ink! linting checks. Basic clippy and ink! lints which we + /// are deem important are run anyways. #[clap(long)] lint: bool, /// Which build artifacts to generate. @@ -179,7 +179,7 @@ impl BuildCommand { unstable_flags, optimization_passes: self.optimization_passes, keep_debug_symbols: self.keep_debug_symbols, - dylint: self.lint, + extra_lints: self.lint, output_type, skip_wasm_validation: self.skip_wasm_validation, target: self.target, @@ -215,7 +215,7 @@ impl CheckCommand { unstable_flags: Default::default(), optimization_passes: Some(OptimizationPasses::Zero), keep_debug_symbols: false, - dylint: false, + extra_lints: false, output_type: OutputType::default(), skip_wasm_validation: false, target: Default::default(), diff --git a/crates/cargo-contract/src/cmd/verify.rs b/crates/cargo-contract/src/cmd/verify.rs index e9b9517e4..f60bbdd8d 100644 --- a/crates/cargo-contract/src/cmd/verify.rs +++ b/crates/cargo-contract/src/cmd/verify.rs @@ -144,7 +144,7 @@ impl VerifyCommand { optimization_passes: Some(build_info.wasm_opt_settings.optimization_passes), keep_debug_symbols: build_info.wasm_opt_settings.keep_debug_symbols, image: ImageVariant::from(metadata.image.clone()), - dylint: false, + extra_lints: false, ..Default::default() }; From f736546a9cf7d799c4f597e50cc5467aa2b33524 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Tue, 21 Nov 2023 17:52:04 +0700 Subject: [PATCH 02/32] feat(build): Don't check `cargo` output since we have a lint for this --- crates/build/src/lib.rs | 72 ++++++++++++----------------------------- 1 file changed, 21 insertions(+), 51 deletions(-) diff --git a/crates/build/src/lib.rs b/crates/build/src/lib.rs index 496a7c45a..0e7ef6b9d 100644 --- a/crates/build/src/lib.rs +++ b/crates/build/src/lib.rs @@ -94,9 +94,7 @@ use parity_wasm::elements::{ }; use semver::Version; use std::{ - collections::VecDeque, fs, - io, path::{ Path, PathBuf, @@ -344,10 +342,13 @@ fn exec_cargo_for_onchain_target( env.push(("CARGO_ENCODED_RUSTFLAGS", Some(rustflags))); }; - let cargo = - util::cargo_cmd(command, &args, manifest_path.directory(), *verbosity, env); - - invoke_cargo_and_scan_for_error(cargo) + execute_cargo(util::cargo_cmd( + command, + &args, + manifest_path.directory(), + *verbosity, + env, + )) }; if unstable_flags.original_manifest { @@ -432,7 +433,7 @@ fn check_buffer_size_invoke_cargo_clean( "Detected a change in the configured buffer size. Rebuilding the project." .bold() ); - invoke_cargo_and_scan_for_error(cargo)?; + execute_cargo(cargo)?; } Err(_) => { verbose_eprintln!( @@ -442,7 +443,7 @@ fn check_buffer_size_invoke_cargo_clean( "Cannot find the previous size of the static buffer. Rebuilding the project." .bold() ); - invoke_cargo_and_scan_for_error(cargo)?; + execute_cargo(cargo)?; } } } @@ -451,50 +452,19 @@ fn check_buffer_size_invoke_cargo_clean( /// Executes the supplied cargo command, reading the output and scanning for known errors. /// Writes the captured stderr back to stderr and maintains the cargo tty progress bar. -fn invoke_cargo_and_scan_for_error(cargo: duct::Expression) -> Result<()> { - macro_rules! eprintln_red { - ($value:expr) => {{ - use colored::Colorize as _; - ::std::eprintln!("{}", $value.bright_red().bold()); - }}; - } - - // unchecked: Even capture output on non exit return status - let cargo = util::cargo_tty_output(cargo).unchecked(); - - let missing_main_err = "error[E0601]".as_bytes(); - let mut err_buf = VecDeque::with_capacity(missing_main_err.len()); - - let mut reader = cargo.stderr_to_stdout().reader()?; - let mut buffer = [0u8; 1]; - - loop { - let bytes_read = io::Read::read(&mut reader, &mut buffer)?; - for byte in buffer[0..bytes_read].iter() { - err_buf.push_back(*byte); - if err_buf.len() > missing_main_err.len() { - let byte = err_buf.pop_front().expect("buffer is not empty"); - io::Write::write(&mut io::stderr(), &[byte])?; - } - } - if missing_main_err == err_buf.make_contiguous() { - eprintln_red!("\nExited with error: [E0601]"); - eprintln_red!( - "Your contract must be annotated with the `no_main` attribute.\n" - ); - eprintln_red!("Examples how to do this:"); - eprintln_red!(" - `#![cfg_attr(not(feature = \"std\"), no_std, no_main)]`"); - eprintln_red!(" - `#[no_main]`\n"); - return Err(anyhow::anyhow!("missing `no_main` attribute")) - } - if bytes_read == 0 { - // flush the remaining buffered bytes - io::Write::write(&mut io::stderr(), err_buf.make_contiguous())?; - break +fn execute_cargo(cargo: duct::Expression) -> Result<()> { + match util::cargo_tty_output(cargo) + .unchecked() + .stderr_capture() + .run() + { + Ok(out) if out.status.success() => Ok(()), + Ok(out) => anyhow::bail!(String::from_utf8_lossy(&out.stderr).to_string()), + Err(e) => { + dbg!(e); + anyhow::bail!("Cannot run `cargo` command") } - buffer = [0u8; 1]; } - Ok(()) } /// Run linting that involves two steps: `clippy` and `dylint`. Both are mandatory as @@ -534,7 +504,7 @@ fn exec_cargo_clippy(crate_metadata: &CrateMetadata, verbosity: Verbosity) -> Re "-Dclippy::arithmetic_side_effects", ]; // we execute clippy with the plain manifest no temp dir required - invoke_cargo_and_scan_for_error(util::cargo_cmd( + execute_cargo(util::cargo_cmd( "clippy", args, crate_metadata.manifest_path.directory(), From ddabef62156f59da783f042409b53f4ec23507fe Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Mon, 27 Nov 2023 18:59:32 +0700 Subject: [PATCH 03/32] chore(build): cleanup --- crates/build/src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/build/src/lib.rs b/crates/build/src/lib.rs index 0e7ef6b9d..8d33b5a65 100644 --- a/crates/build/src/lib.rs +++ b/crates/build/src/lib.rs @@ -460,10 +460,7 @@ fn execute_cargo(cargo: duct::Expression) -> Result<()> { { Ok(out) if out.status.success() => Ok(()), Ok(out) => anyhow::bail!(String::from_utf8_lossy(&out.stderr).to_string()), - Err(e) => { - dbg!(e); - anyhow::bail!("Cannot run `cargo` command") - } + Err(e) => anyhow::bail!("Cannot run `cargo` command") } } From 7946de2de2ecfd7ab6161a6686f1bfb69c590947 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Sat, 23 Dec 2023 15:28:41 -0300 Subject: [PATCH 04/32] feat: Implement `extra_lints` ink_linting was splitted up to two libraries in https://github.com/paritytech/ink/pull/2032 --- crates/build/src/lib.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/build/src/lib.rs b/crates/build/src/lib.rs index 8d33b5a65..3c6ecb538 100644 --- a/crates/build/src/lib.rs +++ b/crates/build/src/lib.rs @@ -515,7 +515,7 @@ fn exec_cargo_clippy(crate_metadata: &CrateMetadata, verbosity: Verbosity) -> Re /// We create a temporary folder, extract the linting driver there and run /// `cargo dylint` with it. fn exec_cargo_dylint( - _extra_lints: bool, + extra_lints: bool, crate_metadata: &CrateMetadata, verbosity: Verbosity, ) -> Result<()> { @@ -528,7 +528,10 @@ fn exec_cargo_dylint( }; let target_dir = &crate_metadata.target_directory.to_string_lossy(); - let args = vec!["--lib=ink_linting"]; + let mut args = vec!["--lib=ink_linting_mandatory"]; + if extra_lints { + args.push("--lib=ink_linting"); + } let env = vec![ // We need to set the `CARGO_TARGET_DIR` environment variable in // case `cargo dylint` is invoked. From f2cb3de5f6c721480626f65ecac504956cfa44d6 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Sat, 23 Dec 2023 15:54:31 -0300 Subject: [PATCH 05/32] feat: Pass on-chain build options for the linter This is necessary to expand `cfg_attr` macros in the contract as for on-chain build. Particulary, we need this to enforce the `no_std` attribute on the crate level, because `Cargo.toml` usually contains `features` entry for local builds: ``` [features] default = ["std"] std = [ "ink/std", ] ``` --- crates/build/src/lib.rs | 44 +++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/crates/build/src/lib.rs b/crates/build/src/lib.rs index 3c6ecb538..aa5d46042 100644 --- a/crates/build/src/lib.rs +++ b/crates/build/src/lib.rs @@ -287,14 +287,8 @@ fn exec_cargo_for_onchain_target( "--target-dir={}", crate_metadata.target_directory.to_string_lossy() ); - - let mut args = vec![ - format!("--target={}", target.llvm_target()), - "-Zbuild-std=core,alloc".to_owned(), - "--no-default-features".to_owned(), - "--release".to_owned(), - target_dir, - ]; + let mut args = vec![target_dir, "--release".to_owned()]; + args.extend(onchain_cargo_options(target)); network.append_to_args(&mut args); let mut features = features.clone(); @@ -469,6 +463,7 @@ fn execute_cargo(cargo: duct::Expression) -> Result<()> { fn lint( extra_lints: bool, crate_metadata: &CrateMetadata, + target: &Target, verbosity: &Verbosity, ) -> Result<()> { verbose_eprintln!( @@ -485,7 +480,7 @@ fn lint( "[==]".bold(), "Checking ink! linting rules".bright_green().bold() ); - exec_cargo_dylint(extra_lints, crate_metadata, *verbosity)?; + exec_cargo_dylint(extra_lints, crate_metadata, target, *verbosity)?; Ok(()) } @@ -510,6 +505,15 @@ fn exec_cargo_clippy(crate_metadata: &CrateMetadata, verbosity: Verbosity) -> Re )) } +/// Returns a list of cargo options used for on-chain builds +fn onchain_cargo_options(target: &Target) -> Vec { + return vec![ + format!("--target={}", target.llvm_target()), + "-Zbuild-std=core,alloc".to_owned(), + "--no-default-features".to_owned(), + ] +} + /// Inject our custom lints into the manifest and execute `cargo dylint` . /// /// We create a temporary folder, extract the linting driver there and run @@ -517,6 +521,7 @@ fn exec_cargo_clippy(crate_metadata: &CrateMetadata, verbosity: Verbosity) -> Re fn exec_cargo_dylint( extra_lints: bool, crate_metadata: &CrateMetadata, + target: &Target, verbosity: Verbosity, ) -> Result<()> { check_dylint_requirements(crate_metadata.manifest_path.directory())?; @@ -527,11 +532,20 @@ fn exec_cargo_dylint( Verbosity::Default | Verbosity::Quiet => Verbosity::Quiet, }; + let mut args = if extra_lints { + vec![ + "--lib=ink_linting_mandatory".to_owned(), + "--lib=ink_linting".to_owned(), + ] + } else { + vec!["--lib=ink_linting_mandatory".to_owned()] + }; + args.push("--".to_owned()); + // Pass on-chain build options to ensure the linter expands all conditional `cfg_attr` + // macros, as it does for the release build. + args.extend(onchain_cargo_options(target)); + let target_dir = &crate_metadata.target_directory.to_string_lossy(); - let mut args = vec!["--lib=ink_linting_mandatory"]; - if extra_lints { - args.push("--lib=ink_linting"); - } let env = vec![ // We need to set the `CARGO_TARGET_DIR` environment variable in // case `cargo dylint` is invoked. @@ -809,7 +823,7 @@ pub fn execute(args: ExecuteArgs) -> Result { let (opt_result, metadata_result, dest_wasm) = match build_artifact { BuildArtifacts::CheckOnly => { // Check basically means only running our linter without building. - lint(*extra_lints, &crate_metadata, verbosity)?; + lint(*extra_lints, &crate_metadata, &target, verbosity)?; (None, None, None) } BuildArtifacts::CodeOnly => { @@ -892,7 +906,7 @@ fn local_build( // We always want to lint first so we don't suppress any warnings when a build is // skipped because of a matching fingerprint. - lint(*extra_lints, crate_metadata, verbosity)?; + lint(*extra_lints, crate_metadata, &target, verbosity)?; let pre_fingerprint = Fingerprint::new(crate_metadata)?; From 8f392c4b0f5afafcdcf3b79a9a075ddc1bb68cb3 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Sat, 23 Dec 2023 16:05:01 -0300 Subject: [PATCH 06/32] chore: fmt+clippy --- crates/build/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/build/src/lib.rs b/crates/build/src/lib.rs index aa5d46042..b194d7291 100644 --- a/crates/build/src/lib.rs +++ b/crates/build/src/lib.rs @@ -454,7 +454,7 @@ fn execute_cargo(cargo: duct::Expression) -> Result<()> { { Ok(out) if out.status.success() => Ok(()), Ok(out) => anyhow::bail!(String::from_utf8_lossy(&out.stderr).to_string()), - Err(e) => anyhow::bail!("Cannot run `cargo` command") + Err(e) => anyhow::bail!("Cannot run `cargo` command"), } } @@ -507,7 +507,7 @@ fn exec_cargo_clippy(crate_metadata: &CrateMetadata, verbosity: Verbosity) -> Re /// Returns a list of cargo options used for on-chain builds fn onchain_cargo_options(target: &Target) -> Vec { - return vec![ + vec![ format!("--target={}", target.llvm_target()), "-Zbuild-std=core,alloc".to_owned(), "--no-default-features".to_owned(), @@ -823,7 +823,7 @@ pub fn execute(args: ExecuteArgs) -> Result { let (opt_result, metadata_result, dest_wasm) = match build_artifact { BuildArtifacts::CheckOnly => { // Check basically means only running our linter without building. - lint(*extra_lints, &crate_metadata, &target, verbosity)?; + lint(*extra_lints, &crate_metadata, target, verbosity)?; (None, None, None) } BuildArtifacts::CodeOnly => { @@ -906,7 +906,7 @@ fn local_build( // We always want to lint first so we don't suppress any warnings when a build is // skipped because of a matching fingerprint. - lint(*extra_lints, crate_metadata, &target, verbosity)?; + lint(*extra_lints, crate_metadata, target, verbosity)?; let pre_fingerprint = Fingerprint::new(crate_metadata)?; From d74f7ca932da40bc0d84f75785f61fa166c4ca5a Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Sat, 23 Dec 2023 16:08:34 -0300 Subject: [PATCH 07/32] fix: Unneeded steps when executing `cargo` --- crates/build/src/lib.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/crates/build/src/lib.rs b/crates/build/src/lib.rs index b194d7291..85e2d2f04 100644 --- a/crates/build/src/lib.rs +++ b/crates/build/src/lib.rs @@ -447,11 +447,7 @@ fn check_buffer_size_invoke_cargo_clean( /// Executes the supplied cargo command, reading the output and scanning for known errors. /// Writes the captured stderr back to stderr and maintains the cargo tty progress bar. fn execute_cargo(cargo: duct::Expression) -> Result<()> { - match util::cargo_tty_output(cargo) - .unchecked() - .stderr_capture() - .run() - { + match cargo.unchecked().run() { Ok(out) if out.status.success() => Ok(()), Ok(out) => anyhow::bail!(String::from_utf8_lossy(&out.stderr).to_string()), Err(e) => anyhow::bail!("Cannot run `cargo` command"), From 1d1560166f1ad534258c7cbd08520e52fefd4eea Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Sat, 23 Dec 2023 16:11:08 -0300 Subject: [PATCH 08/32] feat: Print more context on error when running `cargo` --- crates/build/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/build/src/lib.rs b/crates/build/src/lib.rs index 85e2d2f04..fb2e09b6d 100644 --- a/crates/build/src/lib.rs +++ b/crates/build/src/lib.rs @@ -450,7 +450,7 @@ fn execute_cargo(cargo: duct::Expression) -> Result<()> { match cargo.unchecked().run() { Ok(out) if out.status.success() => Ok(()), Ok(out) => anyhow::bail!(String::from_utf8_lossy(&out.stderr).to_string()), - Err(e) => anyhow::bail!("Cannot run `cargo` command"), + Err(e) => anyhow::bail!("Cannot run `cargo` command: {:?}", e), } } From 83df3fbe21fcd597025b2437fb10a0043b1d9fa9 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Sat, 23 Dec 2023 16:13:44 -0300 Subject: [PATCH 09/32] chore: Bump changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9175cd7d..0bfcf1e36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add warning message when using incompatible contract's ink! version - [#1334](https://github.com/paritytech/cargo-contract/pull/1334) - Bump `subxt` to `0.32.0` - [#1352](https://github.com/paritytech/cargo-contract/pull/1352) - Remove check for compatible `scale` and `scale-info` versions - [#1370](https://github.com/paritytech/cargo-contract/pull/1370) +- Mandatory dylint-based lints - [#1412](https://github.com/paritytech/cargo-contract/pull/1412) ## [4.0.0-alpha] From 8950df25f574e24399c4b41f2e8b9645c4d5fb83 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Sat, 23 Dec 2023 16:37:56 -0300 Subject: [PATCH 10/32] feat: Install `cargo-dylint` and `dylint-link` in CI --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b869bd374..416306b07 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -141,6 +141,10 @@ jobs: target: wasm32-unknown-unknown components: rust-src, clippy + # Required to run mandatory lints in `cargo contract build` tests + - name: Install cargo-dylint and dylint-link + run: cargo install cargo-dylint dylint-link + - name: Cache uses: Swatinem/rust-cache@v2 From 577fa728a2f775cafb329247116bd4cd1e730791 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Sat, 23 Dec 2023 16:44:33 -0300 Subject: [PATCH 11/32] fix(ci): Install missing packages in `template` section --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 416306b07..60e83c799 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -189,6 +189,10 @@ jobs: target: wasm32-unknown-unknown components: rust-src, clippy + # Required to run mandatory lints in `cargo contract build` + - name: Install cargo-dylint and dylint-link + run: cargo install cargo-dylint dylint-link + - name: Cache uses: Swatinem/rust-cache@v2 From b3cbec9c5e5f8092617c56973a085450f957e33d Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Sat, 23 Dec 2023 16:47:28 -0300 Subject: [PATCH 12/32] fix(readme): Mark the `dylint` installation step as mandatory --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 716d92c40..82d3de6f6 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Modern releases of gcc and clang, as well as Visual Studio 2019+ should work. - Step 2: `cargo install --force --locked cargo-contract`. -- Step 3: (**Optional**) Install `dylint` for linting. +- Step 3: Install `dylint` for linting. - (MacOS) `brew install openssl` - `cargo install cargo-dylint dylint-link`. From f21e2b806e7bcd3640a3abcd0be2ca0f7830449d Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Sat, 23 Dec 2023 16:53:18 -0300 Subject: [PATCH 13/32] feat(docker): Add `dylint` installation to the docker image --- build-image/Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build-image/Dockerfile b/build-image/Dockerfile index 1f2541039..06a8bbc48 100644 --- a/build-image/Dockerfile +++ b/build-image/Dockerfile @@ -91,6 +91,8 @@ RUN apt-get -y update && apt-get -y install gcc=${GCC_VERSION} g++=${G_VERSION} fi \ && echo "Executing ${COMMAND}" \ && eval "${COMMAND}" \ + && echo "Installing `cargo-dylint` and `dylint-link`" \ + && cargo install cargo-dylint dylint-link \ # Cleanup after `cargo install` && rm -rf ${CARGO_HOME}/"registry" ${CARGO_HOME}/"git" /root/.cache/sccache \ # apt clean up From af380eb87024a8e37eabf0803d44153d5562193e Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Mon, 15 Jan 2024 12:53:20 -0300 Subject: [PATCH 14/32] fix(ci): Install linting dependencies --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 60e83c799..db2c4399a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -142,8 +142,8 @@ jobs: components: rust-src, clippy # Required to run mandatory lints in `cargo contract build` tests - - name: Install cargo-dylint and dylint-link - run: cargo install cargo-dylint dylint-link + - name: Install linting dependencies + run: cargo install cargo-dylint dylint-link ink_linting_mandatory ink_linting - name: Cache uses: Swatinem/rust-cache@v2 From 6c4be8ddfaee4691e10df3c68dba96f4287acabf Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Mon, 15 Jan 2024 14:21:33 -0300 Subject: [PATCH 15/32] fix(ci): deps --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index db2c4399a..eed3f0dfc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -143,7 +143,7 @@ jobs: # Required to run mandatory lints in `cargo contract build` tests - name: Install linting dependencies - run: cargo install cargo-dylint dylint-link ink_linting_mandatory ink_linting + run: cargo install cargo-dylint dylint-link ink_linting_mandatory - name: Cache uses: Swatinem/rust-cache@v2 @@ -191,7 +191,7 @@ jobs: # Required to run mandatory lints in `cargo contract build` - name: Install cargo-dylint and dylint-link - run: cargo install cargo-dylint dylint-link + run: cargo install cargo-dylint dylint-link ink_linting_mandatory - name: Cache uses: Swatinem/rust-cache@v2 From 9fb52220cc9bb73cd2973088686436612eaa3136 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Mon, 15 Jan 2024 14:25:17 -0300 Subject: [PATCH 16/32] fix: Install dependencies --- README.md | 6 +++--- build-image/Dockerfile | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a192d2259..6a2a2dddb 100644 --- a/README.md +++ b/README.md @@ -42,10 +42,10 @@ Modern releases of gcc and clang, as well as Visual Studio 2019+ should work. - Step 2: `cargo install --force --locked cargo-contract`. -- Step 3: Install `dylint` for linting. +- Step 3: Install `dylint` libraries for linting. - (MacOS) `brew install openssl` - - `cargo install cargo-dylint dylint-link`. + - `cargo install cargo-dylint dylint-link ink_linting_mandatory`. - Step 4: (**Optional**) Install [Docker Engine](https://docs.docker.com/engine/install) to be able to produce verifiable builds. @@ -163,7 +163,7 @@ Generate schema and print it to STDOUT. ##### `cargo contract verify-schema` -Verify a metadata file or a contract bundle containing metadata against the schema file. +Verify a metadata file or a contract bundle containing metadata against the schema file. ##### `cargo contract storage` diff --git a/build-image/Dockerfile b/build-image/Dockerfile index 06a8bbc48..d77be7a0e 100644 --- a/build-image/Dockerfile +++ b/build-image/Dockerfile @@ -91,8 +91,8 @@ RUN apt-get -y update && apt-get -y install gcc=${GCC_VERSION} g++=${G_VERSION} fi \ && echo "Executing ${COMMAND}" \ && eval "${COMMAND}" \ - && echo "Installing `cargo-dylint` and `dylint-link`" \ - && cargo install cargo-dylint dylint-link \ + && echo "Installing linting dependencies" \ + && cargo install cargo-dylint dylint-link ink_linting_mandatory ink_linting \ # Cleanup after `cargo install` && rm -rf ${CARGO_HOME}/"registry" ${CARGO_HOME}/"git" /root/.cache/sccache \ # apt clean up From 4a22459a7847227020fcf8faa2cf38b66de9897c Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Thu, 18 Jan 2024 21:13:54 -0300 Subject: [PATCH 17/32] fix(build): Fix installation for `ink_linting` libraries Reuse the previous approach with hacking the workspace manifest in the contract project. We cannot add those libraries as direct dependencies to cargo-contract because we use a different version of the toolchain. To make it possible for these libraries to be found at runtime, we would also have to hack dylint. --- .github/workflows/ci.yml | 4 ++-- README.md | 4 ++-- build-image/Dockerfile | 2 +- crates/build/src/lib.rs | 2 +- crates/build/src/workspace/manifest.rs | 12 ++++++++---- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eed3f0dfc..720bef0a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -143,7 +143,7 @@ jobs: # Required to run mandatory lints in `cargo contract build` tests - name: Install linting dependencies - run: cargo install cargo-dylint dylint-link ink_linting_mandatory + run: cargo install cargo-dylint dylint-link - name: Cache uses: Swatinem/rust-cache@v2 @@ -191,7 +191,7 @@ jobs: # Required to run mandatory lints in `cargo contract build` - name: Install cargo-dylint and dylint-link - run: cargo install cargo-dylint dylint-link ink_linting_mandatory + run: cargo install cargo-dylint dylint-link - name: Cache uses: Swatinem/rust-cache@v2 diff --git a/README.md b/README.md index 6a2a2dddb..581d0c5ed 100644 --- a/README.md +++ b/README.md @@ -42,10 +42,10 @@ Modern releases of gcc and clang, as well as Visual Studio 2019+ should work. - Step 2: `cargo install --force --locked cargo-contract`. -- Step 3: Install `dylint` libraries for linting. +- Step 3: Install `dylint` for linting. - (MacOS) `brew install openssl` - - `cargo install cargo-dylint dylint-link ink_linting_mandatory`. + - `cargo install cargo-dylint dylint-link`. - Step 4: (**Optional**) Install [Docker Engine](https://docs.docker.com/engine/install) to be able to produce verifiable builds. diff --git a/build-image/Dockerfile b/build-image/Dockerfile index d77be7a0e..2f528c1dc 100644 --- a/build-image/Dockerfile +++ b/build-image/Dockerfile @@ -92,7 +92,7 @@ RUN apt-get -y update && apt-get -y install gcc=${GCC_VERSION} g++=${G_VERSION} && echo "Executing ${COMMAND}" \ && eval "${COMMAND}" \ && echo "Installing linting dependencies" \ - && cargo install cargo-dylint dylint-link ink_linting_mandatory ink_linting \ + && cargo install cargo-dylint dylint-link \ # Cleanup after `cargo install` && rm -rf ${CARGO_HOME}/"registry" ${CARGO_HOME}/"git" /root/.cache/sccache \ # apt clean up diff --git a/crates/build/src/lib.rs b/crates/build/src/lib.rs index 8b45c07e9..977cda79f 100644 --- a/crates/build/src/lib.rs +++ b/crates/build/src/lib.rs @@ -560,7 +560,7 @@ fn exec_cargo_dylint( Workspace::new(&crate_metadata.cargo_meta, &crate_metadata.root_package.id)? .with_root_package_manifest(|manifest| { - manifest.with_dylint()?.with_empty_workspace(); + manifest.with_dylint()?; Ok(()) })? .using_temp(|manifest_path| { diff --git a/crates/build/src/workspace/manifest.rs b/crates/build/src/workspace/manifest.rs index c9ae6195e..22f00c97f 100644 --- a/crates/build/src/workspace/manifest.rs +++ b/crates/build/src/workspace/manifest.rs @@ -314,11 +314,15 @@ impl Manifest { } pub fn with_dylint(&mut self) -> Result<&mut Self> { - let ink_dylint = { + let ink_dylint = |lib_name: &str| { let mut map = value::Table::new(); map.insert("git".into(), "https://github.com/paritytech/ink/".into()); - map.insert("tag".into(), "v4.0.0-alpha.3".into()); - map.insert("pattern".into(), "linting/".into()); + // TODO: Add the latest release tag before merging + // map.insert("tag".into(), "master".into()); + map.insert( + "pattern".into(), + value::Value::String(format!("linting/{}", lib_name)), + ); value::Value::Table(map) }; @@ -339,7 +343,7 @@ impl Manifest { .or_insert(value::Value::Array(Default::default())) .as_array_mut() .context("workspace.metadata.dylint.libraries section should be an array")? - .push(ink_dylint); + .extend(vec![ink_dylint("mandatory"), ink_dylint("extra")]); Ok(self) } From a97de2fab657810becbc4936a259231fa176ee72 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Fri, 19 Jan 2024 11:30:08 -0300 Subject: [PATCH 18/32] chore: Add rust-toolchain.toml --- rust-toolchain.toml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 rust-toolchain.toml diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 000000000..b00da7b4b --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,10 @@ +# This file corresponds to the `rust-toolchain` file used in `ink_linting` here: +# https://github.com/paritytech/ink/blob/1c029a153ead15cd0bd76631613967c9e679e0c1/linting/rust-toolchain.toml#L5 +# +# The `ink_linting` lints are integrated into each contract's manifest file and +# compiled for each project. We need this file here to build the entire project +# using the same toolchain, thereby simplifying the installation process. + +[toolchain] +channel = "nightly-2023-12-28" +components = ["llvm-tools-preview", "rustc-dev"] From 557a6960ddd646f44f12d3b95bdf3afaff0dc733 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Fri, 19 Jan 2024 11:57:51 -0300 Subject: [PATCH 19/32] fix(ci): Use rust-toolchain.toml from the repo --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 720bef0a4..5534f2f51 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,6 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly default: true components: rustfmt From 45c531fe198a7dfe20c853fdad1b17610c18849f Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Fri, 19 Jan 2024 12:05:50 -0300 Subject: [PATCH 20/32] fix(ci): Toolchain version --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5534f2f51..477aa9373 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,6 +17,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal + toolchain: nightly-2023-12-28 default: true components: rustfmt From 823dac9054467b787b039a7f179fde106dbe752a Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Fri, 19 Jan 2024 12:17:51 -0300 Subject: [PATCH 21/32] fix(ci): Try to run it with the nightly toolchain --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 477aa9373..91042b81c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,7 +53,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: stable + toolchain: nightly-2023-12-28 - name: Cache uses: Swatinem/rust-cache@v2 @@ -75,7 +75,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: stable + toolchain: nightly-2023-12-28 default: true components: clippy @@ -136,7 +136,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: stable + toolchain: nightly-2023-12-28 default: true target: wasm32-unknown-unknown components: rust-src, clippy From 3d4af86f48a5d44b70e9b8ed1b91c3927188796a Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Fri, 19 Jan 2024 15:09:22 -0300 Subject: [PATCH 22/32] fix(ci): Force `stable` toolchain for the `clippy` action Otherwise, it takes the toolchain version from `rust-toolchain.toml` --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 91042b81c..87d08d968 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,12 +70,14 @@ jobs: clippy: runs-on: ubuntu-latest + env: + RUSTUP_TOOLCHAIN: stable steps: - name: Install toolchain uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly-2023-12-28 + toolchain: stable default: true components: clippy From 84780ba855ae19674e9694a7c7ef50a0ec88b417 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Fri, 19 Jan 2024 19:34:56 -0300 Subject: [PATCH 23/32] fix(tests): Failing test --- crates/build/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/build/src/tests.rs b/crates/build/src/tests.rs index 022a87ad7..475b65b4f 100644 --- a/crates/build/src/tests.rs +++ b/crates/build/src/tests.rs @@ -307,7 +307,7 @@ fn building_contract_with_build_rs_must_work(manifest_path: &ManifestPath) -> Re let path = manifest_path.directory().expect("dir must exist"); let build_rs_path = path.join(Path::new("build.rs")); - fs::write(build_rs_path, "fn main() {}")?; + fs::write(build_rs_path, "#![cfg_attr(dylint_lib = \"ink_linting_mandatory\", allow(no_main))]\n\nfn main() {}")?; let args = ExecuteArgs { manifest_path: manifest_path.clone(), From 01dd588344db31ee46d61730c91211769d1eb215 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Sat, 20 Jan 2024 05:51:10 -0300 Subject: [PATCH 24/32] fix(readme) --- crates/build/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/build/README.md b/crates/build/README.md index d789dfb0b..6e03a498c 100644 --- a/crates/build/README.md +++ b/crates/build/README.md @@ -32,7 +32,7 @@ let args = contract_build::ExecuteArgs { unstable_flags: UnstableFlags::default(), optimization_passes: Some(OptimizationPasses::default()), keep_debug_symbols: false, - dylint: false, + extra_lints: false, output_type: OutputType::Json, skip_wasm_validation: false, target: Target::Wasm, From 44cf7b1000e04d72e9088aff56bc3e25f638a541 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Sat, 20 Jan 2024 08:01:40 -0300 Subject: [PATCH 25/32] fix(ci): Use the `nightly` toolchain for template builds This is necessary to avoid installing two versions of toolchains. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 87d08d968..b2bb2c39d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -186,7 +186,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: stable + toolchain: nightly-2023-12-28 default: true target: wasm32-unknown-unknown components: rust-src, clippy From 38c8bcd8264d0139d76e9b60d9830cc9dfc8963e Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Tue, 23 Jan 2024 07:37:27 -0300 Subject: [PATCH 26/32] chore: Remove the fixed toolchain version --- .github/workflows/ci.yml | 8 +++----- rust-toolchain.toml | 10 ---------- 2 files changed, 3 insertions(+), 15 deletions(-) delete mode 100644 rust-toolchain.toml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b2bb2c39d..283720990 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly-2023-12-28 + toolchain: nightly default: true components: rustfmt @@ -53,7 +53,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly-2023-12-28 + toolchain: stable - name: Cache uses: Swatinem/rust-cache@v2 @@ -70,8 +70,6 @@ jobs: clippy: runs-on: ubuntu-latest - env: - RUSTUP_TOOLCHAIN: stable steps: - name: Install toolchain uses: actions-rs/toolchain@v1 @@ -138,7 +136,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly-2023-12-28 + toolchain: stable default: true target: wasm32-unknown-unknown components: rust-src, clippy diff --git a/rust-toolchain.toml b/rust-toolchain.toml deleted file mode 100644 index b00da7b4b..000000000 --- a/rust-toolchain.toml +++ /dev/null @@ -1,10 +0,0 @@ -# This file corresponds to the `rust-toolchain` file used in `ink_linting` here: -# https://github.com/paritytech/ink/blob/1c029a153ead15cd0bd76631613967c9e679e0c1/linting/rust-toolchain.toml#L5 -# -# The `ink_linting` lints are integrated into each contract's manifest file and -# compiled for each project. We need this file here to build the entire project -# using the same toolchain, thereby simplifying the installation process. - -[toolchain] -channel = "nightly-2023-12-28" -components = ["llvm-tools-preview", "rustc-dev"] From f3212f3e72547fe32b640a8e354a753f0439a475 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Tue, 23 Jan 2024 11:21:39 -0300 Subject: [PATCH 27/32] feat(build): Make `dylint` mandatory only for RiscV or `--lint` --- crates/build/src/lib.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/crates/build/src/lib.rs b/crates/build/src/lib.rs index 977cda79f..3a81d2901 100644 --- a/crates/build/src/lib.rs +++ b/crates/build/src/lib.rs @@ -471,13 +471,18 @@ fn lint( ); exec_cargo_clippy(crate_metadata, *verbosity)?; - verbose_eprintln!( - verbosity, - " {} {}", - "[==]".bold(), - "Checking ink! linting rules".bright_green().bold() - ); - exec_cargo_dylint(extra_lints, crate_metadata, target, *verbosity)?; + // TODO (jubnzv): Dylint needs a custom toolchain installed by the user. Currently, + // it's required only for RiscV target. We're working on the toolchain integration + // and will make this step mandatory for all targets in future releases. + if extra_lints || matches!(target, Target::RiscV) { + verbose_eprintln!( + verbosity, + " {} {}", + "[==]".bold(), + "Checking ink! linting rules".bright_green().bold() + ); + exec_cargo_dylint(extra_lints, crate_metadata, target, *verbosity)?; + } Ok(()) } From 284f0ffb3f6f48d10071dfa632de230d63f27ee5 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Tue, 23 Jan 2024 11:24:23 -0300 Subject: [PATCH 28/32] feat(build): Check `ink_linting` toolchain --- crates/build/src/lib.rs | 39 +++++++++++++++++++++++++- crates/build/src/workspace/manifest.rs | 5 ++-- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/crates/build/src/lib.rs b/crates/build/src/lib.rs index 3a81d2901..b6189f07c 100644 --- a/crates/build/src/lib.rs +++ b/crates/build/src/lib.rs @@ -110,6 +110,19 @@ pub const DEFAULT_MAX_MEMORY_PAGES: u32 = 16; /// Version of the currently executing `cargo-contract` binary. const VERSION: &str = env!("CARGO_PKG_VERSION"); +/// Configuration of the linting module. +/// +/// Ensure it is kept up-to-date when updating `cargo-contract`. +pub(crate) mod linting { + /// Toolchain used to build ink_linting: + /// https://github.com/paritytech/ink/blob/master/linting/rust-toolchain.toml + pub const TOOLCHAIN_VERSION: &str = "nightly-2023-12-28"; + /// Git repository with ink_linting libraries + pub const GIT_URL: &str = "https://github.com/paritytech/ink/"; + /// Git revision number of the linting crate + pub const GIT_REV: &str = "1c029a153ead15cd0bd76631613967c9e679e0c1"; +} + /// Arguments to use when executing `build` or `check` commands. #[derive(Clone)] pub struct ExecuteArgs { @@ -586,7 +599,8 @@ fn exec_cargo_dylint( /// Checks if all requirements for `dylint` are installed. /// /// We require both `cargo-dylint` and `dylint-link` because the driver is being -/// built at runtime on demand. +/// built at runtime on demand. These must be built using a custom version of the +/// toolchain, as the linter utilizes the unstable rustc API. /// /// This function takes a `_working_dir` which is only used for unit tests. fn check_dylint_requirements(_working_dir: Option<&Path>) -> Result<()> { @@ -608,6 +622,29 @@ fn check_dylint_requirements(_working_dir: Option<&Path>) -> Result<()> { }) }; + // Check if the required toolchain is present and is installed with `rustup`. + if let Ok(output) = Command::new("rustup").arg("toolchain").arg("list").output() { + anyhow::ensure!( + String::from_utf8_lossy(&output.stdout).contains(linting::TOOLCHAIN_VERSION), + format!( + "Toolchain `{0}` was not found!\n\ + This specific version is required to provide additional source code analysis.\n\n + You can install it by executing `rustup install {0}`.", + linting::TOOLCHAIN_VERSION, + ) + .to_string() + .bright_yellow()); + } else { + anyhow::bail!(format!( + "Toolchain `{0}` was not found!\n\ + This specific version is required to provide additional source code analysis.\n\n + Install `rustup` according to https://rustup.rs/ and then run: `rustup install {0}`.", + linting::TOOLCHAIN_VERSION, + ) + .to_string() + .bright_yellow()); + } + // when testing this function we should never fall back to a `cargo` specified // in the env variable, as this would mess with the mocked binaries. #[cfg(not(test))] diff --git a/crates/build/src/workspace/manifest.rs b/crates/build/src/workspace/manifest.rs index 22f00c97f..0601f0c8f 100644 --- a/crates/build/src/workspace/manifest.rs +++ b/crates/build/src/workspace/manifest.rs @@ -316,9 +316,8 @@ impl Manifest { pub fn with_dylint(&mut self) -> Result<&mut Self> { let ink_dylint = |lib_name: &str| { let mut map = value::Table::new(); - map.insert("git".into(), "https://github.com/paritytech/ink/".into()); - // TODO: Add the latest release tag before merging - // map.insert("tag".into(), "master".into()); + map.insert("git".into(), crate::linting::GIT_URL.into()); + map.insert("rev".into(), crate::linting::GIT_REV.into()); map.insert( "pattern".into(), value::Value::String(format!("linting/{}", lib_name)), From df89a7547e70120bdc550628030b59deda065287 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Tue, 23 Jan 2024 12:19:43 -0300 Subject: [PATCH 29/32] fix: `cargo-dylint` test --- crates/build/src/tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/build/src/tests.rs b/crates/build/src/tests.rs index 475b65b4f..80bd5c7f0 100644 --- a/crates/build/src/tests.rs +++ b/crates/build/src/tests.rs @@ -396,6 +396,7 @@ fn missing_cargo_dylint_installation_must_be_detected( let args = ExecuteArgs { manifest_path: manifest_path.clone(), extra_lints: true, + target: Target::RiscV, ..Default::default() }; let res = super::execute(args).map(|_| ()).unwrap_err(); From 789087c3dc8a10a9a4fa6ebff92ca650d4c1bcc3 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Tue, 23 Jan 2024 14:35:20 -0300 Subject: [PATCH 30/32] fix(ci): Remove `dylint` installation There is a test that checks if `cargo-dylint` is not installed for the native toolchain. Right now we can left it as it is. It will be fixed when we automate installation of the toolchain. --- .github/workflows/ci.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 283720990..3a937ffe3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -141,10 +141,6 @@ jobs: target: wasm32-unknown-unknown components: rust-src, clippy - # Required to run mandatory lints in `cargo contract build` tests - - name: Install linting dependencies - run: cargo install cargo-dylint dylint-link - - name: Cache uses: Swatinem/rust-cache@v2 @@ -189,10 +185,6 @@ jobs: target: wasm32-unknown-unknown components: rust-src, clippy - # Required to run mandatory lints in `cargo contract build` - - name: Install cargo-dylint and dylint-link - run: cargo install cargo-dylint dylint-link - - name: Cache uses: Swatinem/rust-cache@v2 From 169da843f841b6444e84e2b9aba0b27c8fb94809 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Tue, 23 Jan 2024 15:05:23 -0300 Subject: [PATCH 31/32] fix(test): Run only on mandatory lints which are disabled --- crates/build/src/tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/build/src/tests.rs b/crates/build/src/tests.rs index 80bd5c7f0..07a303624 100644 --- a/crates/build/src/tests.rs +++ b/crates/build/src/tests.rs @@ -395,8 +395,8 @@ fn missing_cargo_dylint_installation_must_be_detected( // when let args = ExecuteArgs { manifest_path: manifest_path.clone(), - extra_lints: true, - target: Target::RiscV, + extra_lints: false, + target: Target::Wasm, ..Default::default() }; let res = super::execute(args).map(|_| ()).unwrap_err(); From a86c38b8e88653924ef6aee0c523569905523f01 Mon Sep 17 00:00:00 2001 From: Georgiy Komarov Date: Tue, 23 Jan 2024 17:48:01 -0300 Subject: [PATCH 32/32] fix(test): Fix to check for non-existing toolchain --- crates/build/src/tests.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/crates/build/src/tests.rs b/crates/build/src/tests.rs index 07a303624..119ea6e5d 100644 --- a/crates/build/src/tests.rs +++ b/crates/build/src/tests.rs @@ -76,7 +76,7 @@ build_tests!( build_with_json_output_works, building_contract_with_source_file_in_subfolder_must_work, building_contract_with_build_rs_must_work, - missing_cargo_dylint_installation_must_be_detected, + missing_linting_toolchain_installation_must_be_detected, generates_metadata, unchanged_contract_skips_optimization_and_metadata_steps, unchanged_contract_no_metadata_artifacts_generates_metadata @@ -378,7 +378,7 @@ fn build_with_json_output_works(manifest_path: &ManifestPath) -> Result<()> { } #[cfg(unix)] -fn missing_cargo_dylint_installation_must_be_detected( +fn missing_linting_toolchain_installation_must_be_detected( manifest_path: &ManifestPath, ) -> Result<()> { use super::util::tests::create_executable; @@ -386,23 +386,19 @@ fn missing_cargo_dylint_installation_must_be_detected( // given let manifest_dir = manifest_path.directory().unwrap(); - // mock existing `dylint-link` binary - let _tmp0 = create_executable(&manifest_dir.join("dylint-link"), "#!/bin/sh\nexit 0"); - - // mock a non-existing `cargo dylint` installation. - let _tmp1 = create_executable(&manifest_dir.join("cargo"), "#!/bin/sh\nexit 1"); + // mock non-existing `rustup` binary + let _tmp0 = create_executable(&manifest_dir.join("rustup"), "#!/bin/sh\nexit 1"); // when let args = ExecuteArgs { manifest_path: manifest_path.clone(), - extra_lints: false, - target: Target::Wasm, + extra_lints: true, ..Default::default() }; let res = super::execute(args).map(|_| ()).unwrap_err(); // then - assert!(format!("{res:?}").contains("cargo-dylint was not found!")); + assert!(format!("{res:?}").contains("` was not found!")); Ok(()) }