library(testthat)
# Load already included functions if relevant
pkgload::load_all(export_all = FALSE)
#' dependencies_file_text
#' 
#' Create the text of create_dependencies_file()
#' 
#' @param ll a vector of all packages
#' @param remotes_orig a vector of remotes 
#' @param install_only_if_missing Logical Modify the installation instructions to check, beforehand, if the packages are missing . (default FALSE)
#' @importFrom glue glue
#' @return a list
#' 
#' @noRd
dependencies_file_text <- function(ll, remotes_orig, install_only_if_missing = FALSE){

  if (length(remotes_orig) != 0) {

    remotes_orig_pkg <- gsub("^.*/|^local::|.git$", "", remotes_orig)
    remotes_without_orig <- gsub("^.*::/{0,1}", "", remotes_orig)
    # Remove remotes from ll
    ll <- ll[!ll %in% remotes_orig_pkg]
    # Install script
    inst_remotes <- remotes_orig
    # _If no (), then bioc
    w.bioc <- grepl("bioc::", remotes_orig)
    inst_remotes[w.bioc] <- glue("remotes::install_bioc('{remotes_without_orig[w.bioc]}')")
    # _If no (), then local
    w.local <- grepl("local::", remotes_orig)
    inst_remotes[w.local] <- glue("remotes::install_local('{remotes_without_orig[w.local]}')")
    # _If no (), then git
    w.git <- grepl("git::", remotes_orig)
    inst_remotes[w.git] <- glue("remotes::install_git('{remotes_without_orig[w.git]}')")
    # _If no (), then git
    w.gitlab <- grepl("gitlab::", remotes_orig)
    inst_remotes[w.gitlab] <- glue("remotes::install_gitlab('{remotes_without_orig[w.gitlab]}')")
    # _If no (), then github
    w.github <- !grepl("\\(", remotes_orig) & !grepl("remotes::", inst_remotes)
    inst_remotes[w.github] <- glue("remotes::install_github('{remotes_without_orig[w.github]}')")


    # Install only if missing
    if (isTRUE(install_only_if_missing)) {
      inst_remotes <-
        paste0(
          "if(isFALSE(requireNamespace('",
          remotes_orig_pkg,
          "', quietly = TRUE))) {","message('installation of " ,remotes_orig_pkg,"');", inst_remotes,
          "}"
        )

      install_pkg_remote <-
        "if(isFALSE(requireNamespace('remotes', quietly = TRUE))) {install.packages(\"remotes\")}"
    } else {
       install_pkg_remote <- "install.packages(\"remotes\")"
      }

    # _Others (WIP...)
    inst_remotes[!(w.github | w.local | w.bioc | w.git | w.gitlab)] <- remotes_orig[!(w.github | w.local | w.bioc | w.git | w.gitlab)]

    # Store content
    remotes_content <- paste("# Remotes ----",
                             install_pkg_remote,
                             paste(inst_remotes, collapse = "\n"),
                             sep = "\n")
  } else {
    remotes_content <- "# No Remotes ----"
  }



  if (length(ll) != 0) {

    attachment_content <- glue::glue(
      '
# Attachments ----
to_install <- c("*{glue::glue_collapse(as.character(ll), sep="\\", \\"")}*")
  for (i in to_install) {
    message(paste("looking for ", i))
    if (!requireNamespace(i, quietly = TRUE)) {
      message(paste("     installing", i))
      install.packages(i)
    }
  }\n\n', .open = "*{", .close = "}*")
  } else {
    attachment_content <- glue::glue(
      '
# No attachments ----
      \n\n', .open = "*{", .close = "}*")
  }
    content <- list("remotes_content" = remotes_content,
                    "attachment_content" = attachment_content)
  return(content)
}
test_that("dependencies_file_text works", {
  expect_true(inherits(dependencies_file_text, "function")) 

  remo <- c("ThinkR-open/attachment", "local::/path/fakelocal", "gitlab::statnmap/fakepkg", 
"bioc::3.3/fakepkgbioc", "git::https://github.com/fakepkggit.git", 
"git::https://MyForge.com/fakepkggit2r", "ThinkR-open/fusen")
ll <- c("knitr", "magrittr", "rmarkdown", "testthat")

thetext <- dependencies_file_text(ll, remo, FALSE)
  expect_equal(class(thetext), "list")
  expect_length(thetext, 2)
  expect_equal(names(thetext), c("remotes_content","attachment_content"))



  expect_equal(thetext[1], list(remotes_content = "# Remotes ----\ninstall.packages(\"remotes\")\nremotes::install_github('ThinkR-open/attachment')\nremotes::install_local('path/fakelocal')\nremotes::install_gitlab('statnmap/fakepkg')\nremotes::install_bioc('3.3/fakepkgbioc')\nremotes::install_git('https://github.com/fakepkggit.git')\nremotes::install_git('https://MyForge.com/fakepkggit2r')\nremotes::install_github('ThinkR-open/fusen')"))

  expect_equal(thetext[2], list(attachment_content = structure("# Attachments ----\nto_install <- c(\"knitr\", \"magrittr\", \"rmarkdown\", \"testthat\")\n  for (i in to_install) {\n    message(paste(\"looking for \", i))\n    if (!requireNamespace(i, quietly = TRUE)) {\n      message(paste(\"     installing\", i))\n      install.packages(i)\n    }\n  }\n", class = c("glue", 
"character"))))



remo <- NULL
ll <- NULL

thetext2 <- dependencies_file_text(ll, remo, FALSE)
expect_equal(class(thetext2), "list")
expect_length(thetext2, 2)
expect_equal(names(thetext2), c("remotes_content", "attachment_content"))

expect_equal(thetext2[1],list(remotes_content = "# No Remotes ----"))

expect_equal(thetext2[2], list(attachment_content = structure("# No attachments ----\n      \n", class = c("glue", 
"character"))))

})

Write instructions to install all dependencies from a "DESCRIPTION" file

create_dependencies_file() creates "inst/dependencies.R" file with the instructions to install all dependencies listed in your "DESCRIPTION". This accounts for the "Remotes" field and write instructions accordingly.

Use create_dependencies_file(to = NULL) to only retrieve the output as a list of character and not save a "inst/dependencies.R" file in your project. This can be used in a Readme.Rmd file for instance, to get the full list of each dependency to install and how, from any "DESCRIPTION" file.

#' Create the list of instructions to install dependencies from a DESCRIPTION file
#' 
#' Outputs the list of instructions and a "dependencies.R" file with instructions in the "inst/" directory
#'
#' @param path path to the DESCRIPTION file
#' @param field DESCRIPTION field to parse, "Import" and "Depends" by default. Can add "Suggests"
#' @param to path where to save the dependencies file. Set to "inst/dependencies.R" by default. Set to `NULL` if you do not want the file, but only the instructions as a list of character.
#' @param open_file Logical. Open the file created in an editor
#' @param ignore_base Logical. Whether to ignore package coming with base, as they cannot be installed (default TRUE)
#' @param install_only_if_missing Logical Modify the installation instructions to check, beforehand, if the packages are missing . (default FALSE)
#' @export
#' @return List of R instructions to install all dependencies from a DESCRIPTION file. Side effect: creates a R file containing these instructions.
#' @importFrom glue glue glue_collapse
#' @importFrom desc description
#' @importFrom utils packageDescription
#'
#' @examples
create_dependencies_file <- function(path = "DESCRIPTION",
                                     field = c("Depends", "Imports"),
                                     to = "inst/dependencies.R", 
                                     open_file = TRUE,
                                     ignore_base = TRUE,
                                     install_only_if_missing = FALSE) {

  # get all packages
  ll <- att_from_description(path = path, field = field)
  # get pkg in remotes
  if (isTRUE(ignore_base)) {
    to_remove <-
      which(lapply(ll , packageDescription, field = "Priority") == "base")
    if (length(to_remove) > 0) {
      ll <- ll[-to_remove]
    }

  }

  desc <- description$new(path)
  # Get previous dependencies in Description in case version is set
  remotes_orig <- desc$get_remotes()

  content <- dependencies_file_text(ll = ll,
                         remotes_orig = remotes_orig,
                         install_only_if_missing = install_only_if_missing)

  if (!is.null(to)) {
    if (!dir.exists(dirname(to))) {
      dir.create(dirname(to), recursive = TRUE, showWarnings = FALSE)
      dir_to <- normalizePath(dirname(to))
    } else {
      dir_to <- normalizePath(dirname(to))
    }

    the_file <- file.path(dir_to, basename(to))
    # file.create(the_file)
    cat(unlist(content), sep  = "\n", file = the_file)

    if (interactive() && open_file) {
      utils::file.edit(file, editor = "internal")
    }

    return(invisible(content))
  } else {
    return(content)
  }


}
# Create a fake package
tmpdir <- tempfile(pattern = "depsfile")
dir.create(tmpdir)
file.copy(system.file("dummypackage",package = "attachment"), tmpdir,
          recursive = TRUE)
dummypackage <- file.path(tmpdir, "dummypackage")

# Create the dependencies commands but no file
create_dependencies_file(
  path = file.path(dummypackage,"DESCRIPTION"),
  to = NULL,
  open_file = FALSE)

# Create the dependencies files in the package
create_dependencies_file(
  path = file.path(dummypackage,"DESCRIPTION"),
  to = file.path(dummypackage, "inst/dependencies.R"),
  open_file = FALSE)
list.files(file.path(dummypackage, "inst"))
# browseURL(dummypackage)

# Clean temp files after this example
unlink(tmpdir, recursive = TRUE)
# Copy package in a temporary directory
tmpdir <- tempfile(pattern = "pkgdeps")
dir.create(tmpdir)
file.copy(system.file("dummypackage",package = "attachment"), tmpdir, recursive = TRUE)
dummypackage <- file.path(tmpdir, "dummypackage")
# browseURL(dummypackage)

# Create the dependencies commands but no file
test_that("create_dependencies_file does not create file with NULL", {

  out_list <- create_dependencies_file(
    path = file.path(dummypackage, "DESCRIPTION"),
    to = NULL,
    field = c("Depends", "Imports", "Suggests"),
    open_file = FALSE)

  expect_false(file.exists(
    file.path(dummypackage, "inst", "dependencies.R")))
})

create_dependencies_file(
  path = file.path(dummypackage, "DESCRIPTION"),
  to = file.path(dummypackage, "inst", "dependencies.R"),
  field = c("Depends", "Imports", "Suggests"),
  open_file = FALSE)

  expect_true(file.exists(
    file.path(dummypackage, "inst", "dependencies.R")))

dep_file_without_remotes <- readLines(file.path(dummypackage, "inst", "dependencies.R"))

test_that("create-dependencies-file works without remotes", {
  expect_equal(dep_file_without_remotes[1], "# No Remotes ----")
  expect_equal(dep_file_without_remotes[3], "to_install <- c(\"glue\", \"knitr\", \"magrittr\", \"rmarkdown\", \"stringr\", \"testthat\")")
})


  # Add remotes in DESCRIPTION

  # Test internal_remotes_to_desc ----
  tmpdir <- tempfile(pattern = "pkgwithremotes")
  dir.create(tmpdir)
  file.copy(system.file("dummypackage",package = "attachment"), tmpdir, recursive = TRUE)
  dummypackage <- file.path(tmpdir, "dummypackage")

  path.d <- file.path(dummypackage, "DESCRIPTION")

  cat(c("Remotes:\n     ThinkR-open/attachment,\n     local::/path/fakelocal,\n     gitlab::statnmap/fakepkg,\n     bioc::3.3/fakepkgbioc,\n     git::https://github.com/fakepkggit.git,\n     git::https://MyForge.com/fakepkggit2r,\n     ThinkR-open/fusen\n"), append = TRUE,
      file = path.d)

  new_desc <- readLines(path.d)

create_dependencies_file(path = file.path(dummypackage,"DESCRIPTION"),
                         to = file.path(dummypackage, "inst/dependencies.R"),
                         field = c("Depends", "Imports", "Suggests"),
                         open_file = FALSE)

dep_file_with_remotes <- readLines(file.path(tmpdir, "dummypackage", "inst/dependencies.R"))

test_that("create-dependencies-file works with remotes", {
  expect_equal(dep_file_with_remotes[1], "# Remotes ----")
  expect_equal(dep_file_with_remotes[3], "remotes::install_github('ThinkR-open/attachment')")
  expect_equal(dep_file_with_remotes[4], "remotes::install_local('path/fakelocal')")
  expect_equal(dep_file_with_remotes[5], "remotes::install_gitlab('statnmap/fakepkg')")
  expect_equal(dep_file_with_remotes[6], "remotes::install_bioc('3.3/fakepkgbioc')")
  expect_equal(dep_file_with_remotes[7], "remotes::install_git('https://github.com/fakepkggit.git')")
  expect_equal(dep_file_with_remotes[8], "remotes::install_git('https://MyForge.com/fakepkggit2r')")
  expect_equal(dep_file_with_remotes[9], "remotes::install_github('ThinkR-open/fusen')")
  expect_equal(dep_file_with_remotes[11], "to_install <- c(\"glue\", \"knitr\", \"magrittr\", \"rmarkdown\", \"stringr\", \"testthat\")")
})


create_dependencies_file(path = file.path(dummypackage,"DESCRIPTION"),
                         to = file.path(dummypackage, "inst/dependencies.R"),
                         field = c("Depends", "Imports", "Suggests"),
                         open_file = FALSE,
                         install_only_if_missing = TRUE)

dep_file_with_remotes_install_only_if_missing <- readLines(file.path(tmpdir, "dummypackage", "inst/dependencies.R"))

test_that("create-dependencies-file works with remotes install_only_if_missing", {
  expect_equal(dep_file_with_remotes_install_only_if_missing[1], "# Remotes ----")
  expect_equal(dep_file_with_remotes_install_only_if_missing[3], "if(isFALSE(requireNamespace('attachment', quietly = TRUE))) {message('installation of attachment');remotes::install_github('ThinkR-open/attachment')}")
  expect_equal(dep_file_with_remotes_install_only_if_missing[4], "if(isFALSE(requireNamespace('fakelocal', quietly = TRUE))) {message('installation of fakelocal');remotes::install_local('path/fakelocal')}")
  expect_equal(dep_file_with_remotes_install_only_if_missing[5], "if(isFALSE(requireNamespace('fakepkg', quietly = TRUE))) {message('installation of fakepkg');remotes::install_gitlab('statnmap/fakepkg')}")
  expect_equal(dep_file_with_remotes_install_only_if_missing[6], "if(isFALSE(requireNamespace('fakepkgbioc', quietly = TRUE))) {message('installation of fakepkgbioc');remotes::install_bioc('3.3/fakepkgbioc')}")
  expect_equal(dep_file_with_remotes_install_only_if_missing[7], "if(isFALSE(requireNamespace('fakepkggit', quietly = TRUE))) {message('installation of fakepkggit');remotes::install_git('https://github.com/fakepkggit.git')}")
  expect_equal(dep_file_with_remotes_install_only_if_missing[8],  "if(isFALSE(requireNamespace('fakepkggit2r', quietly = TRUE))) {message('installation of fakepkggit2r');remotes::install_git('https://MyForge.com/fakepkggit2r')}")
  expect_equal(dep_file_with_remotes_install_only_if_missing[9], "if(isFALSE(requireNamespace('fusen', quietly = TRUE))) {message('installation of fusen');remotes::install_github('ThinkR-open/fusen')}")
  expect_equal(dep_file_with_remotes_install_only_if_missing[11], "to_install <- c(\"glue\", \"knitr\", \"magrittr\", \"rmarkdown\", \"stringr\", \"testthat\")")
})

# Clean temp files after this example
unlink(tmpdir, recursive = TRUE)
# Run but keep eval=FALSE to avoid infinite loop
# Execute in the console directly
fusen::inflate(flat_file = "dev/flat_create_dependencies_file.Rmd", vignette_name = "Create dependencies file",
               check = FALSE,
               open_vignette = FALSE,
               overwrite = TRUE,
               document = TRUE)


ThinkR-open/attachment documentation built on June 15, 2025, 1:24 a.m.