diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/rust/cfg_aliases/src | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/cfg_aliases/src')
-rw-r--r-- | third_party/rust/cfg_aliases/src/lib.rs | 372 |
1 files changed, 372 insertions, 0 deletions
diff --git a/third_party/rust/cfg_aliases/src/lib.rs b/third_party/rust/cfg_aliases/src/lib.rs new file mode 100644 index 0000000000..860a4f0044 --- /dev/null +++ b/third_party/rust/cfg_aliases/src/lib.rs @@ -0,0 +1,372 @@ +//! # CFG Aliases +//! +//! CFG Aliases is a tiny utility to help save you a lot of effort with long winded `#[cfg()]` checks. This crate provides a single [`cfg_aliases!`] macro that doesn't have any dependencies and specifically avoids pulling in `syn` or `quote` so that the impact on your comile times should be negligible. +//! +//! You use the the [`cfg_aliases!`] macro in your `build.rs` script to define aliases such as `x11` that could then be used in the `cfg` attribute or macro for conditional compilation: `#[cfg(x11)]`. +//! +//! ## Example +//! +//! **Cargo.toml:** +//! +//! ```toml +//! [build-dependencies] +//! cfg_aliases = "0.1.0" +//! ``` +//! +//! **build.rs:** +//! +//! ```rust +//! use cfg_aliases::cfg_aliases; +//! +//! fn main() { +//! // Setup cfg aliases +//! cfg_aliases! { +//! // Platforms +//! wasm: { target_arch = "wasm32" }, +//! android: { target_os = "android" }, +//! macos: { target_os = "macos" }, +//! linux: { target_os = "linux" }, +//! // Backends +//! surfman: { all(unix, feature = "surfman", not(wasm)) }, +//! glutin: { all(feature = "glutin", not(wasm)) }, +//! wgl: { all(windows, feature = "wgl", not(wasm)) }, +//! dummy: { not(any(wasm, glutin, wgl, surfman)) }, +//! } +//! } +//! ``` +//! +//! Now that we have our aliases setup we can use them just like you would expect: +//! +//! ```rust +//! #[cfg(wasm)] +//! println!("This is running in WASM"); +//! +//! #[cfg(surfman)] +//! { +//! // Do stuff related to surfman +//! } +//! +//! #[cfg(dummy)] +//! println!("We're in dummy mode, specify another feature if you want a smarter app!"); +//! ``` +//! +//! This greatly improves what would otherwise look like this without the aliases: +//! +//! ```rust +//! #[cfg(target_arch = "wasm32")] +//! println!("We're running in WASM"); +//! +//! #[cfg(all(unix, feature = "surfman", not(target_arch = "22")))] +//! { +//! // Do stuff related to surfman +//! } +//! +//! #[cfg(not(any( +//! target_arch = "wasm32", +//! all(unix, feature = "surfman", not(target_arch = "wasm32")), +//! all(windows, feature = "wgl", not(target_arch = "wasm32")), +//! all(feature = "glutin", not(target_arch = "wasm32")), +//! )))] +//! println!("We're in dummy mode, specify another feature if you want a smarter app!"); +//! ``` +//! +//! You can also use the `cfg!` macro or combine your aliases with other checks using `all()`, `not()`, and `any()`. Your aliases are genuine `cfg` flags now! +//! +//! ```rust +//! if cfg!(glutin) { +//! // use glutin +//! } else { +//! // Do something else +//! } +//! +//! #[cfg(all(glutin, surfman))] +//! compile_error!("You cannot specify both `glutin` and `surfman` features"); +//! ``` +//! +//! ## Syntax and Error Messages +//! +//! The aliase names are restricted to the same rules as rust identifiers which, for one, means that they cannot have dashes ( `-` ) in them. Additionally, if you get certain syntax elements wrong, such as the alias name, the macro will error saying that the recursion limit was reached instead of giving a clear indication of what actually went wrong. This is due to a nuance with the macro parser and it might be fixed in a later release of this crate. It is also possible that aliases with dashes in the name might be supported in a later release. Open an issue if that is something that you would like implemented. +//! +//! Finally, you can also induce an infinite recursion by having rules that both reference each-other, but this isn't a real limitation because that doesn't make logical sense anyway: +//! +//! ```rust,ignore +//! // This causes an error! +//! cfg_aliases! { +//! test1: { not(test2) }, +//! test2: { not(test1) }, +//! } +//! ``` +//! +//! ## Attribution and Thanks +//! +//! - Thanks to my God and Father who led me through figuring this out and to whome I owe everything. +//! - Thanks to @Yandros on the Rust forum for [showing me][sm] some crazy macro hacks! +//! - Thanks to @sfackler for [pointing out][po] the way to make cargo add the cfg flags. +//! - Thanks to the authors of the [`tectonic_cfg_support::target_cfg`] macro from which most of the cfg attribute parsing logic is taken from. Also thanks to @ratmice for [bringing it up][bip] on the Rust forum. +//! +//! [`tectonic_cfg_support::target_cfg`]: https://docs.rs/tectonic_cfg_support/0.0.1/src/tectonic_cfg_support/lib.rs.html#166-298 +//! [po]: https://users.rust-lang.org/t/any-such-thing-as-cfg-aliases/40100/2 +//! [bip]: https://users.rust-lang.org/t/any-such-thing-as-cfg-aliases/40100/13 +//! [sm]: https://users.rust-lang.org/t/any-such-thing-as-cfg-aliases/40100/3 + +#![allow(clippy::needless_doctest_main)] + +// In the `cfg_aliases!` macro below, all of the rules that start with @parser were derived from +// the `target_cfg!` macro here: +// +// https://docs.rs/tectonic_cfg_support/0.0.1/src/tectonic_cfg_support/lib.rs.html#166-298. +// +// The `target_cfg!` macro is excellently commented while the one below is not very well commented +// yet, so if you need some help understanding it you might benefit by reading that implementation. +// Also check out this forum topic for more history on the macro development: +// +// https://users.rust-lang.org/t/any-such-thing-as-cfg-aliases/40100?u=zicklag + +/// Create `cfg` aliases +/// +/// **build.rs:** +/// +/// ```rust +/// # use cfg_aliases::cfg_aliases; +/// // Setup cfg aliases +/// cfg_aliases! { +/// // Platforms +/// wasm: { target_arch = "wasm32" }, +/// android: { target_os = "android" }, +/// macos: { target_os = "macos" }, +/// linux: { target_os = "linux" }, +/// // Backends +/// surfman: { all(unix, feature = "surfman", not(wasm)) }, +/// glutin: { all(feature = "glutin", not(wasm)) }, +/// wgl: { all(windows, feature = "wgl", not(wasm)) }, +/// dummy: { not(any(wasm, glutin, wgl, surfman)) }, +/// } +/// ``` +/// +/// After you put this in your build script you can then check for those conditions like so: +/// +/// ```rust +/// #[cfg(surfman)] +/// { +/// // Do stuff related to surfman +/// } +/// +/// #[cfg(dummy)] +/// println!("We're in dummy mode, specify another feature if you want a smarter app!"); +/// ``` +/// +/// This greatly improves what would otherwise look like this without the aliases: +/// +/// ```rust +/// #[cfg(all(unix, feature = "surfman", not(target_arch = "wasm32")))] +/// { +/// // Do stuff related to surfman +/// } +/// +/// #[cfg(not(any( +/// target_arch = "wasm32", +/// all(unix, feature = "surfman", not(target_arch = "wasm32")), +/// all(windows, feature = "wgl", not(target_arch = "wasm32")), +/// all(feature = "glutin", not(target_arch = "wasm32")), +/// )))] +/// println!("We're in dummy mode, specify another feature if you want a smarter app!"); +/// ``` +#[macro_export] +macro_rules! cfg_aliases { + // Helper that just checks whether the CFG environment variable is set + (@cfg_is_set $cfgname:ident) => { + std::env::var( + format!( + "CARGO_CFG_{}", + &stringify!($cfgname).to_uppercase().replace("-", "_") + ) + ).is_ok() + }; + // Helper to check for the presense of a feature + (@cfg_has_feature $feature:expr) => { + { + std::env::var( + format!( + "CARGO_FEATURE_{}", + &stringify!($feature).to_uppercase().replace("-", "_").replace('"', "") + ) + ).map(|x| x == "1").unwrap_or(false) + } + }; + + // Helper that checks whether a CFG environment contains the given value + (@cfg_contains $cfgname:ident = $cfgvalue:expr) => { + std::env::var( + format!( + "CARGO_CFG_{}", + &stringify!($cfgname).to_uppercase().replace("-", "_") + ) + ).unwrap_or("".to_string()).split(",").find(|x| x == &$cfgvalue).is_some() + }; + + // Emitting `any(clause1,clause2,...)`: convert to `$crate::cfg_aliases!(clause1) && $crate::cfg_aliases!(clause2) && ...` + ( + @parser_emit + all + $({$($grouped:tt)+})+ + ) => { + ($( + ($crate::cfg_aliases!(@parser $($grouped)+)) + )&&+) + }; + + // Likewise for `all(clause1,clause2,...)`. + ( + @parser_emit + any + $({$($grouped:tt)+})+ + ) => { + ($( + ($crate::cfg_aliases!(@parser $($grouped)+)) + )||+) + }; + + // "@clause" rules are used to parse the comma-separated lists. They munch + // their inputs token-by-token and finally invoke an "@emit" rule when the + // list is all grouped. The general pattern for recording the parser state + // is: + // + // ``` + // $crate::cfg_aliases!( + // @clause $operation + // [{grouped-clause-1} {grouped-clause-2...}] + // [not-yet-parsed-tokens...] + // current-clause-tokens... + // ) + // ``` + + // This rule must come first in this section. It fires when the next token + // to parse is a comma. When this happens, we take the tokens in the + // current clause and add them to the list of grouped clauses, adding + // delimeters so that the grouping can be easily extracted again in the + // emission stage. + ( + @parser_clause + $op:ident + [$({$($grouped:tt)+})*] + [, $($rest:tt)*] + $($current:tt)+ + ) => { + $crate::cfg_aliases!(@parser_clause $op [ + $( + {$($grouped)+} + )* + {$($current)+} + ] [ + $($rest)* + ]); + }; + + // This rule comes next. It fires when the next un-parsed token is *not* a + // comma. In this case, we add that token to the list of tokens in the + // current clause, then move on to the next one. + ( + @parser_clause + $op:ident + [$({$($grouped:tt)+})*] + [$tok:tt $($rest:tt)*] + $($current:tt)* + ) => { + $crate::cfg_aliases!(@parser_clause $op [ + $( + {$($grouped)+} + )* + ] [ + $($rest)* + ] $($current)* $tok); + }; + + // This rule fires when there are no more tokens to parse in this list. We + // finish off the "current" token group, then delegate to the emission + // rule. + ( + @parser_clause + $op:ident + [$({$($grouped:tt)+})*] + [] + $($current:tt)+ + ) => { + $crate::cfg_aliases!(@parser_emit $op + $( + {$($grouped)+} + )* + {$($current)+} + ); + }; + + + // `all(clause1, clause2...)` : we must parse this comma-separated list and + // partner with `@emit all` to output a bunch of && terms. + ( + @parser + all($($tokens:tt)+) + ) => { + $crate::cfg_aliases!(@parser_clause all [] [$($tokens)+]) + }; + + // Likewise for `any(clause1, clause2...)` + ( + @parser + any($($tokens:tt)+) + ) => { + $crate::cfg_aliases!(@parser_clause any [] [$($tokens)+]) + }; + + // `not(clause)`: compute the inner clause, then just negate it. + ( + @parser + not($($tokens:tt)+) + ) => { + !($crate::cfg_aliases!(@parser $($tokens)+)) + }; + + // `feature = value`: test for a feature. + (@parser feature = $value:expr) => { + $crate::cfg_aliases!(@cfg_has_feature $value) + }; + // `param = value`: test for equality. + (@parser $key:ident = $value:expr) => { + $crate::cfg_aliases!(@cfg_contains $key = $value) + }; + // Parse a lone identifier that might be an alias + (@parser $e:ident) => { + __cfg_aliases_matcher__!($e) + }; + + // Entrypoint that defines the matcher + ( + @with_dollar[$dol:tt] + $( $alias:ident : { $($config:tt)* } ),* $(,)? + ) => { + // Create a macro that expands other aliases and outputs any non + // alias by checking whether that CFG value is set + macro_rules! __cfg_aliases_matcher__ { + // Parse config expression for the alias + $( + ( $alias ) => { + $crate::cfg_aliases!(@parser $($config)*) + }; + )* + // Anything that doesn't match evaluate the item + ( $dol e:ident ) => { + $crate::cfg_aliases!(@cfg_is_set $dol e) + }; + } + + $( + if $crate::cfg_aliases!(@parser $($config)*) { + println!("cargo:rustc-cfg={}", stringify!($alias)); + } + )* + }; + + // Catch all that starts the macro + ($($tokens:tt)*) => { + $crate::cfg_aliases!(@with_dollar[$] $($tokens)*) + } +} |