1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
//! A low-level libR binding library which is kept deliberately
//! minimal.
//!
//! In particular, it has no external dependencies other that libR
//! installed on the target.
//!
//! ## Synopsis
//!
//! The `libR-sys` crate is a low level bindgen wrapper for the R
//! programming language. The intention is to allow one or more extension
//! mechanisms to be implemented for rust.
//!
//! Effort to make the extension libraries platform-independent can be
//! concentrated here.
//!
//! # Examples
//!
//! ```no_run
//! use libR_sys::{Rf_initialize_R, R_CStackLimit, setup_Rmainloop};
//! use std::os::raw;
//!
//! unsafe {
//! std::env::set_var("R_HOME", "/usr/lib/R");
//! let arg0 = "R\0".as_ptr() as *mut raw::c_char;
//! Rf_initialize_R(1, [arg0].as_mut_ptr());
//! R_CStackLimit = usize::max_value();
//! setup_Rmainloop();
//! }
//! ```
//!
//! # Conditional compilation depending on R installation
//!
//! The libR-sys crate provides these environmental variables that you can use in `build.rs`:
//!
//! - `DEP_R_R_VERSION_MAJOR`: The major part of the R version (e.g. `4` in version `4.1.0`)
//! - `DEP_R_R_VERSION_MINOR`: The minor part of the R version (e.g. `1` in version `4.1.0`)
//! - `DEP_R_R_VERSION_PATCH`: The patch part of the R version (e.g. `0` in version `4.1.0`)
//! - `DEP_R_R_VERSION_DEVEL`: `true` if the R is a development version, otherwise `false`
//! - `DEP_R_R_VERSION_STRING`: The full version string (e.g. `R version 4.1.0 (2021-05-18)`)
//! - `DEP_R_R_HOME`: The R home directory
//!
//! ## Example `build.rs`
//!
//! ```ignore
//! use std::env;
//!
//! fn main() {
//! // Set R_HOME envvar, and refer to it on compile time by env!("R_HOME")
//! let r_home = env::var("DEP_R_R_HOME").unwrap();
//! println!("cargo:rustc-env=R_HOME={}", r_home);
//!
//! // Enable cfg setting to conditionally compile a code using a feature
//! // available only on newer versions of R
//! let major = env::var("DEP_R_R_VERSION_MAJOR").unwrap();
//! let minor = env::var("DEP_R_R_VERSION_MINOR").unwrap();
//! if &*major >= "4" && &*minor >= "1" {
//! println!("cargo:rustc-cfg=use_a_feature");
//! }
//! }
//! ```
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(improper_ctypes)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
#[cfg(test)]
mod tests {
use super::*;
use std::os::raw;
// Generate constant static strings.
// Much more efficient than CString.
// Generates asciiz.
macro_rules! cstr {
($s: expr) => {
concat!($s, "\0").as_ptr() as *const raw::c_char
};
}
// Generate mutable static strings.
// Much more efficient than CString.
// Generates asciiz.
macro_rules! cstr_mut {
($s: expr) => {
concat!($s, "\0").as_ptr() as *mut raw::c_char
};
}
// Thanks to @qinwf and @scottmmjackson for showing the way here.
fn start_R() {
unsafe {
if std::env::var("R_HOME").is_err() {
// env! gets the build-time R_HOME made in build.rs
std::env::set_var("R_HOME", env!("R_HOME"));
}
// Due to Rf_initEmbeddedR using __libc_stack_end
// We can't call Rf_initEmbeddedR.
// Instead we must follow rustr's example and call the parts.
//let res = unsafe { Rf_initEmbeddedR(1, args.as_mut_ptr()) };
if cfg!(target_os = "windows") && cfg!(target_arch = "x86") {
Rf_initialize_R(
4,
[
cstr_mut!("R"),
cstr_mut!("--arch=i386"),
cstr_mut!("--slave"),
cstr_mut!("--no-save"),
]
.as_mut_ptr(),
);
} else {
Rf_initialize_R(
3,
[cstr_mut!("R"), cstr_mut!("--slave"), cstr_mut!("--no-save")].as_mut_ptr(),
);
}
// In case you are curious.
// Maybe 8MB is a bit small.
// eprintln!("R_CStackLimit={:016x}", R_CStackLimit);
if cfg!(not(target_os = "windows")) {
R_CStackLimit = usize::max_value();
}
setup_Rmainloop();
}
}
// Run some R code. Check the result.
#[test]
fn test_eval() {
start_R();
unsafe {
// In an ideal world, we would do the following.
// let res = R_ParseEvalString(cstr!("1"), R_NilValue);
// But R_ParseEvalString is only in recent packages.
let s = Rf_protect(Rf_mkString(cstr!("1")));
let mut status: ParseStatus = 0;
let status_ptr = &mut status as *mut ParseStatus;
let ps = Rf_protect(R_ParseVector(s, -1, status_ptr, R_NilValue));
let val = Rf_eval(VECTOR_ELT(ps, 0), R_GlobalEnv);
Rf_PrintValue(val);
assert_eq!(TYPEOF(val) as u32, REALSXP);
assert_eq!(*REAL(val), 1.);
Rf_unprotect(2);
}
}
}