rustflags/lib.rs
1//! [![github]](https://github.com/dtolnay/rustflags) [![crates-io]](https://crates.io/crates/rustflags) [![docs-rs]](https://docs.rs/rustflags)
2//!
3//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
4//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
5//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
6//!
7//! <br>
8//!
9//! Parser for CARGO_ENCODED_RUSTFLAGS.
10//!
11//! This is one of the environment variables [provided by Cargo to build
12//! scripts][reference]. It synthesizes several sources of flags affecting
13//! Cargo's rustc invocations that build scripts might care about:
14//!
15//! - Flags passed via the RUSTFLAGS environment variable,
16//! - Cargo config entries under `target.<triple>.rustflags` and
17//! `target.<cfg>.rustflags` and `build.rustflags`, including from the
18//! project-specific Cargo config file and the Cargo config in the user's
19//! CARGO_HOME.
20//!
21//! If a build script needs to make some rustc invocations, or needs to
22//! characterize aspects of the upcoming rustc invocation, it likely needs these
23//! flags.
24//!
25//! [reference]: https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts
26//!
27//! # Example
28//!
29//! This build script uses the `cmake` crate to compile some C code, and must
30//! configure it with a particular C preprocessor #define if the Rust build is
31//! being performed with sanitizers.
32//!
33//! ```no_run
34//! // build.rs
35//!
36//! use rustflags::Flag;
37//! use std::env;
38//! use std::path::PathBuf;
39//!
40//! fn main() {
41//! let manifest_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
42//! let lib_source_dir = manifest_dir.join("lib");
43//! assert!(lib_source_dir.join("CMakeLists.txt").exists());
44//!
45//! let mut builder = cmake::Config::new(lib_source_dir);
46//!
47//! // Look for -Zsanitizer=address
48//! for flag in rustflags::from_env() {
49//! if matches!(flag, Flag::Z(z) if z == "sanitizer=address") {
50//! builder.define("ENABLE_SANITIZERS", "ON");
51//! builder.define("SANITIZERS", "address");
52//! break;
53//! }
54//! }
55//!
56//! builder.build();
57//! }
58//! ```
59
60#![doc(html_root_url = "https://docs.rs/rustflags/0.1.7")]
61#![allow(
62 clippy::cast_lossless,
63 clippy::derive_partial_eq_without_eq,
64 clippy::doc_markdown,
65 clippy::items_after_statements,
66 clippy::items_after_test_module, // https://github.com/rust-lang/rust-clippy/issues/10713
67 clippy::manual_find,
68 clippy::must_use_candidate,
69 clippy::needless_doctest_main,
70 clippy::too_many_lines,
71 clippy::type_complexity,
72 clippy::uninlined_format_args,
73 clippy::unnecessary_wraps
74)]
75
76mod parse;
77mod render;
78mod string;
79mod write;
80
81use crate::string::{EnvStr, EnvString};
82use std::env;
83use std::ffi::{OsStr, OsString};
84use std::fmt::{self, Display, Write};
85use std::path::PathBuf;
86
87/// Parse flags from CARGO_ENCODED_RUSTFLAGS environment variable.
88pub fn from_env() -> RustFlags {
89 let encoded = env::var_os("CARGO_ENCODED_RUSTFLAGS").unwrap_or_default();
90 RustFlags {
91 encoded: EnvString::new(encoded),
92 pos: 0,
93 repeat: None,
94 short: false,
95 }
96}
97
98/// Parse flags from a string separated with ASCII unit separator ('\x1f').
99///
100/// This is a valid format for the following environment variables:
101///
102/// - `CARGO_ENCODED_RUSTFLAGS` (Cargo 1.55+)
103/// - `CARGO_ENCODED_RUSTDOCFLAGS` (Cargo 1.55+)
104pub fn from_encoded(encoded: &OsStr) -> RustFlags {
105 RustFlags {
106 encoded: EnvString::new(encoded.to_owned()),
107 pos: 0,
108 repeat: None,
109 short: false,
110 }
111}
112
113/// **Iterator of rustc flags**
114pub struct RustFlags {
115 encoded: EnvString,
116 pos: usize,
117 repeat: Option<(fn(&EnvStr) -> Option<(Flag, usize)>, usize)>,
118 short: bool,
119}
120
121impl Iterator for RustFlags {
122 type Item = Flag;
123
124 fn next(&mut self) -> Option<Self::Item> {
125 parse::parse(self)
126 }
127}
128
129/// **One flag recognized by rustc**
130#[derive(Debug, PartialEq)]
131#[non_exhaustive]
132pub enum Flag {
133 /// `-h`, `--help`
134 ///
135 /// Display help message.
136 Help,
137
138 /// `--cfg SPEC`
139 ///
140 /// Configure the compilation environment.
141 Cfg { name: String, value: Option<String> },
142
143 /// `-L [KIND=]PATH`
144 ///
145 /// Add a directory to the library search path.
146 LibrarySearchPath { kind: LibraryKind, path: PathBuf },
147
148 /// `-l [KIND[:MODIFIERS]=]NAME[:RENAME]`
149 ///
150 /// Link the generated crate(s) to the specified native library NAME.
151 /// Optional comma separated MODIFIERS may be specified each with a prefix
152 /// of either '+' to enable or '-' to disable.
153 Link {
154 kind: LinkKind,
155 modifiers: Vec<(LinkModifierPrefix, LinkModifier)>,
156 name: String,
157 rename: Option<String>,
158 },
159
160 /// `--crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]`
161 ///
162 /// Comma separated list of types of crates for the compiler to emit.
163 CrateType(CrateType),
164
165 /// `--crate-name NAME`
166 ///
167 /// Specify the name of the crate being built.
168 CrateName(String),
169
170 /// `--edition 2015|2018|2021`
171 ///
172 /// Specify which edition of the compiler to use when compiling code.
173 Edition(u16),
174
175 /// `--emit [asm|llvm-bc|llvm-ir|obj|metadata|link|dep-info|mir]`
176 ///
177 /// Comma separated list of types of output for the compiler to emit.
178 Emit(Emit),
179
180 /// `--print [crate-name|file-names|sysroot|target-libdir|cfg|target-list|target-cpus|target-features|relocation-models|code-models|tls-models|target-spec-json|native-static-libs|stack-protector-strategies]`
181 ///
182 /// Compiler information to print on stdout.
183 Print(String),
184
185 /// `-o FILENAME`
186 ///
187 /// Write output to \<filename\>.
188 Out(PathBuf),
189
190 /// `--out-dir DIR`
191 ///
192 /// Write output to compiler-chosen filename in \<dir\>.
193 OutDir(PathBuf),
194
195 /// `--explain OPT`
196 ///
197 /// Provide a detailed explanation of an error message.
198 Explain(String),
199
200 /// `--test`
201 ///
202 /// Build a test harness.
203 Test,
204
205 /// `--target TARGET`
206 ///
207 /// Target triple for which the code is compiled.
208 Target(String),
209
210 /// `-A`, `--allow LINT`
211 ///
212 /// Set lint allowed.
213 Allow(String),
214
215 /// `-W`, `--warn LINT`
216 ///
217 /// Set lint warnings.
218 Warn(String),
219
220 /// `--force-warn LINT`
221 ///
222 /// Set lint force-warn.
223 ForceWarn(String),
224
225 /// `-D`, `--deny LINT`
226 ///
227 /// Set lint denied.
228 Deny(String),
229
230 /// `-F`, `--forbid LINT`
231 ///
232 /// Set lint forbidden.
233 Forbid(String),
234
235 /// `--cap-lints LEVEL`
236 ///
237 /// Set the most restrictive lint level. More restrictive lints are capped
238 /// at this level.
239 CapLints(LintLevel),
240
241 /// `-C`, `--codegen OPT[=VALUE]`
242 ///
243 /// Set a codegen option.
244 Codegen { opt: String, value: Option<String> },
245
246 /// `-V`, `--version`
247 ///
248 /// Print version info and exit.
249 Version,
250
251 /// `-v`, `--verbose`
252 ///
253 /// Use verbose output.
254 Verbose,
255
256 /// `--extern NAME[=PATH]`
257 ///
258 /// Specify where an external rust library is located.
259 Extern { name: String, path: Option<PathBuf> },
260
261 /// `--extern-location NAME=LOCATION`
262 ///
263 /// Location where an external crate dependency is specified.
264 ExternLocation { name: String, location: OsString },
265
266 /// `--sysroot PATH`
267 ///
268 /// Override the system root.
269 Sysroot(PathBuf),
270
271 /// `-Z FLAG`
272 ///
273 /// Set internal debugging options.
274 Z(String),
275
276 /// `--error-format human|json|short`
277 ///
278 /// How errors and other messages are produced.
279 ErrorFormat(ErrorFormat),
280
281 /// `--json CONFIG`
282 ///
283 /// Configure the JSON output of the compiler.
284 Json(String),
285
286 /// `--color auto|always|never`
287 ///
288 /// Configure coloring of output.
289 Color(Color),
290
291 /// `--remap-path-prefix FROM=TO`
292 ///
293 /// Remap source names in all output (compiler messages and output files).
294 RemapPathPrefix { from: PathBuf, to: PathBuf },
295}
296
297/// Argument of `-L`
298#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)]
299#[non_exhaustive]
300pub enum LibraryKind {
301 /// `dependency`
302 Dependency,
303 /// `crate`
304 Crate,
305 /// `native`
306 Native,
307 /// `framework`
308 Framework,
309 /// `all` (the default)
310 #[default]
311 All,
312}
313
314impl Display for LibraryKind {
315 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
316 formatter.write_str(match self {
317 LibraryKind::Dependency => "dependency",
318 LibraryKind::Crate => "crate",
319 LibraryKind::Native => "native",
320 LibraryKind::Framework => "framework",
321 LibraryKind::All => "all",
322 })
323 }
324}
325
326/// Argument of `-l`
327#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)]
328#[non_exhaustive]
329pub enum LinkKind {
330 /// `static`
331 Static,
332 /// `framework`
333 Framework,
334 /// `dylib` (the default)
335 #[default]
336 Dylib,
337}
338
339impl Display for LinkKind {
340 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
341 formatter.write_str(match self {
342 LinkKind::Static => "static",
343 LinkKind::Framework => "framework",
344 LinkKind::Dylib => "dylib",
345 })
346 }
347}
348
349/// Argument of `-l`
350#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
351pub enum LinkModifierPrefix {
352 /// `+`
353 Enable,
354 /// `-`
355 Disable,
356}
357
358impl Display for LinkModifierPrefix {
359 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
360 formatter.write_char(match self {
361 LinkModifierPrefix::Enable => '+',
362 LinkModifierPrefix::Disable => '-',
363 })
364 }
365}
366
367/// Argument of `-l`
368#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
369#[non_exhaustive]
370pub enum LinkModifier {
371 /// `bundle`
372 Bundle,
373 /// `verbatim`
374 Verbatim,
375 /// `whole-archive`
376 WholeArchive,
377 /// `as-needed`
378 AsNeeded,
379}
380
381impl Display for LinkModifier {
382 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
383 formatter.write_str(match self {
384 LinkModifier::Bundle => "bundle",
385 LinkModifier::Verbatim => "verbatim",
386 LinkModifier::WholeArchive => "whole-archive",
387 LinkModifier::AsNeeded => "as-needed",
388 })
389 }
390}
391
392/// Argument of `--crate-type`
393#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
394#[non_exhaustive]
395pub enum CrateType {
396 /// `bin`
397 Bin,
398 /// `lib`
399 Lib,
400 /// `rlib`
401 Rlib,
402 /// `dylib`
403 Dylib,
404 /// `cdylib`
405 Cdylib,
406 /// `staticlib`
407 Staticlib,
408 /// `proc-macro`
409 ProcMacro,
410}
411
412impl Display for CrateType {
413 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
414 formatter.write_str(match self {
415 CrateType::Bin => "bin",
416 CrateType::Lib => "lib",
417 CrateType::Rlib => "rlib",
418 CrateType::Dylib => "dylib",
419 CrateType::Cdylib => "Cdylib",
420 CrateType::Staticlib => "staticlib",
421 CrateType::ProcMacro => "proc-macro",
422 })
423 }
424}
425
426/// Argument of `--emit`
427#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
428#[non_exhaustive]
429pub enum Emit {
430 /// `asm`
431 Asm,
432 /// `llvm-bc`
433 LlvmBc,
434 /// `llvm-ir`
435 LlvmIr,
436 /// `obj`
437 Obj,
438 /// `metadata`
439 Metadata,
440 /// `link`
441 Link,
442 /// `dep-info`
443 DepInfo,
444 /// `mir`
445 Mir,
446}
447
448impl Display for Emit {
449 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
450 formatter.write_str(match self {
451 Emit::Asm => "asm",
452 Emit::LlvmBc => "llvm-bc",
453 Emit::LlvmIr => "llvm-ir",
454 Emit::Obj => "obj",
455 Emit::Metadata => "metadata",
456 Emit::Link => "link",
457 Emit::DepInfo => "dep-info",
458 Emit::Mir => "mir",
459 })
460 }
461}
462
463/// Argument of `--cap-lints`
464#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
465pub enum LintLevel {
466 /// `allow`
467 Allow,
468 /// `warn`
469 Warn,
470 /// `deny`
471 Deny,
472 /// `forbid`
473 Forbid,
474}
475
476impl Display for LintLevel {
477 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
478 formatter.write_str(match self {
479 LintLevel::Allow => "allow",
480 LintLevel::Warn => "warn",
481 LintLevel::Deny => "deny",
482 LintLevel::Forbid => "forbid",
483 })
484 }
485}
486
487/// Argument of `--error-format`
488#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
489#[non_exhaustive]
490pub enum ErrorFormat {
491 /// `human`
492 Human,
493 /// `json`
494 Json,
495 /// `short`
496 Short,
497}
498
499impl Display for ErrorFormat {
500 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
501 formatter.write_str(match self {
502 ErrorFormat::Human => "human",
503 ErrorFormat::Json => "json",
504 ErrorFormat::Short => "short",
505 })
506 }
507}
508
509/// Argument of `--color`
510#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)]
511pub enum Color {
512 /// Colorize, if output goes to a tty (default).
513 #[default]
514 Auto,
515 /// Always colorize output.
516 Always,
517 /// Never colorize output.
518 Never,
519}
520
521impl Display for Color {
522 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
523 formatter.write_str(match self {
524 Color::Auto => "auto",
525 Color::Always => "always",
526 Color::Never => "never",
527 })
528 }
529}