rb_sys_env/
rb_env.rs

1use crate::{Defines, RubyVersion};
2use std::{collections::HashMap, rc::Rc};
3
4const ENV_PREFIX: &str = "DEP_RB_";
5const RBCONFIG_PREFIX: &str = "RBCONFIG_";
6const CARGO_FEATURE_PREFIX: &str = "CARGO_FEATURE_";
7
8/// Information about the rb-sys environment.
9#[derive(Debug, Clone)]
10pub struct RbEnv {
11    defines: Defines,
12    vars: Rc<HashMap<String, String>>,
13}
14
15impl RbEnv {
16    /// The current Ruby version.
17    pub fn ruby_version(&self) -> RubyVersion {
18        RubyVersion::from_raw_environment(&self.vars)
19    }
20
21    /// The (major, minor) tuple of the current Ruby version.
22    pub fn ruby_major_minor(&self) -> (u8, u8) {
23        self.ruby_version().major_minor()
24    }
25
26    /// Get a value from the current Ruby's `RbConfig::CONFIG`.
27    pub fn get_rbconfig_value(&self, key: &str) -> Option<&str> {
28        self.vars
29            .get(&format!("{}{}", RBCONFIG_PREFIX, key))
30            .map(|v| v.as_str())
31    }
32
33    /// List the Cargo features of rb-sys
34    pub fn cargo_features(&self) -> Vec<String> {
35        let keys = self.vars.keys();
36        let keys = keys.filter(|k| k.starts_with(CARGO_FEATURE_PREFIX));
37        let keys = keys.map(|k| k.trim_start_matches(CARGO_FEATURE_PREFIX));
38
39        keys.map(|k| k.replace('_', "-").to_lowercase()).collect()
40    }
41
42    /// Tell Cargo to link to libruby, even if `rb-sys` decided not to.
43    pub fn force_link_ruby(self) -> Self {
44        let libdir = self.vars.get("LIBDIR").expect("DEP_RB_LIBDIR is not set");
45        let lib = self.vars.get("LIB").expect("DEP_RB_LIB is not set");
46
47        println!("cargo:rustc-link-search=native={}", libdir);
48        println!("cargo:rustc-link-lib={}", lib);
49
50        self
51    }
52
53    // Decodes the cargo args from `DEP_RB_ENCODED_CARGO_ARGS` environment variable.
54    pub fn encoded_cargo_args(&self) -> Vec<String> {
55        if let Some(raw_args) = self.vars.get("ENCODED_CARGO_ARGS") {
56            let lines = raw_args.split('\x1E');
57            let unescaped = lines.map(|line| line.replace('\x1F', "\n"));
58            unescaped.collect()
59        } else {
60            vec![]
61        }
62    }
63
64    /// Indicates if we are using libruby-static.
65    pub fn is_ruby_static(&self) -> bool {
66        self.vars
67            .get("RUBY_STATIC")
68            .map(|v| v == "true")
69            .unwrap_or(false)
70    }
71
72    /// Prints args for rustc (i.e. `cargo:rustc-cfg=...`).
73    pub fn print_cargo_rustc_cfg(&self) {
74        self.defines.print_cargo_rustc_cfg();
75        self.ruby_version().print_cargo_rustc_cfg();
76    }
77
78    /// Prints directives for rustc (i.e. `cargo:rustc-link-lib=...`).
79    pub fn print_encoded_cargo_args(&self) {
80        for line in self.encoded_cargo_args() {
81            println!("{}", line);
82        }
83    }
84
85    /// Prints directives for re-runs (i.e. `cargo:rerun-if-env-changed=...`)
86    pub fn print_cargo_rerun_if_changed(&self) {
87        for key in self.vars.keys() {
88            println!("cargo:rerun-if-env-changed={}{}", ENV_PREFIX, key);
89        }
90
91        println!("cargo:rerun-if-changed=build.rs");
92        println!("cargo:rerun-if-env-changed=RB_SYS_ENV_DEBUG");
93        println!("cargo:rerun-if-env-changed=RUBY");
94        println!("cargo:rerun-if-env-changed=RUBY_ROOT");
95        println!("cargo:rerun-if-env-changed=RUBY_VERSION");
96    }
97}
98
99impl Default for RbEnv {
100    fn default() -> Self {
101        let vars = std::env::vars();
102        let vars = vars.filter(|(key, _)| key.starts_with(ENV_PREFIX));
103        let vars = vars.map(|(key, value)| (key.trim_start_matches(ENV_PREFIX).to_string(), value));
104        let vars: HashMap<String, String> = vars.collect();
105        let vars = Rc::new(vars);
106        let defines = Defines::from_raw_environment(vars.clone());
107
108        Self { defines, vars }
109    }
110}