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}