Skip to content

Commit

Permalink
Add r_command() for executing commands over R and R.bat (#599)
Browse files Browse the repository at this point in the history
* Add `r_command()` for executing commands over `R` and `R.bat`

* Rework `r_command()` for readability
  • Loading branch information
DavisVaughan authored Oct 22, 2024
1 parent e06f5fa commit e07efa9
Show file tree
Hide file tree
Showing 9 changed files with 94 additions and 17 deletions.
9 changes: 6 additions & 3 deletions crates/ark/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ use crossbeam::channel::unbounded;
use crossbeam::channel::Receiver;
use crossbeam::channel::Sender;
use crossbeam::select;
use harp::command::r_command;
use harp::environment::r_ns_env;
use harp::environment::Environment;
use harp::environment::R_ENVS;
Expand Down Expand Up @@ -355,12 +356,14 @@ impl RMain {
args.push(CString::new(arg).unwrap().into_raw());
}

// Get `R_HOME`, typically set by Positron / CI / kernel specification
// Get `R_HOME` from env var, typically set by Positron / CI / kernel specification
let r_home = match std::env::var("R_HOME") {
Ok(home) => PathBuf::from(home),
Err(_) => {
// Get `R_HOME` from `PATH`, via R
let Ok(result) = std::process::Command::new("R").arg("RHOME").output() else {
// Get `R_HOME` from `PATH`, via `R`
let Ok(result) = r_command(|command| {
command.arg("RHOME");
}) else {
panic!("Can't find R or `R_HOME`");
};
let r_home = String::from_utf8(result.stdout).unwrap();
Expand Down
25 changes: 13 additions & 12 deletions crates/ark/src/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
use std::collections::HashMap;
use std::env;
use std::path::PathBuf;
use std::process::Command;

use anyhow::Context;
use harp::command::r_command;
use harp::object::RObject;
use itertools::Itertools;
use libr::SEXP;
Expand All @@ -31,24 +31,25 @@ pub struct RVersion {
}

pub fn detect_r() -> anyhow::Result<RVersion> {
let output = Command::new("R")
.arg("RHOME")
.output()
.context("Failed to execute R to determine R_HOME")?;
let output = r_command(|command| {
command.arg("RHOME");
})
.context("Failed to execute R to determine R_HOME")?;

// Convert the output to a string
let r_home = String::from_utf8(output.stdout)
.context("Failed to convert R_HOME output to string")?
.trim()
.to_string();

let output = Command::new("R")
.arg("--vanilla")
.arg("-s")
.arg("-e")
.arg("cat(version$major, \".\", version$minor, sep = \"\")")
.output()
.context("Failed to execute R to determine version number")?;
let output = r_command(|command| {
command
.arg("--vanilla")
.arg("-s")
.arg("-e")
.arg("cat(version$major, \".\", version$minor, sep = \"\")");
})
.context("Failed to execute R to determine version number")?;

let version = String::from_utf8(output.stdout)
.context("Failed to convert R version number to a string")?
Expand Down
49 changes: 49 additions & 0 deletions crates/harp/src/command.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//
// command.rs
//
// Copyright (C) 2024 Posit Software, PBC. All rights reserved.
//
//

use std::io;
use std::process::Command;
use std::process::Output;

use crate::sys::command::COMMAND_R_LOCATIONS;

/// Execute a `Command` for R, trying multiple locations where R might exist
///
/// - For unix, this look at `R`
/// - For Windows, this looks at `R` (`R.exe`) and `R.bat` (for rig compatibility)
///
/// Returns the `Ok()` value of the first success, or the `Err()` value of the
/// last failure if all locations fail.
pub fn r_command<F>(build: F) -> io::Result<Output>
where
F: Fn(&mut Command),
{
assert!(COMMAND_R_LOCATIONS.len() > 0);

let mut out = None;

for program in COMMAND_R_LOCATIONS.iter() {
// Build the `Command` from the user's function
let mut command = Command::new(program);
build(&mut command);

// Run it, waiting on it to finish.
// Store it as `out` no matter what. If all locations fail
// we end up returning the last failure.
let result = command.output();
let ok = result.is_ok();
out = Some(result);

if ok {
// We had a successful command, don't try any more
break;
}
}

// SAFETY: The `assert!` above ensures at least 1 program location is provided
out.unwrap()
}
7 changes: 5 additions & 2 deletions crates/harp/src/fixtures/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@

use std::os::raw::c_char;
use std::path::PathBuf;
use std::process::Command;
use std::sync::Once;

use libr::setup_Rmainloop;
use libr::R_CStackLimit;
use libr::Rf_initialize_R;
use stdext::cargs;

use crate::command::r_command;
use crate::library::RLibraries;
use crate::R_MAIN_THREAD_ID;

Expand Down Expand Up @@ -53,7 +53,10 @@ pub fn r_test_init() {
let r_home = match std::env::var("R_HOME") {
Ok(r_home) => PathBuf::from(r_home),
Err(_) => {
let result = Command::new("R").arg("RHOME").output().unwrap();
let result = r_command(|command| {
command.arg("RHOME");
})
.expect("Can't locate R to determine `R_HOME`.");
let r_home = String::from_utf8(result.stdout).unwrap();
let r_home = r_home.trim();
unsafe { std::env::set_var("R_HOME", r_home) };
Expand Down
1 change: 1 addition & 0 deletions crates/harp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//
pub mod attrib;
pub mod call;
pub mod command;
pub mod data_frame;
pub mod environment;
pub mod environment_iter;
Expand Down
1 change: 1 addition & 0 deletions crates/harp/src/sys/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*
*/

pub mod command;
pub mod library;
pub mod line_ending;
pub mod polled_events;
9 changes: 9 additions & 0 deletions crates/harp/src/sys/unix/command.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* command.rs
*
* Copyright (C) 2024 Posit Software, PBC. All rights reserved.
*
*/

/// Locations on the `PATH` to look for R when using `Command::new()`
pub(crate) const COMMAND_R_LOCATIONS: [&str; 1] = ["R"];
1 change: 1 addition & 0 deletions crates/harp/src/sys/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*
*/

pub mod command;
pub mod library;
pub mod line_ending;
mod locale;
Expand Down
9 changes: 9 additions & 0 deletions crates/harp/src/sys/windows/command.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* command.rs
*
* Copyright (C) 2024 Posit Software, PBC. All rights reserved.
*
*/

/// Locations on the `PATH` to look for R when using `Command::new()`
pub(crate) const COMMAND_R_LOCATIONS: [&str; 2] = ["R", "R.bat"];

0 comments on commit e07efa9

Please sign in to comment.