Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ jobs:

env:
R_REMOTES_NO_ERRORS_FROM_WARNINGS: true
REXTENDR_SKIP_DEV_TESTS: TRUE

# This environment variable enables support for pseudo multi-target cargo builds.
# Current stable Rust does not support multi-targeting,
Expand Down Expand Up @@ -238,7 +239,7 @@ jobs:
"\" }"),

# uncomment this line when we need to depend on the dev version of libR-sys
'libR-sys = { git = "https://github.com/extendr/libR-sys" }',
# 'libR-sys = { git = "https://github.com/extendr/libR-sys", features = array("non-api", 1) }',

sep = ";")

Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride

# Generated by Cargo
# will have compiled files and executables
**/target/
Expand Down
31 changes: 30 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,36 @@

## Changed

- Potentially breaking: `RArray::from_parts` no longer requires a pointer to the underlying data vector [[#657]](https://github.com/extendr/extendr/pull/657)
- [_Potentially breaking_]: `RArray::from_parts` no longer requires a pointer to the underlying data
vector [[#657]](https://github.com/extendr/extendr/pull/657)
- `#[extendr(use_try_from = true)` is now the default setting, therefore the option `use_try_from` has been removed [[#759]](https://github.com/extendr/extendr/pull/759)

#### Breaking changes

- R-devel Non-API changes:
- R's C API is being formalized. While the changes are formalized, non-API functions are hidden behind a feature flag to prevent removal from CRAN.
- Non-API [changes are in flux in R-devel](https://github.com/r-devel/r-svn/blob/71afe1e304b11f7febaa536e96817c63a7c1c7ab/src/library/tools/R/sotools.R#L564), however, CRAN has set a July 9th date to remove any package that uses non-API functions. This includes almost every extendr based package on CRAN.
- See [[Rd] clarifying and adjusting the C API for R](https://stat.ethz.ch/pipermail/r-devel/2024-June/083449.html)
- [nonAPI.txt](https://github.com/r-devel/r-svn/blob/f36c203d3a53a74d56a81d4f97a68d24993e0652/src/library/tools/R/sotools.R#L564) functions are hidden behind the `non-api` feature flag.
- Removed from default include (but may not be limited to):
- `global_var()`, `local_var()`, `base_env()`, various `Environment`, `Function`, `Primitive`, and `Promise` methods.
- `Attributes` trait now returns a mutable reference
to `Self`. [[#745]](https://github.com/extendr/extendr/pull/745). Previously `.set_attrib()` would modify an object in
place, and then return an untyped owned pointer (Robj). Instead, now we return `&mut Self`.
- In `AltRep` the `unserialize_ex`, `set_parent`, `set_envflags` are
now hidden behind the feature flag `non-api`. Also, `Promise::from_parts` is marked as non-API.
- Floating point numbers with decimal part can no longer be converted to integer types via
rounding [[#757]](https://github.com/extendr/extendr/pull/757)
- You can no longer create an `Robj` from a reference `&T`, where `T` is an `extendr`-impl. [[#759]](https://github.com/extendr/extendr/pull/759)
- You can no longer use `from_robj`, as the trait `FromRobj` as been removed. Instead, use `try_from`.
- It is no longer possible to access an R integer vector as a `&[u32]` [[#767]](https://github.com/extendr/extendr/pull/767)

### Fixed

- returning `&Self` or `&mut Self` from a method in an `#[extendr]`-impl would
result in unintended cloning [[#614]](https://github.com/extendr/extendr/issues/614)
- `TryFrom<&Robj>` and `FromRobj` for integer scalars now correctly handles conversions
from `f64` [[#757]](https://github.com/extendr/extendr/pull/757)

## 0.6.0

Expand Down
11 changes: 4 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ resolver = "2"
members = ["extendr-api", "extendr-engine", "extendr-macros", "xtask"]

[workspace.package]
version = "0.6.0"
version = "0.7.0"
authors = [
"andy-thomason <andy@andythomason.com>",
"Thomas Down",
Expand All @@ -17,16 +17,13 @@ authors = [
edition = "2021"
license = "MIT"
repository = "https://github.com/extendr/extendr"
rust-version = "1.60"
rust-version = "1.70"

[workspace.dependencies]
# When updating extendr's version, this version also needs to be updated
extendr-macros = { path = "./extendr-macros", version = "0.6.0" }
extendr-macros = { path = "./extendr-macros", version = "0.7.0" }

libR-sys = "0.6.0"

[patch.crates-io]
# When uncommenting this, do not forget to uncomment the same line in
# ./tests/extendrtests/src/rust/Cargo.toml, and "Run R integration tests using
# {rextendr}" on .github/workflows/test.yml !
libR-sys = { git = "https://github.com/extendr/libR-sys" }
libR-sys = { version = "0.7" }
12 changes: 11 additions & 1 deletion extendr-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ license.workspace = true
repository.workspace = true

# Note: it seems cargo-msrv doesn't support rust-version.workspace = true.
rust-version = "1.64"
rust-version = "1.70"

[dependencies]
libR-sys = { workspace = true }
Expand All @@ -20,6 +20,7 @@ libc = { version = "0.2", optional = true }
ndarray = { version = "0.15.3", optional = true }
num-complex = { version = "0.4", optional = true }
serde = { version = "1.0", features = ["derive"], optional = true }
faer = { version = "0.19", optional = true }

[dev-dependencies]
extendr-engine = { path = "../extendr-engine" }
Expand All @@ -38,6 +39,11 @@ result_condition = []
# but do not add functionality (such as `libR-sys/use-bindgen`) are excluded
full-functionality = ["graphics", "either", "ndarray", "num-complex", "serde"]

# Parts of the R-API are locked behind non-API, as CRAN frowns upon the presence
# of non-API items in packages. You may enable this feature, to generate
# bindings to these non-API items, and their usage in extendr
non-api = ["libR-sys/non-api"]

# libc is needed to allocate a DevDesc (c.f., https://bugs.r-project.org/show_bug.cgi?id=18292)
graphics = ["libc"]

Expand All @@ -55,3 +61,7 @@ tests-all = ["tests", "graphics"]

[package.metadata.docs.rs]
features = ["full-functionality", "libR-sys/use-bindgen"]

[[test]]
name = "non_api_tests"
required-features = ["non-api"]
4 changes: 4 additions & 0 deletions extendr-api/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,8 @@ fn main() {
if &*major >= "4" && &*minor >= "3" {
println!("cargo:rustc-cfg=use_r_altlist");
}

if &*major >= "4" && &*minor >= "4" {
println!("cargo:rustc-cfg=use_objsxp");
}
}
18 changes: 6 additions & 12 deletions extendr-api/src/functions.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate as extendr_api;
use crate::*;

#[cfg(feature = "non-api")]
/// Get a global variable from global_env() and ancestors.
/// If the result is a promise, evaulate the promise.
///
Expand All @@ -17,6 +18,7 @@ pub fn global_var<K: Into<Robj>>(key: K) -> Result<Robj> {
global_env().find_var(key)?.eval_promise()
}

#[cfg(feature = "non-api")]
/// Get a local variable from current_env() and ancestors.
///
/// If the result is a promise, evaulate the promise.
Expand Down Expand Up @@ -131,15 +133,7 @@ pub fn new_env(parent: Environment, hash: bool, capacity: i32) -> Environment {
.unwrap()
}

/// The base environment; formerly R_NilValue
///
/// ```
/// use extendr_api::prelude::*;
/// test! {
/// global_env().set_local(sym!(x), "hello");
/// assert_eq!(base_env().local(sym!(+)), Ok(r!(Primitive::from_string("+"))));
/// }
/// ```
/// The base environment; formerly `R_NilValue`
pub fn base_env() -> Environment {
unsafe { Robj::from_sexp(R_BaseEnv).try_into().unwrap() }
}
Expand Down Expand Up @@ -209,12 +203,12 @@ pub fn blank_scalar_string() -> Robj {
pub fn parse(code: &str) -> Result<Expressions> {
single_threaded(|| unsafe {
use libR_sys::*;
let mut status = 0_u32;
let status_ptr = &mut status as _;
let mut status = ParseStatus::PARSE_NULL;
let status_ptr = &mut status as *mut _;
let codeobj: Robj = code.into();
let parsed = Robj::from_sexp(R_ParseVector(codeobj.get(), -1, status_ptr, R_NilValue));
match status {
1 => parsed.try_into(),
ParseStatus::PARSE_OK => parsed.try_into(),
_ => Err(Error::ParseError(code.into())),
}
})
Expand Down
46 changes: 19 additions & 27 deletions extendr-api/src/graphics/device_driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ pub trait DeviceDriver: std::marker::Sized {

// It seems `NA` is just treated as `true`. Probably it doesn't matter much here.
// c.f. https://github.com/wch/r-source/blob/6b22b60126646714e0f25143ac679240be251dbe/src/library/grDevices/src/devPS.c#L4235
let winding = winding != 0;
let winding = winding != Rboolean::FALSE;

data.path(coords, winding, *gc, *dd);
}
Expand Down Expand Up @@ -519,7 +519,7 @@ pub trait DeviceDriver: std::marker::Sized {
rot,
// It seems `NA` is just treated as `true`. Probably it doesn't matter much here.
// c.f. https://github.com/wch/r-source/blob/6b22b60126646714e0f25143ac679240be251dbe/src/library/grDevices/src/devPS.c#L4062
interpolate != 0,
interpolate != Rboolean::FALSE,
*gc,
*dd,
);
Expand Down Expand Up @@ -589,11 +589,7 @@ pub trait DeviceDriver: std::marker::Sized {
dd: pDevDesc,
) -> Rboolean {
let data = ((*dd).deviceSpecific as *mut T).as_mut().unwrap();
if let Ok(confirm) = data.new_frame_confirm(*dd).try_into() {
confirm
} else {
false.into()
}
data.new_frame_confirm(*dd).into()
}

unsafe extern "C" fn device_driver_holdflush<T: DeviceDriver>(
Expand All @@ -610,11 +606,7 @@ pub trait DeviceDriver: std::marker::Sized {
dd: pDevDesc,
) -> Rboolean {
let data = ((*dd).deviceSpecific as *mut T).as_mut().unwrap();
if let Ok(success) = data.locator(x, y, *dd).try_into() {
success
} else {
false.into()
}
data.locator(x, y, *dd).into()
}

unsafe extern "C" fn device_driver_eventHelper<T: DeviceDriver>(dd: pDevDesc, code: c_int) {
Expand Down Expand Up @@ -753,12 +745,12 @@ pub trait DeviceDriver: std::marker::Sized {
(*p_dev_desc).gamma = 1.0;

(*p_dev_desc).canClip = match <T>::CLIPPING_STRATEGY {
ClippingStrategy::Engine => 0,
_ => 1,
ClippingStrategy::Engine => Rboolean::FALSE,
_ => Rboolean::TRUE,
};

// As described above, gamma is not supported.
(*p_dev_desc).canChangeGamma = 0;
(*p_dev_desc).canChangeGamma = Rboolean::FALSE;

(*p_dev_desc).canHAdj = CanHAdjOption::VariableAdjustment as _;

Expand All @@ -773,22 +765,22 @@ pub trait DeviceDriver: std::marker::Sized {
// A raw pointer to the data specific to the device.
(*p_dev_desc).deviceSpecific = deviceSpecific;

(*p_dev_desc).displayListOn = if <T>::USE_PLOT_HISTORY { 1 } else { 0 };
(*p_dev_desc).displayListOn = <T>::USE_PLOT_HISTORY.into();

// These are currently not used, so just set FALSE.
(*p_dev_desc).canGenMouseDown = 0;
(*p_dev_desc).canGenMouseMove = 0;
(*p_dev_desc).canGenMouseUp = 0;
(*p_dev_desc).canGenKeybd = 0;
(*p_dev_desc).canGenIdle = 0;
(*p_dev_desc).canGenMouseDown = Rboolean::FALSE;
(*p_dev_desc).canGenMouseMove = Rboolean::FALSE;
(*p_dev_desc).canGenMouseUp = Rboolean::FALSE;
(*p_dev_desc).canGenKeybd = Rboolean::FALSE;
(*p_dev_desc).canGenIdle = Rboolean::FALSE;

// The header file says:
//
// This is set while getGraphicsEvent is actively looking for events.
//
// It seems no implementation sets this, so this is probably what is
// modified on the engine's side.
(*p_dev_desc).gettingEvent = 0;
(*p_dev_desc).gettingEvent = Rboolean::FALSE;

(*p_dev_desc).activate = Some(device_driver_activate::<T>);
(*p_dev_desc).circle = Some(device_driver_circle::<T>);
Expand Down Expand Up @@ -829,7 +821,7 @@ pub trait DeviceDriver: std::marker::Sized {
(*p_dev_desc).newFrameConfirm = Some(device_driver_new_frame_confirm::<T>);

// UTF-8 support
(*p_dev_desc).hasTextUTF8 = if <T>::ACCEPT_UTF8_TEXT { 1 } else { 0 };
(*p_dev_desc).hasTextUTF8 = <T>::ACCEPT_UTF8_TEXT.into();
(*p_dev_desc).textUTF8 = if <T>::ACCEPT_UTF8_TEXT {
Some(device_driver_text::<T>)
} else {
Expand All @@ -840,7 +832,7 @@ pub trait DeviceDriver: std::marker::Sized {
} else {
None
};
(*p_dev_desc).wantSymbolUTF8 = if <T>::ACCEPT_UTF8_TEXT { 1 } else { 0 };
(*p_dev_desc).wantSymbolUTF8 = <T>::ACCEPT_UTF8_TEXT.into();

// R internals says:
//
Expand All @@ -850,7 +842,7 @@ pub trait DeviceDriver: std::marker::Sized {
//
// It seems this is used only by plot3d, so FALSE should be appropriate in
// most of the cases.
(*p_dev_desc).useRotatedTextInContour = 0;
(*p_dev_desc).useRotatedTextInContour = Rboolean::FALSE;

(*p_dev_desc).eventEnv = empty_env().get();
(*p_dev_desc).eventHelper = Some(device_driver_eventHelper::<T>);
Expand Down Expand Up @@ -900,8 +892,8 @@ pub trait DeviceDriver: std::marker::Sized {
(*p_dev_desc).deviceVersion = R_GE_definitions as _;

(*p_dev_desc).deviceClip = match <T>::CLIPPING_STRATEGY {
ClippingStrategy::Device => 1,
_ => 0,
ClippingStrategy::Device => Rboolean::TRUE,
_ => Rboolean::FALSE,
};
}

Expand Down
Loading