cargo_rustc_cfg/
lib.rs

1// Copyright (C) 2020 Christopher R. Field.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! The goal of this library, a.k.a. crate, is to provide access to the compiler
16//! configuration at Cargo build time of a project for use with [third-party]
17//! [Cargo custom subcommands] by running the `cargo rustc --lib -- --print cfg`
18//! command and parsing its output. This library is _not_ recommended for [build
19//! scripts] as the compiler configuration information is available via [Cargo
20//! environment variables] that are passed to build scripts at run
21//! time.
22//!
23//! If the Rust compiler (rustc) target is `x86_64-pc-windows-msvc`, then the
24//! output from the `cargo rustc --lib -- --print cfg` command will look similar to
25//! this:
26//!
27//! ```powershell
28//! PS C:\Path\to\Rust\Project> cargo rustc --lib -- --print cfg
29//!   Compiling <PACKAGE> vX.X.X (<PACKAGE_PATH>)
30//! debug_assertions
31//! target_arch="x86_64"
32//! target_endian="little"
33//! target_env="msvc"
34//! target_family="windows"
35//! target_feature="fxsr"
36//! target_feature="sse"
37//! target_feature="sse2"
38//! target_os="windows"
39//! target_pointer_width="64"
40//! target_vendor="pc"
41//! windows
42//!   Finished dev [unoptimized + debuginfo] target(s) in 0.10s
43//! ```
44//!
45//! where `<PACKAGE>` is replaced with the name of the Rust package, the
46//! `vX.X.X` is replaced with the Semantic Version number defined in the
47//! package's manifest (Cargo.toml), and the `<PACKAGE_PATH>` is replaced with
48//! the absolute path to the package's root directory. The output may vary
49//! depending on the rustc target and development environment.
50//!
51//! This crate parses the above output and provides the [`Cfg`] and [`Target`]
52//! types for accessing the various values from the output. The values for any
53//! lines containing a key-value pair and prepended by the `target_` string are
54//! available in the [`Target`] type with the double quotes, `"`, removed. Any
55//! lines that are not recognized and/or not a target key-value pair are stored
56//! (unaltered) and can be obtained with the [`Cfg::extras`] method.
57//!
58//! The [`CargoRustcPrintCfg`] type can be used to customize the `cargo rustc
59//! --lib -- --print cfg` command.
60//!
61//! # Examples
62//!
63//! Get the host configuration with Cargo modifications if the host is
64//! `x86_64-pc-windows-msvc`:
65//!
66//! ```
67//! # extern crate cargo_rustc_cfg;
68//! # #[cfg(all(target_arch = "x86_64", target_os = "windows", target_env = "msvc", target_vendor = "pc"))]
69//! # mod x86_64_pc_windows_msvc {
70//! # use cargo_rustc_cfg::{Cfg, Error};
71//! # fn main() -> std::result::Result<(), Error> {
72//! let cfg = Cfg::host()?;
73//! assert_eq!(cfg.target().arch(), "x86_64");
74//! assert_eq!(cfg.target().endian(), "little");
75//! assert_eq!(cfg.target().env(), Some("msvc"));
76//! assert_eq!(cfg.target().family(), Some("windows"));
77//! assert_eq!(cfg.target().os(), "windows");
78//! assert_eq!(cfg.target().pointer_width(), "64");
79//! assert_eq!(cfg.target().vendor(), Some("pc"));
80//! # Ok(())
81//! # }
82//! # }
83//! ```
84//!
85//! If the host is `x86_64-pc-windows-gnu`, then:
86//!
87//! ```
88//! # extern crate cargo_rustc_cfg;
89//! # #[cfg(all(target_arch = "x86_64", target_os = "windows", target_env = "gnu", target_vendor = "pc"))]
90//! # mod x86_64_pc_windows_gnu {
91//! # use cargo_rustc_cfg::{Cfg, Error};
92//! # fn main() -> std::result::Result<(), Error> {
93//! let cfg = Cfg::host()?;
94//! assert_eq!(cfg.target().arch(), "x86_64");
95//! assert_eq!(cfg.target().endian(), "little");
96//! assert_eq!(cfg.target().env(), Some("gnu"));
97//! assert_eq!(cfg.target().family(), Some("windows"));
98//! assert_eq!(cfg.target().os(), "windows");
99//! assert_eq!(cfg.target().pointer_width(), "64");
100//! assert_eq!(cfg.target().vendor(), Some("pc"));
101//! # Ok(())
102//! # }
103//! # }
104//! ```
105//!
106//! If the host is `x86_64-unknown-linux-gnu`, then:
107//!
108//! ```
109//! # extern crate cargo_rustc_cfg;
110//! # #[cfg(all(target_arch = "x86_64", target_os = "linux"))]
111//! # mod x86_64_unknown_linux_gnu {
112//! # use cargo_rustc_cfg::{Cfg, Error};
113//! # fn main() -> std::result::Result<(), Error> {
114//! let cfg = Cfg::host()?;
115//! assert_eq!(cfg.target().arch(), "x86_64");
116//! assert_eq!(cfg.target().endian(), "little");
117//! assert_eq!(cfg.target().env(), None);
118//! assert_eq!(cfg.target().family(), Some("unix"));
119//! assert_eq!(cfg.target().os(), "os");
120//! assert_eq!(cfg.target().pointer_width(), "64");
121//! assert_eq!(cfg.target().vendor(), Some("unknown"));
122//! # Ok(())
123//! # }
124//! # }
125//! ```
126//!
127//! If the host is `x86_64-apple-darwin`, then:
128//!
129//! ```
130//! # extern crate cargo_rustc_cfg;
131//! # #[cfg(all(target_arch = "x86_64", target_os = "macos"))]
132//! # mod x86_64_apple_darwin {
133//! # use cargo_rustc_cfg::{Cfg, Error};
134//! # fn main() -> std::result::Result<(), Error> {
135//! let cfg = Cfg::host()?;
136//! assert_eq!(cfg.target().arch(), "x86_64");
137//! assert_eq!(cfg.target().endian(), "little");
138//! assert_eq!(cfg.target().env(), None);
139//! assert_eq!(cfg.target().family(), Some("unix"));
140//! assert_eq!(cfg.target().os(), "os");
141//! assert_eq!(cfg.target().pointer_width(), "64");
142//! assert_eq!(cfg.target().vendor(), Some("apple"));
143//! # Ok(())
144//! # }
145//! # }
146//! ```
147//!
148//! If the host is `i686-pc-windows-msvc`, then:
149//!
150//! ```
151//! # extern crate cargo_rustc_cfg;
152//! # #[cfg(all(target_arch = "x86", target_os = "windows", target_env = "msvc", target_vendor = "pc"))]
153//! # mod i686_pc_windows_msvc {
154//! # use cargo_rustc_cfg::{Cfg, Error};
155//! # fn main() -> std::result::Result<(), Error> {
156//! let cfg = Cfg::host()?;
157//! assert_eq!(cfg.target().arch(), "x86");
158//! assert_eq!(cfg.target().endian(), "little");
159//! assert_eq!(cfg.target().env(), Some("msvc"));
160//! assert_eq!(cfg.target().family(), Some("windows"));
161//! assert_eq!(cfg.target().os(), "windows");
162//! assert_eq!(cfg.target().pointer_width(), "32");
163//! assert_eq!(cfg.target().vendor(), Some("pc"));
164//! # Ok(())
165//! # }
166//! # }
167//! ```
168//!
169//! If the host is `i686-pc-windows-gnu`, then:
170//!
171//! ```
172//! # extern crate cargo_rustc_cfg;
173//! # #[cfg(all(target_arch = "x86", target_os = "windows", target_env = "gnu", target_vendor = "pc"))]
174//! # mod i686_pc_windows_gnu {
175//! # use cargo_rustc_cfg::{Cfg, Error};
176//! # fn main() -> std::result::Result<(), Error> {
177//! let cfg = Cfg::host()?;
178//! assert_eq!(cfg.target().arch(), "x86_64");
179//! assert_eq!(cfg.target().endian(), "little");
180//! assert_eq!(cfg.target().env(), Some("gnu"));
181//! assert_eq!(cfg.target().family(), Some("windows"));
182//! assert_eq!(cfg.target().os(), "windows");
183//! assert_eq!(cfg.target().pointer_width(), "32");
184//! assert_eq!(cfg.target().vendor(), Some("pc"));
185//! # Ok(())
186//! # }
187//! # }
188//! ```
189//!
190//! If the host is `i686-unknown-linux-gnu`, then:
191//!
192//! ```
193//! # extern crate cargo_rustc_cfg;
194//! # #[cfg(all(target_arch = "x86", target_os = "linux"))]
195//! # mod i686_unknown_linux_gnu {
196//! # use cargo_rustc_cfg::{Cfg, Error};
197//! # fn main() -> std::result::Result<(), Error> {
198//! let cfg = Cfg::host()?;
199//! assert_eq!(cfg.target().arch(), "x86");
200//! assert_eq!(cfg.target().endian(), "little");
201//! assert_eq!(cfg.target().env(), None);
202//! assert_eq!(cfg.target().family(), Some("unix"));
203//! assert_eq!(cfg.target().os(), "os");
204//! assert_eq!(cfg.target().pointer_width(), "32");
205//! assert_eq!(cfg.target().vendor(), Some("unknown"));
206//! # Ok(())
207//! # }
208//! # }
209//! ```
210//!
211//! If the host is `i686-apple-darwin`, then:
212//!
213//! ```
214//! # extern crate cargo_rustc_cfg;
215//! # #[cfg(all(target_arch = "x86", target_os = "macos"))]
216//! # mod i686_apple_darwin {
217//! # use cargo_rustc_cfg::{Cfg, Error};
218//! # fn main() -> std::result::Result<(), Error> {
219//! let cfg = Cfg::host()?;
220//! assert_eq!(cfg.target().arch(), "x86");
221//! assert_eq!(cfg.target().endian(), "little");
222//! assert_eq!(cfg.target().env(), None);
223//! assert_eq!(cfg.target().family(), Some("unix"));
224//! assert_eq!(cfg.target().os(), "os");
225//! assert_eq!(cfg.target().pointer_width(), "32");
226//! assert_eq!(cfg.target().vendor(), Some("apple"));
227//! # Ok(())
228//! # }
229//! # }
230//! ```
231//!
232//! Get the configuration for a rustc target that is not the host, i.e.
233//! cross-compilation, using the [`CargoRustcPrintCfg`] type and the
234//! [`rustc_target`] method:
235//!
236//! ```
237//! # extern crate cargo_rustc_cfg;
238//! # use cargo_rustc_cfg::{CargoRustcPrintCfg, Error};
239//! # fn main() -> std::result::Result<(), Error> {
240//! let cfg = CargoRustcPrintCfg::default()
241//!     .rustc_target("i686-pc-windows-msvc")
242//!     .execute()?;
243//! assert_eq!(cfg.target().arch(), "x86");
244//! assert_eq!(cfg.target().endian(), "little");
245//! assert_eq!(cfg.target().env(), Some("msvc"));
246//! assert_eq!(cfg.target().family(), Some("windows"));
247//! assert_eq!(cfg.target().os(), "windows");
248//! assert_eq!(cfg.target().pointer_width(), "32");
249//! assert_eq!(cfg.target().vendor(), Some("pc"));
250//! # Ok(())
251//! # }
252//! ```
253//!
254//! The above use-case is relatively common, but it is tedious to
255//! routinely use the [`CargoRustcPrintCfg`] builder. The [`Cfg::rustc_target`]
256//! method is available as a shorthand for the previous example:
257//!
258//! ```
259//! # extern crate cargo_rustc_cfg;
260//! # use cargo_rustc_cfg::{Cfg, Error};
261//! # fn main() -> std::result::Result<(), Error> {
262//! let cfg = Cfg::rustc_target("i686-pc-windows-msvc")?;
263//! assert_eq!(cfg.target().arch(), "x86");
264//! assert_eq!(cfg.target().endian(), "little");
265//! assert_eq!(cfg.target().env(), Some("msvc"));
266//! assert_eq!(cfg.target().family(), Some("windows"));
267//! assert_eq!(cfg.target().os(), "windows");
268//! assert_eq!(cfg.target().pointer_width(), "32");
269//! assert_eq!(cfg.target().vendor(), Some("pc"));
270//! # Ok(())
271//! # }
272//! ```
273//!
274//! Regardless of using the long or short form to specify a rustc target, the
275//! rustc target must still be installed and available on the host system. It is
276//! recommended to use [rustup] and the `rustup target add <TRIPLE>` command to
277//! install rustc targets.
278//!
279//! [`Cfg`]: struct.Cfg.html
280//! [`Target`]: struct.Target.html
281//! [`Cfg::extras`]: struct.Cfg.html#method.extras
282//! [`CargoRustcPrintCfg`]: struct.CargoRustcPrintCfg.html
283//! [`rustc_target`]: struct.CargoRustcPrintCfg.html#rustc_target
284//! [`Cfg::rustc_target`]: struct.Cfg.html#method.rustc_target
285//! [Cargo]: https://doc.rust-lang.org/cargo/index.html
286//! [third-party]: https://github.com/rust-lang/cargo/wiki/Third-party-cargo-subcommands
287//! [Cargo custom subcommands]: https://doc.rust-lang.org/1.30.0/cargo/reference/external-tools.html#custom-subcommands
288//! [build scripts]: https://doc.rust-lang.org/cargo/reference/build-scripts.html
289//! [Cargo environment variables]: https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts
290//! [rustup]: https://rust-lang.github.io/rustup/
291
292use std::env;
293use std::ffi::{OsStr, OsString};
294use std::path::PathBuf;
295use std::process::{Command, Output};
296
297/// The command line name of the Cargo application.
298pub const CARGO: &str = "cargo";
299
300/// The environment variable name for the Cargo appplication.
301pub const CARGO_VARIABLE: &str = "CARGO";
302
303/// The command line name of the Rust compiler subcommand for Cargo.
304pub const RUSTC: &str = "rustc";
305
306/// The various types of Cargo targets.
307///
308/// The default variant is [`Library`].
309///
310/// This should _not_ be confused with Rust compiler (rustc) targets, which are
311/// typically represented by a "triple" string. Cargo targets are defined and
312/// listed in a package's manifest (Cargo.toml).
313///
314/// [`Library`]: enum.CargoTarget.html#variant.Library
315#[derive(Clone, Debug, PartialEq)]
316pub enum CargoTarget {
317    /// A `--bench <NAME>` Cargo target as defined in the package's manifest
318    /// (Cargo.toml).
319    Benchmark(OsString),
320    /// A `--bin <NAME>` Cargo target as defined in the package's manifest
321    /// (Cargo.toml).
322    Binary(OsString),
323    /// An `--example <NAME>` Cargo target as defined in the package's manifest
324    /// (Cargo.toml).
325    Example(OsString),
326    /// The default Cargo target.
327    Library,
328    /// A `--test <NAME>` Cargo target as defined in the package's manifest
329    /// (Cargo.toml).
330    Test(OsString),
331}
332
333impl CargoTarget {
334    /// Converts the Cargo target into the command line arguments for Cargo.
335    pub fn to_args(&self) -> Vec<&OsStr> {
336        match self {
337            Self::Benchmark(name) => vec![OsStr::new("--bench"), name],
338            Self::Binary(name) => vec![OsStr::new("--bin"), name],
339            Self::Example(name) => vec![OsStr::new("--example"), name],
340            Self::Library => vec![OsStr::new("--lib")],
341            Self::Test(name) => vec![OsStr::new("--test"), name],
342        }
343    }
344
345    /// Consumes the Cargo target to convert into the command line arguments for
346    /// Cargo.
347    pub fn into_args(self) -> Vec<OsString> {
348        match self {
349            Self::Benchmark(name) => vec![OsString::from("--bench"), name],
350            Self::Binary(name) => vec![OsString::from("--bin"), name],
351            Self::Example(name) => vec![OsString::from("--example"), name],
352            Self::Library => vec![OsString::from("--lib")],
353            Self::Test(name) => vec![OsString::from("--test"), name],
354        }
355    }
356}
357
358impl Default for CargoTarget {
359    fn default() -> Self {
360        Self::Library
361    }
362}
363
364/// A builder type for the `cargo rustc --lib -- --print cfg` command.
365///
366/// For reference, the default command signature is:
367///
368/// ```text
369/// cargo rustc --lib -- --print cfg
370/// ```
371///
372/// and the more generic command signature represented by this type is:
373///
374/// ```text
375/// cargo <TOOLCHAIN> rustc <CARGO_ARGS> <CARGO_TARGET> <RUSTC_TARGET> -- <RUSTC_ARGS> --print cfg
376/// ```
377///
378/// where `<TOOLCHAIN>` is replaced with the [`cargo_toolchain`] value, the
379/// `<CARGO_ARGS>` is replaced with the [`cargo_args`] value, the
380/// `<CARGO_TARGET>` is replaced with the [`cargo_target`] value, the
381/// `<RUSTC_TARGET>` is replaced with the [`rustc_target`] value, and the
382/// `<RUSTC_ARGS>` is replaced with the [`rustc_args`] value.
383///
384/// [`cargo_toolchain`]: #method.cargo_toolchain
385/// [`cargo_args`]: #method.cargo_args
386/// [`cargo_target`]: #method.cargo_target
387/// [`rustc_target`]: #method.rustc_target
388/// [`rustc_args`]: #method.rustc_args
389#[derive(Clone, Debug, PartialEq)]
390pub struct CargoRustcPrintCfg {
391    cargo_args: Option<Vec<OsString>>,
392    cargo_target: CargoTarget,
393    cargo_toolchain: Option<OsString>,
394    rustc_args: Option<Vec<OsString>>,
395    rustc_target: Option<OsString>,
396}
397
398impl CargoRustcPrintCfg {
399    /// Adds arguments to the Cargo command after the `rustc` subcommand but
400    /// before the `<CARGO_TARGET>` and `<RUSTC_TARGET>` arguments.
401    ///
402    /// For reference, the default command is:
403    ///
404    /// ```text
405    /// cargo rustc --lib -- --print cfg
406    /// ```
407    ///
408    /// and this method adds arguments between `rustc` and `--lib` to yield:
409    ///
410    /// ```text
411    /// cargo rustc <CARGO_ARGS> --lib -- --print cfg
412    /// ```
413    pub fn cargo_args<A, S>(&mut self, a: A) -> &mut Self
414    where
415        A: IntoIterator<Item = S>,
416        S: AsRef<OsStr>
417    {
418        self.cargo_args = Some(a.into_iter().map(|s| s.as_ref().into()).collect());
419        self
420    }
421
422    /// Specifies a single Cargo target.
423    ///
424    /// When passing arguments to the Rust compiler (rustc) using the `--` flag,
425    /// only one _Cargo_ target can be used. By default, the library target will
426    /// be used. Use this method to specify a specific Cargo target other than
427    /// the library target.
428    ///
429    /// For reference, the default command is:
430    ///
431    /// ```text
432    /// cargo rustc --lib -- --print cfg
433    /// ```
434    ///
435    /// and this method replaces the `--lib` with `--bin <NAME>`, `--bench
436    /// <NAME>`, `--example <NAME>`, or `--test <NAME>`, respectively.
437    pub fn cargo_target(&mut self, t: CargoTarget) -> &mut Self
438    {
439        self.cargo_target = t;
440        self
441    }
442
443    /// Specify a toolchain to use.
444    ///
445    /// The toolchain must be installed on the host system before specifying it
446    /// with this method. It is recommended to install and manage various
447    /// toolchains using the [`rustup`] application.
448    ///
449    /// The plus sign, `+`, is prepended automatically. Please do not include it
450    /// as part of the toolchain value.
451    ///
452    /// For reference, the default command is:
453    ///
454    /// ```text
455    /// cargo rustc --lib -- --print cfg
456    /// ```
457    ///
458    /// and this method would add `+<TOOLCHAIN>` between `cargo` and `rustc` to yield:
459    ///
460    /// ```text
461    /// cargo +<TOOLCHAIN> rustc --lib -- --print cfg
462    /// ```
463    ///
464    /// [`rustup`]: https://rust-lang.github.io/rustup/
465    pub fn cargo_toolchain<T>(&mut self, t: T) -> &mut Self
466    where
467        T: AsRef<OsStr>
468    {
469        self.cargo_toolchain = Some(t.as_ref().into());
470        self
471    }
472
473    /// Adds arguments to the Cargo command after the `--` flag but
474    /// before the `--print cfg` arguments.
475    ///
476    /// For reference, the default command is:
477    ///
478    /// ```text
479    /// cargo rustc --lib -- --print cfg
480    /// ```
481    ///
482    /// and this method adds arguments between `--` and `--print cfg` to yield:
483    ///
484    /// ```text
485    /// cargo rustc --lib -- <RUSTC_ARGS> --print cfg
486    /// ```
487    pub fn rustc_args<A, S>(&mut self, a: A) -> &mut Self
488    where
489        A: IntoIterator<Item = S>,
490        S: AsRef<OsStr>
491    {
492        self.rustc_args = Some(a.into_iter().map(|s| s.as_ref().into()).collect());
493        self
494    }
495
496    /// Specify a Rust compiler (rustc) target via a target triple.
497    ///
498    /// The rustc target must be installed on the host system before specifying
499    /// it with this method. It is recommended to install and manage targets for
500    /// various toolchains using the [`rustup`] application.
501    ///
502    /// The `--target` argument is prepended automatically. Please do not include it
503    /// as part of the target triple value.
504    ///
505    /// For reference, the default command is:
506    ///
507    /// ```text
508    /// cargo rustc --lib -- --print cfg
509    /// ```
510    ///
511    /// and this method would add `--target <RUSTC_TARGET>` between `--lib` and `--` to yield:
512    ///
513    /// ```text
514    /// cargo rustc --lib --target <RUSTC_TARGET> -- --print cfg
515    /// ```
516    ///
517    /// where `<RUSTC_TARGET>` is a target triple from the `rustc --print
518    /// target-list` output..
519    ///
520    /// [`rustup`]: https://rust-lang.github.io/rustup/
521    pub fn rustc_target<T>(&mut self, t: T) -> &mut Self
522        where T: AsRef<OsStr>
523    {
524        self.rustc_target = Some(t.as_ref().into());
525        self
526    }
527
528    /// This executes the `cargo rustc` subcommand with the appropriate options.
529    ///
530    /// For reference, the generic command signature:
531    ///
532    /// ```text
533    /// `cargo <TOOLCHAIN> rustc <CARGO_ARGS> <CARGO_TARGET> <RUSTC_TARGET> -- <RUSTC_ARGS> --print cfg`
534    /// ```
535    ///
536    /// where `<TOOLCHAIN>` is replaced
537    /// with the [`cargo_toolchain`] value, the `<CARGO_ARGS>` is replaced with
538    /// the [`cargo_args`] value, the `<CARGO_TARGET>` is replaced with the
539    /// [`cargo_target`] value, the `<RUSTC_TARGET>` is appropriately replaced
540    /// with `--target <RUSTC_TARGET>` from the [`rustc_target`] value, and the
541    /// `<RUSTC_ARGS>` is replaced with the [`rustc_args`] value.
542    ///
543    /// # Examples
544    ///
545    /// If the host is a Windows target:
546    ///
547    /// ```
548    /// # extern crate cargo_rustc_cfg;
549    /// # #[cfg(all(target_arch = "x86_64", target_os = "windows", target_env = "msvc", target_vendor = "pc"))]
550    /// # mod x86_64_pc_windows_msvc {
551    /// # use cargo_rustc_cfg::{CargoRustcPrintCfg, Error};
552    /// # fn main() -> std::result::Result<(), Error> {
553    /// let cfg = CargoRustcPrintCfg::default().execute()?;
554    /// assert_eq!(cfg.target().arch(), "x86_64");
555    /// assert_eq!(cfg.target().endian(), "little");
556    /// assert_eq!(cfg.target().env(), Some("msvc"));
557    /// assert_eq!(cfg.target().family(), Some("windows"));
558    /// assert_eq!(cfg.target().os(), "windows");
559    /// assert_eq!(cfg.target().pointer_width(), "64");
560    /// assert_eq!(cfg.target().vendor(), Some("pc"));
561    /// # Ok(())
562    /// # }
563    /// # }
564    /// ```
565    ///
566    /// If the host is a Linux target:
567    ///
568    /// ```
569    /// # extern crate cargo_rustc_cfg;
570    /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))]
571    /// # mod x86_64_unknown_linux_gnu {
572    /// # use cargo_rustc_cfg::{CargoRustcPrintCfg, Error};
573    /// # fn main() -> std::result::Result<(), Error> {
574    /// let cfg = CargoRustcPrintCfg::default().execute()?;
575    /// assert_eq!(cfg.target().arch(), "x86_64");
576    /// assert_eq!(cfg.target().endian(), "little");
577    /// assert_eq!(cfg.target().env(), None);
578    /// assert_eq!(cfg.target().family(), Some("unix"));
579    /// assert_eq!(cfg.target().os(), "os");
580    /// assert_eq!(cfg.target().pointer_width(), "64");
581    /// assert_eq!(cfg.target().vendor(), Some("unknown"));
582    /// # Ok(())
583    /// # }
584    /// # }
585    /// ```
586    ///
587    /// If the host is an Apple target:
588    ///
589    /// ```
590    /// # extern crate cargo_rustc_cfg;
591    /// # #[cfg(all(target_arch = "x86_64", target_os = "macos"))]
592    /// # mod x86_64_apple_darwin {
593    /// # use cargo_rustc_cfg::{CargoRustcPrintCfg, Error};
594    /// # fn main() -> std::result::Result<(), Error> {
595    /// let cfg = CargoRustcPrintCfg::default().execute()?;
596    /// assert_eq!(cfg.target().arch(), "x86_64");
597    /// assert_eq!(cfg.target().endian(), "little");
598    /// assert_eq!(cfg.target().env(), None);
599    /// assert_eq!(cfg.target().family(), Some("unix"));
600    /// assert_eq!(cfg.target().os(), "os");
601    /// assert_eq!(cfg.target().pointer_width(), "64");
602    /// assert_eq!(cfg.target().vendor(), Some("apple"));
603    /// # Ok(())
604    /// # }
605    /// # }
606    /// ```
607    ///
608    /// [`cargo_toolchain`]: #method.cargo_toolchain
609    /// [`cargo_args`]: #method.cargo_args
610    /// [`cargo_target`]: #method.cargo_target
611    /// [`rustc_target`]: #method.rustc_target
612    /// [`rustc_args`]: #method.rustc_args
613    pub fn execute(&self) -> Result<Cfg, Error> {
614        let mut cmd = Command::new(
615            env::var(CARGO_VARIABLE)
616                .map(PathBuf::from)
617                .ok()
618                .unwrap_or_else(|| PathBuf::from(CARGO)),
619        );
620        if let Some(toolchain) = &self.cargo_toolchain {
621            let mut arg = OsString::from("+");
622            arg.push(toolchain);
623            cmd.arg(arg);
624        }
625        cmd.arg(RUSTC);
626        if let Some(cargo_args) = &self.cargo_args {
627            cmd.args(cargo_args);
628        }
629        if let Some(rustc_target) = &self.rustc_target {
630            cmd.arg("--target");
631            cmd.arg(rustc_target);
632        }
633        cmd.args(self.cargo_target.to_args());
634        cmd.arg("--");
635        if let Some(rustc_args) = &self.rustc_args {
636            cmd.args(rustc_args);
637        }
638        cmd.arg("--print");
639        cmd.arg("cfg");
640        let output = cmd.output()?;
641        if !output.status.success() {
642            return Err(Error::Command(output));
643        }
644        let mut extras = Vec::new();
645        let mut arch = None;
646        let mut endian = None;
647        let mut env = None;
648        let mut family = None;
649        let mut features = Vec::new();
650        let mut os = None;
651        let mut pointer_width = None;
652        let mut vendor = None;
653        let specification = String::from_utf8(output.stdout)?;
654        for entry in specification.lines() {
655            let mut parts = entry.split('=');
656
657            if let (Some(key), Some(value)) = (parts.next(), parts.next()) {
658                match key {
659                    "target_arch" => arch = Some(value.trim_matches('"').to_string()),
660                    "target_endian" => endian = Some(value.trim_matches('"').to_string()),
661                    "target_env" => {
662                        env = {
663                            let env = value.trim_matches('"').to_string();
664                            if env.is_empty() {
665                                None
666                            } else {
667                                Some(env)
668                            }
669                        }
670                    }
671                    "target_family" => {
672                        family = {
673                            let family = value.trim_matches('"').to_string();
674                            if family.is_empty() {
675                                None
676                            } else {
677                                Some(family)
678                            }
679                        }
680                    }
681                    "target_feature" => features.push(value.trim_matches('"').to_string()),
682                    "target_os" => os = Some(value.trim_matches('"').to_string()),
683                    "target_pointer_width" => {
684                        pointer_width = Some(value.trim_matches('"').to_string())
685                    }
686                    "target_vendor" => {
687                        vendor = {
688                            let vendor = value.trim_matches('"').to_string();
689                            if vendor.is_empty() {
690                                None
691                            } else {
692                                Some(vendor)
693                            }
694                        }
695                    }
696                    _ => {
697                        extras.push(String::from(entry));
698                    }
699                }
700            } else {
701                extras.push(String::from(entry));
702            }
703        }
704
705        Ok(Cfg {
706            extras,
707            target: Target {
708                arch: arch.ok_or_else(|| Error::MissingOutput("target_arch"))?,
709                endian: endian.ok_or_else(|| Error::MissingOutput("target_endian"))?,
710                env,
711                family,
712                features,
713                os: os.ok_or_else(|| Error::MissingOutput("target_os"))?,
714                pointer_width: pointer_width
715                    .ok_or_else(|| Error::MissingOutput("target_pointer_width"))?,
716                vendor,
717            },
718        })
719    }
720}
721
722impl Default for CargoRustcPrintCfg {
723    fn default() -> Self {
724        Self {
725            cargo_args: None,
726            cargo_target: CargoTarget::default(),
727            cargo_toolchain: None,
728            rustc_args: None,
729            rustc_target: None,
730        }
731    }
732}
733
734/// A container for the parsed output from the `cargo rustc --lib -- --print
735/// cfg` command.
736#[derive(Clone, Debug, PartialEq)]
737pub struct Cfg {
738    extras: Vec<String>,
739    target: Target,
740}
741
742impl Cfg {
743    /// Obtains the host configuration.
744    ///
745    /// This is a helper method for using the [`CargoRustcPrintCfg`], such that:
746    ///
747    /// ```
748    /// # extern crate cargo_rustc_cfg;
749    /// # #[cfg(all(target_arch = "x86_64", target_os = "windows", target_env = "msvc", target_vendor = "pc"))]
750    /// # mod x86_64_pc_windows_msvc {
751    /// # use cargo_rustc_cfg::{CargoRustcPrintCfg, Error};
752    /// # fn main() -> std::result::Result<(), Error> {
753    /// let cfg = CargoRustcPrintCfg::default().execute()?;
754    /// # assert_eq!(cfg.target().arch(), "x86_64");
755    /// # assert_eq!(cfg.target().endian(), "little");
756    /// # assert_eq!(cfg.target().env(), Some("msvc"));
757    /// # assert_eq!(cfg.target().family(), Some("windows"));
758    /// # assert_eq!(cfg.target().os(), "windows");
759    /// # assert_eq!(cfg.target().pointer_width(), "64");
760    /// # assert_eq!(cfg.target().vendor(), Some("pc"));
761    /// # Ok(())
762    /// # }
763    /// # }
764    /// ```
765    ///
766    /// becomes:
767    ///
768    /// ```
769    /// # extern crate cargo_rustc_cfg;
770    /// # #[cfg(all(target_arch = "x86_64", target_os = "windows", target_env = "msvc", target_vendor = "pc"))]
771    /// # mod x86_64_pc_windows_msvc {
772    /// # use cargo_rustc_cfg::{Cfg, Error};
773    /// # fn main() -> std::result::Result<(), Error> {
774    /// let cfg = Cfg::host()?;
775    /// # assert_eq!(cfg.target().arch(), "x86_64");
776    /// # assert_eq!(cfg.target().endian(), "little");
777    /// # assert_eq!(cfg.target().env(), Some("msvc"));
778    /// # assert_eq!(cfg.target().family(), Some("windows"));
779    /// # assert_eq!(cfg.target().os(), "windows");
780    /// # assert_eq!(cfg.target().pointer_width(), "64");
781    /// # assert_eq!(cfg.target().vendor(), Some("pc"));
782    /// # Ok(())
783    /// # }
784    /// # }
785    /// ```
786    ///
787    /// [`CargoRustcPrintCfg`]: struct.CargoRustcPrintCfg.html
788    pub fn host() -> Result<Self, Error> {
789        CargoRustcPrintCfg::default().execute()
790    }
791
792    /// Obtains a configuration for a Rust compiler (rustc) target.
793    ///
794    /// This is a helper method for using the [`CargoRustcPrintCfg`], such that:
795    ///
796    /// ```
797    /// # extern crate cargo_rustc_cfg;
798    /// # use cargo_rustc_cfg::{CargoRustcPrintCfg, Error};
799    /// # fn main() -> std::result::Result<(), Error> {
800    /// let cfg = CargoRustcPrintCfg::default().rustc_target("x86_64-pc-windows-msvc").execute()?;
801    /// # assert_eq!(cfg.target().arch(), "x86_64");
802    /// # assert_eq!(cfg.target().endian(), "little");
803    /// # assert_eq!(cfg.target().env(), Some("msvc"));
804    /// # assert_eq!(cfg.target().family(), Some("windows"));
805    /// # assert_eq!(cfg.target().os(), "windows");
806    /// # assert_eq!(cfg.target().pointer_width(), "64");
807    /// # assert_eq!(cfg.target().vendor(), Some("pc"));
808    /// # Ok(())
809    /// # }
810    /// ```
811    ///
812    /// becomes:
813    ///
814    /// ```
815    /// # extern crate cargo_rustc_cfg;
816    /// # use cargo_rustc_cfg::{Cfg, Error};
817    /// # fn main() -> std::result::Result<(), Error> {
818    /// let cfg = Cfg::rustc_target("x86_64-pc-windows-msvc")?;
819    /// # assert_eq!(cfg.target().arch(), "x86_64");
820    /// # assert_eq!(cfg.target().endian(), "little");
821    /// # assert_eq!(cfg.target().env(), Some("msvc"));
822    /// # assert_eq!(cfg.target().family(), Some("windows"));
823    /// # assert_eq!(cfg.target().os(), "windows");
824    /// # assert_eq!(cfg.target().pointer_width(), "64");
825    /// # assert_eq!(cfg.target().vendor(), Some("pc"));
826    /// # Ok(())
827    /// # }
828    /// ```
829    ///
830    /// [`CargoRustcPrintCfg`]: struct.CargoRustcPrintCfg.html
831    pub fn rustc_target<S>(t: S) -> Result<Self, Error>
832    where
833        S: AsRef<OsStr>
834    {
835        CargoRustcPrintCfg::default().rustc_target(t).execute()
836    }
837
838    /// Any and all additional lines from the output of the `cargo rustc --print
839    /// cfg` command that are not recognized as target key-value pairs.
840    ///
841    /// These are any lines that were not recognized as target key-value lines,
842    /// i.e. `key="value"`. Unlike the target key-value lines, any double
843    /// quotes, `"`, are _not_ removed.
844    ///
845    /// # Examples
846    ///
847    /// ```
848    /// # extern crate cargo_rustc_cfg;
849    /// # use cargo_rustc_cfg::{Cfg, Error};
850    /// # fn main() -> std::result::Result<(), Error> {
851    /// let cfg = Cfg::rustc_target("x86_64-pc-windows-msvc")?;
852    /// assert!(cfg.extras().contains(&"debug_assertions"));
853    /// assert!(cfg.extras().contains(&"windows"));
854    /// # Ok(())
855    /// # }
856    /// ```
857    pub fn extras(&self) -> Vec<&str> {
858        self.extras.iter().map(|s| &**s).collect()
859    }
860
861    /// All output that is prepended by the `target_` string.
862    ///
863    /// These are all the recognized target key-value lines, i.e.
864    /// `target_<key>="<value>"`. The double quotes, `"` are removed for the
865    /// values.
866    pub fn target(&self) -> &Target {
867        &self.target
868    }
869
870    /// Consumes this configuration and converts it into the target
871    /// configuration.
872    ///
873    /// The target configuration is all recognized key-value lines prepended
874    /// with the `target_` string.
875    pub fn into_target(self) -> Target {
876        self.target
877    }
878}
879
880/// A container for all lines from the output that are prefixed with the
881/// `target_` string.
882///
883/// For more information about possible values and recognized key-value pairs,
884/// see the [Rust Reference book] on [Conditional Compilation].
885///
886/// [Rust Reference book]: https://doc.rust-lang.org/reference/introduction.html
887/// [Conditional Compilation]: https://doc.rust-lang.org/reference/conditional-compilation.html
888#[derive(Clone, Debug, PartialEq)]
889pub struct Target {
890    arch: String,
891    endian: String,
892    env: Option<String>,
893    family: Option<String>,
894    features: Vec<String>,
895    os: String,
896    pointer_width: String,
897    vendor: Option<String>,
898}
899
900impl Target {
901    /// The target's CPU architecture.
902    ///
903    /// This is the `target_arch` line in the output. See the [`target_arch`]
904    /// section on [Conditional Compilation] in the [Rust Reference book] for
905    /// example values. The surrounding double quotes, `"`, of the raw output of
906    /// the `cargo rustc -- --print cfg` command are removed.
907    ///
908    /// # Examples
909    ///
910    /// For a 32-bit Intel x86 target:
911    ///
912    /// ```
913    /// # extern crate cargo_rustc_cfg;
914    /// # use cargo_rustc_cfg::{Cfg, Error};
915    /// # fn main() -> std::result::Result<(), Error> {
916    /// let cfg = Cfg::rustc_target("i686-pc-windows-msvc")?;
917    /// assert_eq!(cfg.target().arch(), "x86");
918    /// # Ok(())
919    /// # }
920    /// ```
921    ///
922    /// For a 64-bit Intel x86 target:
923    ///
924    /// ```
925    /// # extern crate cargo_rustc_cfg;
926    /// # use cargo_rustc_cfg::{Cfg, Error};
927    /// # fn main() -> std::result::Result<(), Error> {
928    /// let cfg = Cfg::rustc_target("x86_64-pc-windows-msvc")?;
929    /// assert_eq!(cfg.target().arch(), "x86_64");
930    /// # Ok(())
931    /// # }
932    /// ```
933    ///
934    /// For a 32-bit ARM target:
935    ///
936    /// ```
937    /// # extern crate cargo_rustc_cfg;
938    /// # use cargo_rustc_cfg::{Cfg, Error};
939    /// # fn main() -> std::result::Result<(), Error> {
940    /// let cfg = Cfg::rustc_target("thumbv7a-pc-windows-msvc")?;
941    /// assert_eq!(cfg.target().arch(), "arm");
942    /// # Ok(())
943    /// # }
944    /// ```
945    ///
946    /// For a 64-bit ARM target:
947    ///
948    /// ```
949    /// # extern crate cargo_rustc_cfg;
950    /// # use cargo_rustc_cfg::{Cfg, Error};
951    /// # fn main() -> std::result::Result<(), Error> {
952    /// let cfg = Cfg::rustc_target("aarch64-pc-windows-msvc")?;
953    /// assert_eq!(cfg.target().arch(), "aarch64");
954    /// # Ok(())
955    /// # }
956    /// ```
957    ///
958    /// [`target_arch`]: https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch
959    /// [Conditional Compilation]: https://doc.rust-lang.org/reference/conditional-compilation.html
960    /// [Rust Reference book]: https://doc.rust-lang.org/reference/introduction.html
961    pub fn arch(&self) -> &str {
962        &self.arch
963    }
964
965    /// The target's CPU endianness.
966    ///
967    /// This is the `target_endian` line in the output. See the
968    /// [`target_endian`] section on [Conditional Compilation] in the [Rust
969    /// Reference book] for example values. The surrounding double quotes, `"`,
970    /// of the raw output of the `cargo rustc -- --print cfg` command are
971    /// removed.
972    ///
973    /// # Examples
974    ///
975    /// For a little endian target:
976    ///
977    /// ```
978    /// # extern crate cargo_rustc_cfg;
979    /// # use cargo_rustc_cfg::{Cfg, Error};
980    /// # fn main() -> std::result::Result<(), Error> {
981    /// let cfg = Cfg::rustc_target("x86_64-pc-windows-msvc")?;
982    /// assert_eq!(cfg.target().endian(), "little");
983    /// # Ok(())
984    /// # }
985    /// ```
986    ///
987    /// For a big endian target:
988    ///
989    /// ```
990    /// # extern crate cargo_rustc_cfg;
991    /// # use cargo_rustc_cfg::{Cfg, Error};
992    /// # fn main() -> std::result::Result<(), Error> {
993    /// let cfg = Cfg::rustc_target("sparc64-unknown-linux-gnu")?;
994    /// assert_eq!(cfg.target().endian(), "big");
995    /// # Ok(())
996    /// # }
997    /// ```
998    ///
999    /// [`target_endian`]: https://doc.rust-lang.org/reference/conditional-compilation.html#target_endian
1000    /// [Conditional Compilation]: https://doc.rust-lang.org/reference/conditional-compilation.html
1001    /// [Rust Reference book]: https://doc.rust-lang.org/reference/introduction.html
1002    pub fn endian(&self) -> &str {
1003        &self.endian
1004    }
1005
1006    /// The Application Binary Interface (ABI) or `libc` used by the target.
1007    ///
1008    /// This is the `target_env` line in the output. See the
1009    /// [`target_env`] section on [Conditional Compilation] in the [Rust
1010    /// Reference book] for example values. The surrounding double quotes, `"`,
1011    /// of the raw output of the `cargo rustc -- --print cfg` command are
1012    /// removed.
1013    ///
1014    /// This will return `None` if the `target_env` line is missing from the
1015    /// output or the value is the empty string, `""`.
1016    ///
1017    /// # Examples
1018    ///
1019    /// ```
1020    /// # extern crate cargo_rustc_cfg;
1021    /// # use cargo_rustc_cfg::{Cfg, Error};
1022    /// # fn main() -> std::result::Result<(), Error> {
1023    /// let cfg = Cfg::rustc_target("x86_64-pc-windows-gnu")?;
1024    /// assert_eq!(cfg.target().env(), Some("gnu"));
1025    /// # Ok(())
1026    /// # }
1027    /// ```
1028    ///
1029    /// [`target_env`]: https://doc.rust-lang.org/reference/conditional-compilation.html#target_env
1030    /// [Conditional Compilation]: https://doc.rust-lang.org/reference/conditional-compilation.html
1031    /// [Rust Reference book]: https://doc.rust-lang.org/reference/introduction.html
1032    pub fn env(&self) -> Option<&str> {
1033        self.env.as_deref()
1034    }
1035
1036    /// The target's operating system family.
1037    ///
1038    /// This is the `target_family` line in the output. See the
1039    /// [`target_family`] section on [Conditional Compilation] in the [Rust
1040    /// Reference book] for example values. The surrounding double quotes, `"`,
1041    /// of the raw output of the `cargo rustc -- --print cfg` command are
1042    /// removed.
1043    ///
1044    /// This will return `None` if the `target_family` key-value pair was missing
1045    /// from the output or the value was the empty string, `""`.
1046    ///
1047    /// # Examples
1048    ///
1049    /// For a Windows target:
1050    ///
1051    /// ```
1052    /// # extern crate cargo_rustc_cfg;
1053    /// # use cargo_rustc_cfg::{Cfg, Error};
1054    /// # fn main() -> std::result::Result<(), Error> {
1055    /// let cfg = Cfg::rustc_target("x86_64-pc-windows-msvc")?;
1056    /// assert_eq!(cfg.target().family(), Some("windows"));
1057    /// # Ok(())
1058    /// # }
1059    /// ```
1060    ///
1061    /// For a Linux target:
1062    ///
1063    /// ```
1064    /// # extern crate cargo_rustc_cfg;
1065    /// # use cargo_rustc_cfg::{Cfg, Error};
1066    /// # fn main() -> std::result::Result<(), Error> {
1067    /// let cfg = Cfg::rustc_target("x86_64-unknown-linux-gnu")?;
1068    /// assert_eq!(cfg.target().family(), Some("unix"));
1069    /// # Ok(())
1070    /// # }
1071    /// ```
1072    ///
1073    /// For an Apple target:
1074    ///
1075    /// ```
1076    /// # extern crate cargo_rustc_cfg;
1077    /// # use cargo_rustc_cfg::{Cfg, Error};
1078    /// # fn main() -> std::result::Result<(), Error> {
1079    /// let cfg = Cfg::rustc_target("x86_64-apple-darwin")?;
1080    /// assert_eq!(cfg.target().family(), Some("unix"));
1081    /// # Ok(())
1082    /// # }
1083    /// ```
1084    ///
1085    /// [`target_family`]: https://doc.rust-lang.org/reference/conditional-compilation.html#target_family
1086    /// [Conditional Compilation]: https://doc.rust-lang.org/reference/conditional-compilation.html
1087    /// [Rust Reference book]: https://doc.rust-lang.org/reference/introduction.html
1088    pub fn family(&self) -> Option<&str> {
1089        self.family.as_deref()
1090    }
1091
1092    /// The features enabled for a target's compilation.
1093    ///
1094    /// This is any `target_feature` line in the output. See the
1095    /// [`target_feature`] section on [Conditional Compilation] in the [Rust
1096    /// Reference book] for example values. The surrounding double quotes, `"`,
1097    /// of the raw output of the `cargo rustc -- --print cfg` command are
1098    /// removed.
1099    ///
1100    /// Compiler features are enabled and disabled using either the Rust
1101    /// compiler's (rustc) [`-C/--codegen`] command line option, the Cargo
1102    /// [`rustflags`] key-value [configuration], or the [`RUSTFLAGS`] environment
1103    /// variable supported by Cargo but not rustc.
1104    ///
1105    /// # Examples
1106    ///
1107    /// Using the [`RUSTFLAGS`] environment variable to add the static linking
1108    /// feature to the target's compiler configuration:
1109    ///
1110    /// ```
1111    /// # extern crate cargo_rustc_cfg;
1112    /// # use cargo_rustc_cfg::{Cfg, Error};
1113    /// # fn main() -> std::result::Result<(), Error> {
1114    /// std::env::set_var("RUSTFLAGS", "-C target-feature=+crt-static");
1115    /// let cfg = Cfg::rustc_target("x86_64-pc-windows-msvc")?;
1116    /// std::env::set_var("RUSTFLAGS", "");
1117    /// assert!(cfg.target().features().contains(&"crt-static"));
1118    /// # Ok(())
1119    /// # }
1120    /// ```
1121    ///
1122    /// Using the [`-C/--codegen`] command line option to add the static linking
1123    /// feature to the target's compiler configuration:
1124    ///
1125    /// ```
1126    /// # extern crate cargo_rustc_cfg;
1127    /// # use cargo_rustc_cfg::{CargoRustcPrintCfg, Error};
1128    /// # fn main() -> std::result::Result<(), Error> {
1129    /// let cfg = CargoRustcPrintCfg::default()
1130    ///     .rustc_target("x86_64-pc-windows-msvc")
1131    ///     .rustc_args(&["-C", "target-feature=+crt-static"])
1132    ///     .execute()?;
1133    /// assert!(cfg.target().features().contains(&"crt-static"));
1134    /// # Ok(())
1135    /// # }
1136    /// ```
1137    ///
1138    /// [`target_feature`]: https://doc.rust-lang.org/reference/conditioal-compilation.html#target_feature
1139    /// [Conditional Compilation]: https://doc.rust-lang.org/reference/conditional-compilation.html
1140    /// [Rust Reference book]: https://doc.rust-lang.org/reference/introduction.html
1141    /// [`-C/--codegen`]: https://doc.rust-lang.org/rustc/command-line-arguments.html#-c--codegen-code-generation-options
1142    /// [`rustflags`]: https://doc.rust-lang.org/cargo/reference/config.html#buildrustflags
1143    /// [configuration]: https://doc.rust-lang.org/cargo/reference/config.html
1144    /// [`RUSTFLAGS`]: https://doc.rust-lang.org/cargo/reference/environment-variables.html
1145    pub fn features(&self) -> Vec<&str> {
1146        self.features.iter().map(|s| &**s).collect()
1147    }
1148
1149    /// The target's operating system.
1150    ///
1151    /// This is the `target_os` line in the output. See the
1152    /// [`target_os`] section on [Conditional Compilation] in the [Rust
1153    /// Reference book] for example values. The surrounding double quotes, `"`,
1154    /// of the raw output of the `cargo rustc -- --print cfg` command are
1155    /// removed.
1156    ///
1157    /// # Examples
1158    ///
1159    /// For a Windows target:
1160    ///
1161    /// ```
1162    /// # extern crate cargo_rustc_cfg;
1163    /// # use cargo_rustc_cfg::{Cfg, Error};
1164    /// # fn main() -> std::result::Result<(), Error> {
1165    /// let cfg = Cfg::rustc_target("x86_64-pc-windows-msvc")?;
1166    /// assert_eq!(cfg.target().os(), "windows");
1167    /// # Ok(())
1168    /// # }
1169    /// ```
1170    ///
1171    /// For a Linux target:
1172    ///
1173    /// ```
1174    /// # extern crate cargo_rustc_cfg;
1175    /// # use cargo_rustc_cfg::{Cfg, Error};
1176    /// # fn main() -> std::result::Result<(), Error> {
1177    /// let cfg = Cfg::rustc_target("x86_64-unknown-linux-gnu")?;
1178    /// assert_eq!(cfg.target().os(), "linux");
1179    /// # Ok(())
1180    /// # }
1181    /// ```
1182    ///
1183    /// For an Apple target:
1184    ///
1185    /// ```
1186    /// # extern crate cargo_rustc_cfg;
1187    /// # use cargo_rustc_cfg::{Cfg, Error};
1188    /// # fn main() -> std::result::Result<(), Error> {
1189    /// let cfg = Cfg::rustc_target("x86_64-apple-darwin")?;
1190    /// assert_eq!(cfg.target().os(), "macos");
1191    /// # Ok(())
1192    /// # }
1193    /// ```
1194    ///
1195    /// Note, the target's OS is different from the target's family for Apple
1196    /// targets.
1197    ///
1198    /// [`target_family`]: https://doc.rust-lang.org/reference/conditional-compilation.html#target_family
1199    /// [Conditional Compilation]: https://doc.rust-lang.org/reference/conditional-compilation.html
1200    /// [Rust Reference book]: https://doc.rust-lang.org/reference/introduction.html
1201    pub fn os(&self) -> &str {
1202        &self.os
1203    }
1204
1205    /// The target's pointer width in bits, but as string.
1206    ///
1207    /// This is the `target_pointer_width` line in the output. See the
1208    /// [`target_pointer_width`] section on [Conditional Compilation] in the
1209    /// [Rust Reference book] for example values. The surrounding double quotes,
1210    /// `"`, of the raw output of the `cargo rustc -- --print cfg` command are
1211    /// removed.
1212    ///
1213    /// # Examples
1214    ///
1215    /// For a 64-bit target:
1216    ///
1217    /// ```
1218    /// # extern crate cargo_rustc_cfg;
1219    /// # use cargo_rustc_cfg::{Cfg, Error};
1220    /// # fn main() -> std::result::Result<(), Error> {
1221    /// let cfg = Cfg::rustc_target("x86_64-pc-windows-msvc")?;
1222    /// assert_eq!(cfg.target().pointer_width(), "64");
1223    /// # Ok(())
1224    /// # }
1225    /// ```
1226    ///
1227    /// For a 32-bit target:
1228    ///
1229    /// ```
1230    /// # extern crate cargo_rustc_cfg;
1231    /// # use cargo_rustc_cfg::{Cfg, Error};
1232    /// # fn main() -> std::result::Result<(), Error> {
1233    /// let cfg = Cfg::rustc_target("i686-pc-windows-msvc")?;
1234    /// assert_eq!(cfg.target().pointer_width(), "32");
1235    /// # Ok(())
1236    /// # }
1237    /// ```
1238    ///
1239    /// [`target_pointer_width`]: https://doc.rust-lang.org/reference/conditional-compilation.html#target_pointer_width
1240    /// [Conditional Compilation]: https://doc.rust-lang.org/reference/conditional-compilation.html
1241    /// [Rust Reference book]: https://doc.rust-lang.org/reference/introduction.html
1242    pub fn pointer_width(&self) -> &str {
1243        &self.pointer_width
1244    }
1245
1246    /// The target's vendor.
1247    ///
1248    /// This is the `target_vendor` line in the output. See the
1249    /// [`target_vendor`] section on [Conditional Compilation] in the [Rust
1250    /// Reference book] for example values. The surrounding double quotes, `"`,
1251    /// of the raw output of the `cargo rustc -- --print cfg` command are
1252    /// removed.
1253    ///
1254    /// This will return `None` if the `target_vendor` line is missing or the
1255    /// value is the empty string, `""`.
1256    ///
1257    /// # Examples
1258    ///
1259    /// For a Windows target:
1260    ///
1261    /// ```
1262    /// # extern crate cargo_rustc_cfg;
1263    /// # use cargo_rustc_cfg::{Cfg, Error};
1264    /// # fn main() -> std::result::Result<(), Error> {
1265    /// let cfg = Cfg::rustc_target("x86_64-pc-windows-msvc")?;
1266    /// assert_eq!(cfg.target().vendor(), Some("pc"));
1267    /// # Ok(())
1268    /// # }
1269    /// ```
1270    ///
1271    /// For a Linux target:
1272    ///
1273    /// ```
1274    /// # extern crate cargo_rustc_cfg;
1275    /// # use cargo_rustc_cfg::{Cfg, Error};
1276    /// # fn main() -> std::result::Result<(), Error> {
1277    /// let cfg = Cfg::rustc_target("x86_64-unknown-linux-gnu")?;
1278    /// assert_eq!(cfg.target().vendor(), Some("unknown"));
1279    /// # Ok(())
1280    /// # }
1281    /// ```
1282    ///
1283    /// For an Apple target:
1284    ///
1285    /// ```
1286    /// # extern crate cargo_rustc_cfg;
1287    /// # use cargo_rustc_cfg::{Cfg, Error};
1288    /// # fn main() -> std::result::Result<(), Error> {
1289    /// let cfg = Cfg::rustc_target("x86_64-apple-darwin")?;
1290    /// assert_eq!(cfg.target().vendor(), Some("apple"));
1291    /// # Ok(())
1292    /// # }
1293    /// ```
1294    ///
1295    /// [`target_vendor`]: https://doc.rust-lang.org/reference/conditional-compilation.html#target_vendor
1296    /// [Conditional Compilation]: https://doc.rust-lang.org/reference/conditional-compilation.html
1297    /// [Rust Reference book]: https://doc.rust-lang.org/reference/introduction.html
1298    pub fn vendor(&self) -> Option<&str> {
1299        self.vendor.as_deref()
1300    }
1301}
1302
1303/// The error type for cargo-rustc-cfg operations and associated traits.
1304///
1305/// Errors mostly originate from the dependencies and executing the `cargo rustc
1306/// -- --print cfg` command, i.e. Input/Output (IO) errors, but custom instances
1307/// of `Error` can be created with the `Generic` variant and a message.
1308#[derive(Debug)]
1309pub enum Error {
1310    /// A command operation failed. Any content in the STDERR stream is used as
1311    /// part of the error message.
1312    Command(Output),
1313    /// UTF-8 string conversion failed.
1314    FromUtf8(std::string::FromUtf8Error),
1315    /// A generic, or custom, error occurred. The message should contain the detailed information.
1316    Generic(String),
1317    /// An I/O operation failed.
1318    Io(std::io::Error),
1319    /// An expected output from the `cargo rustc -- --print cfg` command is missing.
1320    MissingOutput(&'static str),
1321}
1322
1323impl std::fmt::Display for Error {
1324    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1325        match self {
1326            Self::Command(output) => write!(
1327                f,
1328                "{:?}: {}",
1329                output,
1330                String::from_utf8_lossy(&output.stderr)
1331            ),
1332            Self::FromUtf8(err) => write!(f, "{}", err),
1333            Self::Generic(msg) => write!(f, "{}", msg),
1334            Self::Io(err) => write!(f, "{}", err),
1335            Self::MissingOutput(key) => write!(f, "The '{}' is missing from the output", key),
1336        }
1337    }
1338}
1339
1340impl std::error::Error for Error {
1341    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1342        match self {
1343            Self::Command(..) => None,
1344            Self::FromUtf8(err) => Some(err),
1345            Self::Generic(..) => None,
1346            Self::Io(err) => Some(err),
1347            Self::MissingOutput(..) => None,
1348        }
1349    }
1350}
1351
1352impl From<&'static str> for Error {
1353    fn from(e: &str) -> Self {
1354        Error::Generic(String::from(e))
1355    }
1356}
1357
1358impl From<String> for Error {
1359    fn from(e: String) -> Self {
1360        Error::Generic(e)
1361    }
1362}
1363
1364impl From<std::io::Error> for Error {
1365    fn from(e: std::io::Error) -> Self {
1366        Self::Io(e)
1367    }
1368}
1369
1370impl From<std::string::FromUtf8Error> for Error {
1371    fn from(e: std::string::FromUtf8Error) -> Self {
1372        Self::FromUtf8(e)
1373    }
1374}