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