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}