Nothing
#' Compile a .dll/.so from source.
#'
#' `compile_dll` performs a fake R CMD install so code that
#' works here should work with a regular install (and vice versa).
#' During compilation, debug flags are set with
#' \code{\link{compiler_flags}(TRUE)}.
#'
#' Invisibly returns the names of the DLL.
#'
#' ## Configuration
#'
#' ### Options
#'
#' * `pkg.build_extra_flags`: set this to `FALSE` to to opt out from adding
#' debug compiler flags in `compile_dll()`. Takes precedence over the
#' `PKG_BUILD_EXTRA_FLAGS` environment variable. Possible values:
#'
#' - `TRUE`: add extra flags,
#' - `FALSE`: do not add extra flags,
#' - `"missing"`: add extra flags if the user does not have a
#' `$HOME/.R/Makevars` file.
#'
#' ### Environment variables
#'
#' * `PKG_BUILD_EXTRA_FLAGS`: set this to `false` to to opt out from adding
#' debug compiler flags in `compile_dll()`. The `pkg.build_extra_flags` option
#' takes precedence over this environment variable. Possible values:
#'
#' - `"true"`: add extra flags,
#' - `"false"`: do not add extra flags,
#' - `"missing"`: add extra flags if the user does not have a
#' `$HOME/.R/Makevars` file.
#'
#' @note If this is used to compile code that uses Rcpp, you will need to
#' add the following line to your `Makevars` file so that it
#' knows where to find the Rcpp headers:
#' `PKG_CPPFLAGS=`$(R_HOME)/bin/Rscript -e 'Rcpp:::CxxFlags()'``
#'
#' @inheritParams build
#' @param force If `TRUE`, for compilation even if [needs_compile()] is
#' `FALSE`.
#' @param debug If `TRUE`, and if no user Makevars is found, then the build
#' runs without optimisation (`-O0`) and with debug symbols (`-g`). See
#' [compiler_flags()] for details. If you have a user Makevars (e.g.,
#' `~/.R/Makevars`) then this argument is ignored.
#' @seealso [clean_dll()] to delete the compiled files.
#' @export
compile_dll <- function(path = ".",
force = FALSE,
compile_attributes = pkg_links_to_cpp11(path) || pkg_links_to_rcpp(path),
register_routines = FALSE,
quiet = FALSE,
debug = TRUE) {
path <- pkg_path(path)
if (!needs_compile(path) && !isTRUE(force)) {
return(invisible())
}
check_build_tools(quiet = TRUE)
update_registration(path, compile_attributes, register_routines, quiet)
# Mock install the package to generate the DLL
xflags <- should_add_compiler_flags()
if (!quiet) {
cli::cli_alert_info(c(
"Re-compiling {.pkg {pkg_name(path)}}",
if (debug && xflags) " (debug build)"
))
}
install_dir <- tempfile("devtools_install_")
dir.create(install_dir)
build <- function() {
install_min(
path,
dest = install_dir,
components = "libs",
args = if (needs_clean(path)) "--preclean",
quiet = quiet
)
invisible(dll_path(file.path(install_dir, pkg_name(path))))
}
if (xflags) {
withr_with_makevars(compiler_flags(debug), {
if (debug) {
withr_with_envvar(c(DEBUG = "true"), build())
} else {
build()
}
})
} else {
build()
}
}
#' Remove compiled objects from /src/ directory
#'
#' Invisibly returns the names of the deleted files.
#'
#' @inheritParams build
#' @seealso [compile_dll()]
#' @export
clean_dll <- function(path = ".") {
path <- pkg_path(path)
# Clean out the /src/ directory and children:
# * individual object files
# * overall package definition file
# * symbols.rds (added when run inside R CMD check)
pattern <- sprintf(
"\\.(o|sl|so|dylib|a|dll)$|(%s\\.def)$|^symbols.rds$",
pkg_name(path)
)
files <- dir(
file.path(path, "src"),
pattern = pattern,
full.names = TRUE,
recursive = TRUE
)
unlink(files)
invisible(files)
}
# Returns the full path and name of the DLL file
dll_path <- function(path = ".") {
name <- paste(pkg_name(path), .Platform$dynlib.ext, sep = "")
file.path(path, "src", name)
}
mtime <- function(x) {
x <- x[file.exists(x)]
if (length(x) == 0) {
return(NULL)
}
max(file.info(x)$mtime)
}
globs <- function(path = ".", x) {
old <- getwd()
on.exit(setwd(old), add = TRUE)
setwd(path)
Sys.glob(x)
}
# List all source files in the package
sources <- function(path = ".") {
srcdir <- file.path(path, "src")
src <- c(
dir(srcdir, "\\.(c.*|f|rs)$", recursive = TRUE, full.names = TRUE),
dir(srcdir, "^Cargo\\.toml$", recursive = TRUE, full.names = TRUE)
)
extra <- desc::desc_get("Config/build/extra-sources", path)
if (!is.na(extra)) {
glb <- trimws(strsplit(extra, ",", fixed = TRUE)[[1]])
xs <- file.path(path, globs(path, glb))
xfls <- unlist(lapply(
xs,
dir,
recursive = TRUE,
full.names = TRUE
))
src <- c(src, xs, xfls)
}
src
}
# List all header files in the package
headers <- function(path = ".") {
incldir <- file.path(path, "inst", "include")
srcdir <- file.path(path, "src")
c(
dir(srcdir, "^Makevars.*$", recursive = TRUE, full.names = TRUE),
dir(srcdir, "\\.h.*$", recursive = TRUE, full.names = TRUE),
dir(incldir, "\\.h.*$", recursive = TRUE, full.names = TRUE)
)
}
#' Does the package need recompiling?
#' (i.e. is there a source or header file newer than the dll)
#' @inheritParams build
#' @keywords internal
#' @export
needs_compile <- function(path = ".") {
source <- mtime(c(sources(path), headers(path)))
# no source files, so doesn't need compile
if (is.null(source)) {
return(FALSE)
}
dll <- mtime(dll_path(path))
# no dll, so needs compile
if (is.null(dll)) {
return(TRUE)
}
source > dll
}
# Does the package need a clean compile?
# (i.e. is there a header or Makevars newer than the dll)
needs_clean <- function(path = ".") {
headers <- mtime(headers(path))
# no headers, so never needs clean compile
if (is.null(headers)) {
return(FALSE)
}
dll <- mtime(dll_path(path))
# no dll, so needs compile
if (is.null(dll)) {
return(TRUE)
}
headers > dll
}
install_min <- function(path = ".", dest, components = NULL, args = NULL, quiet = FALSE) {
stopifnot(is.character(dest), length(dest) == 1, file.exists(dest))
poss <- c("R", "data", "help", "demo", "inst", "docs", "exec", "libs")
if (!is.null(components)) {
components <- match.arg(components, poss, several.ok = TRUE)
}
no <- setdiff(poss, components)
no_args <- paste0("--no-", no)
rcmd_build_tools(
"INSTALL",
c(
path,
paste("--library=", dest, sep = ""),
no_args,
"--no-multiarch",
"--no-test-load",
args
),
fail_on_status = TRUE,
quiet = quiet
)
invisible(file.path(dest, pkg_name(path)))
}
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.