target-spec-3.5.7/.cargo_vcs_info.json0000644000000001511046102023000133050ustar { "git": { "sha1": "2aeb469af0b0786296af644efdf1893f767a71e2" }, "path_in_vcs": "target-spec" }target-spec-3.5.7/CHANGELOG.md000064400000000000000000000345141046102023000136760ustar 00000000000000# Changelog ## [3.5.7] - 2026-01-30 ### Changed - Updated `cfg-expr` to version 0.20.6, updating builtin targets to Rust 1.93. ## [3.5.6] - 2026-01-12 ### Fixed - In custom targets, support arbitrary strings such as `"immediate-abort"` for `panic-strategy`. ## [3.5.5] - 2025-12-26 ### Changed - Updated `cfg-expr` to version 0.20.5, updating builtin targets to Rust 1.92. ## [3.5.4] - 2025-10-13 ### Fixed - For custom targets, updated the deserializer to handle [`target-pointer-width` becoming an integer](https://github.com/rust-lang/rust/pull/144443) in the newest Rust nightlies. ## [3.5.3] - 2025-10-12 ### Changed - Updated `cfg-expr` to version 0.20.3, updating builtin targets to Rust 1.90. ## [3.5.2] - 2025-09-29 ### Fixed Replaced obsolete `doc_auto_cfg` with `doc_cfg`, to fix Rust nightly builds with the `doc_cfg` flag enabled. ## [3.5.1] - 2025-09-14 ### Changed - Updated `cfg-expr` to version 0.20.2, updating builtin targets to Rust 1.89. ## [3.5.0] - 2025-07-11 ### Changed - Internal dependency `cfg-expr` updated to 0.20.2, updating builtin targets to Rust 1.88. - MSRV updated to Rust 1.86, as required by dependencies. (Apologies for not fulfilling the 6 month support promise for this version series.) ## [3.4.2] - 2025-02-20 ### Changed Internal dependency `cfg-expr` updated to 0.18.0, updating builtin targets to Rust 1.85. ## [3.4.1] - 2025-02-15 ### Fixed - Removed unused direct dependency on unicode-ident. Thanks [hanna-kruppe](https://github.com/hanna-kruppe) for your contribution! ## [3.4.0] - 2025-02-08 ### Added - Added `Triple::from_rustc_version_verbose` and `Platform::from_rustc_version_verbose` to parse `rustc -vV` output and obtain a triple. ### Changed - Renamed `Platform::current` to `Platform::build_target` to indicate that it is determined at build time, not at runtime. The old method is still available but has been marked deprecated. ## [3.3.1] - 2024-12-23 ### Changed - Improved error message for `CustomTripleCreateError::Unavailable`. ## [3.3.0] - 2024-12-22 ### Added - `CustomTripleCreateError` now has `input`, `input_string`, `line_and_column`, and `label` methods. These methods aid in implementing [`target-spec-miette`](https://docs.rs/target-spec-miette), and also allow dependencies to be oblivious to whether the `custom` feature is enabled. ### Fixed - Custom platforms now deserialize the `target-family` and `target-endian` fields correctly. Previously, these fields were ignored and always treated as empty. ### Deprecated - `Error::CustomTripleCreate` is now deprecated. This error was never actually created, and will be removed in the future. - `CustomTripleCreateError::Deserialize` is now deprecated. target-spec now creates a different `DeserializeJson` variant when deserialization fails. This variant also contains the original input being deserialized. ### Changed - MSRV updated to Rust 1.82. - Internal dependency `cfg-expr` updated to 0.17.2, updating builtin targets to Rust 1.83. ## [3.2.2] - 2024-09-11 ### Changed - Internal dependency `cfg-expr` updated to 0.17.0, updating builtin targets to Rust 1.81. ## [3.2.1] - 2024-07-31 ### Fixed - Update minimum version of `target-lexicon` dependency to 0.12.16, to ensure that minimal-version builds work. ## [3.2.0] - 2024-07-29 ### Changed - MSRV updated to Rust 1.75. - Internal dependency `cfg-expr` updated to 0.16.0, updating builtin targets to Rust 1.80. ## [3.1.0] - 2024-02-03 ### Changed - MSRV updated to Rust 1.73. - Internal dependency `cfg-expr` updated to 0.15.6, updating builtin targets to Rust 1.75. ## [3.0.1] - 2023-07-29 ### Changed - Internal dependency `cfg-expr` updated to 0.15.4, updating builtin targets to Rust 1.71. ## [3.0.0] - 2023-06-25 ### Changed - `TargetSpec` now stores plain strings rather than parsed triples. This matches what Cargo does. - `TargetExpression` has been renamed renamed to `TargetSpecExpression`. - `Error::UnknownTargetTriple` has been renamed to `Error::InvalidTargetSpecString`, and returns a new `PlainStringParseError` type. ### Added - `TargetSpec`, `TargetSpecExpression` and `TargetSpecPlainString` now implement `std::fmt::Display`. ## [2.0.1] - 2023-06-19 ### Fixed - `Triple`'s `Eq`, `PartialEq`, `Ord`, `PartialOrd` and `Hash` now take into account custom platforms. ## [2.0.0] - 2023-06-19 ### Added #### Custom platforms Added support for custom triples and platforms via [JSON specifications](https://docs.rust-embedded.org/embedonomicon/custom-target.html): - Added support for custom triples and platforms, under the optional `custom` feature. - New methods on `Platform` and `Triple`: - `is_standard`: returns true if this is a standard platform (builtin or heuristic). - `is_custom`: returns true if this is a custom platform. - `is_builtin`: returns true if this is a builtin platform. - `is_heuristic`: returns true if this platform was determined heuristically. #### Other additions - Added `new_strict` methods to `Platform` and `Triple`, to disable heuristic target parsing. ### Fixed - `target_os = "none"` is now correctly handled. ### Changed - Internal dependency `cfg-expr` updated to 0.15.3, updating builtin targets to Rust 1.70. - `PlatformSummary` is now non-exhaustive. - `PlatformSummary::new` now creates a new `PlatformSummary` from a triple string. (The old `PlatformSummary::new` has been renamed to `PlatformSummary::from_platform`). ### Removed - Removed deprecated `Error::UnknownPredicate` variant. - `Error` no longer implements `Eq` or `PartialEq` due to one of its variants now containing `serde_json::Error`. ## [1.4.0] - 2023-04-15 ### Changed - Internal dependency `cfg-expr` updated to 0.15.0, updating builtin targets to Rust 1.68. - MSRV updated to Rust 1.66. ## [1.3.1] - 2023-01-08 ### Added Added note to readme about MSRV for target-spec 1.3.x. ## [1.3.0] - 2023-01-08 ### Changed - Internal dependency `cfg-expr` updated to 0.13.0, updating builtin targets to Rust 1.66. - MSRV updated to Rust 1.62. ## [1.2.2] - 2022-11-07 ### Updated Internal dependency `cfg-expr` updated to 0.12.0, enabling parsing of `target_abi`. ## [1.2.1] - 2022-10-25 ### Added - `ExpressionParseError` now carries information about the reason a target expression couldn't be parsed. This has been done to support pretty-printing via the new [target-spec-miette](https://crates.io/crates/target-spec-miette) crate. ## [1.2.0] - 2022-09-30 ### Changed - Repository location update. - Internal dependency updates. - MSRV updated to Rust 1.58. Thanks to [Carol Nichols](https://github.com/carols10cents) for her contributions to this release! ## [1.1.0] - 2022-08-30 ### Fixed - Unknown predicates now evaluate to false, matching Cargo's behavior. - As a result, `Error::UnknownPredicate` is no longer in use and has been deprecated. ## [1.0.2] - 2022-05-29 ### Changed - Internal dependency updates. - MSRV updated to Rust 1.56. ## [1.0.1] - 2022-02-07 ### Added - Update badges in README. - Add `doc_cfg` to [the docs.rs build](https://docs.rs/target-spec). ## [1.0.0] - 2022-02-06 No breaking changes in this release compared to version 0.9. ### Changed - Internal dependency version bump: `cfg-expr` updated to 0.10.0. ### [0.9.0] - 2021-10-01 ## Added - Target triples can now be parsed directly into a `PlatformSummary`. ### Changed - `PlatformSummary::new` is now infallible. - MSRV updated to Rust 1.53. ### Fixed - `target-spec` now uses `cfg-expr`'s builtins by default, falling back to `target-lexicon` if `cfg-expr` isn't available. - This is because `target-lexicon` [may not always produce results](https://github.com/bytecodealliance/target-lexicon/issues/78) that match `rustc`'s target JSONs. ## [0.8.0] - 2021-09-13 ### Added - `Triple` represents a target triple, uniquely identified by a triple string. - `TargetExpression` represents a target expression beginning with `cfg(`. ### Changed - `target-spec` now uses [`target-lexicon`](https://github.com/bytecodealliance/target-lexicon) to parse triples, while continuing to use `cfg-expr` for expressions and evaluation. - Updated supported builtin targets to Rust 1.55. - `target-spec` is now more forward compatible, since new targets in future versions of Rust can be supported with non-breaking updates to `target-lexicon`. - `TargetSpec` is now an enum with `Triple` and `TargetExpression` variants. - `Platform` no longer has a lifetime parameter. - Updated supported builtin targets to Rust 1.55. - `cfg-expr` is now a private dependency again (`target-lexicon` is also a private dependency). - MSRV updated to Rust 1.51. ## [0.7.0] - 2021-02-23 ### Changed - Public dependency version bumps: - `cfg-expr` updated to 0.7.1. - `proptest` updated to version 1 and the corresponding feature renamed to `proptest1`. ## [0.6.1] - 2021-02-14 ### Changed - `cfg-expr` version requirement relaxed: 0.6 through 0.7 are now supported. There are no API changes between the two versions. ## [0.6.0] - 2021-02-03 ### Added - `Platform` now implements `Hash + Eq + Ord`. ### Changed - `TargetFeatures` and `Platform::add_flags` now accept `Cow<'static, str>`, simplifying lifetime management in many cases. - `cfg-expr` updated to 0.6.0. ## [0.5.0] - 2020-12-02 ### Changed - Updated `cfg-expr` dependency to 0.5.0. ## [0.4.1] - 2020-08-28 ### Fixed - Fixed compilation on platforms without target features ([#175](https://github.com/guppy-rs/guppy/issues/175)). ## [0.4.0] - 2020-06-20 ### Added - New, optional feature `summaries` to provide serialization and deserialization for `Platform` and `TargetFeatures`. - `Platform::is_custom` returns true if the platform was created with the `custom` constructor. ### Changed - The error types have been unified into a single `Error` type. - `Platform::new` and `Platform::current` now return errors instead of `None`. ## [0.3.0] - 2020-06-12 ### Added - `Platform::custom` creates platforms that are unknown to rustc. - This is supported through `cfg-expr`, which is now a public dependency. - Custom platforms are often found in embedded Rust. ### Changed - In order to support custom platforms, `Platform::triple` now returns a `&'a str` instead of a `&'static str`. ## [0.2.4] - 2020-05-06 ### Added - New feature `proptest010` to generate random platforms for property testing. ## [0.2.3] - 2020-04-15 ### Fixed - Better handling of unknown flags. - Unknown flags now evaluate to false instead of erroring out. - Added `Platform::add_flags` to allow setting flags that evaluate to true. These changes were prompted by how [`cargo-web`](https://github.com/koute/cargo-web) sets the `cargo_web` flag to true for `cargo web build`. ## 0.2.2 This was mistakenly published and was yanked. ## [0.2.1] - 2020-04-07 ### Changed - Updated repository links. ## [0.2.0] - 2020-04-05 ### Added - Added support for parsing specs and platforms separately from evaluating them, making error-less evaluation possible. - Added support for target features, including situations when target features are unknown. ### Changed - Switched to [`cfg-expr`](https://github.com/EmbarkStudios/cfg-expr) as the backend for `cfg()` expressions. ## [0.1.0] - 2020-03-20 - Initial release. [3.5.7]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-3.5.7 [3.5.6]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-3.5.6 [3.5.5]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-3.5.5 [3.5.4]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-3.5.4 [3.5.3]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-3.5.3 [3.5.2]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-3.5.2 [3.5.1]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-3.5.1 [3.5.0]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-3.5.0 [3.4.2]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-3.4.2 [3.4.1]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-3.4.1 [3.4.0]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-3.4.0 [3.3.1]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-3.3.1 [3.3.0]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-3.3.0 [3.2.2]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-3.2.2 [3.2.1]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-3.2.1 [3.2.0]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-3.2.0 [3.1.0]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-3.1.0 [3.0.1]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-3.0.1 [3.0.0]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-3.0.0 [2.0.1]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-2.0.1 [2.0.0]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-2.0.0 [1.4.0]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-1.4.0 [1.3.1]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-1.3.1 [1.3.0]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-1.3.0 [1.2.2]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-1.2.2 [1.2.1]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-1.2.1 [1.2.0]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-1.2.0 [1.1.0]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-1.1.0 [1.0.2]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-1.0.2 [1.0.1]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-1.0.1 [1.0.0]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-1.0.0 [0.9.0]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-0.9.0 [0.8.0]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-0.8.0 [0.7.0]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-0.7.0 [0.6.1]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-0.6.1 [0.6.0]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-0.6.0 [0.5.0]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-0.5.0 [0.4.1]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-0.4.1 [0.4.0]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-0.4.0 [0.3.0]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-0.3.0 [0.2.4]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-0.2.4 [0.2.3]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-0.2.3 [0.2.1]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-0.2.1 [0.2.0]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-0.2.0 [0.1.0]: https://github.com/guppy-rs/guppy/releases/tag/target-spec-0.1.0 target-spec-3.5.7/Cargo.lock0000644000000342251046102023000112710ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "autocfg" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bit-set" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitflags" version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "cfg-expr" version = "0.20.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78cef5b5a1a6827c7322ae2a636368a573006b27cfa76c7ebd53e834daeaab6a" dependencies = [ "smallvec", "target-lexicon", ] [[package]] name = "cfg-if" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "errno" version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", "windows-sys 0.59.0", ] [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "getrandom" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", "r-efi", "wasi", ] [[package]] name = "guppy-workspace-hack" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92620684d99f750bae383ecb3be3748142d6095760afd5cbcf2261e9a279d780" [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "linux-raw-sys" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "memchr" version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "ppv-lite86" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ "zerocopy", ] [[package]] name = "proc-macro2" version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fcdab19deb5195a31cf7726a210015ff1496ba1464fd42cb4f537b8b01b471f" dependencies = [ "bit-set", "bit-vec", "bitflags", "lazy_static", "num-traits", "rand", "rand_chacha", "rand_xorshift", "regex-syntax", "rusty-fork", "tempfile", "unarray", ] [[package]] name = "quick-error" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" [[package]] name = "rand" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ "getrandom", ] [[package]] name = "rand_xorshift" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" dependencies = [ "rand_core", ] [[package]] name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustix" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys 0.61.2", ] [[package]] name = "rusty-fork" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" dependencies = [ "fnv", "quick-error", "tempfile", "wait-timeout", ] [[package]] name = "ryu" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "serde" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", ] [[package]] name = "serde_core" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", "serde_core", ] [[package]] name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "syn" version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "target-lexicon" version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" [[package]] name = "target-spec" version = "3.5.7" dependencies = [ "cfg-expr", "guppy-workspace-hack", "proptest", "serde", "serde_json", "target-lexicon", "test-case", "toml", ] [[package]] name = "tempfile" version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" dependencies = [ "fastrand", "getrandom", "once_cell", "rustix", "windows-sys 0.61.2", ] [[package]] name = "test-case" version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8" dependencies = [ "test-case-macros", ] [[package]] name = "test-case-core" version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f" dependencies = [ "cfg-if", "proc-macro2", "quote", "syn", ] [[package]] name = "test-case-macros" version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", "syn", "test-case-core", ] [[package]] name = "toml" version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "serde", ] [[package]] name = "unarray" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-ident" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "wait-timeout" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" dependencies = [ "libc", ] [[package]] name = "wasi" version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-sys" version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ "windows-link", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "wit-bindgen-rt" version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ "bitflags", ] [[package]] name = "zerocopy" version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", "syn", ] target-spec-3.5.7/Cargo.toml0000644000000037421046102023000113140ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2024" rust-version = "1.86" name = "target-spec" version = "3.5.7" authors = [ "Jack Moffitt ", "Rain ", ] build = "build.rs" autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Evaluate Cargo.toml target specifications" documentation = "https://docs.rs/target-spec" readme = "README.md" keywords = [ "cargo", "targets", "platforms", "os", "cpu", ] categories = [ "development-tools", "parser-implementations", ] license = "MIT OR Apache-2.0" repository = "https://github.com/guppy-rs/guppy" resolver = "2" [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg=doc_cfg"] [badges.maintenance] status = "actively-developed" [features] custom = [ "dep:serde", "dep:serde_json", ] proptest1 = ["proptest"] summaries = ["dep:serde"] [lib] name = "target_spec" path = "src/lib.rs" [dependencies.cfg-expr] version = "0.20.6" features = ["targets"] [dependencies.guppy-workspace-hack] version = "0.1.0" [dependencies.proptest] version = "1.7.0" optional = true [dependencies.serde] version = "1.0.228" features = ["derive"] optional = true [dependencies.serde_json] version = "1.0.145" optional = true [dependencies.target-lexicon] version = "0.13.2" features = ["std"] [dev-dependencies.test-case] version = "3.3.1" [dev-dependencies.toml] version = "0.5.11" [lints.rust.unexpected_cfgs] level = "warn" priority = 0 check-cfg = [ "cfg(doc_cfg)", "cfg(guppy_nightly)", ] target-spec-3.5.7/Cargo.toml.orig000064400000000000000000000022061046102023000147450ustar 00000000000000[package] name = "target-spec" version = "3.5.7" description = "Evaluate Cargo.toml target specifications" documentation = "https://docs.rs/target-spec" repository = "https://github.com/guppy-rs/guppy" authors = ["Jack Moffitt ", "Rain "] license = "MIT OR Apache-2.0" readme = "README.md" keywords = ["cargo", "targets", "platforms", "os", "cpu"] categories = ["development-tools", "parser-implementations"] edition = "2024" rust-version.workspace = true [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg=doc_cfg"] [badges] maintenance = { status = "actively-developed" } [dependencies] cfg-expr = { workspace = true, features = ["targets"] } proptest = { version = "1.7.0", optional = true } serde = { version = "1.0.228", optional = true, features = ["derive"] } serde_json = { version = "1.0.145", optional = true } target-lexicon = { version = "0.13.2", features = ["std"] } guppy-workspace-hack.workspace = true [dev-dependencies] test-case = "3.3.1" toml = "0.5.11" [features] custom = ["dep:serde", "dep:serde_json"] proptest1 = ["proptest"] summaries = ["dep:serde"] [lints] workspace = true target-spec-3.5.7/README.md000064400000000000000000000065001046102023000133360ustar 00000000000000# target-spec [![target-spec on crates.io](https://img.shields.io/crates/v/target-spec)](https://crates.io/crates/target-spec) [![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen.svg)](https://docs.rs/target-spec/) [![Documentation (main)](https://img.shields.io/badge/docs-main-purple)](https://guppy-rs.github.io/guppy/rustdoc/target_spec/) [![Changelog](https://img.shields.io/badge/changelog-latest-blue)](CHANGELOG.md) [![License](https://img.shields.io/badge/license-Apache-green.svg)](../LICENSE-APACHE) [![License](https://img.shields.io/badge/license-MIT-green.svg)](../LICENSE-MIT) Evaluate `Cargo.toml` target specifications against platform triples. Cargo supports [platform-specific dependencies](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#platform-specific-dependencies). These dependencies can be specified in one of two ways: ```toml # 1. As Rust-like `#[cfg]` syntax. [target.'cfg(all(unix, target_arch = "x86_64"))'.dependencies] native = { path = "native/x86_64" } # 2. Listing out the full target triple. [target.x86_64-pc-windows-gnu.dependencies] winhttp = "0.4.0" ``` `target-spec` provides the `eval` API which can be used to figure out whether such a dependency will be included on a particular platform. ```rust use target_spec::eval; // Evaluate Rust-like `#[cfg]` syntax. let cfg_target = "cfg(all(unix, target_arch = \"x86_64\"))"; assert_eq!(eval(cfg_target, "x86_64-unknown-linux-gnu").unwrap(), Some(true)); assert_eq!(eval(cfg_target, "i686-unknown-linux-gnu").unwrap(), Some(false)); assert_eq!(eval(cfg_target, "x86_64-pc-windows-msvc").unwrap(), Some(false)); // Evaluate a full target-triple. assert_eq!(eval("x86_64-unknown-linux-gnu", "x86_64-unknown-linux-gnu").unwrap(), Some(true)); assert_eq!(eval("x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc").unwrap(), Some(false)); ``` For more advanced usage, see `Platform` and `TargetSpec`. ### Optional features * **`custom`**: Adds support for [custom targets](https://docs.rust-embedded.org/embedonomicon/custom-target.html) via `Platform::new_custom`. * **`summaries`**: Adds the `summaries` module to enable serialization of `Platform` and `TargetFeatures`. * **`proptest1`**: Enables support for property-based testing of `Platform` and `TargetFeatures` using `proptest`. ### Minimum supported Rust version The minimum supported Rust version (MSRV) is **Rust 1.82**. The MSRV history is: * For target-spec 3.0.x: **Rust 1.66**. * For target-spec 3.1.x: **Rust 1.73**. * For target-spec 3.2.x: **Rust 1.75**. * For target-spec 3.3.x and 3.4.x: **Rust 1.82**. * For target-spec 3.5.x: **Rust 1.86**. Within the 3.x series, MSRV bumps will be accompanied by a minor version update. The last 6 months of stable Rust releases will be supported. ### Related crates To pretty-print target-spec errors, consider using the [miette](https://docs.rs/miette) diagnostic library with [target-spec-miette](https://crates.io/crates/target-spec-miette). ## Contributing See the [CONTRIBUTING](../CONTRIBUTING.md) file for how to help out. ## License This project is available under the terms of either the [Apache 2.0 license](../LICENSE-APACHE) or the [MIT license](../LICENSE-MIT). target-spec-3.5.7/README.tpl000064400000000000000000000017571046102023000135460ustar 00000000000000# {{crate}} [![target-spec on crates.io](https://img.shields.io/crates/v/target-spec)](https://crates.io/crates/target-spec) [![Documentation (latest release)](https://img.shields.io/badge/docs-latest-brightgreen.svg)](https://docs.rs/target-spec/) [![Documentation (main)](https://img.shields.io/badge/docs-main-purple)](https://guppy-rs.github.io/guppy/rustdoc/target_spec/) [![Changelog](https://img.shields.io/badge/changelog-latest-blue)](CHANGELOG.md) [![License](https://img.shields.io/badge/license-Apache-green.svg)](../LICENSE-APACHE) [![License](https://img.shields.io/badge/license-MIT-green.svg)](../LICENSE-MIT) {{readme}} ## Contributing See the [CONTRIBUTING](../CONTRIBUTING.md) file for how to help out. ## License This project is available under the terms of either the [Apache 2.0 license](../LICENSE-APACHE) or the [MIT license](../LICENSE-MIT). target-spec-3.5.7/build.rs000064400000000000000000000020361046102023000135240ustar 00000000000000// Copyright (c) The cargo-guppy Contributors // SPDX-License-Identifier: MIT OR Apache-2.0 use std::{env, fs, path::Path}; fn main() { let out_dir = env::var_os("OUT_DIR").unwrap(); let dest_path = Path::new(&out_dir).join("build_target.rs"); let target = env::var("TARGET").unwrap(); // Non-x86/amd64 platforms don't have this environment variable defined at all. let features = env::var("CARGO_CFG_TARGET_FEATURE").unwrap_or_else(|_| "".to_string()); // The features are in the format |foo,bar|. Convert to |&["foo", "bar", ]|; let mut out = vec!["&["]; for feature in features.split(',') { out.push("\""); out.push(feature); out.push("\", "); } out.push("]"); let features = out.join(""); fs::write( dest_path, format!( "static BUILD_TARGET: &str = \"{target}\";\n\ \n\ static BUILD_TARGET_FEATURES: &[&str] = {features};\ ", ), ) .unwrap(); println!("cargo:rerun-if-changed=build.rs"); } target-spec-3.5.7/src/custom.rs000064400000000000000000000170661046102023000145370ustar 00000000000000// Copyright (c) The cargo-guppy Contributors // SPDX-License-Identifier: MIT OR Apache-2.0 //! Parse custom platforms. use std::borrow::Cow; use cfg_expr::targets::{ Abi, Arch, Env, Families, Family, HasAtomic, HasAtomics, Os, TargetInfo, Triple, Vendor, }; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Deserialize, Serialize, Eq, Hash, Ord, PartialEq, PartialOrd)] #[serde(rename_all = "kebab-case")] pub(crate) struct TargetDefinition { // TODO: it would be nice to use target-spec-json for this, but that has a // few limitations as of v0.1: // // * target-pointer-width is a string before roughly nightly-2025-10-12 (it // was changed to an integer after that). // * Os and Env deserialized to enums, but we would really like them to be strings. // // --- arch: String, #[serde(rename = "target-pointer-width", with = "target_pointer_width")] pointer_width: u8, // These parameters are not used by target-spec but are mandatory in Target, so we require them // here. https://doc.rust-lang.org/nightly/nightly-rustc/rustc_target/spec/struct.Target.html #[allow(dead_code)] llvm_target: String, #[allow(dead_code)] data_layout: String, // These are optional parameters used by target-spec. #[serde(default)] os: Option, #[serde(default)] abi: Option, #[serde(default)] env: Option, #[serde(default)] vendor: Option, #[serde(default)] target_family: Vec, #[serde(default)] target_endian: Endian, #[serde(default)] min_atomic_width: Option, #[serde(default)] max_atomic_width: Option, #[serde(default)] panic_strategy: Option, } impl TargetDefinition { pub(crate) fn into_target_info(self, triple: Cow<'static, str>) -> TargetInfo { // Per https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_target/spec/mod.rs.html, // the default value for min_atomic_width is 8. let min_atomic_width = self.min_atomic_width.unwrap_or(8); // The default max atomic width is the pointer width. let max_atomic_width = self.max_atomic_width.unwrap_or(self.pointer_width as u16); let mut has_atomics = Vec::new(); // atomic_width should always be a power of two, but rather than checking that we just // start counting up from 8. let mut atomic_width = 8; while atomic_width <= max_atomic_width { if atomic_width < min_atomic_width { atomic_width *= 2; continue; } has_atomics.push(HasAtomic::IntegerSize(atomic_width)); if atomic_width == self.pointer_width as u16 { has_atomics.push(HasAtomic::Pointer); } atomic_width *= 2; } let panic_strategy = match self.panic_strategy { None => cfg_expr::targets::Panic::unwind, Some(s) => cfg_expr::targets::Panic::new(s), }; TargetInfo { triple: Triple::new(triple), os: self.os.map(Os::new), abi: self.abi.map(Abi::new), arch: Arch::new(self.arch), env: self.env.map(Env::new), vendor: self.vendor.map(Vendor::new), families: Families::new(self.target_family.into_iter().map(Family::new)), pointer_width: self.pointer_width, endian: self.target_endian.to_cfg_expr(), has_atomics: HasAtomics::new(has_atomics), panic: panic_strategy, } } } mod target_pointer_width { use serde::{Deserializer, Serializer}; pub(super) fn deserialize<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, { use std::fmt; struct PointerWidthVisitor; impl<'de> serde::de::Visitor<'de> for PointerWidthVisitor { type Value = u8; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a string or integer representing pointer width") } fn visit_u64(self, value: u64) -> Result where E: serde::de::Error, { value .try_into() .map_err(|_| E::custom(format!("pointer width {value} out of range for u8"))) } fn visit_str(self, value: &str) -> Result where E: serde::de::Error, { value .parse::() .map_err(|error| E::custom(format!("error parsing as integer: {error}"))) } } deserializer.deserialize_any(PointerWidthVisitor) } pub(super) fn serialize(value: &u8, serializer: S) -> Result where S: Serializer, { // Should change this in the future to serialize as an integer? serializer.serialize_str(&value.to_string()) } } #[derive( Copy, Clone, Debug, Deserialize, Serialize, Default, Eq, Hash, Ord, PartialEq, PartialOrd, )] #[serde(rename_all = "kebab-case")] enum Endian { #[default] Little, Big, } impl Endian { fn to_cfg_expr(self) -> cfg_expr::targets::Endian { match self { Self::Little => cfg_expr::targets::Endian::little, Self::Big => cfg_expr::targets::Endian::big, } } } #[cfg(test)] mod tests { use super::*; use std::{collections::BTreeMap, process::Command}; #[derive(Deserialize)] #[serde(transparent)] struct AllTargets(BTreeMap); #[test] fn test_all_builtin_specs_recognized() { let rustc_bin: String = std::env::var("RUSTC").unwrap_or_else(|_| "rustc".to_owned()); let output = Command::new(rustc_bin) // Used for -Zunstable-options. This is test-only code so it doesn't matter. .env("RUSTC_BOOTSTRAP", "1") .args(["-Z", "unstable-options", "--print", "all-target-specs-json"]) .output() .expect("rustc command succeeded"); assert!(output.status.success(), "rustc command succeeded"); let all_targets: AllTargets = serde_json::from_slice(&output.stdout) .expect("deserializing all-target-specs-json succeeded"); for (triple, target_def) in all_targets.0 { eprintln!("*** testing {triple}"); // Just make sure this doesn't panic. (If this becomes fallible in the future, then this // shouldn't return an error either.) target_def.clone().into_target_info(triple.clone().into()); let json = serde_json::to_string(&target_def).expect("target def serialized successfully"); eprintln!("* minified json: {json}"); let target_def_2 = serde_json::from_str(&json).expect("target def 2 deserialized"); assert_eq!(target_def, target_def_2, "matches"); // Do some spot checks for things like big-endian targets. if triple.starts_with("powerpc-") || triple.starts_with("powerpc64-") { assert_eq!( target_def.target_endian, Endian::Big, "powerpc is big-endian" ); } if triple.contains("-linux") { assert!( target_def.target_family.contains(&"unix".to_owned()), "linux target_family should contain unix (was {:#?})", target_def.target_family, ); } } } } target-spec-3.5.7/src/errors.rs000064400000000000000000000426071046102023000145400ustar 00000000000000// Copyright (c) The cargo-guppy Contributors // SPDX-License-Identifier: MIT OR Apache-2.0 //! Errors returned by `target-spec`. use std::{borrow::Cow, error, fmt, string::FromUtf8Error}; /// An error that happened during `target-spec` parsing or evaluation. #[derive(Clone, Debug)] #[non_exhaustive] pub enum Error { /// A `cfg()` expression was invalid and could not be parsed. InvalidExpression(ExpressionParseError), /// The provided plain string (in the position that a `cfg()` expression would be) was unknown. InvalidTargetSpecString(PlainStringParseError), /// The provided platform triple was unknown. UnknownPlatformTriple(TripleParseError), /// Deprecated: this variant is no longer used. #[deprecated( since = "3.3.0", note = "this variant is no longer returned: instead, use CustomPlatformCreate" )] #[doc(hidden)] CustomTripleCreate(CustomTripleCreateError), /// An error occurred while creating a custom platform. CustomPlatformCreate(CustomTripleCreateError), /// An error occurred while parsing `rustc -vV` output. RustcVersionVerboseParse(RustcVersionVerboseParseError), } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Error::InvalidExpression(_) => write!(f, "invalid cfg() expression"), Error::InvalidTargetSpecString(_) => { write!(f, "failed to parse target spec as a plain string") } Error::UnknownPlatformTriple(_) => { write!(f, "unknown platform triple") } #[allow(deprecated)] Error::CustomTripleCreate(_) => write!(f, "error creating custom triple"), Error::CustomPlatformCreate(_) => { write!(f, "error creating custom platform") } Error::RustcVersionVerboseParse(_) => { write!(f, "error parsing `rustc -vV` output") } } } } impl error::Error for Error { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { Error::InvalidExpression(err) => Some(err), Error::InvalidTargetSpecString(err) => Some(err), Error::UnknownPlatformTriple(err) => Some(err), #[allow(deprecated)] Error::CustomTripleCreate(err) => Some(err), Error::CustomPlatformCreate(err) => Some(err), Error::RustcVersionVerboseParse(err) => Some(err), } } } // Note: ExpressionParseError is a duplicate of cfg_expr::error::ParseError, and is copied here // because we don't want to expose that in a stable (1.0+) API. /// An error returned in case a `TargetExpression` cannot be parsed. #[derive(Clone, Debug, PartialEq, Eq)] #[non_exhaustive] pub struct ExpressionParseError { /// The string we tried to parse. pub input: String, /// The range of characters in the original string that resulted /// in this error. pub span: std::ops::Range, /// The kind of error that occurred. pub kind: ExpressionParseErrorKind, } impl ExpressionParseError { pub(crate) fn new(input: &str, error: cfg_expr::ParseError) -> Self { // The error returned by cfg_expr::ParseError does not include the leading 'cfg('. Use the // original input and add 4 which is the length of 'cfg('. let span = if input.starts_with("cfg(") && input.ends_with(')') { (error.span.start + 4)..(error.span.end + 4) } else { error.span }; Self { input: input.to_owned(), span, kind: ExpressionParseErrorKind::from_cfg_expr(error.reason), } } } impl fmt::Display for ExpressionParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "error parsing cfg() expression") } } impl error::Error for ExpressionParseError {} /// The kind of [`ExpressionParseError`] that occurred. #[derive(Clone, Debug, PartialEq, Eq)] #[non_exhaustive] pub enum ExpressionParseErrorKind { /// not() takes exactly 1 predicate, unlike all() and any() InvalidNot(usize), /// The characters are not valid in an cfg expression InvalidCharacters, /// An opening parens was unmatched with a closing parens UnclosedParens, /// A closing parens was unmatched with an opening parens UnopenedParens, /// An opening quotes was unmatched with a closing quotes UnclosedQuotes, /// A closing quotes was unmatched with an opening quotes UnopenedQuotes, /// The expression does not contain any valid terms Empty, /// Found an unexpected term, which wasn't one of the expected terms that /// is listed Unexpected { /// The list of expected terms. expected: &'static [&'static str], }, /// Failed to parse an integer value InvalidInteger, /// The root cfg() may only contain a single predicate MultipleRootPredicates, /// A `target_has_atomic` predicate didn't correctly parse. InvalidHasAtomic, /// An element was not part of the builtin information in rustc UnknownBuiltin, } impl ExpressionParseErrorKind { fn from_cfg_expr(reason: cfg_expr::error::Reason) -> Self { use cfg_expr::error::Reason::*; match reason { InvalidCharacters => Self::InvalidCharacters, UnclosedParens => Self::UnclosedParens, UnopenedParens => Self::UnopenedParens, UnclosedQuotes => Self::UnclosedQuotes, UnopenedQuotes => Self::UnopenedQuotes, Empty => Self::Empty, Unexpected(expected) => Self::Unexpected { expected }, InvalidNot(np) => Self::InvalidNot(np), InvalidInteger => Self::InvalidInteger, MultipleRootPredicates => Self::MultipleRootPredicates, InvalidHasAtomic => Self::InvalidHasAtomic, UnknownBuiltin => Self::UnknownBuiltin, } } } impl fmt::Display for ExpressionParseErrorKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use ExpressionParseErrorKind::*; match self { InvalidCharacters => f.write_str("invalid character(s)"), UnclosedParens => f.write_str("unclosed parens"), UnopenedParens => f.write_str("unopened parens"), UnclosedQuotes => f.write_str("unclosed quotes"), UnopenedQuotes => f.write_str("unopened quotes"), Empty => f.write_str("empty expression"), Unexpected { expected } => { if expected.len() > 1 { f.write_str("expected one of ")?; for (i, exp) in expected.iter().enumerate() { f.write_fmt(format_args!("{}`{exp}`", if i > 0 { ", " } else { "" }))?; } f.write_str(" here") } else if !expected.is_empty() { f.write_fmt(format_args!("expected a `{}` here", expected[0])) } else { f.write_str("the term was not expected here") } } InvalidNot(np) => f.write_fmt(format_args!("not() takes 1 predicate, found {np}")), InvalidInteger => f.write_str("invalid integer"), MultipleRootPredicates => f.write_str("multiple root predicates"), InvalidHasAtomic => f.write_str("expected integer or \"ptr\""), UnknownBuiltin => f.write_str("unknown built-in"), } } } /// An error that occurred while parsing a [`TargetSpecPlainString`](crate::TargetSpecPlainString). #[derive(Clone, Debug, PartialEq, Eq)] #[non_exhaustive] pub struct PlainStringParseError { /// The input we failed to parse. pub input: String, /// The character index (in bytes) at which the input failed to parse. pub char_index: usize, /// The character that failed to parse. pub character: char, } impl PlainStringParseError { pub(crate) fn new(input: String, char_index: usize, character: char) -> Self { Self { input, char_index, character, } } /// Returns the range of characters in the input that resulted in this error. pub fn span(&self) -> std::ops::Range { let end = self.char_index + self.character.len_utf8(); self.char_index..end } } impl fmt::Display for PlainStringParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "failed to parse `{}` at index {}: character \ must be alphanumeric, `-`, `_` or `.`", self.input, self.char_index, ) } } impl error::Error for PlainStringParseError { fn source(&self) -> Option<&(dyn error::Error + 'static)> { None } } /// An error returned while parsing a single target. /// /// This is produced when both of the following are true: /// /// 1. The triple is not in the builtin set. /// 2. If heuristic parsing is enabled, it failed. #[derive(Clone, Debug, PartialEq, Eq)] pub struct TripleParseError { triple_str: Cow<'static, str>, kind: TripleParseErrorKind, } impl TripleParseError { pub(crate) fn new( triple_str: Cow<'static, str>, lexicon_err: cfg_expr::target_lexicon::ParseError, ) -> Self { Self { triple_str, kind: TripleParseErrorKind::Lexicon(lexicon_err), } } pub(crate) fn new_strict(triple_str: Cow<'static, str>) -> Self { Self { triple_str, kind: TripleParseErrorKind::LexiconDisabled, } } /// Returns the triple string that could not be parsed. pub fn triple_str(&self) -> &str { &self.triple_str } } impl fmt::Display for TripleParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "unknown triple string: {}", self.triple_str) } } impl error::Error for TripleParseError { fn source(&self) -> Option<&(dyn error::Error + 'static)> { Some(&self.kind) } } #[derive(Clone, Debug, PartialEq, Eq)] enum TripleParseErrorKind { Lexicon(cfg_expr::target_lexicon::ParseError), LexiconDisabled, } impl fmt::Display for TripleParseErrorKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Lexicon(_) => write!( f, "triple not in builtin platforms and heuristic parsing failed" ), Self::LexiconDisabled => write!( f, "triple not in builtin platforms and heuristic parsing disabled" ), } } } impl error::Error for TripleParseErrorKind { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { Self::Lexicon(error) => Some(error), Self::LexiconDisabled => None, } } } /// An error returned while creating a custom platform. #[derive(Clone, Debug)] #[non_exhaustive] pub enum CustomTripleCreateError { #[cfg(feature = "custom")] /// Deprecated, and no longer used: instead, use [`Self::DeserializeJson`]. #[deprecated( since = "3.3.0", note = "this variant is no longer returned: instead, \ use DeserializeJson which also includes the input string" )] #[doc(hidden)] Deserialize { /// The specified triple. triple: String, /// The deserialization error that occurred. error: std::sync::Arc, }, /// A custom platform was asked to be created, but the `custom` feature is currently disabled. /// /// Currently, this can only happen if a custom platform is deserialized from a /// [`PlatformSummary`](crate::summaries::PlatformSummary), Unavailable, #[cfg(feature = "custom")] /// An error occurred while deserializing serde data. DeserializeJson { /// The specified triple. triple: String, /// The input string that caused the error. input: String, /// The deserialization error that occurred. error: std::sync::Arc, }, } impl CustomTripleCreateError { /// Returns the provided input that caused the error, if available. #[inline] pub fn input(&self) -> Option<&str> { self.input_string().map(String::as_str) } /// A version of [`Self::input`] that returns a `&String` rather than a /// `&str`. /// /// This is a workaround for a miette limitation -- `&str` can't be cast to /// `&dyn SourceCode`, but `&String` can. pub fn input_string(&self) -> Option<&String> { match self { #[cfg(feature = "custom")] Self::DeserializeJson { input, .. } => Some(input), #[cfg(feature = "custom")] #[allow(deprecated)] Self::Deserialize { .. } => None, Self::Unavailable => None, } } /// Returns the line and column number that caused the error, if available /// and the error is not an I/O error. /// /// The line and column number are 1-based, though the column number can be /// 0 if the error occurred between lines. #[inline] pub fn line_and_column(&self) -> Option<(usize, usize)> { match self { #[cfg(feature = "custom")] Self::DeserializeJson { error, .. } => Some((error.line(), error.column())), #[cfg(feature = "custom")] #[allow(deprecated)] Self::Deserialize { .. } => None, Self::Unavailable => None, } } /// Returns a label suitable for the error message to label at /// [`Self::line_and_column`]. /// /// This label drops line and column information if available. pub fn label(&self) -> Option { match self { #[cfg(feature = "custom")] Self::DeserializeJson { error, .. } => { let label = error.to_string(); // serde_json appends " at line M column N" -- remove it. let trimmed = match label.rfind(" at line ") { Some(idx) => label[..idx].to_string(), None => label, }; Some(trimmed) } #[cfg(feature = "custom")] #[allow(deprecated)] Self::Deserialize { .. } => None, Self::Unavailable => None, } } } impl fmt::Display for CustomTripleCreateError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { #[cfg(feature = "custom")] #[allow(deprecated)] Self::DeserializeJson { triple, .. } | Self::Deserialize { triple, .. } => { write!(f, "error deserializing custom target JSON for `{triple}`") } Self::Unavailable => { write!( f, "custom platforms are currently unavailable: \ to enable them, add the `custom` feature to target-spec" ) } } } } impl error::Error for CustomTripleCreateError { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { #[cfg(feature = "custom")] #[allow(deprecated)] Self::DeserializeJson { error, .. } | Self::Deserialize { error, .. } => Some(error), Self::Unavailable => None, } } } /// An error occurred while parsing `rustc -vV` output. /// /// Returned by [`Platform::from_rustc_version_verbose`](crate::Platform::from_rustc_version_verbose). #[derive(Clone, Debug)] #[non_exhaustive] pub enum RustcVersionVerboseParseError { /// The output was invalid UTF-8. InvalidUtf8(FromUtf8Error), /// The output did not contain a `host: ` line. MissingHostLine { /// The output that was parsed. output: String, }, } impl fmt::Display for RustcVersionVerboseParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { RustcVersionVerboseParseError::InvalidUtf8(_) => { write!(f, "output from `rustc -vV` was not valid UTF-8") } RustcVersionVerboseParseError::MissingHostLine { output } => { write!( f, "output from `rustc -vV` did not contain a `host: ` line; output:\n---\n{output}---" ) } } } } impl error::Error for RustcVersionVerboseParseError { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { RustcVersionVerboseParseError::InvalidUtf8(err) => Some(err), RustcVersionVerboseParseError::MissingHostLine { .. } => None, } } } #[cfg(test)] mod tests { use crate::{TargetSpecExpression, TargetSpecPlainString}; use test_case::test_case; #[test_case("cfg()", 4..4; "empty expression results in span inside cfg")] #[test_case("target_os = \"macos", 12..18; "unclosed quote specified without cfg")] fn test_expression_parse_error_span(input: &str, expected_span: std::ops::Range) { let err = TargetSpecExpression::new(input).unwrap_err(); assert_eq!(err.span, expected_span); } #[test_case("foobar$", 6..7; "dollar sign at end of string")] #[test_case("my🛑triple", 2..6; "multibyte character")] fn test_plain_string_parse_error_span(input: &str, expected_span: std::ops::Range) { let err = TargetSpecPlainString::new(input.to_owned()).unwrap_err(); assert_eq!(err.span(), expected_span); } } target-spec-3.5.7/src/lib.rs000064400000000000000000000061011046102023000137570ustar 00000000000000// Copyright (c) The cargo-guppy Contributors // SPDX-License-Identifier: MIT OR Apache-2.0 //! Evaluate `Cargo.toml` target specifications against platform triples. //! //! Cargo supports [platform-specific //! dependencies](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#platform-specific-dependencies). //! These dependencies can be specified in one of two ways: //! //! ```toml //! # 1. As Rust-like `#[cfg]` syntax. //! [target.'cfg(all(unix, target_arch = "x86_64"))'.dependencies] //! native = { path = "native/x86_64" } //! //! # 2. Listing out the full target triple. //! [target.x86_64-pc-windows-gnu.dependencies] //! winhttp = "0.4.0" //! ``` //! //! `target-spec` provides the `eval` API which can be used to figure out whether such a dependency //! will be included on a particular platform. //! //! ```rust //! use target_spec::eval; //! //! // Evaluate Rust-like `#[cfg]` syntax. //! let cfg_target = "cfg(all(unix, target_arch = \"x86_64\"))"; //! assert_eq!(eval(cfg_target, "x86_64-unknown-linux-gnu").unwrap(), Some(true)); //! assert_eq!(eval(cfg_target, "i686-unknown-linux-gnu").unwrap(), Some(false)); //! assert_eq!(eval(cfg_target, "x86_64-pc-windows-msvc").unwrap(), Some(false)); //! //! // Evaluate a full target-triple. //! assert_eq!(eval("x86_64-unknown-linux-gnu", "x86_64-unknown-linux-gnu").unwrap(), Some(true)); //! assert_eq!(eval("x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc").unwrap(), Some(false)); //! ``` //! //! For more advanced usage, see [`Platform`] and [`TargetSpec`]. //! //! ## Optional features //! //! * **`custom`**: Adds support for [custom //! targets](https://docs.rust-embedded.org/embedonomicon/custom-target.html) via //! [`Platform::new_custom`]. //! * **`summaries`**: Adds the [`summaries`] module to enable serialization of [`Platform`] and //! [`TargetFeatures`]. //! * **`proptest1`**: Enables support for property-based testing of [`Platform`] and //! [`TargetFeatures`] using [`proptest`]. //! //! ## Minimum supported Rust version //! //! The minimum supported Rust version (MSRV) is **Rust 1.82**. The MSRV history is: //! //! * For target-spec 3.0.x: **Rust 1.66**. //! * For target-spec 3.1.x: **Rust 1.73**. //! * For target-spec 3.2.x: **Rust 1.75**. //! * For target-spec 3.3.x and 3.4.x: **Rust 1.82**. //! * For target-spec 3.5.x: **Rust 1.86**. //! //! Within the 3.x series, MSRV bumps will be accompanied by a minor version //! update. The last 6 months of stable Rust releases will be supported. //! //! ## Related crates //! //! To pretty-print target-spec errors, consider using the [miette](https://docs.rs/miette) //! diagnostic library with [target-spec-miette](https://crates.io/crates/target-spec-miette). #![warn(missing_docs)] #![forbid(unsafe_code)] #![cfg_attr(doc_cfg, feature(doc_cfg))] #[cfg(feature = "custom")] mod custom; pub mod errors; mod platform; #[cfg(feature = "proptest1")] mod proptest_helpers; mod simple_eval; mod spec; #[cfg(feature = "summaries")] pub mod summaries; mod triple; pub use errors::Error; pub use platform::*; pub use simple_eval::*; pub use spec::*; pub use triple::*; target-spec-3.5.7/src/platform.rs000064400000000000000000000234051046102023000150430ustar 00000000000000// Copyright (c) The cargo-guppy Contributors // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::{Error, Triple}; use std::{borrow::Cow, collections::BTreeSet, ops::Deref}; // This is generated by the build script. include!(concat!(env!("OUT_DIR"), "/build_target.rs")); /// A platform to evaluate target specifications against. /// /// # Standard and custom platforms /// /// `target-spec` recognizes two kinds of platforms: /// /// * **Standard platforms:** These platforms are only specified by their triple string. For /// example, the platform `x86_64-unknown-linux-gnu` is a standard platform since it is recognized /// by Rust as a tier 1 platform. /// /// All [builtin platforms](https://doc.rust-lang.org/nightly/rustc/platform-support.html) are /// standard platforms. /// /// By default, if a platform isn't builtin, target-spec attempts to heuristically determine the /// characteristics of the platform based on the triple string. (Use the /// [`new_strict`](Self::new_strict) constructor to disable this.) /// /// * **Custom platforms:** These platforms are specified via both a triple string and a JSON file /// in the format [defined by /// Rust](https://docs.rust-embedded.org/embedonomicon/custom-target.html). Custom platforms are /// used for targets not recognized by Rust. #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[must_use] pub struct Platform { triple: Triple, target_features: TargetFeatures, flags: BTreeSet>, } impl Platform { /// Creates a new standard `Platform` from the given triple and target features. /// /// Returns an error if this platform wasn't known to `target-spec`. pub fn new( triple_str: impl Into>, target_features: TargetFeatures, ) -> Result { let triple = Triple::new(triple_str.into()).map_err(Error::UnknownPlatformTriple)?; Ok(Self::from_triple(triple, target_features)) } /// Creates a new standard `Platform` from the given triple and target features. /// /// This constructor only consults the builtin platform table, and does not attempt to /// heuristically determine the platform's characteristics based on the triple string. pub fn new_strict( triple_str: impl Into>, target_features: TargetFeatures, ) -> Result { let triple = Triple::new_strict(triple_str.into()).map_err(Error::UnknownPlatformTriple)?; Ok(Self::from_triple(triple, target_features)) } /// Creates a new standard `Platform` from `rustc -vV` output and the given /// target features. pub fn from_rustc_version_verbose( output: impl AsRef<[u8]>, target_features: TargetFeatures, ) -> Result { let triple = Triple::from_rustc_version_verbose(output.as_ref())?; Ok(Self::from_triple(triple, target_features)) } /// Previous name for [`Self::build_target`], renamed to clarify what /// `current` means. /// /// This method is deprecated and will be removed in a future version. #[deprecated( since = "3.4.0", note = "this method has been renamed to `build_target`" )] #[inline] pub fn current() -> Result { Self::build_target() } /// Returns the target platform, as detected at build time. /// /// In case of cross-compilation, this will be the **target platform**, not /// the host platform. /// /// Some target platforms are runtime cross-compatible, e.g. /// `x86_64-unknown-linux-musl` binaries can be run on /// `x86_64-unknown-linux-gnu`. In that case, this function returns /// `x86_64-unknown-linux-musl`. To obtain `x86_64-unknown-linux-gnu`, run /// `${RUSTC:-rustc} -vV` and pass it into [`Self::from_rustc_version_verbose`]. /// /// This is currently always a standard platform, and will return an error if the current /// platform was unknown to this version of `target-spec`. /// /// # Notes /// /// In the future, this constructor may also support custom platforms. This will not be /// considered a breaking change. pub fn build_target() -> Result { let triple = Triple::new(BUILD_TARGET).map_err(Error::UnknownPlatformTriple)?; let target_features = TargetFeatures::features(BUILD_TARGET_FEATURES.iter().copied()); Ok(Self { triple, target_features, flags: BTreeSet::new(), }) } /// Creates a new standard platform from a `Triple` and target features. pub fn from_triple(triple: Triple, target_features: TargetFeatures) -> Self { Self { triple, target_features, flags: BTreeSet::new(), } } /// Creates a new custom `Platform` from the given triple, platform, and target features. #[cfg(feature = "custom")] pub fn new_custom( triple_str: impl Into>, json: &str, target_features: TargetFeatures, ) -> Result { let triple = Triple::new_custom(triple_str, json).map_err(Error::CustomPlatformCreate)?; Ok(Self { triple, target_features, flags: BTreeSet::new(), }) } /// Adds a set of flags to accept. /// /// A flag is a single token like the `foo` in `cfg(not(foo))`. /// /// A default `cargo build` will always evaluate flags to false, but custom wrappers may cause /// some flags to evaluate to true. For example, as of version 0.6, `cargo web build` will cause /// `cargo_web` to evaluate to true. pub fn add_flags(&mut self, flags: impl IntoIterator>>) { self.flags.extend(flags.into_iter().map(|s| s.into())); } /// Returns the target triple string for this platform. pub fn triple_str(&self) -> &str { self.triple.as_str() } /// Returns the set of flags enabled for this platform. pub fn flags(&self) -> impl ExactSizeIterator { self.flags.iter().map(|flag| flag.deref()) } /// Returns true if this flag was set with `add_flags`. pub fn has_flag(&self, flag: impl AsRef) -> bool { self.flags.contains(flag.as_ref()) } /// Returns true if this is a standard platform. /// /// A standard platform can be either builtin, or heuristically determined. /// /// # Examples /// /// ``` /// use target_spec::{Platform, TargetFeatures}; /// /// // x86_64-unknown-linux-gnu is Linux x86_64. /// let platform = Platform::new("x86_64-unknown-linux-gnu", TargetFeatures::Unknown).unwrap(); /// assert!(platform.is_standard()); /// ``` pub fn is_standard(&self) -> bool { self.triple.is_standard() } /// Returns true if this is a builtin platform. /// /// All builtin platforms are standard, but not all standard platforms are builtin. /// /// # Examples /// /// ``` /// use target_spec::{Platform, TargetFeatures}; /// /// // x86_64-unknown-linux-gnu is Linux x86_64, which is a Rust tier 1 platform. /// let platform = Platform::new("x86_64-unknown-linux-gnu", TargetFeatures::Unknown).unwrap(); /// assert!(platform.is_builtin()); /// ``` pub fn is_builtin(&self) -> bool { self.triple.is_builtin() } /// Returns true if this is a heuristically determined platform. /// /// All heuristically determined platforms are standard, but most of the time, standard /// platforms are builtin. /// /// # Examples /// /// ``` /// use target_spec::{Platform, TargetFeatures}; /// /// // armv5te-apple-darwin is not a real platform, but target-spec can heuristically /// // guess at its characteristics. /// let platform = Platform::new("armv5te-apple-darwin", TargetFeatures::Unknown).unwrap(); /// assert!(platform.is_heuristic()); /// ``` pub fn is_heuristic(&self) -> bool { self.triple.is_heuristic() } /// Returns true if this is a custom platform. /// /// This is always available, but if the `custom` feature isn't turned on this always returns /// false. pub fn is_custom(&self) -> bool { self.triple.is_custom() } /// Returns the underlying [`Triple`]. pub fn triple(&self) -> &Triple { &self.triple } /// Returns the set of target features for this platform. pub fn target_features(&self) -> &TargetFeatures { &self.target_features } #[cfg(feature = "summaries")] pub(crate) fn custom_json(&self) -> Option<&str> { self.triple.custom_json() } } /// A set of target features to match. #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[non_exhaustive] pub enum TargetFeatures { /// The target features are unknown. Unknown, /// Only match the specified features. Features(BTreeSet>), /// Match all features. All, } impl TargetFeatures { /// Creates a new `TargetFeatures` which matches some features. pub fn features(features: impl IntoIterator>>) -> Self { TargetFeatures::Features(features.into_iter().map(|s| s.into()).collect()) } /// Creates a new `TargetFeatures` which doesn't match any features. pub fn none() -> Self { TargetFeatures::Features(BTreeSet::new()) } /// Returns `Some(true)` if this feature is a match, `Some(false)` if it isn't, and `None` if /// the set of target features is unknown. pub fn matches(&self, feature: &str) -> Option { match self { TargetFeatures::Unknown => None, TargetFeatures::Features(features) => Some(features.contains(feature)), TargetFeatures::All => Some(true), } } } target-spec-3.5.7/src/proptest_helpers.rs000064400000000000000000000071621046102023000166230ustar 00000000000000// Copyright (c) The cargo-guppy Contributors // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::{Platform, TargetFeatures}; use cfg_expr::targets::ALL_BUILTINS; use proptest::{collection::btree_set, prelude::*, sample::select}; use std::borrow::Cow; /// ## Helpers for property testing /// /// The methods in this section allow `Platform` instances to be used in property-based testing /// scenarios. /// /// Currently, [proptest 1](https://docs.rs/proptest/1) is supported if the `proptest1` /// feature is enabled. impl Platform { /// Given a way to generate `TargetFeatures` instances, this returns a `Strategy` that generates /// a platform at random. /// /// Requires the `proptest1` feature to be enabled. /// /// ## Examples /// /// ``` /// use proptest::prelude::*; /// use target_spec::{Platform, TargetFeatures}; /// /// // target_features is a strategy that always produces TargetFeatures::Unknown. /// let target_features = Just(TargetFeatures::Unknown); /// let strategy = Platform::strategy(target_features); /// ``` pub fn strategy( target_features: impl Strategy, ) -> impl Strategy { let flags = btree_set(flag_strategy(), 0..3); (0..ALL_BUILTINS.len(), target_features, flags).prop_map(|(idx, target_features, flags)| { let mut platform = Platform::new( ALL_BUILTINS[idx].triple.as_str().to_owned(), target_features, ) .expect("known triple"); platform.add_flags(flags); platform }) } /// A version of `strategy` that allows target triples to be filtered. /// /// Requires the `proptest1` feature to be enabled. pub fn filtered_strategy( triple_filter: impl Fn(&'static str) -> bool, target_features: impl Strategy, ) -> impl Strategy { let filtered: Vec<_> = ALL_BUILTINS .iter() .filter(|target_info| triple_filter(target_info.triple.as_str())) .collect(); let flags = btree_set(flag_strategy(), 0..3); (0..filtered.len(), target_features, flags).prop_map( move |(idx, target_features, flags)| { let mut platform = Platform::new(filtered[idx].triple.as_str(), target_features) .expect("known triple"); platform.add_flags(flags); platform }, ) } } /// Picks a random flag from a list of known flags. pub fn flag_strategy() -> impl Strategy { static KNOWN_FLAGS: &[&str] = &["cargo_web", "test-flag", "abc", "foo", "bar", "flag-test"]; select(KNOWN_FLAGS) } /// The `Arbitrary` implementation for `TargetFeatures` uses a predefined list of features. impl Arbitrary for TargetFeatures { type Parameters = (); type Strategy = BoxedStrategy; fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { // https://doc.rust-lang.org/reference/attributes/codegen.html#available-features static KNOWN_FEATURES: &[&str] = &[ "aes", "avx", "avx2", "bmi1", "bmi2", "fma", "rdrand", "sha", "sse", "sse2", "sse3", "sse4.1", "sse4.2", "ssse3", "xsave", "xsavec", "xsaveopt", "xsaves", ]; let known_features_strategy = select(KNOWN_FEATURES).prop_map(Cow::Borrowed); prop_oneof![ Just(TargetFeatures::Unknown), Just(TargetFeatures::All), btree_set(known_features_strategy, 0..8).prop_map(TargetFeatures::Features), ] .boxed() } } target-spec-3.5.7/src/simple_eval.rs000064400000000000000000000161211046102023000155140ustar 00000000000000// Copyright (c) The cargo-guppy Contributors // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::{ Error, TargetSpec, platform::{Platform, TargetFeatures}, }; /// Evaluates the given spec against the provided target and returns `Some(true)` on a successful /// match, and `Some(false)` on a failing match. /// /// This defaults to treating target features as unknown, and returns `None` if the overall result /// is unknown. /// /// For more advanced uses, see [`TargetSpec::eval`]. /// /// For more information, see the crate-level documentation. pub fn eval(spec_or_triple: &str, platform: &str) -> Result, Error> { let target_spec = spec_or_triple.parse::()?; let platform = Platform::new(platform.to_owned(), TargetFeatures::Unknown)?; Ok(target_spec.eval(&platform)) } #[cfg(test)] mod tests { use super::*; #[test] fn test_windows() { assert_eq!( eval("cfg(windows)", "x86_64-pc-windows-msvc").unwrap(), Some(true), ); } #[test] fn test_not_target_os() { assert_eq!( eval( "cfg(not(target_os = \"windows\"))", "x86_64-unknown-linux-gnu" ) .unwrap(), Some(true), ); } #[test] fn test_not_target_os_false() { assert_eq!( eval( "cfg(not(target_os = \"windows\"))", "x86_64-pc-windows-msvc" ) .unwrap(), Some(false), ); } #[test] fn test_exact_triple() { assert_eq!( eval("x86_64-unknown-linux-gnu", "x86_64-unknown-linux-gnu").unwrap(), Some(true), ); } #[test] fn test_redox() { assert_eq!( eval( "cfg(any(unix, target_os = \"redox\"))", "x86_64-unknown-linux-gnu" ) .unwrap(), Some(true), ); } #[test] fn test_bogus_families() { // Known bogus families. for family in &["test", "debug_assertions", "proc_macro"] { let cfg = format!("cfg({family})"); let cfg_not = format!("cfg(not({family}))"); assert_eq!(eval(&cfg, "x86_64-unknown-linux-gnu").unwrap(), Some(false)); assert_eq!( eval(&cfg_not, "x86_64-unknown-linux-gnu").unwrap(), Some(true) ); } // Unknown bogus families. let platform = Platform::new("x86_64-unknown-linux-gnu", TargetFeatures::Unknown).unwrap(); let mut platform_with_flags = platform.clone(); platform_with_flags.add_flags(["foo", "bar"].iter().copied()); for family in &["foo", "bar"] { let cfg = format!("cfg({family})"); let cfg_not = format!("cfg(not({family}))"); // eval always means flags are evaluated to false. assert_eq!(eval(&cfg, "x86_64-unknown-linux-gnu").unwrap(), Some(false)); assert_eq!( eval(&cfg_not, "x86_64-unknown-linux-gnu").unwrap(), Some(true) ); let spec: TargetSpec = cfg.parse().unwrap(); let spec_not: TargetSpec = cfg_not.parse().unwrap(); // flag missing means false. assert_eq!(spec.eval(&platform), Some(false)); assert_eq!(spec_not.eval(&platform), Some(true)); // flag present means true. assert_eq!(spec.eval(&platform_with_flags), Some(true)); assert_eq!(spec_not.eval(&platform_with_flags), Some(false)); } for family in &["baz", "nonsense"] { let cfg = format!("cfg({family})"); let cfg_not = format!("cfg(not({family}))"); // eval always means flags are evaluated to false. assert_eq!(eval(&cfg, "x86_64-unknown-linux-gnu").unwrap(), Some(false)); assert_eq!( eval(&cfg_not, "x86_64-unknown-linux-gnu").unwrap(), Some(true) ); let spec: TargetSpec = cfg.parse().unwrap(); let spec_not: TargetSpec = cfg_not.parse().unwrap(); // flag missing means false. assert_eq!(spec.eval(&platform), Some(false)); assert_eq!(spec_not.eval(&platform), Some(true)); // flag still missing means false. assert_eq!(spec.eval(&platform_with_flags), Some(false)); assert_eq!(spec_not.eval(&platform_with_flags), Some(true)); } } #[test] fn test_target_feature() { // target features are unknown by default. assert_eq!( eval("cfg(target_feature = \"sse\")", "x86_64-unknown-linux-gnu").unwrap(), None, ); assert_eq!( eval( "cfg(target_feature = \"atomics\")", "x86_64-unknown-linux-gnu", ) .unwrap(), None, ); assert_eq!( eval( "cfg(not(target_feature = \"fxsr\"))", "x86_64-unknown-linux-gnu", ) .unwrap(), None, ); fn eval_unknown(spec: &str, platform: &str) -> Option { let platform = Platform::new( platform.to_owned(), TargetFeatures::features(["sse", "sse2"].iter().copied()), ) .expect("platform should be found"); let spec: TargetSpec = spec.parse().unwrap(); spec.eval(&platform) } assert_eq!( eval_unknown("cfg(target_feature = \"sse\")", "x86_64-unknown-linux-gnu"), Some(true), ); assert_eq!( eval_unknown( "cfg(not(target_feature = \"sse\"))", "x86_64-unknown-linux-gnu", ), Some(false), ); assert_eq!( eval_unknown("cfg(target_feature = \"fxsr\")", "x86_64-unknown-linux-gnu"), Some(false), ); assert_eq!( eval_unknown( "cfg(not(target_feature = \"fxsr\"))", "x86_64-unknown-linux-gnu", ), Some(true), ); fn eval_all(spec: &str, platform: &str) -> Option { let platform = Platform::new(platform.to_owned(), TargetFeatures::All) .expect("platform should be found"); let spec: TargetSpec = spec.parse().unwrap(); spec.eval(&platform) } assert_eq!( eval_all("cfg(target_feature = \"sse\")", "x86_64-unknown-linux-gnu"), Some(true), ); assert_eq!( eval_all( "cfg(not(target_feature = \"sse\"))", "x86_64-unknown-linux-gnu", ), Some(false), ); assert_eq!( eval_all("cfg(target_feature = \"fxsr\")", "x86_64-unknown-linux-gnu"), Some(true), ); assert_eq!( eval_all( "cfg(not(target_feature = \"fxsr\"))", "x86_64-unknown-linux-gnu", ), Some(false), ); } } target-spec-3.5.7/src/spec.rs000064400000000000000000000374361046102023000141620ustar 00000000000000// Copyright (c) The cargo-guppy Contributors // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::{ Error, Platform, Triple, errors::{ExpressionParseError, PlainStringParseError}, }; use cfg_expr::{Expression, Predicate}; use std::{borrow::Cow, fmt, str::FromStr, sync::Arc}; /// A parsed target specification or triple string, as found in a `Cargo.toml` file. /// /// ## Expressions and triple strings /// /// Cargo supports [platform-specific /// dependencies](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#platform-specific-dependencies). /// These dependencies can be specified in one of two ways: /// /// ```toml /// # 1. As Rust-like `#[cfg]` syntax. /// [target.'cfg(all(unix, target_arch = "x86_64"))'.dependencies] /// native = { path = "native/x86_64" } /// /// # 2. Listing out the full target triple. /// [target.x86_64-pc-windows-gnu.dependencies] /// winhttp = "0.4.0" /// ``` /// /// ### The `cfg()` syntax /// /// The first [`cfg()` syntax](https://doc.rust-lang.org/reference/conditional-compilation.html) is /// represented via the [`TargetSpec::Expression`] variant. This variant represents an arbitrary /// expression against certain properties of the target. To evaluate a [`Platform`] against this /// variant, `target-spec` builds an expression tree (currently via /// [`cfg-expr`](https://github.com/EmbarkStudios/cfg-expr)). /// /// ### Plain string syntax /// /// The second syntax, listing just the name of a platform, is represented via the /// [`TargetSpec::PlainString`] variant. In target-spec's model, the contained data is arbitrary /// and used only for string comparisons. For example, /// `TargetSpec::PlainString("x86_64-pc-windows-gnu")` will match the `x86_64-pc-windows-gnu` /// platform. /// /// `target-spec` checks that the string looks sufficiently like a triple. This check is duplicated /// from Cargo's own check and is implemented in [`Self::looks_like_plain_string`]. /// /// ## Examples /// /// ``` /// use target_spec::{Platform, TargetFeatures, TargetSpec}; /// /// let i686_windows = Platform::new("i686-pc-windows-gnu", TargetFeatures::Unknown).unwrap(); /// let x86_64_mac = Platform::new("x86_64-apple-darwin", TargetFeatures::none()).unwrap(); /// let i686_linux = Platform::new( /// "i686-unknown-linux-gnu", /// TargetFeatures::features(["sse2"].iter().copied()), /// ).unwrap(); /// /// let spec: TargetSpec = "cfg(any(windows, target_arch = \"x86_64\"))".parse().unwrap(); /// assert_eq!(spec.eval(&i686_windows), Some(true), "i686 Windows"); /// assert_eq!(spec.eval(&x86_64_mac), Some(true), "x86_64 MacOS"); /// assert_eq!(spec.eval(&i686_linux), Some(false), "i686 Linux (should not match)"); /// /// let spec: TargetSpec = "cfg(any(target_feature = \"sse2\", target_feature = \"sse\"))".parse().unwrap(); /// assert_eq!(spec.eval(&i686_windows), None, "i686 Windows features are unknown"); /// assert_eq!(spec.eval(&x86_64_mac), Some(false), "x86_64 MacOS matches no features"); /// assert_eq!(spec.eval(&i686_linux), Some(true), "i686 Linux matches some features"); /// ``` #[derive(Clone, Debug)] pub enum TargetSpec { /// A complex expression. /// /// Parsed from strings like `"cfg(any(windows, target_arch = \"x86_64\"))"`. Expression(TargetSpecExpression), /// A plain string representing a triple. /// /// This string hasn't been validated because it may represent a custom platform. To validate /// this string, use [`Self::is_known`]. PlainString(TargetSpecPlainString), } impl TargetSpec { /// Creates a new target spec from a string. pub fn new(input: impl Into>) -> Result { let input = input.into(); if Self::looks_like_expression(&input) { match TargetSpecExpression::new(&input) { Ok(expression) => Ok(Self::Expression(expression)), Err(error) => Err(Error::InvalidExpression(error)), } } else { match TargetSpecPlainString::new(input) { Ok(plain_str) => Ok(Self::PlainString(plain_str)), Err(error) => Err(Error::InvalidTargetSpecString(error)), } } } /// Returns true if the input will be parsed as a target expression. /// /// In other words, returns true if the input begins with `"cfg("`. pub fn looks_like_expression(input: &str) -> bool { input.starts_with("cfg(") } /// Returns true if the input will be understood to be a plain string. /// /// This check is borrowed from /// [`cargo-platform`](https://github.com/rust-lang/cargo/blob/5febbe5587b74108165f748e79a4f8badbdf5e0e/crates/cargo-platform/src/lib.rs#L40-L63). /// /// Note that this currently accepts an empty string. This matches Cargo's behavior as of Rust /// 1.70. pub fn looks_like_plain_string(input: &str) -> bool { TargetSpecPlainString::validate(input).is_ok() } /// Returns true if an input looks like it's known and understood. /// /// * For [`Self::Expression`], the inner [`TargetSpecExpression`] has already been parsed as /// valid, so this returns true. /// * For [`Self::PlainString`], this returns true if the string matches a builtin triple or has /// heuristically been determined to be valid. /// /// This method does not take into account custom platforms. If you know about custom platforms, /// consider checking against those as well. pub fn is_known(&self) -> bool { match self { TargetSpec::PlainString(plain_str) => { Triple::new(plain_str.as_str().to_owned()).is_ok() } TargetSpec::Expression(_) => true, } } /// Evaluates this specification against the given platform. /// /// Returns `Some(true)` if there's a match, `Some(false)` if there's none, or `None` if the /// result of the evaluation is unknown (typically found if target features are involved). #[inline] pub fn eval(&self, platform: &Platform) -> Option { match self { TargetSpec::PlainString(plain_str) => Some(platform.triple_str() == plain_str.as_str()), TargetSpec::Expression(expr) => expr.eval(platform), } } } impl FromStr for TargetSpec { type Err = Error; fn from_str(input: &str) -> Result { Self::new(input.to_owned()) } } impl fmt::Display for TargetSpec { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { TargetSpec::Expression(expr) => write!(f, "{expr}"), TargetSpec::PlainString(plain_str) => write!(f, "{plain_str}"), } } } /// A target expression, parsed from a string beginning with `cfg(`. /// /// For more information, see [`TargetSpec`]. #[derive(Clone, Debug)] pub struct TargetSpecExpression { inner: Arc, } impl TargetSpecExpression { /// Creates a new `TargetSpecExpression` from a string beginning with `cfg(`. /// /// Returns an error if the string could not be parsed, or if the string contains a predicate /// that wasn't understood by `target-spec`. pub fn new(input: &str) -> Result { let expr = Expression::parse(input).map_err(|err| ExpressionParseError::new(input, err))?; Ok(Self { inner: Arc::new(expr), }) } /// Returns the string that was parsed into `self`. #[inline] pub fn expression_str(&self) -> &str { self.inner.original() } /// Evaluates this expression against the given platform. /// /// Returns `Some(true)` if there's a match, `Some(false)` if there's none, or `None` if the /// result of the evaluation is unknown (typically found if target features are involved). pub fn eval(&self, platform: &Platform) -> Option { self.inner.eval(|pred| { match pred { Predicate::Target(target) => Some(platform.triple().matches(target)), Predicate::TargetFeature(feature) => platform.target_features().matches(feature), Predicate::Test | Predicate::DebugAssertions | Predicate::ProcMacro => { // Known families that always evaluate to false. See // https://docs.rs/cargo-platform/0.1.1/src/cargo_platform/lib.rs.html#76. Some(false) } Predicate::Feature(_) => { // NOTE: This is not supported by Cargo which always evaluates this to false. See // https://github.com/rust-lang/cargo/issues/7442 for more details. Some(false) } Predicate::Flag(flag) => { // This returns false by default but true in some cases. Some(platform.has_flag(flag)) } Predicate::KeyValue { .. } => { // This is always interpreted by Cargo as false. Some(false) } } }) } } impl FromStr for TargetSpecExpression { type Err = ExpressionParseError; fn from_str(input: &str) -> Result { Self::new(input) } } impl fmt::Display for TargetSpecExpression { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.expression_str()) } } /// A plain string as contained within a [`TargetSpec::PlainString`]. /// /// For more information, see [`TargetSpec`]. #[derive(Clone, Debug)] pub struct TargetSpecPlainString(Cow<'static, str>); impl TargetSpecPlainString { /// Creates a new instance of `TargetSpecPlainString`, after validating it. pub fn new(input: impl Into>) -> Result { let input = input.into(); Self::validate(&input)?; Ok(Self(input)) } /// Returns the string as parsed. pub fn as_str(&self) -> &str { &self.0 } fn validate(input: &str) -> Result<(), PlainStringParseError> { if let Some((char_index, c)) = input .char_indices() .find(|&(_, c)| !(c.is_alphanumeric() || c == '_' || c == '-' || c == '.')) { Err(PlainStringParseError::new(input.to_owned(), char_index, c)) } else { Ok(()) } } } impl FromStr for TargetSpecPlainString { type Err = PlainStringParseError; fn from_str(input: &str) -> Result { Self::new(input.to_owned()) } } impl fmt::Display for TargetSpecPlainString { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&self.0) } } #[cfg(test)] mod tests { use super::*; use cfg_expr::{ Predicate, TargetPredicate, targets::{Abi, Arch, Family, Os}, }; #[test] fn test_triple() { let res = TargetSpec::new("x86_64-apple-darwin"); assert!(matches!( res, Ok(TargetSpec::PlainString(triple)) if triple.as_str() == "x86_64-apple-darwin" )); } #[test] fn test_single() { let expr = match TargetSpec::new("cfg(windows)").unwrap() { TargetSpec::PlainString(triple) => { panic!("expected expression, got triple: {triple:?}") } TargetSpec::Expression(expr) => expr, }; assert_eq!( expr.inner.predicates().collect::>(), vec![Predicate::Target(TargetPredicate::Family(Family::windows))], ); } #[test] fn test_target_abi() { let expr = match TargetSpec::new("cfg(any(target_arch = \"wasm32\", target_abi = \"unknown\"))") .unwrap() { TargetSpec::PlainString(triple) => { panic!("expected expression, got triple: {triple:?}") } TargetSpec::Expression(expr) => expr, }; assert_eq!( expr.inner.predicates().collect::>(), vec![ Predicate::Target(TargetPredicate::Arch(Arch("wasm32".into()))), Predicate::Target(TargetPredicate::Abi(Abi("unknown".into()))), ], ); } #[test] fn test_not() { assert!(matches!( TargetSpec::new("cfg(not(windows))"), Ok(TargetSpec::Expression(_)) )); } #[test] fn test_testequal() { let expr = match TargetSpec::new("cfg(target_os = \"windows\")").unwrap() { TargetSpec::PlainString(triple) => { panic!("expected spec, got triple: {triple:?}") } TargetSpec::Expression(expr) => expr, }; assert_eq!( expr.inner.predicates().collect::>(), vec![Predicate::Target(TargetPredicate::Os(Os::windows))], ); } #[test] fn test_identifier_like_triple() { // This used to be "x86_64-pc-darwin", but target-lexicon can parse that. let spec = TargetSpec::new("cannotbeknown") .expect("triples that look like identifiers should parse correctly"); assert!(!spec.is_known(), "spec isn't known"); } #[test] fn test_triple_string_identifier() { // We generally trust that unicode-ident is correct. Just do some basic checks. let valid = ["", "foo", "foo-bar", "foo_baz", "-foo", "quux-"]; let invalid = ["foo+bar", "foo bar", " "]; for input in valid { assert!( TargetSpec::looks_like_plain_string(input), "`{input}` looks like triple string" ); } for input in invalid { assert!( !TargetSpec::looks_like_plain_string(input), "`{input}` does not look like triple string" ); } } #[test] fn test_unknown_triple() { // This used to be "x86_64-pc-darwin", but target-lexicon can parse that. let err = TargetSpec::new("cannotbeknown!!!") .expect_err("unknown triples should parse correctly"); assert!(matches!( err, Error::InvalidTargetSpecString(s) if s.input == "cannotbeknown!!!" )); } #[test] fn test_unknown_flag() { let expr = match TargetSpec::new("cfg(foo)").unwrap() { TargetSpec::PlainString(triple) => { panic!("expected spec, got triple: {triple:?}") } TargetSpec::Expression(expr) => expr, }; assert_eq!( expr.inner.predicates().collect::>(), vec![Predicate::Flag("foo")], ); } #[test] fn test_unknown_predicate() { let expr = match TargetSpec::new("cfg(bogus_key = \"bogus_value\")") .expect("unknown predicate should parse") { TargetSpec::PlainString(triple) => { panic!("expected spec, got triple: {triple:?}") } TargetSpec::Expression(expr) => expr, }; assert_eq!( expr.inner.predicates().collect::>(), vec![Predicate::KeyValue { key: "bogus_key", val: "bogus_value" }], ); let platform = Platform::build_target().unwrap(); // This should always evaluate to false. assert_eq!(expr.eval(&platform), Some(false)); let expr = TargetSpec::new("cfg(not(bogus_key = \"bogus_value\"))") .expect("unknown predicate should parse"); // This is a cfg(not()), so it should always evaluate to true. assert_eq!(expr.eval(&platform), Some(true)); } #[test] fn test_extra() { let res = TargetSpec::new("cfg(unix)this-is-extra"); res.expect_err("extra content at the end"); } #[test] fn test_incomplete() { // This fails because the ) at the end is missing. let res = TargetSpec::new("cfg(not(unix)"); res.expect_err("missing ) at the end"); } } target-spec-3.5.7/src/summaries.rs000064400000000000000000000357011046102023000152260ustar 00000000000000// Copyright (c) The cargo-guppy Contributors // SPDX-License-Identifier: MIT OR Apache-2.0 //! Serialized versions of platform and target features. //! //! Some users of `target-spec` may want to serialize and deserialize its data structures into, say, //! TOML files. This module provides facilities for that. //! //! Summaries require the `summaries` feature to be enabled. use crate::{Error, Platform, TargetFeatures}; use serde::{Deserialize, Serialize}; use std::{borrow::Cow, collections::BTreeSet}; impl Platform { /// Converts this `Platform` to a serializable form. /// /// Requires the `summaries` feature to be enabled. #[inline] pub fn to_summary(&self) -> PlatformSummary { PlatformSummary::from_platform(self) } } /// An owned, serializable version of [`Platform`]. /// /// This structure can be serialized and deserialized using `serde`. /// /// Requires the `summaries` feature to be enabled. #[derive(Clone, Debug, Eq, PartialEq, Serialize)] #[serde(rename_all = "kebab-case")] #[non_exhaustive] pub struct PlatformSummary { /// The platform triple. pub triple: String, /// JSON for custom platforms. #[serde(skip_serializing_if = "Option::is_none", default)] pub custom_json: Option, /// The target features used. pub target_features: TargetFeaturesSummary, /// The flags enabled. #[serde(skip_serializing_if = "BTreeSet::is_empty", default)] pub flags: BTreeSet, } impl PlatformSummary { /// Creates a new `PlatformSummary` with the provided triple and default options. /// /// The default options are: /// /// * `custom_json` is set to None. /// * `target_features` is set to [`TargetFeaturesSummary::Unknown`]. /// * `flags` is empty. pub fn new(triple_str: impl Into) -> Self { Self { triple: triple_str.into(), custom_json: None, target_features: TargetFeaturesSummary::Unknown, flags: BTreeSet::new(), } } /// If this represents a custom platform, sets the target definition JSON for it. /// /// For more about target definition JSON, see [Creating a custom /// target](https://docs.rust-embedded.org/embedonomicon/custom-target.html) on the Rust /// Embedonomicon. pub fn with_custom_json(mut self, custom_json: impl Into) -> Self { self.custom_json = Some(custom_json.into()); self } /// Sets the target features for this platform. pub fn with_target_features(mut self, target_features: TargetFeaturesSummary) -> Self { self.target_features = target_features; self } /// Adds flags for this platform. pub fn with_added_flags(mut self, flags: impl IntoIterator>) -> Self { self.flags.extend(flags.into_iter().map(|flag| flag.into())); self } /// Creates a new `PlatformSummary` instance from a platform. pub fn from_platform(platform: &Platform) -> Self { Self { triple: platform.triple_str().to_string(), custom_json: platform.custom_json().map(|s| s.to_owned()), target_features: TargetFeaturesSummary::new(platform.target_features()), flags: platform.flags().map(|flag| flag.to_string()).collect(), } } /// Converts `self` to a `Platform`. /// /// Returns an `Error` if the platform was unknown. pub fn to_platform(&self) -> Result { #[allow(unused_variables)] // in some feature branches, json isn't used let mut platform = if let Some(json) = &self.custom_json { #[cfg(not(feature = "custom"))] return Err(Error::CustomPlatformCreate( crate::errors::CustomTripleCreateError::Unavailable, )); #[cfg(feature = "custom")] Platform::new_custom( self.triple.to_owned(), json, self.target_features.to_target_features(), )? } else { Platform::new( self.triple.to_owned(), self.target_features.to_target_features(), )? }; platform.add_flags(self.flags.iter().cloned()); Ok(platform) } } /// An owned, serializable version of [`TargetFeatures`]. /// /// This type can be serialized and deserialized using `serde`. /// /// Requires the `summaries` feature to be enabled. #[derive(Clone, Debug, Eq, PartialEq)] #[non_exhaustive] #[derive(Default)] pub enum TargetFeaturesSummary { /// The target features are unknown. /// /// This is the default. #[default] Unknown, /// Only match the specified features. Features(BTreeSet), /// Match all features. All, } impl TargetFeaturesSummary { /// Creates a new `TargetFeaturesSummary` from a `TargetFeatures`. pub fn new(target_features: &TargetFeatures) -> Self { match target_features { TargetFeatures::Unknown => TargetFeaturesSummary::Unknown, TargetFeatures::Features(features) => TargetFeaturesSummary::Features( features.iter().map(|feature| feature.to_string()).collect(), ), TargetFeatures::All => TargetFeaturesSummary::All, } } /// Converts `self` to a `TargetFeatures` instance. pub fn to_target_features(&self) -> TargetFeatures { match self { TargetFeaturesSummary::Unknown => TargetFeatures::Unknown, TargetFeaturesSummary::All => TargetFeatures::All, TargetFeaturesSummary::Features(features) => { let features = features .iter() .map(|feature| Cow::Owned(feature.clone())) .collect(); TargetFeatures::Features(features) } } } } mod platform_impl { use super::*; use serde::Deserializer; impl<'de> Deserialize<'de> for PlatformSummary { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { let d = PlatformSummaryDeserialize::deserialize(deserializer)?; match d { PlatformSummaryDeserialize::String(triple) => Ok(PlatformSummary { triple, custom_json: None, target_features: TargetFeaturesSummary::default(), flags: BTreeSet::default(), }), PlatformSummaryDeserialize::Full { triple, custom_json, target_features, flags, } => Ok(PlatformSummary { triple, custom_json, target_features, flags, }), } } } #[derive(Deserialize)] #[serde(untagged)] enum PlatformSummaryDeserialize { String(String), #[serde(rename_all = "kebab-case")] Full { triple: String, #[serde(default)] custom_json: Option, /// The target features used. #[serde(default)] target_features: TargetFeaturesSummary, /// The flags enabled. #[serde(skip_serializing_if = "BTreeSet::is_empty", default)] flags: BTreeSet, }, } } mod target_features_impl { use super::*; use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error}; impl Serialize for TargetFeaturesSummary { fn serialize(&self, serializer: S) -> Result where S: Serializer, { match self { TargetFeaturesSummary::Unknown => "unknown".serialize(serializer), TargetFeaturesSummary::All => "all".serialize(serializer), TargetFeaturesSummary::Features(features) => features.serialize(serializer), } } } impl<'de> Deserialize<'de> for TargetFeaturesSummary { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { let d = TargetFeaturesDeserialize::deserialize(deserializer)?; match d { TargetFeaturesDeserialize::String(target_features) => { match target_features.as_str() { "unknown" => Ok(TargetFeaturesSummary::Unknown), "all" => Ok(TargetFeaturesSummary::All), other => Err(D::Error::custom(format!( "unknown string for target features: {other}", ))), } } TargetFeaturesDeserialize::List(target_features) => { Ok(TargetFeaturesSummary::Features(target_features)) } } } } #[derive(Deserialize)] #[serde(untagged)] enum TargetFeaturesDeserialize { String(String), List(BTreeSet), } } #[cfg(test)] mod tests { #![allow(clippy::vec_init_then_push)] use super::*; #[test] fn platform_deserialize_valid() { // Need a wrapper because of TOML restrictions #[derive(Debug, Deserialize, Serialize, Eq, PartialEq)] struct Wrapper { platform: PlatformSummary, } let mut valid = vec![]; valid.push(( r#"platform = "x86_64-unknown-linux-gnu""#, PlatformSummary { triple: "x86_64-unknown-linux-gnu".into(), custom_json: None, target_features: TargetFeaturesSummary::Unknown, flags: BTreeSet::new(), }, )); valid.push(( r#"platform = { triple = "x86_64-unknown-linux-gnu" }"#, PlatformSummary { triple: "x86_64-unknown-linux-gnu".into(), custom_json: None, target_features: TargetFeaturesSummary::Unknown, flags: BTreeSet::new(), }, )); valid.push(( r#"platform = { triple = "x86_64-unknown-linux-gnu", target-features = "unknown" }"#, PlatformSummary { triple: "x86_64-unknown-linux-gnu".into(), custom_json: None, target_features: TargetFeaturesSummary::Unknown, flags: BTreeSet::new(), }, )); valid.push(( r#"platform = { triple = "x86_64-unknown-linux-gnu", target-features = "all" }"#, PlatformSummary { triple: "x86_64-unknown-linux-gnu".into(), custom_json: None, target_features: TargetFeaturesSummary::All, flags: BTreeSet::new(), }, )); valid.push(( r#"platform = { triple = "x86_64-unknown-linux-gnu", target-features = [] }"#, PlatformSummary { triple: "x86_64-unknown-linux-gnu".into(), custom_json: None, target_features: TargetFeaturesSummary::Features(BTreeSet::new()), flags: BTreeSet::new(), }, )); let custom_json = r#"{"arch":"x86_64","target-pointer-width":"64","llvm-target":"x86_64-unknown-haiku","data-layout":"e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128","os":"haiku","abi":null,"env":null,"vendor":null,"families":[],"endian":"little","min-atomic-width":null,"max-atomic-width":64,"panic-strategy":"unwind"}"#; let toml = format!( r#"platform = {{ triple = "x86_64-unknown-haiku", custom-json = '{custom_json}' }}"# ); valid.push(( &toml, PlatformSummary { triple: "x86_64-unknown-haiku".into(), custom_json: Some(custom_json.to_owned()), target_features: TargetFeaturesSummary::Unknown, flags: BTreeSet::new(), }, )); let mut flags = BTreeSet::new(); flags.insert("cargo_web".to_owned()); valid.push(( r#"platform = { triple = "x86_64-unknown-linux-gnu", flags = ["cargo_web"] }"#, PlatformSummary { triple: "x86_64-unknown-linux-gnu".into(), custom_json: None, target_features: TargetFeaturesSummary::Unknown, flags, }, )); for (input, expected) in valid { let actual: Wrapper = toml::from_str(input).unwrap_or_else(|err| panic!("input {input} is valid: {err}")); assert_eq!(actual.platform, expected, "for input: {input}"); // Serialize and deserialize again. let serialized = toml::to_string(&actual).expect("serialized correctly"); let actual_2: Wrapper = toml::from_str(&serialized) .unwrap_or_else(|err| panic!("serialized input: {input} is valid: {err}")); assert_eq!(actual, actual_2, "for input: {input}"); // Check that custom JSON functionality works. if actual.platform.custom_json.is_some() { #[cfg(feature = "custom")] { let platform = actual .platform .to_platform() .expect("custom platform parsed successfully"); assert!(platform.is_custom(), "this is a custom platform"); } #[cfg(not(feature = "custom"))] { use crate::errors::CustomTripleCreateError; let error = actual .platform .to_platform() .expect_err("custom platforms are disabled"); assert!(matches!( error, Error::CustomPlatformCreate(CustomTripleCreateError::Unavailable) )); } } } } } #[cfg(all(test, feature = "proptest1"))] mod proptests { use super::*; use proptest::prelude::*; use std::collections::HashSet; proptest! { #[test] fn summary_roundtrip(platform in Platform::strategy(any::())) { let summary = PlatformSummary::from_platform(&platform); let serialized = toml::ser::to_string(&summary).expect("serialization succeeded"); let deserialized: PlatformSummary = toml::from_str(&serialized).expect("deserialization succeeded"); assert_eq!(summary, deserialized, "summary and deserialized should match"); let platform2 = deserialized.to_platform().expect("conversion to Platform succeeded"); assert_eq!(platform.triple_str(), platform2.triple_str(), "triples match"); assert_eq!(platform.target_features(), platform2.target_features(), "target features match"); assert_eq!(platform.flags().collect::>(), platform2.flags().collect::>(), "flags match"); } } } target-spec-3.5.7/src/triple.rs000064400000000000000000000365021046102023000145200ustar 00000000000000// Copyright (c) The cargo-guppy Contributors // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::{ Error, Platform, errors::{RustcVersionVerboseParseError, TripleParseError}, }; use cfg_expr::{ TargetPredicate, expr::TargetMatcher, target_lexicon, targets::{TargetInfo, get_builtin_target_by_triple}, }; use std::{borrow::Cow, cmp::Ordering, hash, str::FromStr}; /// A single, specific target, uniquely identified by a triple. /// /// A `Triple` may be constructed through `new` or the `FromStr` implementation. /// /// Every [`Platform`](crate::Platform) is backed by one of these. /// /// # Standard and custom platforms /// /// `target-spec` recognizes two kinds of platforms: /// /// * **Standard platforms:** These platforms are only specified by their triple string, either /// directly or via a [`Triple`]. For example, the platform `x86_64-unknown-linux-gnu` is a /// standard platform since it is recognized by Rust. /// /// All [builtin platforms](https://doc.rust-lang.org/nightly/rustc/platform-support.html) are /// standard platforms. /// /// By default, if a platform isn't builtin, target-spec attempts to heuristically determine the /// characteristics of the platform based on the triple string. (Use the /// [`new_strict`](Self::new_strict) constructor to disable this.) /// /// * **Custom platforms:** These platforms are specified via both a triple string and a JSON file /// in the format [defined by /// Rust](https://docs.rust-embedded.org/embedonomicon/custom-target.html). Custom platforms are /// used for targets not recognized by Rust. /// /// # Examples /// /// ``` /// use target_spec::Triple; /// /// // Parse a simple target. /// let target = Triple::new("x86_64-unknown-linux-gnu").unwrap(); /// // This is not a valid triple. /// let err = Triple::new("cannot-be-known").unwrap_err(); /// ``` #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)] pub struct Triple { inner: TripleInner, } impl Triple { /// Creates a new `Triple` from a triple string. pub fn new(triple_str: impl Into>) -> Result { let inner = TripleInner::new(triple_str.into())?; Ok(Self { inner }) } /// Creates a new `Triple` from a triple string. /// /// This constructor only consults the builtin platform table, and does not attempt to /// heuristically determine the platform's characteristics based on the triple string. pub fn new_strict(triple_str: impl Into>) -> Result { let inner = TripleInner::new_strict(triple_str.into())?; Ok(Self { inner }) } /// Creates a new standard `Triple` from `rustc -vV` output. /// /// # Example /// /// ``` /// // This is the typical output of `rustc -vV`. /// let output = b"rustc 1.84.1 (e71f9a9a9 2025-01-27) /// binary: rustc /// commit-hash: e71f9a9a98b0faf423844bf0ba7438f29dc27d58 /// commit-date: 2025-01-27 /// host: x86_64-unknown-linux-gnu /// release: 1.84.1 /// LLVM version: 19.1.5"; /// /// let triple = target_spec::Triple::from_rustc_version_verbose(output).unwrap(); /// assert_eq!(triple.as_str(), "x86_64-unknown-linux-gnu"); /// ``` pub fn from_rustc_version_verbose(output: impl AsRef<[u8]>) -> Result { let output_slice = output.as_ref(); let output = std::str::from_utf8(output_slice).map_err(|_| { // Get a better error message in this case via // std::string::FromUtf8Error. let output_vec = output_slice.to_vec(); Error::RustcVersionVerboseParse(RustcVersionVerboseParseError::InvalidUtf8( String::from_utf8(output_vec) .expect_err("we just failed to convert to UTF-8 above"), )) })?; // Look for the line beginning with `host: ` and extract the triple. // (This is a bit fragile, but it's what Cargo does.) let triple_str = output .lines() .find_map(|line| line.strip_prefix("host: ")) .ok_or_else(|| { Error::RustcVersionVerboseParse(RustcVersionVerboseParseError::MissingHostLine { output: output.to_owned(), }) })?; // Now look up the triple. Self::new(triple_str.to_owned()).map_err(Error::UnknownPlatformTriple) } /// Creates a new custom `Triple` from the given triple string and JSON specification. #[cfg(feature = "custom")] pub fn new_custom( triple_str: impl Into>, json: &str, ) -> Result { use crate::custom::TargetDefinition; let triple_str = triple_str.into(); let target_def: TargetDefinition = serde_json::from_str(json).map_err(|error| { crate::errors::CustomTripleCreateError::DeserializeJson { triple: triple_str.to_string(), input: json.to_string(), error: error.into(), } })?; #[cfg(feature = "summaries")] let minified_json = serde_json::to_string(&target_def).expect("serialization is infallible"); let target_info = Box::new(target_def.into_target_info(triple_str)); Ok(Self { inner: TripleInner::Custom { target_info, #[cfg(feature = "summaries")] json: minified_json, }, }) } /// Returns the string corresponding to this triple. #[inline] pub fn as_str(&self) -> &str { self.inner.as_str() } /// Returns true if this is a triple corresponding to a standard platform. /// /// A standard platform can be either builtin, or heuristically determined. /// /// # Examples /// /// ``` /// use target_spec::Triple; /// /// // x86_64-unknown-linux-gnu is Linux x86_64. /// let platform = Triple::new("x86_64-unknown-linux-gnu").unwrap(); /// assert!(platform.is_standard()); /// ``` pub fn is_standard(&self) -> bool { self.inner.is_standard() } /// Returns true if this is a triple corresponding to a builtin platform. /// /// # Examples /// /// ``` /// use target_spec::Triple; /// /// // x86_64-unknown-linux-gnu is Linux x86_64, which is a Rust tier 1 platform. /// let triple = Triple::new("x86_64-unknown-linux-gnu").unwrap(); /// assert!(triple.is_builtin()); /// ``` #[inline] pub fn is_builtin(&self) -> bool { self.inner.is_builtin() } /// Returns true if this triple was heuristically determined. /// /// All heuristically determined platforms are standard, but most of the time, standard /// platforms are builtin. /// /// # Examples /// /// ``` /// use target_spec::Triple; /// /// // armv5te-apple-darwin is not a real platform, but target-spec can heuristically /// // guess at its characteristics. /// let triple = Triple::new("armv5te-apple-darwin").unwrap(); /// assert!(triple.is_heuristic()); /// ``` pub fn is_heuristic(&self) -> bool { self.inner.is_heuristic() } /// Returns true if this is a custom platform. /// /// This is always available, but if the `custom` feature isn't turned on this always returns /// false. pub fn is_custom(&self) -> bool { self.inner.is_custom() } /// Evaluates this triple against the given platform. /// /// This simply compares `self`'s string representation against the `Triple` the platform is /// based on, ignoring target features and flags. #[inline] pub fn eval(&self, platform: &Platform) -> bool { self.as_str() == platform.triple_str() } // Use cfg-expr's target matcher. #[inline] pub(crate) fn matches(&self, tp: &TargetPredicate) -> bool { self.inner.matches(tp) } #[cfg(feature = "summaries")] pub(crate) fn custom_json(&self) -> Option<&str> { self.inner.custom_json() } } impl FromStr for Triple { type Err = TripleParseError; fn from_str(triple_str: &str) -> Result { let inner = TripleInner::from_borrowed_str(triple_str)?; Ok(Self { inner }) } } /// Inner representation of a triple. #[derive(Clone, Debug)] enum TripleInner { /// Prefer the builtin representation as it's more accurate. Builtin(&'static TargetInfo), /// A custom triple. #[cfg(feature = "custom")] Custom { target_info: Box, // The JSON is only needed if summaries are enabled. #[cfg(feature = "summaries")] json: String, }, /// Fall back to the lexicon representation. Lexicon { triple_str: Cow<'static, str>, lexicon_triple: target_lexicon::Triple, }, } impl TripleInner { fn new(triple_str: Cow<'static, str>) -> Result { // First try getting the builtin. if let Some(target_info) = get_builtin_target_by_triple(&triple_str) { return Ok(TripleInner::Builtin(target_info)); } // Next, try getting the lexicon representation. match triple_str.parse::() { Ok(lexicon_triple) => Ok(TripleInner::Lexicon { triple_str, lexicon_triple, }), Err(lexicon_err) => Err(TripleParseError::new(triple_str, lexicon_err)), } } fn new_strict(triple_str: Cow<'static, str>) -> Result { if let Some(target_info) = get_builtin_target_by_triple(&triple_str) { return Ok(TripleInner::Builtin(target_info)); } Err(TripleParseError::new_strict(triple_str)) } fn from_borrowed_str(triple_str: &str) -> Result { // First try getting the builtin. if let Some(target_info) = get_builtin_target_by_triple(triple_str) { return Ok(TripleInner::Builtin(target_info)); } // Next, try getting the lexicon representation. match triple_str.parse::() { Ok(lexicon_triple) => Ok(TripleInner::Lexicon { triple_str: triple_str.to_owned().into(), lexicon_triple, }), Err(lexicon_err) => Err(TripleParseError::new( triple_str.to_owned().into(), lexicon_err, )), } } fn is_standard(&self) -> bool { match self { TripleInner::Builtin(_) | TripleInner::Lexicon { .. } => true, #[cfg(feature = "custom")] TripleInner::Custom { .. } => false, } } fn is_builtin(&self) -> bool { match self { TripleInner::Builtin(_) => true, TripleInner::Lexicon { .. } => false, #[cfg(feature = "custom")] TripleInner::Custom { .. } => false, } } fn is_heuristic(&self) -> bool { match self { TripleInner::Builtin(_) => false, TripleInner::Lexicon { .. } => true, #[cfg(feature = "custom")] TripleInner::Custom { .. } => false, } } fn is_custom(&self) -> bool { match self { TripleInner::Builtin(_) | TripleInner::Lexicon { .. } => false, #[cfg(feature = "custom")] TripleInner::Custom { .. } => true, } } fn as_str(&self) -> &str { match self { TripleInner::Builtin(target_info) => target_info.triple.as_str(), #[cfg(feature = "custom")] TripleInner::Custom { target_info, .. } => target_info.triple.as_str(), TripleInner::Lexicon { triple_str, .. } => triple_str, } } fn matches(&self, tp: &TargetPredicate) -> bool { match self { TripleInner::Builtin(target_info) => target_info.matches(tp), #[cfg(feature = "custom")] TripleInner::Custom { target_info, .. } => target_info.matches(tp), TripleInner::Lexicon { lexicon_triple, .. } => lexicon_triple.matches(tp), } } #[cfg(feature = "summaries")] pub(crate) fn custom_json(&self) -> Option<&str> { match self { TripleInner::Builtin(_) => None, #[cfg(feature = "custom")] TripleInner::Custom { json, .. } => Some(json), TripleInner::Lexicon { .. } => None, } } fn project(&self) -> TripleInnerProjected<'_> { match self { TripleInner::Builtin(target_info) => { TripleInnerProjected::Builtin(target_info.triple.as_str()) } #[cfg(feature = "custom")] TripleInner::Custom { target_info, .. } => TripleInnerProjected::Custom(target_info), TripleInner::Lexicon { triple_str, .. } => TripleInnerProjected::Lexicon(triple_str), } } } /// This implementation is used for trait impls. #[derive(Eq, PartialEq, PartialOrd, Ord, Hash)] enum TripleInnerProjected<'a> { // Don't need anything else for builtin and lexicon since it's a pure function of the input. Builtin(&'a str), #[cfg(feature = "custom")] Custom(&'a TargetInfo), Lexicon(&'a str), } impl PartialEq for TripleInner { fn eq(&self, other: &Self) -> bool { self.project().eq(&other.project()) } } impl Eq for TripleInner {} impl PartialOrd for TripleInner { #[inline] fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for TripleInner { #[inline] fn cmp(&self, other: &Self) -> Ordering { self.project().cmp(&other.project()) } } impl hash::Hash for TripleInner { fn hash(&self, state: &mut H) { hash::Hash::hash(&self.project(), state); } } #[cfg(test)] mod tests { use super::*; use target_lexicon::*; #[test] fn test_parse() { let target = super::Triple::new("x86_64-pc-darwin").expect("this triple is known to target-lexicon"); let expected_triple = target_lexicon::Triple { architecture: Architecture::X86_64, vendor: Vendor::Pc, operating_system: OperatingSystem::Darwin(None), environment: Environment::Unknown, binary_format: BinaryFormat::Macho, }; let actual_triple = match target.inner { TripleInner::Lexicon { lexicon_triple, .. } => lexicon_triple, TripleInner::Builtin(_) => { panic!("should not have been able to parse x86_64-pc-darwin as a builtin"); } #[cfg(feature = "custom")] TripleInner::Custom { .. } => { panic!("not a custom platform") } }; assert_eq!( actual_triple, expected_triple, "lexicon triple matched correctly" ); } #[test] fn test_parse_rustc_version_verbose() { let rustc = std::env::var("RUSTC").unwrap_or_else(|_| "rustc".to_string()); let output = std::process::Command::new(rustc) .arg("-vV") .output() .expect("rustc -vV is successful"); if !output.status.success() { panic!("rustc -vV failed: {output:?}"); } let triple = super::Triple::from_rustc_version_verbose(output.stdout).unwrap(); assert!(triple.is_standard()); } }