Skip to main content

cargo_options/
common.rs

1use std::path::PathBuf;
2use std::process::Command;
3
4#[cfg(feature = "serde")]
5use serde::{Deserialize, Serialize};
6
7use crate::heading;
8use clap::{ArgAction, Parser};
9
10/// common cargo options
11#[derive(Clone, Debug, Default, Parser)]
12#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
13pub struct CommonOptions {
14    /// Do not print cargo log messages
15    #[arg(short = 'q', long)]
16    #[cfg_attr(feature = "serde", serde(default))]
17    pub quiet: bool,
18
19    /// Number of parallel jobs, defaults to # of CPUs.
20    #[arg(
21        short = 'j',
22        long,
23        value_name = "N",
24        allow_hyphen_values = true,
25        help_heading = heading::COMPILATION_OPTIONS,
26    )]
27    #[cfg_attr(feature = "serde", serde(default))]
28    pub jobs: Option<usize>,
29
30    /// Do not abort the build as soon as there is an error
31    #[arg(long, help_heading = heading::COMPILATION_OPTIONS)]
32    #[cfg_attr(feature = "serde", serde(default))]
33    pub keep_going: bool,
34
35    /// Build artifacts with the specified profile
36    #[arg(
37        long,
38        value_name = "PROFILE-NAME",
39        help_heading = heading::COMPILATION_OPTIONS,
40    )]
41    #[cfg_attr(feature = "serde", serde(default))]
42    pub profile: Option<String>,
43
44    /// Space or comma separated list of features to activate
45    #[arg(
46        short = 'F',
47        long,
48        action = ArgAction::Append,
49        help_heading = heading::FEATURE_SELECTION,
50    )]
51    #[cfg_attr(feature = "serde", serde(default))]
52    pub features: Vec<String>,
53
54    /// Activate all available features
55    #[arg(long, help_heading = heading::FEATURE_SELECTION)]
56    #[cfg_attr(feature = "serde", serde(default))]
57    pub all_features: bool,
58
59    /// Do not activate the `default` feature
60    #[arg(long, help_heading = heading::FEATURE_SELECTION)]
61    #[cfg_attr(feature = "serde", serde(default))]
62    pub no_default_features: bool,
63
64    /// Build for the target triple
65    #[arg(
66        long,
67        value_name = "TRIPLE",
68        num_args = 0..=1,
69        default_missing_value = "",
70        action = ArgAction::Append,
71        help_heading = heading::COMPILATION_OPTIONS,
72    )]
73    #[cfg_attr(feature = "serde", serde(default))]
74    pub target: Vec<String>,
75
76    /// Directory for all generated artifacts
77    #[arg(
78        long,
79        value_name = "DIRECTORY",
80        help_heading = heading::COMPILATION_OPTIONS,
81    )]
82    #[cfg_attr(feature = "serde", serde(default))]
83    pub target_dir: Option<PathBuf>,
84
85    /// Error format
86    #[arg(
87        long,
88        value_name = "FMT",
89        action = ArgAction::Append,
90        value_delimiter = ',',
91        ignore_case = true,
92        value_parser = [
93            "human",
94            "short",
95            "json",
96            "json-diagnostic-short",
97            "json-diagnostic-rendered-ansi",
98            "json-render-diagnostics",
99        ],
100    )]
101    #[cfg_attr(feature = "serde", serde(default))]
102    pub message_format: Vec<String>,
103
104    /// Use verbose output (-vv very verbose/build.rs output)
105    #[arg(short = 'v', long, action = ArgAction::Count)]
106    #[cfg_attr(feature = "serde", serde(default))]
107    pub verbose: u8,
108
109    /// Coloring
110    #[arg(long, value_name = "WHEN", value_parser = ["auto", "always", "never"])]
111    #[cfg_attr(feature = "serde", serde(default))]
112    pub color: Option<String>,
113
114    /// Equivalent to specifying both --locked and --offline
115    #[arg(long, help_heading = heading::MANIFEST_OPTIONS)]
116    #[cfg_attr(feature = "serde", serde(default))]
117    pub frozen: bool,
118
119    /// Assert that `Cargo.lock` will remain unchanged
120    #[arg(long, help_heading = heading::MANIFEST_OPTIONS)]
121    #[cfg_attr(feature = "serde", serde(default))]
122    pub locked: bool,
123
124    /// Run without accessing the network
125    #[arg(long, help_heading = heading::MANIFEST_OPTIONS)]
126    #[cfg_attr(feature = "serde", serde(default))]
127    pub offline: bool,
128
129    /// Override a configuration value
130    #[arg(long, value_name = "KEY=VALUE|PATH", action = ArgAction::Append)]
131    #[cfg_attr(feature = "serde", serde(default))]
132    pub config: Vec<String>,
133
134    /// Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
135    #[arg(short = 'Z', value_name = "FLAG", action = ArgAction::Append)]
136    #[cfg_attr(feature = "serde", serde(default))]
137    pub unstable_flags: Vec<String>,
138
139    /// Output a build timing report at the end of the build
140    #[arg(long, help_heading = heading::COMPILATION_OPTIONS)]
141    #[cfg_attr(feature = "serde", serde(default))]
142    pub timings: bool,
143}
144
145impl CommonOptions {
146    /// Apply options to `Command`
147    pub fn apply(&self, cmd: &mut Command) {
148        if self.quiet {
149            cmd.arg("--quiet");
150        }
151        if let Some(jobs) = self.jobs {
152            cmd.arg("--jobs").arg(jobs.to_string());
153        }
154        if self.keep_going {
155            cmd.arg("--keep-going");
156        }
157        if let Some(profile) = self.profile.as_ref() {
158            cmd.arg("--profile").arg(profile);
159        }
160        for feature in &self.features {
161            cmd.arg("--features").arg(feature);
162        }
163        if self.all_features {
164            cmd.arg("--all-features");
165        }
166        if self.no_default_features {
167            cmd.arg("--no-default-features");
168        }
169
170        // Support <target_triple>.<glibc_version> syntax
171        // For example: x86_64-unknown-linux-gnu.2.17
172        let rust_targets = self
173            .target
174            .iter()
175            .map(|target| target.split_once('.').map(|(t, _)| t).unwrap_or(target))
176            .collect::<Vec<&str>>();
177        rust_targets.iter().for_each(|target| {
178            cmd.arg("--target");
179            if !target.is_empty() {
180                cmd.arg(target);
181            }
182        });
183
184        if let Some(dir) = self.target_dir.as_ref() {
185            cmd.arg("--target-dir").arg(dir);
186        }
187        for fmt in &self.message_format {
188            cmd.arg("--message-format").arg(fmt);
189        }
190        if self.verbose > 0 {
191            cmd.arg(format!("-{}", "v".repeat(self.verbose.into())));
192        }
193        if let Some(color) = self.color.as_ref() {
194            cmd.arg("--color").arg(color);
195        }
196        if self.frozen {
197            cmd.arg("--frozen");
198        }
199        if self.locked {
200            cmd.arg("--locked");
201        }
202        if self.offline {
203            cmd.arg("--offline");
204        }
205        for config in &self.config {
206            cmd.arg("--config").arg(config);
207        }
208        for flag in &self.unstable_flags {
209            cmd.arg("-Z").arg(flag);
210        }
211        if self.timings {
212            cmd.arg("--timings");
213        }
214    }
215
216    pub(crate) fn cargo_command() -> Command {
217        let cargo = match std::env::var_os("CARGO") {
218            Some(cargo) => cargo.into(),
219            None => PathBuf::from("cargo"),
220        };
221        let mut cmd = Command::new(cargo);
222        cmd.env_remove("CARGO");
223        cmd
224    }
225}