contract_build/
args.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
17use anyhow::Result;
18use clap::Args;
19use std::{
20    convert::TryFrom,
21    fmt,
22};
23
24#[derive(Default, Clone, Debug, Args)]
25pub struct VerbosityFlags {
26    /// No output printed to stdout
27    #[clap(long)]
28    quiet: bool,
29    /// Use verbose output
30    #[clap(long)]
31    verbose: bool,
32}
33
34impl TryFrom<&VerbosityFlags> for Verbosity {
35    type Error = anyhow::Error;
36
37    fn try_from(value: &VerbosityFlags) -> Result<Self, Self::Error> {
38        match (value.quiet, value.verbose) {
39            (false, false) => Ok(Verbosity::Default),
40            (true, false) => Ok(Verbosity::Quiet),
41            (false, true) => Ok(Verbosity::Verbose),
42            (true, true) => anyhow::bail!("Cannot pass both --quiet and --verbose flags"),
43        }
44    }
45}
46
47/// Denotes if output should be printed to stdout.
48#[derive(
49    Clone, Copy, Default, serde::Serialize, serde::Deserialize, Eq, PartialEq, Debug,
50)]
51pub enum Verbosity {
52    /// Use default output
53    #[default]
54    Default,
55    /// No output printed to stdout
56    Quiet,
57    /// Use verbose output
58    Verbose,
59}
60
61impl Verbosity {
62    /// Returns `true` if output should be printed (i.e. verbose output is set).
63    pub fn is_verbose(&self) -> bool {
64        match self {
65            Verbosity::Quiet => false,
66            Verbosity::Default | Verbosity::Verbose => true,
67        }
68    }
69}
70
71/// Use network connection to build contracts and generate metadata or use cached
72/// dependencies only.
73#[derive(Eq, PartialEq, Copy, Clone, Debug, Default, serde::Serialize)]
74pub enum Network {
75    /// Use network
76    #[default]
77    Online,
78    /// Use cached dependencies.
79    Offline,
80}
81
82impl Network {
83    /// If `Network::Offline` append the `--offline` flag for cargo invocations.
84    pub fn append_to_args(&self, args: &mut Vec<String>) {
85        match self {
86            Self::Online => (),
87            Self::Offline => args.push("--offline".to_owned()),
88        }
89    }
90}
91
92/// Describes which artifacts to generate
93#[derive(
94    Copy,
95    Clone,
96    Default,
97    Eq,
98    PartialEq,
99    Debug,
100    clap::ValueEnum,
101    serde::Serialize,
102    serde::Deserialize,
103)]
104#[clap(name = "build-artifacts")]
105pub enum BuildArtifacts {
106    /// Generate the Wasm, the metadata and a bundled `<name>.contract` file
107    #[clap(name = "all")]
108    #[default]
109    All,
110    /// Only the Wasm is created, generation of metadata and a bundled `<name>.contract`
111    /// file is skipped
112    #[clap(name = "code-only")]
113    CodeOnly,
114    /// No artifacts produced: runs the `cargo check` command for the Wasm target, only
115    /// checks for compilation errors.
116    #[clap(name = "check-only")]
117    CheckOnly,
118}
119
120impl BuildArtifacts {
121    /// Returns the number of steps required to complete a build artifact.
122    /// Used as output on the cli.
123    pub fn steps(&self) -> usize {
124        match self {
125            BuildArtifacts::All => 5,
126            BuildArtifacts::CodeOnly => 4,
127            BuildArtifacts::CheckOnly => 1,
128        }
129    }
130}
131
132/// The list of targets that ink! supports.
133#[derive(
134    Eq,
135    PartialEq,
136    Copy,
137    Clone,
138    Debug,
139    Default,
140    clap::ValueEnum,
141    serde::Serialize,
142    serde::Deserialize,
143    strum::EnumIter,
144)]
145pub enum Target {
146    /// WebAssembly
147    #[clap(name = "wasm")]
148    #[default]
149    Wasm,
150    /// RISC-V: Experimental
151    #[clap(name = "riscv")]
152    RiscV,
153}
154
155impl Target {
156    /// The target string to be passed to rustc in order to build for this target.
157    pub fn llvm_target(&self) -> &'static str {
158        match self {
159            Self::Wasm => "wasm32-unknown-unknown",
160            Self::RiscV => "riscv32i-unknown-none-elf",
161        }
162    }
163
164    /// Target specific flags to be set to `CARGO_ENCODED_RUSTFLAGS` while building.
165    pub fn rustflags(&self) -> Option<&'static str> {
166        match self {
167            Self::Wasm => Some("-Clink-arg=-zstack-size=65536\x1f-Clink-arg=--import-memory\x1f-Ctarget-cpu=mvp"),
168            Self::RiscV => None,
169        }
170    }
171
172    /// The file extension that is used by rustc when outputting the binary.
173    pub fn source_extension(&self) -> &'static str {
174        match self {
175            Self::Wasm => "wasm",
176            Self::RiscV => "",
177        }
178    }
179
180    // The file extension that is used to store the post processed binary.
181    pub fn dest_extension(&self) -> &'static str {
182        match self {
183            Self::Wasm => "wasm",
184            Self::RiscV => "riscv",
185        }
186    }
187}
188
189/// The mode to build the contract in.
190#[derive(
191    Eq, PartialEq, Copy, Clone, Debug, Default, serde::Serialize, serde::Deserialize,
192)]
193pub enum BuildMode {
194    /// Functionality to output debug messages is build into the contract.
195    #[default]
196    Debug,
197    /// The contract is built without any debugging functionality.
198    Release,
199    /// the contract is built in release mode and in a deterministic environment.
200    Verifiable,
201}
202
203impl fmt::Display for BuildMode {
204    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205        match self {
206            Self::Debug => write!(f, "debug"),
207            Self::Release => write!(f, "release"),
208            Self::Verifiable => write!(f, "verifiable"),
209        }
210    }
211}
212
213/// The type of output to display at the end of a build.
214#[derive(Clone, Debug, Default)]
215pub enum OutputType {
216    /// Output build results in a human readable format.
217    #[default]
218    HumanReadable,
219    /// Output the build results JSON formatted.
220    Json,
221}
222
223#[derive(Default, Clone, Debug, Args)]
224pub struct UnstableOptions {
225    /// Use the original manifest (Cargo.toml), do not modify for build optimizations
226    #[clap(long = "unstable-options", short = 'Z', number_of_values = 1)]
227    options: Vec<String>,
228}
229
230#[derive(Clone, Default)]
231pub struct UnstableFlags {
232    pub original_manifest: bool,
233}
234
235impl TryFrom<&UnstableOptions> for UnstableFlags {
236    type Error = anyhow::Error;
237
238    fn try_from(value: &UnstableOptions) -> Result<Self, Self::Error> {
239        let valid_flags = ["original-manifest"];
240        let invalid_flags = value
241            .options
242            .iter()
243            .filter(|o| !valid_flags.contains(&o.as_str()))
244            .collect::<Vec<_>>();
245        if !invalid_flags.is_empty() {
246            anyhow::bail!("Unknown unstable-options {:?}", invalid_flags)
247        }
248        Ok(UnstableFlags {
249            original_manifest: value.options.contains(&"original-manifest".to_owned()),
250        })
251    }
252}
253
254/// Define the standard `cargo` features args to be passed through.
255#[derive(Default, Clone, Debug, Args)]
256pub struct Features {
257    /// Space or comma separated list of features to activate
258    #[clap(long, value_delimiter = ',')]
259    features: Vec<String>,
260}
261
262impl Features {
263    /// Appends a feature.
264    pub fn push(&mut self, feature: &str) {
265        self.features.push(feature.to_owned())
266    }
267
268    /// Appends the raw features args to pass through to the `cargo` invocation.
269    pub fn append_to_args(&self, args: &mut Vec<String>) {
270        if !self.features.is_empty() {
271            args.push("--features".to_string());
272            let features = if self.features.len() == 1 {
273                self.features[0].clone()
274            } else {
275                self.features.join(",")
276            };
277            args.push(features);
278        }
279    }
280}