From 7c15961f783bbae875fe7a3a6e76960a3c088548 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Fri, 3 Jul 2026 15:05:15 +0000 Subject: [PATCH 01/10] examples attributes --- ninja-build_rs/src/nightly.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ninja-build_rs/src/nightly.rs b/ninja-build_rs/src/nightly.rs index caf0c57..956778d 100644 --- a/ninja-build_rs/src/nightly.rs +++ b/ninja-build_rs/src/nightly.rs @@ -1,3 +1,4 @@ +#![expect(clippy::test_attr_in_doctest)] //! Checking for experimental or stabilised features is prone to subtle errors which create issues //! for downstream users and verbose when done properly. This provides extensions to the amazing //! [autocfg::AutoCfg] (re-exported via our prelude to make your life easier) to safely identify the @@ -10,7 +11,7 @@ //! //! ### `build.rs` //! -//! ```rust, should_panic +//! ```rust, no_run //! use ninja_build_rs::prelude::*; //! //! fn main() -> Result<()> { @@ -34,7 +35,7 @@ //! //! ### `lib.rs` / `main.rs` //! -//! ```rust, ignore +//! ```rust //! // only enable unstable feature if it is available and has not yet been stabilised //! #![cfg_attr(unstable_assert_matches, feature(assert_matches))] //! From 2b4094b2fa9e33bc215a9eebce9973ee2f1e9e91 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Fri, 3 Jul 2026 15:05:23 +0000 Subject: [PATCH 02/10] autocfg link --- ninja-build_rs/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ninja-build_rs/src/lib.rs b/ninja-build_rs/src/lib.rs index ac05517..6985207 100644 --- a/ninja-build_rs/src/lib.rs +++ b/ninja-build_rs/src/lib.rs @@ -75,7 +75,7 @@ pub enum BuildError { /// /// outputs `IOError(error details)` to stderr IOError(std::io::Error), - /// An error when creating or using [AutoCfg] + /// An error when creating or using [autocfg] /// /// outputs `AutoCfgError(error details)` AutoCfgError(autocfg::Error), From 8aefb3276fd3dd1e0e6b8d9b5a4c494eacc18dc0 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Fri, 3 Jul 2026 15:05:54 +0000 Subject: [PATCH 03/10] headings --- ninja-build_rs/src/nightly.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ninja-build_rs/src/nightly.rs b/ninja-build_rs/src/nightly.rs index 956778d..503ff20 100644 --- a/ninja-build_rs/src/nightly.rs +++ b/ninja-build_rs/src/nightly.rs @@ -9,7 +9,7 @@ //! //! ## Usage //! -//! ### `build.rs` +//! ### In `build.rs` //! //! ```rust, no_run //! use ninja_build_rs::prelude::*; @@ -33,7 +33,7 @@ //! } //! ``` //! -//! ### `lib.rs` / `main.rs` +//! ### In `lib.rs` / `main.rs` //! //! ```rust //! // only enable unstable feature if it is available and has not yet been stabilised From 500f13b0f78c615f183a6c4e07fd34a86d5269d3 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Fri, 3 Jul 2026 15:17:42 +0000 Subject: [PATCH 04/10] add usage example --- ninja-build_rs/src/lib.rs | 41 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/ninja-build_rs/src/lib.rs b/ninja-build_rs/src/lib.rs index 6985207..39700ef 100644 --- a/ninja-build_rs/src/lib.rs +++ b/ninja-build_rs/src/lib.rs @@ -1,7 +1,44 @@ //! Designed to help create good build scripts, with a focus on ease of use for you, //! valuable output in `cargo build -vv` & no annoying surprises for anyone downstream. //! -//! ## Prelude +//! # Usage +//! +//! ```rust, no_run +//! # use std::collections::HashSet; +//! use ninja_build_rs::prelude::*; +//! +//! // Result uses BuildError to give meaningful messages +//! fn main() -> Result<()> { +//! +//! // get an environment variable and re-run build script if it changes. +//! let my_var: String = get_var("MY_VAR")?; +//! +//! // get values from an environment variable, separated by the +//! // OS path separator and re-run build script if it changes. +//! let my_vals: HashSet = split_var("MY_VALUES")?; +//! if my_vals.contains("some_value") { +//! unimplemented!("do something") +//! } +//! +//! // get a new AutoCfg or provide a valuable error +//! // rather than panicing. +//! let ac = AutoCfg::new()?; +//! +//! // check to see if the downstream crate has defined +//! // `unstable.allow-features` in `.cargo/config.toml`. +//! // It is mandatory to perform this check and pass the +//! // result to any calls to `emit_unstable_feature` +//! let allowed_features = cargo_allowed_features()?; +//! +//! // We want to make use of `assert_matches` if it is available +//! ac.emit_unstable_feature(assert_matches, &allowed_features); +//! // ^^^^^^^^^^^^^^ - enum variant to avoid typos +//! +//! Ok(()) +//! } +//! ``` +//! +//! # Prelude //! //! ```rust //! use ninja_build_rs::prelude::*; @@ -17,6 +54,8 @@ //! enum [`UnstableFeature`](nightly::UnstableFeature) to provide a safe way to identify the //! availability of nightly features & handle the future stabilisation process without additional //! effort on your part. All while respecting any `allow-feature` whitelists. +//! + use std::{collections::HashSet, env::VarError, ffi::OsString}; /// Recommended prelude: `use ninja-build_rs::prelude::*` From 1d8fcc65e0ace6a2974ef5aae818428814256ffa Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Fri, 3 Jul 2026 15:24:24 +0000 Subject: [PATCH 05/10] get_var & split_var --- ninja-build_rs/src/lib.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ninja-build_rs/src/lib.rs b/ninja-build_rs/src/lib.rs index 39700ef..bfa8639 100644 --- a/ninja-build_rs/src/lib.rs +++ b/ninja-build_rs/src/lib.rs @@ -75,7 +75,8 @@ pub mod prelude { pub mod nightly; -/// Attempt to get an environment variable. +/// Attempt to get an environment variable, re-run build if it changes or provide a meaningful +/// error if missing. /// /// - Emits `cargo::rerun-if-env-changed=key` to ensure changes trigger a rebuild. /// - If not found the error returned will include the key name in the debug representation. @@ -84,10 +85,12 @@ pub fn get_var(key: &str) -> Result { std::env::var(key).map_err(|err| BuildError::from_var_error(key, err)) } -/// Attempt to get an environment variable and split the values using the OS path separator. +/// Attempt to get an environment variable and split the values using the OS path separator, +/// re-run build if it changes or provide a meaningful error if missing. /// /// - Emits `cargo::rerun-if-env-changed=key` to ensure changes trigger a rebuild. /// - If not found the error returned will include the key name in the debug representation. +/// - Returns a HashSet which implements `.contains()` but does NOT retain order pub fn split_var(key: &str) -> Result> { Ok(std::env::split_paths(&get_var(key)?) .map(|p| p.to_string_lossy().to_string()) From b5b6d4c57cd3bebbeed7bd7d33742358a644b9a9 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Fri, 3 Jul 2026 15:36:49 +0000 Subject: [PATCH 06/10] headings & touch-up nightly --- ninja-build_rs/Cargo.toml | 4 +-- ninja-build_rs/src/lib.rs | 12 +++++---- ninja-build_rs/src/nightly.rs | 47 +++++++++++++++++++---------------- 3 files changed, 34 insertions(+), 29 deletions(-) diff --git a/ninja-build_rs/Cargo.toml b/ninja-build_rs/Cargo.toml index c187b9c..e31e712 100644 --- a/ninja-build_rs/Cargo.toml +++ b/ninja-build_rs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ninja-build_rs" -version = "0.3.0" +version = "0.4.0" edition = "2024" readme = "README.md" description = "build script helpers for working with nightly" @@ -13,9 +13,9 @@ license = "MIT" [dependencies] autocfg = "1.5.1" - derive_more.version = "2.1.1" derive_more.features = ["display"] +indexmap = "2.14.0" [dev-dependencies] tempfile = "3.27.0" diff --git a/ninja-build_rs/src/lib.rs b/ninja-build_rs/src/lib.rs index bfa8639..8efcb1a 100644 --- a/ninja-build_rs/src/lib.rs +++ b/ninja-build_rs/src/lib.rs @@ -4,7 +4,7 @@ //! # Usage //! //! ```rust, no_run -//! # use std::collections::HashSet; +//! # use indexmap::IndexSet; //! use ninja_build_rs::prelude::*; //! //! // Result uses BuildError to give meaningful messages @@ -15,7 +15,7 @@ //! //! // get values from an environment variable, separated by the //! // OS path separator and re-run build script if it changes. -//! let my_vals: HashSet = split_var("MY_VALUES")?; +//! let my_vals: IndexSet = split_var("MY_VALUES")?; //! if my_vals.contains("some_value") { //! unimplemented!("do something") //! } @@ -56,7 +56,9 @@ //! effort on your part. All while respecting any `allow-feature` whitelists. //! -use std::{collections::HashSet, env::VarError, ffi::OsString}; +use std::{env::VarError, ffi::OsString}; + +use indexmap::IndexSet; /// Recommended prelude: `use ninja-build_rs::prelude::*` /// @@ -90,8 +92,8 @@ pub fn get_var(key: &str) -> Result { /// /// - Emits `cargo::rerun-if-env-changed=key` to ensure changes trigger a rebuild. /// - If not found the error returned will include the key name in the debug representation. -/// - Returns a HashSet which implements `.contains()` but does NOT retain order -pub fn split_var(key: &str) -> Result> { +/// - Returns an [IndexSet] which implements `.contains()` AND retains ordering +pub fn split_var(key: &str) -> Result> { Ok(std::env::split_paths(&get_var(key)?) .map(|p| p.to_string_lossy().to_string()) .collect()) diff --git a/ninja-build_rs/src/nightly.rs b/ninja-build_rs/src/nightly.rs index 503ff20..bceca3f 100644 --- a/ninja-build_rs/src/nightly.rs +++ b/ninja-build_rs/src/nightly.rs @@ -7,9 +7,9 @@ //! //! For a list of known features with dedicated probes see [UnstableFeature] //! -//! ## Usage +//! # Usage //! -//! ### In `build.rs` +//! ## In `build.rs` //! //! ```rust, no_run //! use ninja_build_rs::prelude::*; @@ -33,7 +33,7 @@ //! } //! ``` //! -//! ### In `lib.rs` / `main.rs` +//! ## In `lib.rs` / `main.rs` //! //! ```rust //! // only enable unstable feature if it is available and has not yet been stabilised @@ -68,7 +68,7 @@ //! } //! ``` //! -//! ## Note to downstream crates +//! # Note to downstream crates //! //! If you (transiently) depend on a crate which uses `ninja-build_rs` and have implemented a //! whitelist of `allowed-features`. @@ -94,14 +94,14 @@ use derive_more::Display; use crate::{BuildError, Result, get_var}; use probes::{has, make_probe, unstable}; -/// Known features with `unstable_...` & `has_...`. +/// Known features with `unstable_...` & dedicated probes for `has_...`. /// /// If the feature you want is not in this list you can use `Other` to get `unstable_...` /// but please also raise a PR (or open an issue) to add a custom probe for `has_...`. #[allow(non_camel_case_types, reason = "shadowing feature naming")] #[derive(Debug, Clone, PartialEq, Eq, Display)] pub enum UnstableFeature { - /// ### Provides cfg flags: + /// ## Provides cfg flags: /// - `#![cfg_attr(unstable_assert_matches, feature(assert_matches))]` /// - `#[cfg(has_assert_matches)]` /// - ```rust, ignore @@ -113,23 +113,23 @@ pub enum UnstableFeature { /// use std::assert_matches::assert_matches; /// ``` assert_matches, - /// ### Provides cfg flags: + /// ## Provides cfg flags: /// - `#![cfg_attr(unstable_iterator_try_collect, feature(iterator_try_collect))]` /// - `#[cfg(has_iterator_try_collect)]` iterator_try_collect, - /// ### Provides cfg flags: + /// ## Provides cfg flags: /// - `#![cfg_attr(unstable_never_type, feature(never_type))]` /// - `#[cfg(has_never_type)]` never_type, - /// ### Provides cfg flags: + /// ## Provides cfg flags: /// - `#![cfg_attr(unstable_proc_macro_diagnostic, feature(proc_macro_diagnostic))]` /// - `#[cfg(has_proc_macro_diagnostic)]` proc_macro_diagnostic, - /// ### Provides cfg flags: + /// ## Provides cfg flags: /// - `#![cfg_attr(unstable_try_trait_v2, feature(try_trait_v2))]` /// - `#[cfg(has_try_trait_v2)]` try_trait_v2, - /// ### Provides cfg flags: + /// ## Provides cfg flags: /// - `#![cfg_attr(unstable_try_trait_v2_residual, feature(try_trait_v2_residual))]` /// - `#[cfg(has_try_trait_v2_residual)]` try_trait_v2_residual, @@ -272,25 +272,28 @@ use std::ops::Residual; } } +/// Adds [`AutoCfg::emit_unstable_feature`](Nightly::emit_unstable_feature) pub trait Nightly { /// Offers at least 2 cfg flags for all [known features](UnstableFeature) /// - /// You must pass a set of [AllowedFeatures], created by calling [cargo_allowed_features] + /// # Feature enablement: `cfg(unstable_...)` /// - /// ## cfg `unstable_...` /// - To be used at top-level crate via `#![cfg_attr(unstable_foo, feature(foo))]` /// - /// ## Cfg-gating `has_...` - /// Do **not** rely on `cfg(not(unstable_foo))` to suggest that `feature(foo)` is stable! There - /// are 3 reasons that `cfg(unstable_foo)` could be `false`: - /// 1. The build is using `stable`/`beta` or the feature is not on the `allow-features` whitelist - /// 2. The feature has been stabilised - /// 3. The compiler is from before the feature was implemented + /// # Cfg-gating: `cfg(has_...)` /// - /// All [known features](UnstableFeature) have a `has_...` cfg for this purpose. + /// - **Do not rely on `#[cfg(not(unstable_foo))]` to suggest that `feature(foo)` is stable!** + /// - There are 3 reasons that `#[cfg(unstable_foo)]` could be `false`: + /// 1. The build is using `stable`/`beta` or the feature is not on the `allow-features` whitelist + /// 2. The feature has been stabilised + /// 3. The compiler is from before the feature was implemented + /// - All [known features](UnstableFeature) have a `#[cfg(has_...)]` for this purpose. /// - /// If you need to test that a feature is available in order to cfg-gate your code and it is not - /// on the list of [known features](UnstableFeature), please raise a PR with a suggested probe. + /// # Note + /// + /// - You must pass a set of [AllowedFeatures], created by calling [cargo_allowed_features] + /// - If you need to test that a feature is available in order to cfg-gate your code and it is not + /// on the list of [known features](UnstableFeature), please raise a PR with a suggested probe. fn emit_unstable_feature(&self, feature: UnstableFeature, allowed_features: &AllowedFeatures); } From 468daa9139f1133e305126c7846ac1f28aa473b3 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Fri, 3 Jul 2026 15:37:59 +0000 Subject: [PATCH 07/10] cargo lock --- ninja-xtask/Cargo.lock | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ninja-xtask/Cargo.lock b/ninja-xtask/Cargo.lock index 5279552..8b15137 100644 --- a/ninja-xtask/Cargo.lock +++ b/ninja-xtask/Cargo.lock @@ -396,6 +396,8 @@ dependencies = [ [[package]] name = "ninja-build_rs" version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6720c3e3e82280cd954a93e999da565924aab15e13716b08cea9c0ce6f0d70" dependencies = [ "autocfg", "derive_more", @@ -848,3 +850,7 @@ name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[patch.unused]] +name = "ninja-build_rs" +version = "0.4.0" From 73339f81529fccda10cb387770fe3cc06d255ab7 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Fri, 3 Jul 2026 15:42:38 +0000 Subject: [PATCH 08/10] label AutoCfg as re-export --- ninja-build_rs/src/nightly.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ninja-build_rs/src/nightly.rs b/ninja-build_rs/src/nightly.rs index bceca3f..c93124b 100644 --- a/ninja-build_rs/src/nightly.rs +++ b/ninja-build_rs/src/nightly.rs @@ -88,6 +88,8 @@ use std::{ process::{Command, Output}, }; +/// re-exported from autocfg +/// pub use autocfg::AutoCfg; use derive_more::Display; From 101d9833a4a8ae0af4b956dc2b1317f0cf9451d9 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Fri, 3 Jul 2026 15:44:33 +0000 Subject: [PATCH 09/10] update readme --- ninja-build_rs/README.md | 44 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/ninja-build_rs/README.md b/ninja-build_rs/README.md index 0957174..7107087 100644 --- a/ninja-build_rs/README.md +++ b/ninja-build_rs/README.md @@ -3,6 +3,36 @@ Designed to help create good build scripts, with a focus on ease of use for you, valuable output in `cargo build -vv` & no annoying surprises for anyone downstream. +## Usage + +```rust +use ninja_build_rs::prelude::*; +// Result uses BuildError to give meaningful messages +fn main() -> Result<()> { + // get an environment variable and re-run build script if it changes. + let my_var: String = get_var("MY_VAR")?; + // get values from an environment variable, separated by the + // OS path separator and re-run build script if it changes. + let my_vals: IndexSet = split_var("MY_VALUES")?; + if my_vals.contains("some_value") { + unimplemented!("do something") + } + // get a new AutoCfg or provide a valuable error + // rather than panicing. + let ac = AutoCfg::new()?; + // check to see if the downstream crate has defined + // `unstable.allow-features` in `.cargo/config.toml`. + // It is mandatory to perform this check and pass the + // result to any calls to `emit_unstable_feature` + let allowed_features = cargo_allowed_features()?; + // We want to make use of `assert_matches` if it is available + ac.emit_unstable_feature(assert_matches, &allowed_features); + // ^^^^^^^^^^^^^^ - enum variant to avoid typos + Ok(()) +} + +``` + ## Prelude ```rust @@ -19,3 +49,17 @@ provides: enum [`UnstableFeature`](nightly::UnstableFeature) to provide a safe way to identify the availability of nightly features & handle the future stabilisation process without additional effort on your part. All while respecting any `allow-feature` whitelists. + +## Note to downstream crates + +If you (transiently) depend on a crate which uses `ninja-build_rs` and have implemented a +whitelist of `allowed-features`. + +Due to limitations in the information provided by cargo: + +- This will obtain config.toml files based upon `OUT_DIR`. If this is not under the project + root, you can override by providing an alternative path via the environment variable + `NINJA_CARGO_CONFIG_DIR`. See cargo's documentation on config file hierarchical structure + for more details. +- This will not respect additional entries passed at the command line via + `cargo --config unstable.allow-features=[...]` From 156370a24562b51c86700ba15407cf8e28c48109 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Fri, 3 Jul 2026 15:45:43 +0000 Subject: [PATCH 10/10] cargo build -vv was sticking out too much --- ninja-build_rs/README.md | 2 +- ninja-build_rs/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ninja-build_rs/README.md b/ninja-build_rs/README.md index 7107087..758cf38 100644 --- a/ninja-build_rs/README.md +++ b/ninja-build_rs/README.md @@ -1,7 +1,7 @@ # ninja-build_rs Designed to help create good build scripts, with a focus on ease of use for you, -valuable output in `cargo build -vv` & no annoying surprises for anyone downstream. +valuable output in cargo build -vv & no annoying surprises for anyone downstream. ## Usage diff --git a/ninja-build_rs/src/lib.rs b/ninja-build_rs/src/lib.rs index 8efcb1a..85607c5 100644 --- a/ninja-build_rs/src/lib.rs +++ b/ninja-build_rs/src/lib.rs @@ -1,5 +1,5 @@ //! Designed to help create good build scripts, with a focus on ease of use for you, -//! valuable output in `cargo build -vv` & no annoying surprises for anyone downstream. +//! valuable output in cargo build -vv & no annoying surprises for anyone downstream. //! //! # Usage //!