packrat/init.R

local({

  ## Helper function to get the path to the library directory for a
  ## given packrat project.
  getPackratLibDir <- function(projDir = NULL) {
    path <- file.path("packrat", "lib", R.version$platform, getRversion())

    if (!is.null(projDir)) {

      ## Strip trailing slashes if necessary
      projDir <- sub("/+$", "", projDir)

      ## Only prepend path if different from current working dir
      if (!identical(normalizePath(projDir), normalizePath(getwd())))
        path <- file.path(projDir, path)
    }

    path
  }

  ## Ensure that we set the packrat library directory relative to the
  ## project directory. Normally, this should be the working directory,
  ## but we also use '.rs.getProjectDirectory()' if necessary (e.g. we're
  ## rebuilding a project while within a separate directory)
  libDir <- if (exists(".rs.getProjectDirectory"))
    getPackratLibDir(.rs.getProjectDirectory())
  else
    getPackratLibDir()

  ## Unload packrat in case it's loaded -- this ensures packrat _must_ be
  ## loaded from the private library. Note that `requireNamespace` will
  ## succeed if the package is already loaded, regardless of lib.loc!
  if ("packrat" %in% loadedNamespaces())
    try(unloadNamespace("packrat"), silent = TRUE)

  if (suppressWarnings(requireNamespace("packrat", quietly = TRUE, lib.loc = libDir))) {

    # Check 'print.banner.on.startup' -- when NA and RStudio, don't print
    print.banner <- packrat::get_opts("print.banner.on.startup")
    if (print.banner == "auto" && is.na(Sys.getenv("RSTUDIO", unset = NA))) {
      print.banner <- TRUE
    } else {
      print.banner <- FALSE
    }
    return(packrat::on(print.banner = print.banner))
  }

  ## Escape hatch to allow RStudio to handle bootstrapping. This
  ## enables RStudio to provide print output when automagically
  ## restoring a project from a bundle on load.
  if (!is.na(Sys.getenv("RSTUDIO", unset = NA)) &&
      is.na(Sys.getenv("RSTUDIO_PACKRAT_BOOTSTRAP", unset = NA))) {
    Sys.setenv("RSTUDIO_PACKRAT_BOOTSTRAP" = "1")
    setHook("rstudio.sessionInit", function(...) {
      # Ensure that, on sourcing 'packrat/init.R', we are
      # within the project root directory
      if (exists(".rs.getProjectDirectory")) {
        owd <- getwd()
        setwd(.rs.getProjectDirectory())
        on.exit(setwd(owd), add = TRUE)
      }
      source("packrat/init.R")
    })
    return(invisible(NULL))
  }

  ## Bootstrapping -- only performed in interactive contexts,
  ## or when explicitly asked for on the command line
  if (interactive() || "--bootstrap-packrat" %in% commandArgs(TRUE)) {

    message("Packrat is not installed in the local library -- ",
            "attempting to bootstrap an installation...")

    ## We need utils for the following to succeed -- there are calls to functions
    ## in 'restore' that are contained within utils. utils gets loaded at the
    ## end of start-up anyhow, so this should be fine
    library("utils", character.only = TRUE)

    ## Install packrat into local project library
    packratSrcPath <- list.files(full.names = TRUE,
                                 file.path("packrat", "src", "packrat")
    )

    ## No packrat tarballs available locally -- try some other means of installation
    if (!length(packratSrcPath)) {

      message("> No source tarball of packrat available locally")

      ## There are no packrat sources available -- try using a version of
      ## packrat installed in the user library to bootstrap
      if (requireNamespace("packrat", quietly = TRUE) && packageVersion("packrat") >= "0.2.0.99") {
        message("> Using user-library packrat (",
                packageVersion("packrat"),
                ") to bootstrap this project")
      }

      ## Couldn't find a user-local packrat -- try finding and using devtools
      ## to install
      else if (requireNamespace("devtools", quietly = TRUE)) {
        message("> Attempting to use devtools::install_github to install ",
                "a temporary version of packrat")
        library(stats) ## for setNames
        devtools::install_github("rstudio/packrat")
      }

      ## Try downloading packrat from CRAN if available
      else if ("packrat" %in% rownames(available.packages())) {
        message("> Installing packrat from CRAN")
        install.packages("packrat")
      }

      ## Fail -- couldn't find an appropriate means of installing packrat
      else {
        stop("Could not automatically bootstrap packrat -- try running ",
             "\"'install.packages('devtools'); devtools::install_github('rstudio/packrat')\"",
             "and restarting R to bootstrap packrat.")
      }

      # Restore the project, unload the temporary packrat, and load the private packrat
      packrat::restore(prompt = FALSE, restart = TRUE)

      ## This code path only reached if we didn't restart earlier
      unloadNamespace("packrat")
      requireNamespace("packrat", lib.loc = libDir, quietly = TRUE)
      return(packrat::on())

    }

    ## Multiple packrat tarballs available locally -- try to choose one
    ## TODO: read lock file and infer most appropriate from there; low priority because
    ## after bootstrapping packrat a restore should do the right thing
    if (length(packratSrcPath) > 1) {
      warning("Multiple versions of packrat available in the source directory;",
              "using packrat source:\n- ", shQuote(packratSrcPath))
      packratSrcPath <- packratSrcPath[[1]]
    }


    lib <- file.path("packrat", "lib", R.version$platform, getRversion())
    if (!file.exists(lib)) {
      dir.create(lib, recursive = TRUE)
    }
    lib <- normalizePath(lib, winslash = "/")

    message("> Installing packrat into project private library:")
    message("- ", shQuote(lib))

    surround <- function(x, with) {
      if (!length(x)) return(character())
      paste0(with, x, with)
    }

    ## The following is performed because a regular install.packages call can fail
    peq <- function(x, y) paste(x, y, sep = " = ")
    installArgs <- c(
      peq("pkgs", surround(packratSrcPath, with = "'")),
      peq("lib", surround(lib, with = "'")),
      peq("repos", "NULL"),
      peq("type", surround("source", with = "'"))
    )
    installCmd <- paste(sep = "",
                        "utils::install.packages(",
                        paste(installArgs, collapse = ", "),
                        ")")

    fullCmd <- paste(
      surround(file.path(R.home("bin"), "R"), with = "\""),
      "--vanilla",
      "--slave",
      "-e",
      surround(installCmd, with = "\"")
    )
    system(fullCmd)

    ## Tag the installed packrat so we know it's managed by packrat
    ## TODO: should this be taking information from the lockfile? this is a bit awkward
    ## because we're taking an un-annotated packrat source tarball and simply assuming it's now
    ## an 'installed from source' version

    ## -- InstallAgent -- ##
    installAgent <- 'InstallAgent: packrat 0.4.8-1'

    ## -- InstallSource -- ##
    installSource <- 'InstallSource: source'

    packratDescPath <- file.path(lib, "packrat", "DESCRIPTION")
    DESCRIPTION <- readLines(packratDescPath)
    DESCRIPTION <- c(DESCRIPTION, installAgent, installSource)
    cat(DESCRIPTION, file = packratDescPath, sep = "\n")

    # Otherwise, continue on as normal
    message("> Attaching packrat")
    library("packrat", character.only = TRUE, lib.loc = lib)

    message("> Restoring library")
    restore(restart = FALSE)

    # If the environment allows us to restart, do so with a call to restore
    restart <- getOption("restart")
    if (!is.null(restart)) {
      message("> Packrat bootstrap successfully completed. ",
              "Restarting R and entering packrat mode...")
      return(restart())
    }

    # Callers (source-erers) can define this hidden variable to make sure we don't enter packrat mode
    # Primarily useful for testing
    if (!exists(".__DONT_ENTER_PACKRAT_MODE__.") && interactive()) {
      message("> Packrat bootstrap successfully completed. Entering packrat mode...")
      packrat::on()
    }

    Sys.unsetenv("RSTUDIO_PACKRAT_BOOTSTRAP")

  }

})
bjornerstedt/assist documentation built on Sept. 8, 2017, 2:02 p.m.