nu_build/
lib.rs

1use lazy_static::lazy_static;
2use serde::Deserialize;
3use std::collections::BTreeMap;
4use std::collections::HashMap;
5use std::collections::HashSet;
6use std::env;
7use std::path::{Path, PathBuf};
8use std::sync::Mutex;
9
10lazy_static! {
11    static ref WORKSPACES: Mutex<BTreeMap<String, &'static Path>> = Mutex::new(BTreeMap::new());
12}
13
14// got from https://github.com/mitsuhiko/insta/blob/b113499249584cb650150d2d01ed96ee66db6b30/src/runtime.rs#L67-L88
15
16fn get_cargo_workspace(manifest_dir: &str) -> Result<Option<&Path>, Box<dyn std::error::Error>> {
17    let mut workspaces = WORKSPACES.lock()?;
18    if let Some(rv) = workspaces.get(manifest_dir) {
19        Ok(Some(rv))
20    } else {
21        #[derive(Deserialize)]
22        struct Manifest {
23            workspace_root: String,
24        }
25        let output = std::process::Command::new(env!("CARGO"))
26            .arg("metadata")
27            .arg("--format-version=1")
28            .current_dir(manifest_dir)
29            .output()?;
30        let manifest: Manifest = serde_json::from_slice(&output.stdout)?;
31        let path = Box::leak(Box::new(PathBuf::from(manifest.workspace_root)));
32        workspaces.insert(manifest_dir.to_string(), path.as_path());
33        Ok(workspaces.get(manifest_dir).cloned())
34    }
35}
36
37#[derive(Deserialize)]
38struct Feature {
39    #[allow(unused)]
40    description: String,
41    enabled: bool,
42}
43
44pub fn build() -> Result<(), Box<dyn std::error::Error>> {
45    let input = env::var("CARGO_MANIFEST_DIR")?;
46
47    let all_on = env::var("NUSHELL_ENABLE_ALL_FLAGS").is_ok();
48    let flags: HashSet<String> = env::var("NUSHELL_ENABLE_FLAGS")
49        .map(|s| s.split(',').map(|s| s.to_string()).collect())
50        .unwrap_or_else(|_| HashSet::new());
51
52    if all_on && !flags.is_empty() {
53        println!(
54            "cargo:warning=Both NUSHELL_ENABLE_ALL_FLAGS and NUSHELL_ENABLE_FLAGS were set. You don't need both."
55        );
56    }
57
58    let workspace = match get_cargo_workspace(&input)? {
59        // If the crate is being downloaded from crates.io, it won't have a workspace root, and that's ok
60        None => return Ok(()),
61        Some(workspace) => workspace,
62    };
63
64    let path = Path::new(&workspace).join("features.toml");
65
66    // If the crate is being downloaded from crates.io, it won't have a features.toml, and that's ok
67    if !path.exists() {
68        return Ok(());
69    }
70
71    let toml: HashMap<String, Feature> = toml::from_str(&std::fs::read_to_string(path)?)?;
72
73    for (key, value) in toml.iter() {
74        if value.enabled || all_on || flags.contains(key) {
75            println!("cargo:rustc-cfg={}", key);
76        }
77    }
78
79    Ok(())
80}