-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
implement BridgeStan download and model compilation in Rust (#212)
* implement BridgeStan download and module compilation on Rust * rust: add feature compile-stan-model * rust: allow user defined stanc_args and make_args when compiling model * rust: add model_compiling test * rust: updating readme * rust: update documentation * rust: fix tests * rust: skip model_compiling() test on windows * rust: fix race condition in tests * rust: make example path portable * rust: fix windows absolute path resolution * Delete rust/.vscode/settings.json * rust: mark model_compiling test as ignored * rust: use mingw32-make to compile model on windows * rust: change println! to info! * Update README.md * Update Cargo.toml * rust: single compile error message * rust: run tests without feature compile-stan-model * rust: adding comments about std::fs::canonicalize * rust: fix --include-paths to point to model dir * rust: disable enum variant feature gating * rust: fix macos build * rust: make bridgestan src download more explicit * rust: only bridgestan_download_src is to be feature gated * unify .gitignore * test improvements * remove asref generic * fix tests * Update model.rs * Clean up Rust doc, tests --------- Co-authored-by: Brian Ward <bward@flatironinstitute.org>
- Loading branch information
Showing
11 changed files
with
291 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ c-example/example_static | |
# Rust | ||
rust/target/ | ||
rust/Cargo.lock | ||
rust/.vscode | ||
|
||
notes.org | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
use crate::bs_safe::{BridgeStanError, Result}; | ||
use log::info; | ||
use path_absolutize::Absolutize; | ||
use std::path::{Path, PathBuf}; | ||
|
||
const MAKE: &str = if cfg!(target_os = "windows") { | ||
"mingw32-make" | ||
} else { | ||
"make" | ||
}; | ||
|
||
/// Compile a Stan Model. Requires a path to the BridgeStan sources (can be | ||
/// downloaded with [`download_bridgestan_src`](crate::download_bridgestan_src) if that feature | ||
/// is enabled), a path to the `.stan` file, and additional arguments | ||
/// for the Stan compiler and the make command. | ||
pub fn compile_model( | ||
bs_path: &Path, | ||
stan_file: &Path, | ||
stanc_args: &[&str], | ||
make_args: &[&str], | ||
) -> Result<PathBuf> { | ||
// using path_absolutize crate for now since | ||
// std::fs::canonicalize doesn't behave well on windows | ||
// we may switch to std::path::absolute once it stabilizes, see | ||
// https://github.com/roualdes/bridgestan/pull/212#discussion_r1513375667 | ||
let stan_file = stan_file | ||
.absolutize() | ||
.map_err(|e| BridgeStanError::ModelCompilingFailed(e.to_string()))?; | ||
|
||
// get --include-paths=model_dir | ||
let includir_stan_file_dir = stan_file | ||
.parent() | ||
.and_then(Path::to_str) | ||
.map(|x| format!("--include-paths={x}")) | ||
.unwrap_or_default(); | ||
|
||
let includir_stan_file_dir = includir_stan_file_dir.as_str(); | ||
|
||
if stan_file.extension().unwrap_or_default() != "stan" { | ||
return Err(BridgeStanError::ModelCompilingFailed( | ||
"File must be a .stan file".to_owned(), | ||
)); | ||
} | ||
|
||
// add _model suffix and change extension to .so | ||
let output = stan_file.with_extension(""); | ||
let output = output.with_file_name(format!( | ||
"{}_model", | ||
output.file_name().unwrap_or_default().to_string_lossy() | ||
)); | ||
let output = output.with_extension("so"); | ||
|
||
let stanc_args = [&[includir_stan_file_dir], stanc_args].concat(); | ||
let stanc_args = stanc_args.join(" "); | ||
let stanc_args = format!("STANCFLAGS={}", stanc_args); | ||
let stanc_args = [stanc_args.as_str()]; | ||
|
||
let cmd = [ | ||
&[output.to_str().unwrap_or_default()], | ||
make_args, | ||
stanc_args.as_slice(), | ||
] | ||
.concat(); | ||
|
||
info!( | ||
"Compiling model with command: {} \"{}\"", | ||
MAKE, | ||
cmd.join("\" \"") | ||
); | ||
std::process::Command::new(MAKE) | ||
.args(cmd) | ||
.current_dir(bs_path) | ||
.env("STAN_THREADS", "true") | ||
.output() | ||
.map_err(|e| e.to_string()) | ||
.and_then(|proc| { | ||
if !proc.status.success() { | ||
Err(format!( | ||
"{} {}", | ||
String::from_utf8_lossy(proc.stdout.as_slice()).into_owned(), | ||
String::from_utf8_lossy(proc.stderr.as_slice()).into_owned(), | ||
)) | ||
} else { | ||
Ok(()) | ||
} | ||
}) | ||
.map_err(|e| BridgeStanError::ModelCompilingFailed(e.to_string()))?; | ||
info!("Finished compiling model"); | ||
|
||
Ok(output) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
use crate::bs_safe::{BridgeStanError, Result}; | ||
use crate::VERSION; | ||
use flate2::read::GzDecoder; | ||
use log::info; | ||
use std::{env::temp_dir, fs, path::PathBuf}; | ||
use tar::Archive; | ||
|
||
/// Download and unzip the BridgeStan source distribution for this version | ||
/// to `~/.bridgestan/bridgestan-$VERSION`. | ||
pub fn download_bridgestan_src() -> Result<PathBuf> { | ||
let homedir = dirs::home_dir().unwrap_or(temp_dir()); | ||
|
||
let bs_path_download_temp = homedir.join(".bridgestan_tmp_dir"); | ||
let bs_path_download = homedir.join(".bridgestan"); | ||
|
||
let bs_path_download_temp_join_version = | ||
bs_path_download_temp.join(format!("bridgestan-{VERSION}")); | ||
let bs_path_download_join_version = bs_path_download.join(format!("bridgestan-{VERSION}")); | ||
|
||
if !bs_path_download_join_version.exists() { | ||
info!("Downloading BridgeStan"); | ||
|
||
fs::remove_dir_all(&bs_path_download_temp).unwrap_or_default(); | ||
fs::create_dir(&bs_path_download_temp).unwrap_or_default(); | ||
fs::create_dir(&bs_path_download).unwrap_or_default(); | ||
|
||
let url = "https://github.com/roualdes/bridgestan/releases/download/".to_owned() | ||
+ format!("v{VERSION}/bridgestan-{VERSION}.tar.gz").as_str(); | ||
|
||
let response = ureq::get(url.as_str()) | ||
.call() | ||
.map_err(|e| BridgeStanError::DownloadFailed(e.to_string()))?; | ||
let len = response | ||
.header("Content-Length") | ||
.and_then(|s| s.parse::<usize>().ok()) | ||
.unwrap_or(50_000_000); | ||
|
||
let mut bytes: Vec<u8> = Vec::with_capacity(len); | ||
response | ||
.into_reader() | ||
.read_to_end(&mut bytes) | ||
.map_err(|e| BridgeStanError::DownloadFailed(e.to_string()))?; | ||
|
||
let tar = GzDecoder::new(bytes.as_slice()); | ||
let mut archive = Archive::new(tar); | ||
archive | ||
.unpack(&bs_path_download_temp) | ||
.map_err(|e| BridgeStanError::DownloadFailed(e.to_string()))?; | ||
|
||
fs::rename( | ||
bs_path_download_temp_join_version, | ||
&bs_path_download_join_version, | ||
) | ||
.map_err(|e| BridgeStanError::DownloadFailed(e.to_string()))?; | ||
|
||
fs::remove_dir(bs_path_download_temp).unwrap_or_default(); | ||
|
||
info!("Finished downloading BridgeStan"); | ||
} | ||
|
||
Ok(bs_path_download_join_version) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,15 @@ | ||
#![doc = include_str!("../README.md")] | ||
|
||
mod bs_safe; | ||
mod compile; | ||
#[cfg(feature = "download-bridgestan-src")] | ||
mod download; | ||
pub(crate) mod ffi; | ||
|
||
pub use bs_safe::{open_library, BridgeStanError, Model, Rng, StanLibrary}; | ||
pub use compile::compile_model; | ||
|
||
#[cfg(feature = "download-bridgestan-src")] | ||
pub use download::download_bridgestan_src; | ||
|
||
pub const VERSION: &str = env!("CARGO_PKG_VERSION"); |
Oops, something went wrong.