Skip to main content

miniextendr_api/
init.rs

1//! Package initialization for miniextendr R packages.
2//!
3//! [`package_init`](crate::init::package_init) consolidates all initialization steps that were previously
4//! scattered across `entrypoint.c.in`. The `miniextendr_init!` proc macro
5//! generates the `R_init_*` entry point that calls this function.
6//!
7//! # Usage
8//!
9//! In your crate's `lib.rs`:
10//!
11//! ```ignore
12//! miniextendr_init!(mypkg);
13//! ```
14//!
15//! This expands to an `extern "C-unwind" fn R_init_mypkg(dll)` that calls
16//! [`package_init`](crate::init::package_init) with the appropriate package name.
17
18use crate::Rboolean;
19use crate::sys::{DllInfo, R_forceSymbols, R_useDynamicSymbols};
20use std::ffi::CStr;
21
22/// Env var the Makevars wrapper-gen recipe sets before `dyn.load`ing the
23/// freshly-built shared object to generate `R/*-wrappers.R`.
24///
25/// Loading the installed `.so`/`.dll` runs `R_init_<pkg>` on every platform, but
26/// during wrapper-gen that image is `dyn.unload`ed immediately afterwards. So when
27/// this var is present, init takes a minimal path: [`package_init`] skips the
28/// panic hook / locale / ALTREP+mx_abi setup, and [`miniextendr_register_routines`]
29/// skips ALTREP *class* registration — none of which must plant a pointer into an
30/// about-to-be-unloaded image (doing so risks the "malloc unsorted double linked
31/// list" heap corruption that macOS hides but Linux R aborts on).
32///
33/// SINGLE SOURCE OF TRUTH: both read-sites go through [`wrapper_gen_mode`] so the
34/// name can't drift between them. Presence-based — any value (even empty) enables
35/// it — so it MUST NOT leak into a real package-load environment, or the package
36/// loads silently degraded (no panic hook, no ALTREP classes, no mx_abi).
37///
38/// [`miniextendr_register_routines`]: crate::registry::miniextendr_register_routines
39pub(crate) const WRAPPER_GEN_ENV: &str = "MINIEXTENDR_WRAPPER_GEN";
40
41/// `true` when the package was loaded purely for wrapper generation — see
42/// [`WRAPPER_GEN_ENV`].
43pub(crate) fn wrapper_gen_mode() -> bool {
44    std::env::var_os(WRAPPER_GEN_ENV).is_some()
45}
46
47/// Initialize a miniextendr R package.
48///
49/// This performs all initialization steps in the correct order:
50///
51/// 1. Install panic hook for better error messages
52/// 2. Record main thread ID (and optionally spawn worker thread)
53/// 3. Assert UTF-8 locale
54/// 4. Set ALTREP package name
55/// 5. Register mx_abi C-callables for cross-package trait dispatch
56/// 6. Register all `#[miniextendr]` routines and ALTREP classes
57/// 7. Lock down dynamic symbols
58///
59/// # Safety
60///
61/// Must be called from R's main thread during `R_init_*`.
62/// `dll` must be a valid pointer provided by R.
63/// `pkg_name` must be a valid null-terminated C string that lives for the
64/// duration of the R session (typically a string literal).
65pub unsafe fn package_init(dll: *mut DllInfo, pkg_name: &CStr) {
66    unsafe {
67        // When loaded purely for wrapper generation (Makevars dyn.load()s the
68        // installed .so/.dll, which runs this init), skip full init. Only routine
69        // registration is needed so .Call(miniextendr_write_wrappers) works.
70        // See WRAPPER_GEN_ENV. The env var is set by Makevars before dyn.load().
71        let wrapper_gen = wrapper_gen_mode();
72
73        // 1. Record main thread ID (and optionally spawn worker thread)
74        // Always needed: checked FFI variants (R_useDynamicSymbols, etc.)
75        // route through with_r_thread() which requires runtime_init.
76        crate::worker::miniextendr_runtime_init();
77
78        if !wrapper_gen {
79            // 2. Install panic hook for better error messages
80            // Skipped during wrapper-gen: on Windows, set_hook during DLL init
81            // can fail with "failed to initiate panic, error 5" because the
82            // panic infrastructure isn't fully available during DLL loading.
83            crate::backtrace::miniextendr_panic_hook();
84
85            // 3. Assert UTF-8 locale
86            crate::encoding::miniextendr_assert_utf8_locale();
87
88            // 3b. Install R console logger (if log feature enabled)
89            #[cfg(feature = "log")]
90            crate::optionals::log_impl::install_r_logger();
91
92            // 3c. Compute SEXPREC data offset (used by Arrow SEXP recovery, etc.)
93            crate::r_memory::init_sexprec_data_offset();
94
95            // 4. Set ALTREP package name and DllInfo
96            crate::miniextendr_set_altrep_pkg_name(pkg_name.as_ptr());
97            crate::set_altrep_dll_info(dll);
98
99            // 5. Register mx_abi C-callables
100            crate::mx_abi::mx_abi_register(pkg_name);
101        }
102
103        // 6. Register .Call routines (and ALTREP classes, unless wrapper-gen)
104        crate::registry::miniextendr_register_routines(dll);
105
106        // 7. Lock down dynamic symbols
107        R_useDynamicSymbols(dll, Rboolean::FALSE);
108        R_forceSymbols(dll, Rboolean::TRUE);
109    }
110}