Nothing
## Vendor SUNDIALS C source files and public headers from the sundialr source
## tarball into src/ (sources) and src/sundials_inc/ (headers).
##
## The sources and headers MUST come from the same SUNDIALS release; vendoring
## both keeps them in lock-step (https://github.com/nlmixr2/rxode2/issues/1155).
##
## Run this script from the package root when:
## - upgrading to a new SUNDIALS/sundialr version
## - re-generating vendored files after a clean checkout that lost them
## - the committed src/sundials_*.c or src/sundials_inc files need refreshing
##
## Usage (from the package root):
## Rscript build/vendor-sundials.R
## # or from within R:
## source("build/vendor-sundials.R")
##
## After running, review any diff in src/ and commit the updated files.
if (!nzchar(Sys.getenv("RSCRIPT_IS_MAIN"))) {
## Allow sourcing without side-effects during devtools::document()
}
.pkg_root <- tryCatch(devtools::package_file(), error = function(e) getwd())
.old <- setwd(.pkg_root)
on.exit(setwd(.old), add = TRUE)
## sundialr is NOT a dependency of rxode2; it is only the vendoring source.
## Pick the version via the SUNDIALR_VERSION env var, or fall back to the
## locally installed sundialr if there is one.
.sdr_ver <- Sys.getenv("SUNDIALR_VERSION", "")
if (!nzchar(.sdr_ver)) {
.sdr_ver <- tryCatch(as.character(packageVersion("sundialr")),
error = function(e) {
stop("Set SUNDIALR_VERSION (e.g. 'SUNDIALR_VERSION=0.1.7 ",
"Rscript build/vendor-sundials.R') or install sundialr.",
call. = FALSE)
})
}
message("Vendoring SUNDIALS from sundialr ", .sdr_ver, " ...")
## File mapping: path inside the SUNDIALS source tree -> dst in src/
.map <- c(
"cvode/cvode.c" = "sundials_cvode.c",
"cvode/cvode_diag.c" = "sundials_cvode_diag.c",
"cvode/cvode_io.c" = "sundials_cvode_io.c",
"cvode/cvode_ls.c" = "sundials_cvode_ls.c",
"cvode/cvode_nls.c" = "sundials_cvode_nls.c",
"cvode/cvode_proj.c" = "sundials_cvode_proj.c",
"cvode/cvode_impl.h" = "cvode_impl.h",
"cvode/cvode_diag_impl.h" = "cvode_diag_impl.h",
"cvode/cvode_ls_impl.h" = "cvode_ls_impl.h",
"cvode/cvode_proj_impl.h" = "cvode_proj_impl.h",
"nvector/serial/nvector_serial.c" = "sundials_nvector_serial.c",
"sunmemory/system/sundials_system_memory.c" = "sundials_system_memory.c",
"sunmatrix/band/sunmatrix_band.c" = "sundials_sunmatrix_band.c",
"sunmatrix/dense/sunmatrix_dense.c" = "sundials_sunmatrix_dense.c",
"sunmatrix/sparse/sunmatrix_sparse.c" = "sundials_sunmatrix_sparse.c",
"sunlinsol/band/sunlinsol_band.c" = "sundials_sunlinsol_band.c",
"sunlinsol/dense/sunlinsol_dense.c" = "sundials_sunlinsol_dense.c",
"sunlinsol/spgmr/sunlinsol_spgmr.c" = "sunlinsol_spgmr.c",
"sunlinsol/spbcgs/sunlinsol_spbcgs.c" = "sunlinsol_spbcgs.c",
"sunlinsol/sptfqmr/sunlinsol_sptfqmr.c" = "sunlinsol_sptfqmr.c",
"sunnonlinsol/newton/sunnonlinsol_newton.c" = "sundials_sunnonlinsol_newton.c",
"sunnonlinsol/fixedpoint/sunnonlinsol_fixedpoint.c"= "sundials_sunnonlinsol_fixedpoint.c",
"sundials/sundials_adiak_metadata.h" = "sundials_adiak_metadata.h",
"sundials/sundials_cli.h" = "sundials_cli.h",
"sundials/sundials_datanode.h" = "sundials_datanode.h",
"sundials/sundials_hashmap_impl.h" = "sundials_hashmap_impl.h",
"sundials/sundials_iterative_impl.h" = "sundials_iterative_impl.h",
"sundials/sundials_logger_impl.h" = "sundials_logger_impl.h",
"sundials/sundials_macros.h" = "sundials_macros.h",
"sundials/sundials_profiler_impl.h" = "sundials_profiler_impl.h",
"sundials/sundials_utils.h" = "sundials_utils.h",
"sundials/sundials_debug.h" = "sundials_debug.h",
"sundials/sundials_band.c" = "sundials_sundials_band.c",
"sundials/sundials_cli.c" = "sundials_sundials_cli.c",
"sundials/sundials_context.c" = "sundials_sundials_context.c",
"sundials/sundials_dense.c" = "sundials_sundials_dense.c",
"sundials/sundials_direct.c" = "sundials_sundials_direct.c",
"sundials/sundials_errors.c" = "sundials_sundials_errors.c",
"sundials/sundials_hashmap.c" = "sundials_sundials_hashmap.c",
"sundials/sundials_iterative.c" = "sundials_sundials_iterative.c",
"sundials/sundials_linearsolver.c" = "sundials_sundials_linearsolver.c",
"sundials/sundials_logger.c" = "sundials_sundials_logger.c",
"sundials/sundials_math.c" = "sundials_sundials_math.c",
"sundials/sundials_matrix.c" = "sundials_sundials_matrix.c",
"sundials/sundials_memory.c" = "sundials_sundials_memory.c",
"sundials/sundials_nonlinearsolver.c" = "sundials_sundials_nonlinearsolver.c",
"sundials/sundials_nvector.c" = "sundials_sundials_nvector.c",
"sundials/sundials_profiler.c" = "sundials_sundials_profiler.c",
"sundials/sundials_version.c" = "sundials_sundials_version.c",
"sundials/stl/sunstl_vector.h" = file.path("stl", "sunstl_vector.h")
)
## ---------------------------------------------------------------------------
## Locate or download the sundialr SOURCE tarball.
## Prefer a cached copy; fall back to a direct CRAN download so we always get
## the true source (not a posit RSPM binary served at src/contrib/).
## ---------------------------------------------------------------------------
.tarball_name <- paste0("sundialr_", .sdr_ver, ".tar.gz")
.search_dirs <- c(tempdir(),
Sys.getenv("TEMP", unset = ""),
Sys.getenv("TMP", unset = ""),
tools::R_user_dir("cache", which = "cache"),
getwd())
.search_dirs <- .search_dirs[nzchar(.search_dirs)]
.tarball <- Filter(file.exists, file.path(.search_dirs, .tarball_name))
.tarball <- if (length(.tarball)) .tarball[1] else character(0)
if (!length(.tarball) && !nzchar(Sys.getenv("SUNDIALR_VERSION"))) {
## Try download.packages() with the configured repos first (may be RSPM).
## If that produces a binary (no nested tarball), we fall back below.
## Skipped when SUNDIALR_VERSION is set: download.packages() always fetches
## the repo's CURRENT release and would silently replace the pinned version.
.tarball <- tryCatch(
utils::download.packages("sundialr", destdir = tempdir(),
repos = getOption("repos"), type = "source")[1, 2],
error = function(e) character(0)
)
if (length(.tarball) && basename(.tarball) != .tarball_name) .tarball <- character(0)
}
if (!length(.tarball) || !file.exists(.tarball)) {
## Fall back to the canonical CRAN source URLs -- guaranteed to be the real
## source tarball regardless of which mirror/RSPM the session is using.
## Superseded versions live under src/contrib/Archive/sundialr/.
.dest <- file.path(tempdir(), .tarball_name)
.cran_urls <- paste0("https://cran.r-project.org/src/contrib/",
c("", "Archive/sundialr/"), .tarball_name)
.ok <- FALSE
for (.u in .cran_urls) {
message("Downloading ", .u)
.ok <- tryCatch({
download.file(.u, .dest, mode = "wb", quiet = FALSE)
TRUE
}, error = function(e) FALSE)
if (.ok) break
}
if (!.ok) {
stop("Could not download the sundialr ", .sdr_ver, " source tarball from ",
"CRAN (tried src/contrib and src/contrib/Archive).", call. = FALSE)
}
.tarball <- .dest
}
## ---------------------------------------------------------------------------
## Extract and locate the SUNDIALS source tree.
##
## sundialr packages the SUNDIALS source in one of two ways:
## (a) as a nested tarball sundialr/src/sundials-mod-<ver>.tar.gz
## (b) as flat source files directly under sundialr/src/sundials-<ver>/
## We handle both.
## ---------------------------------------------------------------------------
.td <- tempfile("sundialr-vendor-")
dir.create(.td)
on.exit(unlink(.td, recursive = TRUE), add = TRUE)
message("Extracting ", .tarball, " ...")
utils::untar(.tarball, exdir = .td)
## Strategy (a): nested tarball
.nested <- list.files(file.path(.td, "sundialr", "src"),
pattern = "^sundials-mod-.*\\.tar\\.gz$",
full.names = TRUE)
if (length(.nested)) {
message("Found nested SUNDIALS tarball: ", basename(.nested[1]))
utils::untar(.nested[1], exdir = .td)
}
## Find the extracted SUNDIALS source root (either from nested or flat layout)
.sun_dirs <- list.dirs(.td, recursive = FALSE, full.names = TRUE)
.sun_dirs <- .sun_dirs[grepl("sundials[-_][0-9]", basename(.sun_dirs))]
if (!length(.sun_dirs)) {
## Also try one level deeper in case it was inside sundialr/
.sun_dirs <- list.dirs(file.path(.td, "sundialr"), recursive = FALSE, full.names = TRUE)
.sun_dirs <- .sun_dirs[grepl("sundials[-_][0-9]", basename(.sun_dirs))]
}
if (!length(.sun_dirs)) {
stop("Unable to locate SUNDIALS source tree inside sundialr tarball.\n",
"Checked: ", .td, "\n",
"Run 'list.dirs(\"", .td, "\", recursive=TRUE)' to inspect the layout.",
call. = FALSE)
}
.sun <- .sun_dirs[1]
message("Using SUNDIALS source tree: ", basename(.sun))
## ---------------------------------------------------------------------------
## Copy files
## ---------------------------------------------------------------------------
suppressWarnings(dir.create("src/stl", recursive = TRUE, showWarnings = FALSE))
.copied <- character(0)
.missing <- character(0)
for (.rel in names(.map)) {
.src <- file.path(.sun, "src", .rel)
.dst <- file.path("src", .map[[.rel]])
if (!file.exists(.src)) {
.missing <- c(.missing, .rel)
next
}
dir.create(dirname(.dst), recursive = TRUE, showWarnings = FALSE)
file.copy(.src, .dst, overwrite = TRUE)
.copied <- c(.copied, .map[[.rel]])
}
if (length(.missing)) {
warning("The following source files were not found in the SUNDIALS tree and were skipped:\n ",
paste(.missing, collapse = "\n "), call. = FALSE)
}
message("Copied ", length(.copied), " files to src/.")
## ---------------------------------------------------------------------------
## Vendor the complete SUNDIALS public header tree into src/sundials_inc/.
## sundialr ships it as inst/include; the subdirectory structure must be
## preserved because the sources use the '#include <cvode/cvode.h>' form.
## The sundialr-specific R helper headers at the top level are not part of
## SUNDIALS and are excluded.
## ---------------------------------------------------------------------------
.inc_src <- file.path(.td, "sundialr", "inst", "include")
if (!dir.exists(.inc_src)) {
stop("Unable to locate the SUNDIALS public headers (sundialr/inst/include) ",
"inside the sundialr tarball.", call. = FALSE)
}
unlink("src/sundials_inc", recursive = TRUE)
dir.create("src/sundials_inc", recursive = TRUE)
.hdrs <- list.files(.inc_src, recursive = TRUE, all.files = FALSE)
.hdrs <- .hdrs[!.hdrs %in% c("check_retval.h", "rhs_func.h", "sundials_err_handler.h")]
for (.h in .hdrs) {
.dst <- file.path("src", "sundials_inc", .h)
dir.create(dirname(.dst), recursive = TRUE, showWarnings = FALSE)
file.copy(file.path(.inc_src, .h), .dst, overwrite = TRUE)
}
message("Copied ", length(.hdrs), " headers to src/sundials_inc/.")
## ---------------------------------------------------------------------------
## Strip sensitivity-only functions from the nonlinear solver files.
## CVODE (not CVODES) does not need sensitivity wrapping, and
## sundials_nvector_senswrapper.h is not shipped in the installed sundialr headers.
## ---------------------------------------------------------------------------
.strip_sens <- function(.path, .fn_name) {
if (!file.exists(.path)) return(invisible(NULL))
.lines <- readLines(.path)
.lines <- .lines[!grepl("sundials_nvector_senswrapper\\.h", .lines)]
.start <- grep(paste0("SUNNonlinearSolver\\s+", .fn_name, "\\s*\\("), .lines)
if (length(.start)) {
.cb <- .start[1] - 1L
while (.cb >= 1L && !grepl("^/\\*={10,}", .lines[.cb])) .cb <- .cb - 1L
.cb <- max(.cb, 1L)
.end <- .start[1]
while (.end <= length(.lines) && !grepl("^\\}", .lines[.end])) .end <- .end + 1L
.lines <- .lines[-seq(.cb, .end)]
}
.out <- file(.path, "wb")
writeLines(.lines, .out, sep = "\n")
close(.out)
}
.strip_sens("src/sundials_sunnonlinsol_newton.c", "SUNNonlinSol_NewtonSens")
.strip_sens("src/sundials_sunnonlinsol_fixedpoint.c","SUNNonlinSol_FixedPointSens")
## ---------------------------------------------------------------------------
## Replace sprintf(name, "...") with snprintf(name, N, "...") so CRAN's
## _FORTIFY_SOURCE check does not flag ___sprintf_chk in the compiled .so.
## The buffer size N matches the malloc(N * sizeof(char)) call in each file.
## ---------------------------------------------------------------------------
.fix_sprintf <- function(.path, .bufsz) {
if (!file.exists(.path)) return(invisible(NULL))
.lines <- readLines(.path)
.lines <- gsub(
sprintf("\\bsprintf\\(name,"),
sprintf("snprintf(name, %d,", .bufsz),
.lines, perl = TRUE
)
.out <- file(.path, "wb")
writeLines(.lines, .out, sep = "\n")
close(.out)
message("Fixed sprintf -> snprintf (buf=", .bufsz, ") in ", .path)
}
.fix_sprintf("src/sundials_cvode_diag.c", 30L)
.fix_sprintf("src/sundials_cvode_io.c", 24L)
.fix_sprintf("src/sundials_cvode_ls.c", 30L)
## ---------------------------------------------------------------------------
## malloc -> calloc in the *NewEmpty constructors. If the headers ever gain
## struct fields the vendored sources do not know about, calloc leaves them
## NULL (handled by the existing 'if (ops->foo)' guards) instead of garbage.
## ---------------------------------------------------------------------------
.fix_calloc <- function(.path) {
if (!file.exists(.path)) return(invisible(NULL))
.lines <- readLines(.path)
.new <- gsub("malloc\\(sizeof \\*([A-Za-z_]+)\\)", "calloc(1, sizeof *\\1)", .lines)
.new <- gsub("malloc\\(sizeof\\(struct (SUNMemoryHelper_Ops_|SUNMemoryHelper_|SUNMemory_)\\)\\)",
"calloc(1, sizeof(struct \\1))", .new)
if (!identical(.new, .lines)) {
.out <- file(.path, "wb")
writeLines(.new, .out, sep = "\n")
close(.out)
message("Fixed malloc -> calloc in ", .path)
}
}
for (.f in c("src/sundials_sundials_nonlinearsolver.c",
"src/sundials_sundials_linearsolver.c",
"src/sundials_sundials_matrix.c",
"src/sundials_sundials_nvector.c",
"src/sundials_sundials_memory.c",
"src/sundials_sunnonlinsol_newton.c",
"src/sundials_sunlinsol_dense.c")) {
.fix_calloc(.f)
}
message("Done. Review 'git diff src/' and commit any updated files.")
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.