R/rextendr_document.R

Defines functions check_namespace_file check_if_dyn_lib_used check_if_roxygen_used document

Documented in document

#' Compile Rust code and generate package documentation.
#'
#' @description
#' `r lifecycle::badge("deprecated")`
#'
#' As of `rextendr` 0.4.0, this function is no longer strictly necessary.
#' Packages created with [use_extendr()] now include a `document` binary that
#' generates `R/extendr-wrappers.R` as part of the normal `cargo build` step,
#' so [devtools::document()] works directly without any `rextendr`-specific
#' pre-processing. `rextendr::document()` is retained for backwards
#' compatibility.
#'
#' `rextendr::document()` updates the package documentation for an R package
#' that uses `extendr` code. It is a wrapper for [devtools::document()].
#' @inheritParams devtools::document
#' @return No return value, called for side effects.
#' @export
document <- function(pkg = ".", quiet = FALSE, roclets = NULL) {
  lifecycle::deprecate_warn(
    "0.4.0",
    "rextendr::document()",
    "devtools::document()",
    details = "Call `use_extendr()` to update configs."
  )

  check_string(
    pkg,
    call = rlang::caller_call(),
    class = "rextendr_error"
  )

  check_bool(
    quiet,
    call = rlang::caller_call(),
    class = "rextendr_error"
  )

  check_bool(
    roclets,
    allow_null = TRUE,
    call = rlang::caller_call(),
    class = "rextendr_error"
  )

  withr::local_envvar(devtools::r_env_vars())

  rextendr_setup(path = pkg)

  rlang::check_installed("devtools")
  devtools::document(pkg = pkg, roclets = roclets, quiet = quiet)
  if (!quiet) {
    check_namespace_file(pkg)
  }
}

check_if_roxygen_used <- function(namespace_content) {
  any(stringi::stri_startswith_fixed(
    namespace_content,
    "# Generated by roxygen2:"
  ))
}

check_if_dyn_lib_used <- function(namespace_content, pkg_name) {
  expected_pattern <- glue::glue(
    "useDynLib\\({pkg_name},\\s*\\.registration = TRUE\\)"
  )

  any(stringi::stri_detect_regex(namespace_content, expected_pattern))
}

check_namespace_file <- function(path = ".") {
  namespace_file_path <- rprojroot::find_package_root_file(
    "NAMESPACE",
    path = path
  )
  description_file_path <- rprojroot::find_package_root_file(
    "DESCRIPTION",
    path = path
  )
  package_name <- desc::desc_get_field("Package", file = description_file_path)
  namespace_content <- brio::read_lines(namespace_file_path)
  namespace_content <- stringi::stri_trim_both(namespace_content[nzchar(
    namespace_content
  )])

  is_roxygen_used <- check_if_roxygen_used(namespace_content)
  is_dyn_lib_used <- check_if_dyn_lib_used(namespace_content, package_name)

  if (!is_dyn_lib_used) {
    roxygen_message <- ifelse(
      is_roxygen_used,
      NULL,
      paste(
        "Alternatively, allow {.pkg roxygen2} to generate {.file NAMESPACE} exports for you.",
        "Annotate exported functions with {.code # @export} directive,",
        "delete the {.file NAMESPACE} file and run {.code rextendr::document()} again to regenerate exports."
      )
    )

    # nolint start: object_usage_linter.
    use_dyn_lib_ref <- glue::glue(
      "useDynLib({package_name}, .registration = TRUE)"
    )
    # nolint end

    cli::cli_warn(
      c(
        "The {.file NAMESPACE} file does not contain the expected {.code useDynLib} directive.",
        "x" = "This prevents your package from loading Rust code.",
        "*" = "Add the following line to the {.file NAMESPACE} file: {.code {use_dyn_lib_ref}}",
        "*" = roxygen_message
      )
    )
  }
}

Try the rextendr package in your browser

Any scripts or data that you put into this service are public.

rextendr documentation built on April 18, 2026, 5:07 p.m.