From d6dc4e3b11992d2721ed9c787c8a266503080348 Mon Sep 17 00:00:00 2001 From: Michael Ilyin Date: Mon, 18 Aug 2025 21:38:11 +0200 Subject: [PATCH] featues api based on parsing cargo files --- Cargo.toml | 3 +- prebindgen/Cargo.toml | 1 + prebindgen/src/api/buildrs.rs | 80 ++++++++++++++++++++++++++++------- prebindgen/src/lib.rs | 4 +- 4 files changed, 71 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e99a215..f036589 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,8 @@ roxygen = "1.0.4" tempfile = "3.0" itertools = "0.14.0" if_rust_version = "1.0" -konst = "0.3.0" +konst = "0.3.0" # old one for for rust 1.75 +toml = "0.9.5" [patch] diff --git a/prebindgen/Cargo.toml b/prebindgen/Cargo.toml index f367692..5c28a05 100644 --- a/prebindgen/Cargo.toml +++ b/prebindgen/Cargo.toml @@ -24,6 +24,7 @@ roxygen = { workspace = true } if_rust_version = { workspace = true } konst = { workspace = true, features = ["cmp"] } itertools = { workspace = true } +toml = { workspace = true } [features] debug = [] diff --git a/prebindgen/src/api/buildrs.rs b/prebindgen/src/api/buildrs.rs index f1e031b..1f2537b 100644 --- a/prebindgen/src/api/buildrs.rs +++ b/prebindgen/src/api/buildrs.rs @@ -1,4 +1,4 @@ -use std::{collections::BTreeSet, env, fs}; +use std::{collections::BTreeSet, env, fs, path::Path}; use crate::{CRATE_NAME_FILE, FEATURES_FILE}; @@ -62,7 +62,7 @@ pub fn init_prebindgen_out_dir() { ); }); - let features = get_features(); + let features = get_enabled_features(); // Save features list to features.txt (one per line) let features_path = prebindgen_dir.join(FEATURES_FILE); @@ -92,22 +92,72 @@ pub fn init_prebindgen_out_dir() { ); } -/// Collect enabled Cargo features from environment and store them -/// Cargo exposes enabled features to build.rs as env vars CARGO_FEATURE_ -/// where is uppercased. -/// TODO: there is ambiguity in feature names with '-' and '_' -/// Currently this is ignored, the projects with features with hyphens will -/// not work correctly with this implementation. -pub fn get_features() -> BTreeSet { +/// Read all feature names declared in the current crate's `Cargo.toml`. +/// +/// Notes: +/// - Excludes the special `default` feature from the returned set. +/// - Panics if called outside of build.rs (requires OUT_DIR and CARGO_MANIFEST_DIR). +pub fn get_all_features() -> BTreeSet { + // Ensure we are in build.rs context + env::var("OUT_DIR").expect( + "OUT_DIR environment variable not set. This function should be called from build.rs.", + ); + let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect( + "CARGO_MANIFEST_DIR environment variable not set. This function should be called from build.rs.", + ); + let manifest_path = Path::new(&manifest_dir).join("Cargo.toml"); + + let manifest_content = fs::read_to_string(&manifest_path).unwrap_or_else(|e| { + panic!( + "Failed to read Cargo.toml at {}: {}", + manifest_path.display(), + e + ) + }); + + let doc: toml::Value = toml::from_str(&manifest_content).unwrap_or_else(|e| { + panic!( + "Failed to parse Cargo.toml at {} as TOML: {}", + manifest_path.display(), + e + ) + }); + + let mut set = BTreeSet::new(); + if let Some(features_tbl) = doc.get("features").and_then(|v| v.as_table()) { + for key in features_tbl.keys() { + if key != "default" { + set.insert(key.to_string()); + } + } + } + set +} + +/// Check whether a feature is enabled by looking at the corresponding +/// `CARGO_FEATURE_` environment variable provided to build scripts by Cargo. +/// Hyphens in feature names are converted to underscores to match Cargo's env var format. +pub fn is_feature_enabled(feature: &str) -> bool { + // Ensure we are in build.rs context env::var("OUT_DIR").expect( "OUT_DIR environment variable not set. This function should be called from build.rs.", ); - std::env::vars() - .filter_map(|(k, _)| { - k.strip_prefix("CARGO_FEATURE_") - .map(|name| name.to_string()) - }) - .map(|name| name.to_lowercase()) + let env_key = format!( + "CARGO_FEATURE_{}", + feature + .to_ascii_uppercase() + .chars() + .map(|c| if c == '-' { '_' } else { c }) + .collect::() + ); + env::var_os(env_key).is_some() +} + +/// Filter the full features list to only those that are currently enabled. +pub fn get_enabled_features() -> BTreeSet { + get_all_features() + .into_iter() + .filter(|f| is_feature_enabled(f)) .collect() } diff --git a/prebindgen/src/lib.rs b/prebindgen/src/lib.rs index dae8801..f946850 100644 --- a/prebindgen/src/lib.rs +++ b/prebindgen/src/lib.rs @@ -125,9 +125,11 @@ pub(crate) mod api; pub(crate) mod codegen; pub(crate) mod utils; +pub use crate::api::buildrs::get_all_features; +pub use crate::api::buildrs::get_enabled_features; pub use crate::api::buildrs::get_prebindgen_out_dir; pub use crate::api::buildrs::init_prebindgen_out_dir; -pub use crate::api::buildrs::get_features; +pub use crate::api::buildrs::is_feature_enabled; pub use crate::api::record::SourceLocation; pub use crate::api::source::Source;