Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggest adding no_main for error[E0601] #1113

Merged
merged 16 commits into from
May 17, 2023
44 changes: 44 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/build/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ blake2 = "0.10.6"
cargo_metadata = "0.15.4"
colored = "2.0.0"
clap = { version = "4.2.7", features = ["derive", "env"] }
duct = "0.13.6"
heck = "0.4.0"
hex = "0.4.3"
impl-serde = "0.4.0"
Expand All @@ -31,6 +32,7 @@ semver = { version = "1.0.17", features = ["serde"] }
serde = { version = "1", default-features = false, features = ["derive"] }
serde_json = "1.0.96"
tempfile = "3.5.0"
term_size = "0.3.2"
url = { version = "2.3.1", features = ["serde"] }
wasm-opt = "0.112.0"
which = "4.4.0"
Expand Down
69 changes: 64 additions & 5 deletions crates/build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ use parity_wasm::elements::{
};
use semver::Version;
use std::{
collections::VecDeque,
fs,
io,
path::{
Path,
PathBuf,
Expand Down Expand Up @@ -291,9 +293,10 @@ fn exec_cargo_for_onchain_target(
None
};

util::invoke_cargo(command, &args, manifest_path.directory(), verbosity, env)?;
let cargo =
util::cargo_cmd(command, &args, manifest_path.directory(), verbosity, env);

Ok(())
invoke_cargo_and_scan_for_error(cargo)
};

if unstable_flags.original_manifest {
Expand All @@ -320,6 +323,51 @@ fn exec_cargo_for_onchain_target(
Ok(())
}

/// 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());
}};
}

let cargo = util::cargo_tty_output(cargo);

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!("\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 {
break
}
buffer = [0u8; 1];
}
Ok(())
}

/// Executes `cargo dylint` with the ink! linting driver that is built during
/// the `build.rs`.
///
Expand Down Expand Up @@ -357,8 +405,15 @@ fn exec_cargo_dylint(crate_metadata: &CrateMetadata, verbosity: Verbosity) -> Re
Ok(())
})?
.using_temp(|manifest_path| {
util::invoke_cargo("dylint", &args, manifest_path.directory(), verbosity, env)
.map(|_| ())
let cargo = util::cargo_cmd(
"dylint",
&args,
manifest_path.directory(),
verbosity,
env,
);
cargo.run()?;
Ok(())
})?;

Ok(())
Expand Down Expand Up @@ -548,7 +603,11 @@ fn assert_compatible_ink_dependencies(
) -> 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, vec![])
let cargo =
util::cargo_cmd("tree", args, manifest_path.directory(), verbosity, vec![]);
cargo
.stdout_null()
.run()
.with_context(|| {
format!(
"Mismatching versions of `{dependency}` were found!\n\
Expand Down
7 changes: 4 additions & 3 deletions crates/build/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,16 +149,17 @@ pub(crate) fn execute(
network.append_to_args(&mut args);
features.append_to_args(&mut args);

let stdout = util::invoke_cargo(
let cmd = util::cargo_cmd(
"run",
args,
crate_metadata.manifest_path.directory(),
verbosity,
vec![],
)?;
);
let output = cmd.stdout_capture().run()?;

let ink_meta: serde_json::Map<String, serde_json::Value> =
serde_json::from_slice(&stdout)?;
serde_json::from_slice(&output.stdout)?;
let metadata = ContractMetadata::new(source, contract, user, ink_meta);
{
let mut metadata = metadata.clone();
Expand Down
90 changes: 43 additions & 47 deletions crates/build/src/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,11 @@
pub mod tests;

use crate::Verbosity;
use anyhow::{
Context,
Result,
};
use anyhow::Result;
use duct::Expression;
use std::{
ffi::OsStr,
ffi::OsString,
path::Path,
process::Command,
};

// Returns the current Rust toolchain formatted by `<channel>-<target-triple>`.
Expand All @@ -36,75 +33,74 @@ pub(crate) fn rust_toolchain() -> Result<String> {
Ok(toolchain)
}

/// Invokes `cargo` with the subcommand `command` and the supplied `args`.
/// Builds an [`Expression`] for invoking `cargo`.
///
/// In case `working_dir` is set, the command will be invoked with that folder
/// as the working directory.
///
/// In case `env` is given environment variables can be either set or unset:
/// * To _set_ push an item a la `("VAR_NAME", Some("VAR_VALUE"))` to the `env` vector.
/// * To _unset_ push an item a la `("VAR_NAME", None)` to the `env` vector.
///
/// If successful, returns the stdout bytes.
pub fn invoke_cargo<I, S, P>(
pub fn cargo_cmd<I, S, P>(
command: &str,
args: I,
working_dir: Option<P>,
verbosity: Verbosity,
env: Vec<(&str, Option<String>)>,
) -> Result<Vec<u8>>
) -> Expression
where
I: IntoIterator<Item = S> + std::fmt::Debug,
S: AsRef<OsStr>,
S: Into<OsString>,
P: AsRef<Path>,
{
let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
let mut cmd = Command::new(cargo);
let mut cmd_args = Vec::new();

cmd_args.push(command);
cmd_args.push("--color=always");

match verbosity {
Verbosity::Quiet => cmd_args.push("--quiet"),
Verbosity::Verbose => {
if command != "dylint" {
cmd_args.push("--verbose")
}
}
Verbosity::Default => (),
};

let mut cmd_args: Vec<OsString> = cmd_args.iter().map(Into::into).collect();
for arg in args {
cmd_args.push(arg.into());
}

let mut cmd = duct::cmd(cargo, &cmd_args);

env.iter().for_each(|(env_key, maybe_env_val)| {
match maybe_env_val {
Some(env_val) => cmd.env(env_key, env_val),
None => cmd.env_remove(env_key),
Some(env_val) => cmd = cmd.env(env_key, env_val),
None => cmd = cmd.env_remove(env_key),
};
});

if let Some(path) = working_dir {
tracing::debug!("Setting cargo working dir to '{}'", path.as_ref().display());
cmd.current_dir(path);
cmd = cmd.dir(path.as_ref());
}

cmd.arg(command);
cmd.args(args);
match verbosity {
Verbosity::Quiet => cmd.arg("--quiet"),
Verbosity::Verbose => {
if command != "dylint" {
cmd.arg("--verbose")
} else {
&mut cmd
}
}
Verbosity::Default => &mut cmd,
};
cmd
}

tracing::debug!("Invoking cargo: {:?}", cmd);

let child = cmd
// capture the stdout to return from this function as bytes
.stdout(std::process::Stdio::piped())
.spawn()
.context(format!("Error executing `{cmd:?}`"))?;
let output = child.wait_with_output()?;

if output.status.success() {
Ok(output.stdout)
} else {
anyhow::bail!(
"`{:?}` failed with exit code: {:?}",
cmd,
output.status.code()
);
}
/// Configures the cargo command to output colour and the progress bar.
pub fn cargo_tty_output(cmd: Expression) -> Expression {
// let term_size = term_size::dimensions_stderr()
// .map(|(width, _)| width.to_string())
// .unwrap_or_else(|| "100".to_string());
use term_size::dimensions_stderr;
let term_size = "100";
cmd.env("CARGO_TERM_COLOR", "always")
.env("CARGO_TERM_PROGRESS_WIDTH", term_size)
.env("CARGO_TERM_PROGRESS_WHEN", "always")
}

/// Returns the base name of the path.
Expand Down