diff --git a/dev/flake-module.nix b/dev/flake-module.nix index 504a523..ff31f41 100644 --- a/dev/flake-module.nix +++ b/dev/flake-module.nix @@ -81,6 +81,9 @@ config.packages.nix ]; nativeBuildInputs = [ + pkgs.pre-commit + config.treefmt.build.wrapper + pkgs.rust-analyzer pkgs.nixfmt-rfc-style pkgs.rustfmt @@ -96,7 +99,6 @@ ]; shellHook = '' ${config.pre-commit.installationScript} - source ${../bindgen-gcc.sh} echo 1>&2 "Welcome to the development shell!" ''; # rust-analyzer needs a NIX_PATH for some reason diff --git a/flake.lock b/flake.lock index d098b91..358d842 100644 --- a/flake.lock +++ b/flake.lock @@ -27,11 +27,11 @@ "pyproject-nix": "pyproject-nix" }, "locked": { - "lastModified": 1764021028, - "narHash": "sha256-4OlkDA0yJyqt5iTX9NqtHNghvkWNzYqmtX7FxDmEXt4=", + "lastModified": 1765228272, + "narHash": "sha256-duTz4J4NP1edl/ZBdwZPduPM8j6g0yzjb8YH91T9vU0=", "owner": "nix-community", "repo": "dream2nix", - "rev": "ee20942e4524d3458a91108716c847a2d4299d2e", + "rev": "83c430ce6b6aedf149c5259f066bfff808722dbd", "type": "github" }, "original": { @@ -170,11 +170,11 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1764979397, - "narHash": "sha256-oMSKV0NFQpfwYQ4wcIIDuH+0AwCQl+bYRgPznx38i8k=", + "lastModified": 1765241125, + "narHash": "sha256-/Rr90FVpncaSWprG6+4t5VXHgDKDvFitzJPpdZgo+8I=", "owner": "NixOS", "repo": "nix", - "rev": "525755dadc153c68e5ada8d287ab19af26383fff", + "rev": "7448aedd74e28174bfa33aad0d148c0070c86dfb", "type": "github" }, "original": { @@ -196,11 +196,11 @@ "treefmt": "treefmt" }, "locked": { - "lastModified": 1765001919, - "narHash": "sha256-S5k0HAxjq2sbf5BC5DOly66joE08HfiyEJD8v2U3yLc=", + "lastModified": 1765261286, + "narHash": "sha256-Ll29VFrT+VpnP4HG5LiL33IXuiz6cF2WfTwXONwMj7E=", "owner": "yusdacra", "repo": "nix-cargo-integration", - "rev": "0feff946314851edc88b9c783ad7d2dccd49abcc", + "rev": "92639295bbe953e4c8c32cbffa32dcb0a441118b", "type": "github" }, "original": { @@ -211,11 +211,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1764950072, - "narHash": "sha256-BmPWzogsG2GsXZtlT+MTcAWeDK5hkbGRZTeZNW42fwA=", + "lastModified": 1765186076, + "narHash": "sha256-hM20uyap1a0M9d344I692r+ik4gTMyj60cQWO+hAYP8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "f61125a668a320878494449750330ca58b78c557", + "rev": "addf7cf5f383a3101ecfba091b98d0a1263dc9b8", "type": "github" }, "original": { @@ -355,11 +355,11 @@ ] }, "locked": { - "lastModified": 1764988672, - "narHash": "sha256-FIJtt3Zil89/hLy9i7f0R2xXcJDPc3CeqiiCLfsFV0Y=", + "lastModified": 1765248027, + "narHash": "sha256-ngar+yP06x3+2k2Iey29uU0DWx5ur06h3iPBQXlU+yI=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "086fd19a68e80fcc8a298e9df4674982e4c498a6", + "rev": "7b50ad68415ae5be7ee4cc68fa570c420741b644", "type": "github" }, "original": { diff --git a/nix-bindings-store/Cargo.toml b/nix-bindings-store/Cargo.toml index c1300b7..c9bc96b 100644 --- a/nix-bindings-store/Cargo.toml +++ b/nix-bindings-store/Cargo.toml @@ -20,3 +20,5 @@ tempfile = "3.10" [build-dependencies] pkg-config = "0.3" +# Needed for version parsing in build.rs +nix-bindings-util = { path = "../nix-bindings-util" } diff --git a/nix-bindings-store/build.rs b/nix-bindings-store/build.rs index e97be70..85a20d6 100644 --- a/nix-bindings-store/build.rs +++ b/nix-bindings-store/build.rs @@ -1,39 +1,6 @@ +use nix_bindings_util::nix_version::emit_version_cfg; + fn main() { - // Get nix version let nix_version = pkg_config::probe_library("nix-store-c").unwrap().version; - - // Generate version flags - // Unfortunately, Rust doesn't give us a "greater than" operator in conditional - // compilation, so we pre-evaluate the version comparisons here, making use - // of the multi-valued nature of Rust cfgs. - let relevant_versions = vec!["2.26", "2.33"]; - let versions = relevant_versions - .iter() - .map(|v| format!("\"{}\"", v)) - .collect::>() - .join(","); - - // Declare the known versions, so that Rust can warn about unknown versions - // that aren't part of `relevant_versions` yet - feel free to add entries. - println!( - "cargo:rustc-check-cfg=cfg(nix_at_least,values({}))", - versions - ); - - let nix_version = nix_version.split('.').collect::>(); - let nix_version = ( - nix_version[0].parse::().unwrap(), - nix_version[1].parse::().unwrap(), - ); - - for version_str in relevant_versions { - let version = version_str.split('.').collect::>(); - let version = ( - version[0].parse::().unwrap(), - version[1].parse::().unwrap(), - ); - if nix_version >= version { - println!("cargo:rustc-cfg=nix_at_least=\"{}\"", version_str); - } - } + emit_version_cfg(&nix_version, &["2.26", "2.33.0pre", "2.33"]); } diff --git a/nix-bindings-store/src/derivation.rs b/nix-bindings-store/src/derivation.rs index 4b10127..6be5e75 100644 --- a/nix-bindings-store/src/derivation.rs +++ b/nix-bindings-store/src/derivation.rs @@ -1,4 +1,4 @@ -#![cfg(nix_at_least = "2.33")] +#![cfg(nix_at_least = "2.33.0pre")] use nix_bindings_bindgen_raw as raw; use std::ptr::NonNull; diff --git a/nix-bindings-store/src/store.rs b/nix-bindings-store/src/store.rs index 0f01e76..aff5a66 100644 --- a/nix-bindings-store/src/store.rs +++ b/nix-bindings-store/src/store.rs @@ -6,7 +6,7 @@ use nix_bindings_util::string_return::{ callback_get_result_string, callback_get_result_string_data, }; use nix_bindings_util::{check_call, result_string_init}; -#[cfg(nix_at_least = "2.33")] +#[cfg(nix_at_least = "2.33.0pre")] use std::collections::BTreeMap; use std::collections::HashMap; use std::ffi::{c_char, CString}; @@ -14,7 +14,7 @@ use std::ptr::null_mut; use std::ptr::NonNull; use std::sync::{Arc, Mutex, Weak}; -#[cfg(nix_at_least = "2.33")] +#[cfg(nix_at_least = "2.33.0pre")] use crate::derivation::Derivation; use crate::path::StorePath; @@ -71,7 +71,7 @@ lazy_static! { static ref STORE_CACHE: Arc> = Arc::new(Mutex::new(HashMap::new())); } -#[cfg(nix_at_least = "2.33")] +#[cfg(nix_at_least = "2.33.0pre")] unsafe extern "C" fn callback_get_result_store_path_set( _context: *mut raw::c_context, user_data: *mut std::os::raw::c_void, @@ -88,7 +88,7 @@ unsafe extern "C" fn callback_get_result_store_path_set( ret.push(store_path); } -#[cfg(nix_at_least = "2.33")] +#[cfg(nix_at_least = "2.33.0pre")] fn callback_get_result_store_path_set_data(vec: &mut Vec) -> *mut std::os::raw::c_void { vec as *mut Vec as *mut std::os::raw::c_void } @@ -274,7 +274,7 @@ impl Store { /// # Returns /// A [`Derivation`] object if parsing succeeds, or an error if the JSON is invalid /// or malformed. - #[cfg(nix_at_least = "2.33")] + #[cfg(nix_at_least = "2.33.0pre")] #[doc(alias = "nix_derivation_from_json")] pub fn derivation_from_json(&mut self, json: &str) -> Result { let json_cstr = CString::new(json)?; @@ -302,7 +302,7 @@ impl Store { /// /// # Returns /// The store path of the derivation (ending in `.drv`). - #[cfg(nix_at_least = "2.33")] + #[cfg(nix_at_least = "2.33.0pre")] #[doc(alias = "nix_add_derivation")] pub fn add_derivation(&mut self, drv: &Derivation) -> Result { unsafe { @@ -331,7 +331,7 @@ impl Store { /// # Returns /// A [`BTreeMap`] mapping output names (e.g., "out", "dev", "doc") to their store paths. /// The map is ordered alphabetically by output name for deterministic iteration. - #[cfg(nix_at_least = "2.33")] + #[cfg(nix_at_least = "2.33.0pre")] #[doc(alias = "nix_store_realise")] pub fn realise(&mut self, path: &StorePath) -> Result> { let mut outputs = BTreeMap::new(); @@ -388,7 +388,7 @@ impl Store { /// /// # Returns /// A vector of store paths in the closure, in no particular order. - #[cfg(nix_at_least = "2.33")] + #[cfg(nix_at_least = "2.33.0pre")] #[doc(alias = "nix_store_get_fs_closure")] pub fn get_fs_closure( &mut self, @@ -544,6 +544,7 @@ mod tests { assert!(weak.inner.upgrade().is_none()); } + #[cfg(nix_at_least = "2.33.0pre")] fn create_temp_store() -> (Store, tempfile::TempDir) { let temp_dir = tempfile::tempdir().unwrap(); @@ -616,7 +617,7 @@ mod tests { } #[test] - #[cfg(nix_at_least = "2.33")] + #[cfg(nix_at_least = "2.33.0pre")] fn derivation_from_invalid_json() { let (mut store, temp_dir) = create_temp_store(); let result = store.derivation_from_json("not valid json"); diff --git a/nix-bindings-util/src/lib.rs b/nix-bindings-util/src/lib.rs index 29ad7dd..c74208c 100644 --- a/nix-bindings-util/src/lib.rs +++ b/nix-bindings-util/src/lib.rs @@ -2,3 +2,4 @@ pub mod context; pub mod settings; #[macro_use] pub mod string_return; +pub mod nix_version; diff --git a/nix-bindings-util/src/nix_version.rs b/nix-bindings-util/src/nix_version.rs new file mode 100644 index 0000000..a577261 --- /dev/null +++ b/nix-bindings-util/src/nix_version.rs @@ -0,0 +1,101 @@ +//! Nix version parsing and conditional compilation support. + +/// Emit [`cargo:rustc-cfg`] directives for Nix version-based conditional compilation. +/// +/// Call from build.rs with the Nix version and desired version gates. +/// +/// [`cargo:rustc-cfg`]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-cfg +/// +/// # Example +/// +/// ``` +/// use nix_bindings_util::nix_version::emit_version_cfg; +/// # // Stub pkg_config so that we can render a full usage example +/// # mod pkg_config { pub fn probe_library(_: &str) -> Result { Ok(Library { version: "2.33.0pre".into() }) } +/// # pub struct Library { pub version: String } } +/// +/// let nix_version = pkg_config::probe_library("nix-store-c").unwrap().version; +/// emit_version_cfg(&nix_version, &["2.26", "2.33.0pre", "2.33"]); +/// ``` +/// +/// Emits `nix_at_least="2.26"` and `nix_at_least="2.33.0pre"` for version 2.33.0pre, +/// usable as `#[cfg(nix_at_least = "2.26")]`. +pub fn emit_version_cfg(nix_version: &str, relevant_versions: &[&str]) { + // Declare the known versions for cargo check-cfg + let versions = relevant_versions + .iter() + .map(|v| format!("\"{}\"", v)) + .collect::>() + .join(","); + + println!( + "cargo:rustc-check-cfg=cfg(nix_at_least,values({}))", + versions + ); + + let nix_version = parse_version(nix_version); + + for version_str in relevant_versions { + let version = parse_version(version_str); + if nix_version >= version { + println!("cargo:rustc-cfg=nix_at_least=\"{}\"", version_str); + } + } +} + +/// Parse a Nix version string into a comparable tuple `(major, minor, patch)`. +/// +/// Pre-release versions (containing `"pre"`) get patch = -1, sorting before stable releases. +/// Omitted patch defaults to 0. +/// +/// # Examples +/// +/// ``` +/// use nix_bindings_util::nix_version::parse_version; +/// +/// assert_eq!(parse_version("2.26"), (2, 26, 0)); +/// assert_eq!(parse_version("2.33.0pre"), (2, 33, -1)); +/// assert_eq!(parse_version("2.33"), (2, 33, 0)); +/// assert_eq!(parse_version("2.33.1"), (2, 33, 1)); +/// +/// // Pre-release versions sort before stable +/// assert!(parse_version("2.33.0pre") < parse_version("2.33")); +/// ``` +pub fn parse_version(version_str: &str) -> (u32, u32, i32) { + let parts = version_str.split('.').collect::>(); + let major = parts[0].parse::().unwrap(); + let minor = parts[1].parse::().unwrap(); + let patch = if parts.get(2).map_or(false, |s| s.contains("pre")) { + -1i32 + } else { + parts + .get(2) + .and_then(|s| s.parse::().ok()) + .unwrap_or(0) + }; + (major, minor, patch) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_version() { + assert_eq!(parse_version("2.26"), (2, 26, 0)); + assert_eq!(parse_version("2.33.0pre"), (2, 33, -1)); + assert_eq!(parse_version("2.33"), (2, 33, 0)); + assert_eq!(parse_version("2.33.1"), (2, 33, 1)); + } + + #[test] + fn test_version_ordering() { + // Pre-release versions should sort before stable + assert!(parse_version("2.33.0pre") < parse_version("2.33")); + assert!(parse_version("2.33.0pre") < parse_version("2.33.0")); + + // Normal version ordering + assert!(parse_version("2.26") < parse_version("2.33")); + assert!(parse_version("2.33") < parse_version("2.33.1")); + } +}