Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions ninja-build_rs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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"
46 changes: 45 additions & 1 deletion ninja-build_rs/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,37 @@
# 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

```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<String> = 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.
Comment thread
MusicalNinjaDad marked this conversation as resolved.
Comment thread
MusicalNinjaDad marked this conversation as resolved.
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

Expand All @@ -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=[...]`
58 changes: 51 additions & 7 deletions ninja-build_rs/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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.
//! valuable output in cargo build -vv & no annoying surprises for anyone downstream.
//!
//! ## Prelude
//! # Usage
//!
//! ```rust, no_run
//! # use indexmap::IndexSet;
//! 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<String> = 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()?;
//!
Comment thread
MusicalNinjaDad marked this conversation as resolved.
//! // 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::*;
Expand All @@ -17,7 +54,11 @@
//! 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};
//!

use std::{env::VarError, ffi::OsString};

use indexmap::IndexSet;

/// Recommended prelude: `use ninja-build_rs::prelude::*`
///
Expand All @@ -36,7 +77,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.
Expand All @@ -45,11 +87,13 @@ pub fn get_var(key: &str) -> Result<String> {
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.
pub fn split_var(key: &str) -> Result<HashSet<String>> {
/// - Returns an [IndexSet] which implements `.contains()` AND retains ordering
pub fn split_var(key: &str) -> Result<IndexSet<String>> {
Ok(std::env::split_paths(&get_var(key)?)
.map(|p| p.to_string_lossy().to_string())
.collect())
Expand All @@ -75,7 +119,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),
Expand Down
54 changes: 30 additions & 24 deletions ninja-build_rs/src/nightly.rs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -6,11 +7,11 @@
//!
//! For a list of known features with dedicated probes see [UnstableFeature]
//!
//! ## Usage
//! # Usage
//!
//! ### `build.rs`
//! ## In `build.rs`
//!
//! ```rust, should_panic
//! ```rust, no_run
//! use ninja_build_rs::prelude::*;
//!
//! fn main() -> Result<()> {
Expand All @@ -32,9 +33,9 @@
//! }
//! ```
//!
//! ### `lib.rs` / `main.rs`
//! ## In `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))]
//!
Expand Down Expand Up @@ -67,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`.
Expand All @@ -87,20 +88,22 @@ use std::{
process::{Command, Output},
};

/// re-exported from autocfg
///
pub use autocfg::AutoCfg;
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
Expand All @@ -112,23 +115,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,
Expand Down Expand Up @@ -271,25 +274,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_...)`
///
/// - **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.
///
/// All [known features](UnstableFeature) have a `has_...` cfg for this purpose.
/// # Note
///
/// 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.
/// - 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);
}

Expand Down
6 changes: 6 additions & 0 deletions ninja-xtask/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading