devela/build/
features.rs

1// devela build::features
2//
3//! Features debugging and compile flags enabling for reflexion.
4//
5
6#[cfg(feature = "__dbg")]
7use super::utils::println;
8use std::{collections::HashSet, env, sync::OnceLock};
9
10/// The set of enabled cargo features.
11pub(crate) static ENABLED_CARGO_FEATURES: OnceLock<HashSet<String>> = OnceLock::new();
12/// The set of enabled cfg flags.
13pub(crate) static ENABLED_CFG_FLAGS: OnceLock<HashSet<String>> = OnceLock::new();
14
15pub(crate) fn main() -> Result<(), std::io::Error> {
16    #[cfg(feature = "__dbg")]
17    super::utils::println_heading("Features & Configuration Flags:");
18
19    /* Collect enabled cargo features from CARGO_FEATURE_* environment variables */
20
21    ENABLED_CARGO_FEATURES.get_or_init(|| {
22        let mut features = HashSet::new();
23        for (key, _) in env::vars_os() {
24            if let Some(feature) = key.to_str().and_then(|k| k.strip_prefix("CARGO_FEATURE_")) {
25                features.insert(feature.to_lowercase());
26            }
27        }
28        features
29    });
30    #[cfg(feature = "__dbg")]
31    if let Some(f) = ENABLED_CARGO_FEATURES.get() {
32        println(&format!("Active cargo features ({}): {:?}", f.len(), f));
33        println("");
34    };
35    // Enable reflection flags based on cargo features
36    let _enabled_flags_from_features = reflection::set_ref_flags_from_cargo_features();
37    #[cfg(feature = "__dbg")]
38    {
39        println(&format!(
40            "Reflection flags auto-enabled by features ({}): {:?}",
41            _enabled_flags_from_features.len(),
42            _enabled_flags_from_features,
43        ));
44        println("");
45    }
46
47    /* Collect enabled cfg flags from both RUSTFLAGS and RUSTDOCFLAGS */
48
49    // WARNING: `RUSTDOCFLAGS` changes may not take effect immediately!
50    // ------------------------------------------------------------------
51    // Unlike `RUSTFLAGS`, changes to `RUSTDOCFLAGS` may not always trigger a rebuild.
52    // Cargo caches documentation builds, so previously compiled flags might still apply.
53    ENABLED_CFG_FLAGS.get_or_init(|| {
54        let mut cfg_flags = HashSet::new();
55        if let Ok(value) = env::var("RUSTDOCFLAGS") {
56            cfg_flags.extend(value.split_whitespace().map(String::from));
57        }
58        if let Ok(value) = env::var("CARGO_ENCODED_RUSTFLAGS") {
59            cfg_flags.extend(value.split('\x1f').map(String::from));
60        }
61        cfg_flags
62    });
63    #[cfg(feature = "__dbg")]
64    if let Some(f) = ENABLED_CFG_FLAGS.get() {
65        // IMPROVE FIXME always shows as ""
66        let filtered_flags: Vec<_> = f.iter().filter(|&f| f != "--cfg" && f != "-C").collect();
67        // let filtered_flags: Vec<_> = f.iter().collect(); // SAME FOR THIS (TEMP)
68        println(&format!(
69            "Active compiler cfg flags ({}): {:?}",
70            filtered_flags.len(),
71            filtered_flags
72        ));
73        println("");
74    }
75    // Enable reflection flags based on cfg flags (e.g., RUSTFLAGS)
76    let _enabled_flags_from_cfg_flags = reflection::set_ref_flags_from_cfg_flags();
77    #[cfg(feature = "__dbg")]
78    {
79        println(&format!(
80            "Flags auto-enabled by cfg flags ({}): {:?}",
81            _enabled_flags_from_cfg_flags.len(),
82            _enabled_flags_from_cfg_flags,
83        ));
84        println("");
85    }
86
87    Ok(())
88}
89
90/// Sets configuration options for reflection, based on enabled features.
91//
92// https://doc.rust-lang.org/reference/conditional-compilation.html#set-configuration-options
93#[rustfmt::skip]
94mod reflection {
95    use super::{ENABLED_CARGO_FEATURES, ENABLED_CFG_FLAGS};
96
97    /* FLAGS
98   -------------------------------------------------------------------------
99   - Each `FlagsFlags` entry maps `cfg_flags` (required for activation)
100     to `auto_flags` (which get enabled when any `cfg_flags` is present).
101
102   - Important: Auto-enabled flags **do not propagate recursively**.
103     If a flag (e.g., `nightly_stable`) has its own dependent flags,
104     it must be explicitly included in the `cfg_flags` list of the parent
105     (e.g., `nightly` must list `nightly_stable` if its children should be active).
106    */
107
108    /// Associates a set of automatically enabled flags with a set of `cfg` flags.
109    pub struct FlagsFlags<'a> {
110        /// Flags that are enabled when any of the `cfg_flags` are present.
111        auto_flags: &'a [&'a str],
112        /// `cfg` flags that trigger the corresponding `auto_flags`.
113        cfg_flags: &'a [&'a str],
114    }
115
116    pub const FLAGS_NIGHTLY: FlagsFlags = FlagsFlags {
117        auto_flags: &[
118            // "nightly_autodiff", // FLAG_DISABLED
119            "nightly_allocator", "nightly_bigint", "nightly_coro",
120            "nightly_doc", "nightly_float", "nightly_simd",
121            //
122            "nightly_stable",
123                "nightly_stable_next1", "nightly_stable_next2", "nightly_stable_later",
124        ],
125        cfg_flags: &["nightly"],
126    };
127        pub const FLAGS_NIGHTLY_STABLE: FlagsFlags = FlagsFlags {
128            auto_flags: &["nightly_stable_next1", "nightly_stable_next2", "nightly_stable_later"],
129            cfg_flags: &["nightly_stable"],
130        };
131        pub const FLAGS_NIGHTLY_REFLECT: FlagsFlags = FlagsFlags {
132            auto_flags: &["nightly··"],
133            cfg_flags: &[ "nightly",
134                // "nightly_autodiff", // FLAG_DISABLED
135                "nightly_allocator", "nightly_bigint", "nightly_coro",
136                "nightly_doc", "nightly_float", "nightly_simd",
137                //
138                "nightly_stable",
139                    "nightly_stable_next1", "nightly_stable_next2", "nightly_stable_later",
140            ],
141        };
142
143    /* FEATURES
144   -------------------------------------------------------------------------
145   - Each `FlagsFeatures` entry maps `features` (required for activation)
146     to `ref_flags` (which get enabled when any `features` are present).
147
148   - Features propagate **transitively**—enabling a feature activates all its
149     direct and indirect flags. (e.g., enabling `"sys"` also enables `"mem"` and,
150     through it, `"mem··"`, even though `"mem··"` isn't directly enabled in `"sys"`).
151    */
152
153    /// Associates a set of reflection flags with a set of `cfg` features.
154    pub struct FlagsFeatures<'a> {
155        /// Reflection flags enabled when any of the `features` are present.
156        ref_flags: &'a [&'a str],
157        /// Cargo features that trigger the corresponding `ref_flags`.
158        features: &'a [&'a str],
159    }
160
161    /* # miscellaneous */
162
163    pub const DEVELOPMENT: FlagsFeatures = FlagsFeatures {
164        ref_flags: &[],
165        features: &[
166            "__dbg",
167            "__no_test",
168            "__force_miri_dst",
169            // "default",
170            // "_default",
171            "_docsrs", "_docsrs_min",
172            "_docsrs_stable", "_docsrs_stable_min",
173            "_docs_max", "_docs_min",
174        ]
175    };
176
177    pub const ENVIRONMENT: FlagsFeatures = FlagsFeatures {
178        ref_flags: &[],
179        features: &["std", "alloc", "no_std"]
180    };
181
182    // In sync with ./Cargo.toml::[un][safe][st] & ./src/lib.rs::safety
183    pub const SAFE: FlagsFeatures = FlagsFeatures {
184        ref_flags: &["safe··"],
185        features: &[
186            "safe",
187            "safe_code",
188            "safe_data",
189            "safe_lang",
190            "safe_media",
191                "safe_audio", "safe_color", "safe_draw", "safe_font", "safe_image",
192            "safe_num",
193            "safe_phys",
194                "safe_time",
195            "safe_sys",
196                "safe_io", "safe_mem",
197            "safe_text",
198            "safe_ui", "safe_layout",
199            "safe_work",
200        ]
201    };
202    pub const SAFEST: FlagsFeatures = FlagsFeatures {
203        ref_flags: &[],
204        features: &["safest"],
205    };
206    pub const UNSAFE: FlagsFeatures = FlagsFeatures {
207        ref_flags: &["unsafe··"],
208        features: &[
209            "unsafe", // [11]
210            "unsafe_array", "unsafe_ffi", "unsafe_hint", "unsafe_layout",
211            "unsafe_niche", "unsafe_ptr", "unsafe_slice", "unsafe_str",
212            "unsafe_sync", "unsafe_syscall", "unsafe_thread",
213        ]
214    };
215
216    // In sync with ../Cargo.toml::dep_all & ../src/_dep.rs
217    pub const DEPENDENCY: FlagsFeatures = FlagsFeatures {
218        ref_flags: &["dep··"],
219        features: &include!{"../config/dep_all.rs"},
220    };
221
222    /* # modules */
223
224    pub const CODE: FlagsFeatures = FlagsFeatures {
225        ref_flags: &["code··"],
226        features: &["code", "error"]
227    };
228    pub const DATA: FlagsFeatures = FlagsFeatures {
229        ref_flags: &["data··"],
230        features: &["data", "hash"]
231    };
232    pub const GAME: FlagsFeatures = FlagsFeatures {
233        ref_flags: &["game··"],
234        features: &["game"]
235    };
236    pub const LANG: FlagsFeatures = FlagsFeatures {
237        ref_flags: &["lang··"],
238        features: &["lang", "glsl", "js"]
239    };
240        pub const FFI: FlagsFeatures = FlagsFeatures {
241            ref_flags: &["ffi··"],
242            features: &["glsl", "js"]
243        };
244    pub const MEDIA: FlagsFeatures = FlagsFeatures {
245        ref_flags: &["media··"],
246        features: &["media", "audio", "color", "draw", "font", "image"]
247    };
248    pub const NUM: FlagsFeatures = FlagsFeatures {
249        ref_flags: &["num··"],
250        features: &["num",
251            "geom",
252                "linear", "metric", "shape",
253            "prim",
254                "cast", "join", "split",
255            "rand",
256            "unit",
257        ]
258    };
259        pub const GEOM: FlagsFeatures = FlagsFeatures {
260            ref_flags: &["geom··"],
261            features: &["geom", "linear", "metric", "shape"]
262        };
263        pub const PRIM: FlagsFeatures = FlagsFeatures {
264            ref_flags: &["prim··"],
265            features: &["prim", "cast", "join", "split"]
266        };
267    pub const PHYS: FlagsFeatures = FlagsFeatures {
268        ref_flags: &["phys··"],
269        features: &["phys", "time", "wave"]
270    };
271    pub const SYS: FlagsFeatures = FlagsFeatures {
272        ref_flags: &["sys··"],
273        features: &["sys", "io",
274            "mem", "bit",
275            /* os: */ "linux", "windows"]
276    };
277        // RETHINK:
278        pub const MEM: FlagsFeatures = FlagsFeatures {
279            ref_flags: &["mem··"],
280            features: &["mem", "bit"]
281        };
282    pub const TEXT: FlagsFeatures = FlagsFeatures {
283        ref_flags: &["text··"],
284        features: &["text", "ascii", "fmt", "str"]
285    };
286    pub const UI: FlagsFeatures = FlagsFeatures {
287        ref_flags: &["ui··"],
288        features: &[
289            "ui", "layout",
290            /* front: */ "desk", "term", "web",
291            "dep_crossterm", "dep_fltk", "dep_girls", "dep_miniquad", "dep_sdl2", "dep_sdl3",
292        ]
293    };
294    pub const WORK: FlagsFeatures = FlagsFeatures {
295        ref_flags: &["work··"],
296        features: &["work", "process", "sync", "thread"]
297    };
298
299    /* # capabilities */
300
301    /* ## code */
302
303    pub const UNROLL: FlagsFeatures = FlagsFeatures {
304        ref_flags: &[],
305        features: &[
306            "_unroll", "_unroll_128", "_unroll_256", "_unroll_512", "_unroll_1024", "_unroll_2048",
307        ]
308    };
309
310    /* ## data */
311
312    pub const BIT: FlagsFeatures = FlagsFeatures {
313        ref_flags: &["_bit··"],
314        features: &[
315            "_bit_i8", "_bit_i16", "_bit_i32", "_bit_i64", "_bit_i128", "_bit_isize",
316            "_bit_u8", "_bit_u16", "_bit_u32", "_bit_u64", "_bit_u128", "_bit_usize",
317        ]
318    };
319    pub const TUPLE: FlagsFeatures = FlagsFeatures {
320        ref_flags: &[],
321        features: &["_tuple", "_tuple_24", "_tuple_36", "_tuple_48", "_tuple_72"]
322    };
323
324    // ### collections
325    pub const DESTAQUE: FlagsFeatures = FlagsFeatures {
326        ref_flags: &["_destaque··"],
327        features: &["_destaque_u8", "_destaque_u16", "_destaque_u32", "_destaque_usize"]
328    };
329    pub const GRAPH: FlagsFeatures = FlagsFeatures {
330        ref_flags: &["_graph··"],
331        features: &["_graph_u8", "_graph_u16", "_graph_u32", "_graph_usize"]
332    };
333    pub const NODE: FlagsFeatures = FlagsFeatures {
334        ref_flags: &["_node··"],
335        features: &["_node_u8", "_node_u16", "_node_u32", "_node_usize"]
336    };
337    pub const STACK: FlagsFeatures = FlagsFeatures {
338        ref_flags: &["_stack··"],
339        features: &["_stack_u8", "_stack_u16", "_stack_u32", "_stack_usize"] };
340
341    pub const SORT_INT: FlagsFeatures = FlagsFeatures {
342        ref_flags: &["_sort··", "_sort_int··"],
343        features: &[
344            "_sort_i8", "_sort_i16", "_sort_i32", "_sort_i64", "_sort_i128", "_sort_isize",
345            "_sort_u8", "_sort_u16", "_sort_u32", "_sort_u64", "_sort_u128", "_sort_usize",
346        ]
347    };
348    pub const SORT_FLOAT: FlagsFeatures = FlagsFeatures {
349        ref_flags: &["_sort··", "_sort_float··"],
350        features: &["_sort_f32", "_sort_f64"]
351    };
352
353    /* ## num */
354
355    pub const CMP: FlagsFeatures = FlagsFeatures {
356        ref_flags: &["_cmp··"],
357        features: &[
358            "_cmp_i8", "_cmp_i16", "_cmp_i32", "_cmp_i64", "_cmp_i128", "_cmp_isize",
359            "_cmp_u8", "_cmp_u16", "_cmp_u32", "_cmp_u64", "_cmp_u128",
360            "_cmp_f32", "_cmp_f64",
361        ]
362    };
363
364    // ### numbers
365    pub const FLOAT: FlagsFeatures = FlagsFeatures {
366        ref_flags: &["_float··", "_nums··"],
367        features: &["_float_f32", "_float_f64"] };
368    pub const INT: FlagsFeatures = FlagsFeatures {
369        ref_flags: &["_int_i··", "_int··", "_nums··"],
370        features: &["_int_i8", "_int_i16", "_int_i32", "_int_i64", "_int_i128", "_int_isize"] };
371    pub const UINT: FlagsFeatures = FlagsFeatures {
372        ref_flags: &["_int_u··", "_int··", "_nums··"],
373        features: &["_int_u8", "_int_u16", "_int_u32", "_int_u64", "_int_u128", "_int_usize"] };
374
375    /* ## text */
376
377    pub const STRING_U: FlagsFeatures = FlagsFeatures {
378        ref_flags: &["_str··", "_str_u··"],
379        features: &["_str_u8", "_str_u16", "_str_u32", "_str_usize"] };
380    pub const STRING: FlagsFeatures = FlagsFeatures {
381        ref_flags: &["_str··"],
382        features: &["_str_nonul"] };
383    pub const CHAR: FlagsFeatures = FlagsFeatures {
384        ref_flags: &["_char··"],
385        features: &["_char7", "_char8", "_char16", "_char24", "_char32"] };
386
387    // function helpers
388    // -------------------------------------------------------------------------
389
390    /* cargo features */
391
392    /// Sets the reflection flags for all the corresponding enabled cargo features from the list.
393    ///
394    /// This is the list of the constants defined above.
395    pub(super) fn set_ref_flags_from_cargo_features() -> Vec<String> {
396        let mut enabled_ref_flags = Vec::new();
397
398        for ff in [
399            /* development */
400
401            DEVELOPMENT,
402            ENVIRONMENT,
403            SAFE, SAFEST, UNSAFE,
404            DEPENDENCY,
405
406            /* modules */
407
408            CODE,
409            DATA,
410            GAME,
411            LANG, FFI,
412            MEDIA,
413            NUM, GEOM, PRIM,
414            PHYS,
415            SYS, MEM,
416            TEXT,
417            UI,
418            WORK,
419
420            /* capabilities */
421
422            // code
423            UNROLL,
424            // data
425            BIT, TUPLE,
426            DESTAQUE, GRAPH, NODE, STACK, // collections
427            SORT_INT, SORT_FLOAT,
428            // text
429            CHAR, STRING, STRING_U,
430            // num
431            CMP,
432            FLOAT, INT, UINT, // numbers
433
434        ] { set_flags_dbg_features(ff.ref_flags, ff.features, &mut enabled_ref_flags); }
435
436        enabled_ref_flags
437    }
438    /// Sets reflection flags if some **FEATURES** are enabled.
439    ///
440    /// - flag_names: The name of the reflection flag to set if any feature is enabled.
441    /// - features:   The cargo features names to check.
442    fn set_flags_dbg_features(ref_flags: &[&str], features: &[&str], enabled: &mut Vec<String>) {
443        let is_enabled = features.iter().any(|&f| ENABLED_CARGO_FEATURES.get().unwrap().contains(f));
444        if is_enabled {
445            for flag in ref_flags {
446                println!("cargo:rustc-cfg={flag}");
447                enabled.push(flag.to_string());
448            }
449        }
450    }
451
452    /* cfg flags */
453
454    /// Sets automatic flags, based on enabled cfg flags.
455    pub(super) fn set_ref_flags_from_cfg_flags() -> Vec<String> {
456        let mut enabled_ref_flags = Vec::new();
457        for ff in [
458            FLAGS_NIGHTLY, FLAGS_NIGHTLY_STABLE, FLAGS_NIGHTLY_REFLECT,
459        ] {
460            set_flags_dbg_flags(ff.auto_flags, ff.cfg_flags, &mut enabled_ref_flags);
461        }
462        enabled_ref_flags
463    }
464    /// Sets automatic flags if some **FLAGS** are enabled.
465    ///
466    /// - flag_names: The name of the reflection flag to set if any cfg flag is enabled.
467    /// - cfg_flags:   The cfg flags names to check.
468    fn set_flags_dbg_flags(ref_flags: &[&str], cfg_flags: &[&str], enabled: &mut Vec<String>) {
469        let is_enabled = cfg_flags.iter().any(|&f| ENABLED_CFG_FLAGS.get().unwrap().contains(f));
470        if is_enabled {
471            for flag in ref_flags {
472                println!("cargo:rustc-cfg={flag}");
473                enabled.push(flag.to_string());
474            }
475        }
476    }
477}