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