Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEATURES_LOWERCASE contains features that do not exists #74

Open
barakugav opened this issue Nov 27, 2024 · 4 comments
Open

FEATURES_LOWERCASE contains features that do not exists #74

barakugav opened this issue Nov 27, 2024 · 4 comments

Comments

@barakugav
Copy link

The FEATURES_LOWERCASE static variable is created by searching for env variables with prefix CARGO_FEATURE_, and parsing the suffixes to extract features names.
Its correct that every feature has such env variable, but not each such env variable is a real enabled feature.
For example, when I compile my executable I get these additional features that are not part of my crate:

"libc", "numpy", "pyo3"

I can't say why these environment variables were on, but its not due to exports in bashrc or something like that, probably env variables manipulations by my dependencies build scripts.
Anyway, I think a better approach is to parse Cargo.toml and extract all of the possible features of the current built crate, and than check if there is an env variable called format!(CARGO_FEATURE_{}", feature.to_uppercase().replace("-", "_")).

This is how I do it in my build.rs:

fn enabled_features() -> Vec<String> {
    let cargo_toml = Path::new(&env!("CARGO_MANIFEST_DIR")).join("Cargo.toml");
    fs::read_to_string(cargo_toml)
        .expect("Failed to read Cargo.toml")
        .parse::<toml::Table>()
        .expect("Failed to parse Cargo.toml")
        .into_iter()
        .flat_map(|features| {
            features
                .as_table()
                .expect("features must be a table")
                .keys()
                .filter(|k| *k != "default")
                .cloned()
        })
        .filter(|f| {
            env::var(format!(
                "CARGO_FEATURE_{}",
                f.to_uppercase().replace("-", "_")
            ))
            .is_ok()
        })
        .collect()
}
@lukaslueg
Copy link
Owner

I'm not convinced that what you describe is the behavior we want in the general case: The additional features you are referring to ("libc", "numpy", "pyo3") most likely stem from optional dependencies which were enabled during compilation. That is, if topcrate depends on dep1, and dep1 depends on dep2 with dep2:numpy enabled, I'd rather consider numpy to be an enabled feature. Even though topcrate didn't enable it and has to knob to enable it, it's true that numpy had been a feature that was enabled during compilation.

@barakugav
Copy link
Author

It depends on what API do you want to expose. I expect the built crate to tell me the features enabled in my crate, and not features in my dependencies.
I can't talk for others, but my guess is that most people would assume the same.

If you want to keep the behaviour as is, we can consider a new independent field of an accurate list of enabled features of only the compiled crate.

@lukaslueg
Copy link
Owner

Is there a public repo for which the behavior you observe is reproducible?

@barakugav
Copy link
Author

barakugav commented Dec 6, 2024

No, but here is a toy example:

# Cargo.toml

[package]
name = "temp"
version = "0.1.0"
edition = "2021"

[dependencies]
pyo3 = { version = "0.23", features = ["auto-initialize"], optional = true }
numpy = { version = "0.23", optional = true }

[build-dependencies]
built = "0.7"

[features]
python = ["pyo3", "numpy"]
// build.rs

fn main() {
    built::write_built_file().expect("Failed to acquire build-time information");
}
// main.rs

mod built_info {
    include!(concat!(env!("OUT_DIR"), "/built.rs"));
}

fn main() {
    println!("{:?}", built_info::FEATURES_LOWERCASE);
}

This is the output with the python feature:

> cargo run -q --features python
["numpy", "pyo3", "python"]

I would expect it to be

> cargo run -q --features python
["python"]

This example is slightly different than my original description of the issue, but I can't reproduce it exactly in this toy example and im not sure what causes it in my real application, which is not open source.
Anyway, I think most people would expect the second output, which is what my suggested code will output.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants