raz_core/
rustc_options.rs

1//! Rustc option validation and cargo-to-rustc translation
2
3/// Translate cargo options to rustc equivalents
4pub fn translate_cargo_to_rustc(option: &str) -> Option<&'static str> {
5    match option {
6        "--release" => Some("-O"),
7        "--debug" => Some("-g"),
8        "--target" => Some("--target"), // Same in both
9        "--verbose" | "-v" => Some("-v"),
10        "--quiet" | "-q" => None,        // No rustc equivalent
11        "--features" => None,            // No direct rustc equivalent
12        "--all-features" => None,        // No direct rustc equivalent
13        "--no-default-features" => None, // No direct rustc equivalent
14        "--bin" => None,                 // Handled differently in rustc
15        "--lib" => Some("--crate-type=lib"),
16        "--example" => None, // No direct rustc equivalent
17        "--test" => Some("--test"),
18        "--bench" => None, // No direct rustc equivalent
19        _ => None,
20    }
21}
22
23/// Check if an option is valid for rustc
24pub fn is_valid_rustc_option(option: &str) -> bool {
25    // Short options
26    if option.len() == 2 && option.starts_with('-') {
27        matches!(
28            option,
29            "-h" | "-g"
30                | "-O"
31                | "-o"
32                | "-A"
33                | "-W"
34                | "-D"
35                | "-F"
36                | "-C"
37                | "-V"
38                | "-v"
39                | "-L"
40                | "-l"
41        )
42    } else if option.starts_with("--") {
43        // Long options
44        matches!(
45            option,
46            "--help"
47                | "--cfg"
48                | "--check-cfg"
49                | "--crate-type"
50                | "--crate-name"
51                | "--edition"
52                | "--emit"
53                | "--print"
54                | "--out-dir"
55                | "--explain"
56                | "--test"
57                | "--target"
58                | "--allow"
59                | "--warn"
60                | "--force-warn"
61                | "--deny"
62                | "--forbid"
63                | "--cap-lints"
64                | "--codegen"
65                | "--version"
66                | "--verbose"
67                | "--extern"
68                | "--sysroot"
69                | "--error-format"
70                | "--json"
71                | "--color"
72                | "--diagnostic-width"
73                | "--remap-path-prefix"
74                | "--env-set"
75        )
76    } else if option.starts_with("-C") || option.starts_with("--codegen") {
77        // Codegen options
78        true
79    } else if option.starts_with("-L") || option.starts_with("-l") {
80        // Library options
81        true
82    } else {
83        false
84    }
85}
86
87/// Get rustc optimization flags based on profile
88pub fn get_rustc_optimization_flags(profile: &str) -> Vec<&'static str> {
89    match profile {
90        "release" => vec!["-O", "-C", "lto=yes", "-C", "codegen-units=1"],
91        "debug" => vec!["-g", "-C", "opt-level=0"],
92        "test" => vec!["--test", "-g"],
93        _ => vec![],
94    }
95}
96
97/// Get help text for cargo-to-rustc option translation
98pub fn get_rustc_translation_help() -> &'static str {
99    r#"Cargo to Rustc Option Translation:
100
101Common Cargo options and their Rustc equivalents:
102  --release           →  -O (optimize) + -C lto=yes + -C codegen-units=1
103  --debug             →  -g (debug info)
104  --test              →  --test (build test harness)
105  --lib               →  --crate-type=lib
106  --target <TARGET>   →  --target <TARGET>
107  --verbose/-v        →  -v
108
109Options with no Rustc equivalent (will be ignored):
110  --features, --all-features, --no-default-features
111  --bin, --example, --bench
112  --quiet/-q
113
114For rustc scripts, you can use native rustc options:
115  -O              Optimize (equivalent to -C opt-level=3)
116  -g              Include debug info
117  -C <OPT>=<VAL>  Set codegen options (e.g., -C lto=yes)
118  --edition       Set Rust edition (2015|2018|2021|2024)
119  --crate-type    Set output type (bin|lib|rlib|dylib|cdylib|staticlib)
120  
121Example overrides for rustc:
122  -O                        # Optimize for release
123  -g                        # Include debug symbols
124  --edition 2021            # Use Rust 2021 edition
125  -C target-cpu=native      # Optimize for native CPU
126"#
127}
128
129/// Validate and translate a set of options for rustc
130pub fn validate_and_translate_options(options: &[String], is_rustc: bool) -> Vec<String> {
131    if !is_rustc {
132        return options.to_vec();
133    }
134
135    let mut translated = Vec::new();
136    let mut skip_next = false;
137
138    for (i, option) in options.iter().enumerate() {
139        if skip_next {
140            skip_next = false;
141            continue;
142        }
143
144        // First try to translate cargo option to rustc
145        if let Some(rustc_opt) = translate_cargo_to_rustc(option) {
146            translated.push(rustc_opt.to_string());
147
148            // Check if this option takes a value
149            if matches!(option.as_str(), "--target") && i + 1 < options.len() {
150                translated.push(options[i + 1].clone());
151                skip_next = true;
152            }
153        } else if is_valid_rustc_option(option) {
154            // It's already a valid rustc option
155            translated.push(option.clone());
156
157            // Check if this option takes a value
158            if needs_value(option) && i + 1 < options.len() && !options[i + 1].starts_with('-') {
159                translated.push(options[i + 1].clone());
160                skip_next = true;
161            }
162        } else if option == "--profile" && i + 1 < options.len() {
163            // Special handling for --profile
164            let profile = &options[i + 1];
165            translated.extend(
166                get_rustc_optimization_flags(profile)
167                    .iter()
168                    .map(|s| s.to_string()),
169            );
170            skip_next = true;
171        }
172        // Skip invalid options silently
173    }
174
175    translated
176}
177
178/// Check if a rustc option needs a value
179fn needs_value(option: &str) -> bool {
180    matches!(
181        option,
182        "--cfg"
183            | "--check-cfg"
184            | "--crate-type"
185            | "--crate-name"
186            | "--edition"
187            | "--emit"
188            | "--print"
189            | "--out-dir"
190            | "--explain"
191            | "--target"
192            | "--allow"
193            | "--warn"
194            | "--force-warn"
195            | "--deny"
196            | "--forbid"
197            | "--cap-lints"
198            | "--codegen"
199            | "-o"
200            | "-L"
201            | "-l"
202            | "--extern"
203            | "--sysroot"
204            | "--error-format"
205            | "--json"
206            | "--color"
207            | "--diagnostic-width"
208            | "--remap-path-prefix"
209            | "--env-set"
210    ) || option == "-C"
211        || option == "-A"
212        || option == "-W"
213        || option == "-D"
214        || option == "-F"
215}
216
217#[cfg(test)]
218mod tests {
219    use super::*;
220
221    #[test]
222    fn test_translate_cargo_to_rustc() {
223        assert_eq!(translate_cargo_to_rustc("--release"), Some("-O"));
224        assert_eq!(translate_cargo_to_rustc("--debug"), Some("-g"));
225        assert_eq!(translate_cargo_to_rustc("--test"), Some("--test"));
226        assert_eq!(translate_cargo_to_rustc("--lib"), Some("--crate-type=lib"));
227        assert_eq!(translate_cargo_to_rustc("--features"), None);
228    }
229
230    #[test]
231    fn test_is_valid_rustc_option() {
232        assert!(is_valid_rustc_option("-O"));
233        assert!(is_valid_rustc_option("-g"));
234        assert!(is_valid_rustc_option("--test"));
235        assert!(is_valid_rustc_option("--target"));
236        assert!(is_valid_rustc_option("-C"));
237        assert!(!is_valid_rustc_option("--release"));
238        assert!(!is_valid_rustc_option("--features"));
239    }
240
241    #[test]
242    fn test_validate_and_translate_options() {
243        let options = vec![
244            "--release".to_string(),
245            "--target".to_string(),
246            "wasm32-unknown-unknown".to_string(),
247            "--features".to_string(),
248            "foo,bar".to_string(),
249        ];
250
251        let translated = validate_and_translate_options(&options, true);
252        assert_eq!(
253            translated,
254            vec![
255                "-O".to_string(),
256                "--target".to_string(),
257                "wasm32-unknown-unknown".to_string(),
258            ]
259        );
260    }
261}