contract_build/util/
mod.rs

1// Copyright (C) Use Ink (UK) Ltd.
2// This file is part of cargo-contract.
3//
4// cargo-contract is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// cargo-contract is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with cargo-contract.  If not, see <http://www.gnu.org/licenses/>.
16
17pub mod rustc_wrapper;
18#[cfg(test)]
19pub mod tests;
20
21use crate::Verbosity;
22use anyhow::Result;
23use duct::Expression;
24use std::{
25    ffi::OsString,
26    path::Path,
27};
28use term_size as _;
29
30/// Returns the current Rust toolchain formatted by `<channel>-<target-triple>`.
31pub fn rust_toolchain() -> Result<String> {
32    let meta = rustc_version::version_meta()?;
33    let toolchain = format!("{:?}-{}", meta.channel, meta.host,).to_lowercase();
34
35    Ok(toolchain)
36}
37
38/// Convenience type alias for a list of env vars.
39pub(crate) type EnvVars<'env> = Vec<(&'env str, Option<String>)>;
40
41/// Builds an [`Expression`] for invoking `cargo`.
42///
43/// In case `working_dir` is set, the command will be invoked with that folder
44/// as the working directory.
45///
46/// In case `env` is given environment variables can be either set or unset:
47///   * To _set_ push an item a la `("VAR_NAME", Some("VAR_VALUE"))` to the `env` vector.
48///   * To _unset_ push an item a la `("VAR_NAME", None)` to the `env` vector.
49pub fn cargo_cmd<I, S, P>(
50    command: &str,
51    args: I,
52    working_dir: Option<P>,
53    verbosity: Verbosity,
54    env: EnvVars,
55) -> Expression
56where
57    I: IntoIterator<Item = S> + std::fmt::Debug,
58    S: Into<OsString>,
59    P: AsRef<Path>,
60{
61    let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
62    let mut cmd_args = Vec::new();
63
64    cmd_args.push(command);
65    if command != "dylint" {
66        cmd_args.push("--color=always");
67    }
68
69    match verbosity {
70        Verbosity::Quiet => cmd_args.push("--quiet"),
71        Verbosity::Verbose => {
72            if command != "dylint" {
73                cmd_args.push("--verbose")
74            }
75        }
76        Verbosity::Default => (),
77    };
78
79    let mut cmd_args: Vec<OsString> = cmd_args.iter().map(Into::into).collect();
80    for arg in args {
81        cmd_args.push(arg.into());
82    }
83
84    let mut cmd = duct::cmd(cargo, &cmd_args);
85
86    env.iter().for_each(|(env_key, maybe_env_val)| {
87        match maybe_env_val {
88            Some(env_val) => cmd = cmd.env(env_key, env_val),
89            None => cmd = cmd.env_remove(env_key),
90        };
91    });
92
93    if let Some(path) = working_dir {
94        tracing::debug!("Setting cargo working dir to '{}'", path.as_ref().display());
95        cmd = cmd.dir(path.as_ref());
96    }
97
98    cmd
99}
100
101/// Configures the cargo command to output colour and the progress bar.
102pub fn cargo_tty_output(cmd: Expression) -> Expression {
103    #[cfg(windows)]
104    let term_size = "100";
105
106    #[cfg(not(windows))]
107    let term_size = term_size::dimensions_stderr()
108        .map(|(width, _)| width.to_string())
109        .unwrap_or_else(|| "100".to_string());
110
111    cmd.env("CARGO_TERM_COLOR", "auto")
112        .env("CARGO_TERM_PROGRESS_WIDTH", term_size)
113        .env("CARGO_TERM_PROGRESS_WHEN", "auto")
114}
115
116/// Returns the base name of the path.
117pub(crate) fn base_name(path: &Path) -> &str {
118    path.file_name()
119        .expect("file name must exist")
120        .to_str()
121        .expect("must be valid utf-8")
122}
123
124/// Decode hex string with or without 0x prefix
125pub fn decode_hex(input: &str) -> Result<Vec<u8>, hex::FromHexError> {
126    hex::decode(input.trim_start_matches("0x"))
127}
128
129/// Prints to stderr if `verbosity.is_verbose()` is `true`.
130/// Like `cargo`, we use stderr for verbose output.
131#[macro_export]
132macro_rules! verbose_eprintln {
133    ($verbosity:expr, $($msg:tt)*) => {
134        if $verbosity.is_verbose() {
135            ::std::eprintln!($($msg)*);
136        }
137    };
138}
139
140pub const DEFAULT_KEY_COL_WIDTH: usize = 12;
141
142/// Pretty print name value, name right aligned with colour.
143#[macro_export]
144macro_rules! name_value_println {
145    ($name:tt, $value:expr, $width:expr) => {{
146        use colored::Colorize as _;
147        ::std::println!(
148            "{:>width$} {}",
149            $name.bright_purple().bold(),
150            $value,
151            width = $width,
152        );
153    }};
154    ($name:tt, $value:expr) => {
155        $crate::name_value_println!($name, $value, $crate::DEFAULT_KEY_COL_WIDTH)
156    };
157}