activate.R 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994
  1. local({
  2. # the requested version of renv
  3. version <- "0.16.0"
  4. # the project directory
  5. project <- getwd()
  6. # figure out whether the autoloader is enabled
  7. enabled <- local({
  8. # first, check config option
  9. override <- getOption("renv.config.autoloader.enabled")
  10. if (!is.null(override))
  11. return(override)
  12. # next, check environment variables
  13. # TODO: prefer using the configuration one in the future
  14. envvars <- c(
  15. "RENV_CONFIG_AUTOLOADER_ENABLED",
  16. "RENV_AUTOLOADER_ENABLED",
  17. "RENV_ACTIVATE_PROJECT"
  18. )
  19. for (envvar in envvars) {
  20. envval <- Sys.getenv(envvar, unset = NA)
  21. if (!is.na(envval))
  22. return(tolower(envval) %in% c("true", "t", "1"))
  23. }
  24. # enable by default
  25. TRUE
  26. })
  27. if (!enabled)
  28. return(FALSE)
  29. # avoid recursion
  30. if (identical(getOption("renv.autoloader.running"), TRUE)) {
  31. warning("ignoring recursive attempt to run renv autoloader")
  32. return(invisible(TRUE))
  33. }
  34. # signal that we're loading renv during R startup
  35. options(renv.autoloader.running = TRUE)
  36. on.exit(options(renv.autoloader.running = NULL), add = TRUE)
  37. # signal that we've consented to use renv
  38. options(renv.consent = TRUE)
  39. # load the 'utils' package eagerly -- this ensures that renv shims, which
  40. # mask 'utils' packages, will come first on the search path
  41. library(utils, lib.loc = .Library)
  42. # unload renv if it's already been loaded
  43. if ("renv" %in% loadedNamespaces())
  44. unloadNamespace("renv")
  45. # load bootstrap tools
  46. `%||%` <- function(x, y) {
  47. if (is.environment(x) || length(x)) x else y
  48. }
  49. bootstrap <- function(version, library) {
  50. # attempt to download renv
  51. tarball <- tryCatch(renv_bootstrap_download(version), error = identity)
  52. if (inherits(tarball, "error"))
  53. stop("failed to download renv ", version)
  54. # now attempt to install
  55. status <- tryCatch(renv_bootstrap_install(version, tarball, library), error = identity)
  56. if (inherits(status, "error"))
  57. stop("failed to install renv ", version)
  58. }
  59. renv_bootstrap_tests_running <- function() {
  60. getOption("renv.tests.running", default = FALSE)
  61. }
  62. renv_bootstrap_repos <- function() {
  63. # check for repos override
  64. repos <- Sys.getenv("RENV_CONFIG_REPOS_OVERRIDE", unset = NA)
  65. if (!is.na(repos))
  66. return(repos)
  67. # check for lockfile repositories
  68. repos <- tryCatch(renv_bootstrap_repos_lockfile(), error = identity)
  69. if (!inherits(repos, "error") && length(repos))
  70. return(repos)
  71. # if we're testing, re-use the test repositories
  72. if (renv_bootstrap_tests_running())
  73. return(getOption("renv.tests.repos"))
  74. # retrieve current repos
  75. repos <- getOption("repos")
  76. # ensure @CRAN@ entries are resolved
  77. repos[repos == "@CRAN@"] <- getOption(
  78. "renv.repos.cran",
  79. "https://cloud.r-project.org"
  80. )
  81. # add in renv.bootstrap.repos if set
  82. default <- c(FALLBACK = "https://cloud.r-project.org")
  83. extra <- getOption("renv.bootstrap.repos", default = default)
  84. repos <- c(repos, extra)
  85. # remove duplicates that might've snuck in
  86. dupes <- duplicated(repos) | duplicated(names(repos))
  87. repos[!dupes]
  88. }
  89. renv_bootstrap_repos_lockfile <- function() {
  90. lockpath <- Sys.getenv("RENV_PATHS_LOCKFILE", unset = "renv.lock")
  91. if (!file.exists(lockpath))
  92. return(NULL)
  93. lockfile <- tryCatch(renv_json_read(lockpath), error = identity)
  94. if (inherits(lockfile, "error")) {
  95. warning(lockfile)
  96. return(NULL)
  97. }
  98. repos <- lockfile$R$Repositories
  99. if (length(repos) == 0)
  100. return(NULL)
  101. keys <- vapply(repos, `[[`, "Name", FUN.VALUE = character(1))
  102. vals <- vapply(repos, `[[`, "URL", FUN.VALUE = character(1))
  103. names(vals) <- keys
  104. return(vals)
  105. }
  106. renv_bootstrap_download <- function(version) {
  107. # if the renv version number has 4 components, assume it must
  108. # be retrieved via github
  109. nv <- numeric_version(version)
  110. components <- unclass(nv)[[1]]
  111. # if this appears to be a development version of 'renv', we'll
  112. # try to restore from github
  113. dev <- length(components) == 4L
  114. # begin collecting different methods for finding renv
  115. methods <- c(
  116. renv_bootstrap_download_tarball,
  117. if (dev)
  118. renv_bootstrap_download_github
  119. else c(
  120. renv_bootstrap_download_cran_latest,
  121. renv_bootstrap_download_cran_archive
  122. )
  123. )
  124. for (method in methods) {
  125. path <- tryCatch(method(version), error = identity)
  126. if (is.character(path) && file.exists(path))
  127. return(path)
  128. }
  129. stop("failed to download renv ", version)
  130. }
  131. renv_bootstrap_download_impl <- function(url, destfile) {
  132. mode <- "wb"
  133. # https://bugs.r-project.org/bugzilla/show_bug.cgi?id=17715
  134. fixup <-
  135. Sys.info()[["sysname"]] == "Windows" &&
  136. substring(url, 1L, 5L) == "file:"
  137. if (fixup)
  138. mode <- "w+b"
  139. args <- list(
  140. url = url,
  141. destfile = destfile,
  142. mode = mode,
  143. quiet = TRUE
  144. )
  145. if ("headers" %in% names(formals(utils::download.file)))
  146. args$headers <- renv_bootstrap_download_custom_headers(url)
  147. do.call(utils::download.file, args)
  148. }
  149. renv_bootstrap_download_custom_headers <- function(url) {
  150. headers <- getOption("renv.download.headers")
  151. if (is.null(headers))
  152. return(character())
  153. if (!is.function(headers))
  154. stopf("'renv.download.headers' is not a function")
  155. headers <- headers(url)
  156. if (length(headers) == 0L)
  157. return(character())
  158. if (is.list(headers))
  159. headers <- unlist(headers, recursive = FALSE, use.names = TRUE)
  160. ok <-
  161. is.character(headers) &&
  162. is.character(names(headers)) &&
  163. all(nzchar(names(headers)))
  164. if (!ok)
  165. stop("invocation of 'renv.download.headers' did not return a named character vector")
  166. headers
  167. }
  168. renv_bootstrap_download_cran_latest <- function(version) {
  169. spec <- renv_bootstrap_download_cran_latest_find(version)
  170. type <- spec$type
  171. repos <- spec$repos
  172. message("* Downloading renv ", version, " ... ", appendLF = FALSE)
  173. baseurl <- utils::contrib.url(repos = repos, type = type)
  174. ext <- if (identical(type, "source"))
  175. ".tar.gz"
  176. else if (Sys.info()[["sysname"]] == "Windows")
  177. ".zip"
  178. else
  179. ".tgz"
  180. name <- sprintf("renv_%s%s", version, ext)
  181. url <- paste(baseurl, name, sep = "/")
  182. destfile <- file.path(tempdir(), name)
  183. status <- tryCatch(
  184. renv_bootstrap_download_impl(url, destfile),
  185. condition = identity
  186. )
  187. if (inherits(status, "condition")) {
  188. message("FAILED")
  189. return(FALSE)
  190. }
  191. # report success and return
  192. message("OK (downloaded ", type, ")")
  193. destfile
  194. }
  195. renv_bootstrap_download_cran_latest_find <- function(version) {
  196. # check whether binaries are supported on this system
  197. binary <-
  198. getOption("renv.bootstrap.binary", default = TRUE) &&
  199. !identical(.Platform$pkgType, "source") &&
  200. !identical(getOption("pkgType"), "source") &&
  201. Sys.info()[["sysname"]] %in% c("Darwin", "Windows")
  202. types <- c(if (binary) "binary", "source")
  203. # iterate over types + repositories
  204. for (type in types) {
  205. for (repos in renv_bootstrap_repos()) {
  206. # retrieve package database
  207. db <- tryCatch(
  208. as.data.frame(
  209. utils::available.packages(type = type, repos = repos),
  210. stringsAsFactors = FALSE
  211. ),
  212. error = identity
  213. )
  214. if (inherits(db, "error"))
  215. next
  216. # check for compatible entry
  217. entry <- db[db$Package %in% "renv" & db$Version %in% version, ]
  218. if (nrow(entry) == 0)
  219. next
  220. # found it; return spec to caller
  221. spec <- list(entry = entry, type = type, repos = repos)
  222. return(spec)
  223. }
  224. }
  225. # if we got here, we failed to find renv
  226. fmt <- "renv %s is not available from your declared package repositories"
  227. stop(sprintf(fmt, version))
  228. }
  229. renv_bootstrap_download_cran_archive <- function(version) {
  230. name <- sprintf("renv_%s.tar.gz", version)
  231. repos <- renv_bootstrap_repos()
  232. urls <- file.path(repos, "src/contrib/Archive/renv", name)
  233. destfile <- file.path(tempdir(), name)
  234. message("* Downloading renv ", version, " ... ", appendLF = FALSE)
  235. for (url in urls) {
  236. status <- tryCatch(
  237. renv_bootstrap_download_impl(url, destfile),
  238. condition = identity
  239. )
  240. if (identical(status, 0L)) {
  241. message("OK")
  242. return(destfile)
  243. }
  244. }
  245. message("FAILED")
  246. return(FALSE)
  247. }
  248. renv_bootstrap_download_tarball <- function(version) {
  249. # if the user has provided the path to a tarball via
  250. # an environment variable, then use it
  251. tarball <- Sys.getenv("RENV_BOOTSTRAP_TARBALL", unset = NA)
  252. if (is.na(tarball))
  253. return()
  254. # allow directories
  255. info <- file.info(tarball, extra_cols = FALSE)
  256. if (identical(info$isdir, TRUE)) {
  257. name <- sprintf("renv_%s.tar.gz", version)
  258. tarball <- file.path(tarball, name)
  259. }
  260. # bail if it doesn't exist
  261. if (!file.exists(tarball)) {
  262. # let the user know we weren't able to honour their request
  263. fmt <- "* RENV_BOOTSTRAP_TARBALL is set (%s) but does not exist."
  264. msg <- sprintf(fmt, tarball)
  265. warning(msg)
  266. # bail
  267. return()
  268. }
  269. fmt <- "* Bootstrapping with tarball at path '%s'."
  270. msg <- sprintf(fmt, tarball)
  271. message(msg)
  272. tarball
  273. }
  274. renv_bootstrap_download_github <- function(version) {
  275. enabled <- Sys.getenv("RENV_BOOTSTRAP_FROM_GITHUB", unset = "TRUE")
  276. if (!identical(enabled, "TRUE"))
  277. return(FALSE)
  278. # prepare download options
  279. pat <- Sys.getenv("GITHUB_PAT")
  280. if (nzchar(Sys.which("curl")) && nzchar(pat)) {
  281. fmt <- "--location --fail --header \"Authorization: token %s\""
  282. extra <- sprintf(fmt, pat)
  283. saved <- options("download.file.method", "download.file.extra")
  284. options(download.file.method = "curl", download.file.extra = extra)
  285. on.exit(do.call(base::options, saved), add = TRUE)
  286. } else if (nzchar(Sys.which("wget")) && nzchar(pat)) {
  287. fmt <- "--header=\"Authorization: token %s\""
  288. extra <- sprintf(fmt, pat)
  289. saved <- options("download.file.method", "download.file.extra")
  290. options(download.file.method = "wget", download.file.extra = extra)
  291. on.exit(do.call(base::options, saved), add = TRUE)
  292. }
  293. message("* Downloading renv ", version, " from GitHub ... ", appendLF = FALSE)
  294. url <- file.path("https://api.github.com/repos/rstudio/renv/tarball", version)
  295. name <- sprintf("renv_%s.tar.gz", version)
  296. destfile <- file.path(tempdir(), name)
  297. status <- tryCatch(
  298. renv_bootstrap_download_impl(url, destfile),
  299. condition = identity
  300. )
  301. if (!identical(status, 0L)) {
  302. message("FAILED")
  303. return(FALSE)
  304. }
  305. message("OK")
  306. return(destfile)
  307. }
  308. renv_bootstrap_install <- function(version, tarball, library) {
  309. # attempt to install it into project library
  310. message("* Installing renv ", version, " ... ", appendLF = FALSE)
  311. dir.create(library, showWarnings = FALSE, recursive = TRUE)
  312. # invoke using system2 so we can capture and report output
  313. bin <- R.home("bin")
  314. exe <- if (Sys.info()[["sysname"]] == "Windows") "R.exe" else "R"
  315. r <- file.path(bin, exe)
  316. args <- c(
  317. "--vanilla", "CMD", "INSTALL", "--no-multiarch",
  318. "-l", shQuote(path.expand(library)),
  319. shQuote(path.expand(tarball))
  320. )
  321. output <- system2(r, args, stdout = TRUE, stderr = TRUE)
  322. message("Done!")
  323. # check for successful install
  324. status <- attr(output, "status")
  325. if (is.numeric(status) && !identical(status, 0L)) {
  326. header <- "Error installing renv:"
  327. lines <- paste(rep.int("=", nchar(header)), collapse = "")
  328. text <- c(header, lines, output)
  329. writeLines(text, con = stderr())
  330. }
  331. status
  332. }
  333. renv_bootstrap_platform_prefix <- function() {
  334. # construct version prefix
  335. version <- paste(R.version$major, R.version$minor, sep = ".")
  336. prefix <- paste("R", numeric_version(version)[1, 1:2], sep = "-")
  337. # include SVN revision for development versions of R
  338. # (to avoid sharing platform-specific artefacts with released versions of R)
  339. devel <-
  340. identical(R.version[["status"]], "Under development (unstable)") ||
  341. identical(R.version[["nickname"]], "Unsuffered Consequences")
  342. if (devel)
  343. prefix <- paste(prefix, R.version[["svn rev"]], sep = "-r")
  344. # build list of path components
  345. components <- c(prefix, R.version$platform)
  346. # include prefix if provided by user
  347. prefix <- renv_bootstrap_platform_prefix_impl()
  348. if (!is.na(prefix) && nzchar(prefix))
  349. components <- c(prefix, components)
  350. # build prefix
  351. paste(components, collapse = "/")
  352. }
  353. renv_bootstrap_platform_prefix_impl <- function() {
  354. # if an explicit prefix has been supplied, use it
  355. prefix <- Sys.getenv("RENV_PATHS_PREFIX", unset = NA)
  356. if (!is.na(prefix))
  357. return(prefix)
  358. # if the user has requested an automatic prefix, generate it
  359. auto <- Sys.getenv("RENV_PATHS_PREFIX_AUTO", unset = NA)
  360. if (auto %in% c("TRUE", "True", "true", "1"))
  361. return(renv_bootstrap_platform_prefix_auto())
  362. # empty string on failure
  363. ""
  364. }
  365. renv_bootstrap_platform_prefix_auto <- function() {
  366. prefix <- tryCatch(renv_bootstrap_platform_os(), error = identity)
  367. if (inherits(prefix, "error") || prefix %in% "unknown") {
  368. msg <- paste(
  369. "failed to infer current operating system",
  370. "please file a bug report at https://github.com/rstudio/renv/issues",
  371. sep = "; "
  372. )
  373. warning(msg)
  374. }
  375. prefix
  376. }
  377. renv_bootstrap_platform_os <- function() {
  378. sysinfo <- Sys.info()
  379. sysname <- sysinfo[["sysname"]]
  380. # handle Windows + macOS up front
  381. if (sysname == "Windows")
  382. return("windows")
  383. else if (sysname == "Darwin")
  384. return("macos")
  385. # check for os-release files
  386. for (file in c("/etc/os-release", "/usr/lib/os-release"))
  387. if (file.exists(file))
  388. return(renv_bootstrap_platform_os_via_os_release(file, sysinfo))
  389. # check for redhat-release files
  390. if (file.exists("/etc/redhat-release"))
  391. return(renv_bootstrap_platform_os_via_redhat_release())
  392. "unknown"
  393. }
  394. renv_bootstrap_platform_os_via_os_release <- function(file, sysinfo) {
  395. # read /etc/os-release
  396. release <- utils::read.table(
  397. file = file,
  398. sep = "=",
  399. quote = c("\"", "'"),
  400. col.names = c("Key", "Value"),
  401. comment.char = "#",
  402. stringsAsFactors = FALSE
  403. )
  404. vars <- as.list(release$Value)
  405. names(vars) <- release$Key
  406. # get os name
  407. os <- tolower(sysinfo[["sysname"]])
  408. # read id
  409. id <- "unknown"
  410. for (field in c("ID", "ID_LIKE")) {
  411. if (field %in% names(vars) && nzchar(vars[[field]])) {
  412. id <- vars[[field]]
  413. break
  414. }
  415. }
  416. # read version
  417. version <- "unknown"
  418. for (field in c("UBUNTU_CODENAME", "VERSION_CODENAME", "VERSION_ID", "BUILD_ID")) {
  419. if (field %in% names(vars) && nzchar(vars[[field]])) {
  420. version <- vars[[field]]
  421. break
  422. }
  423. }
  424. # join together
  425. paste(c(os, id, version), collapse = "-")
  426. }
  427. renv_bootstrap_platform_os_via_redhat_release <- function() {
  428. # read /etc/redhat-release
  429. contents <- readLines("/etc/redhat-release", warn = FALSE)
  430. # infer id
  431. id <- if (grepl("centos", contents, ignore.case = TRUE))
  432. "centos"
  433. else if (grepl("redhat", contents, ignore.case = TRUE))
  434. "redhat"
  435. else
  436. "unknown"
  437. # try to find a version component (very hacky)
  438. version <- "unknown"
  439. parts <- strsplit(contents, "[[:space:]]")[[1L]]
  440. for (part in parts) {
  441. nv <- tryCatch(numeric_version(part), error = identity)
  442. if (inherits(nv, "error"))
  443. next
  444. version <- nv[1, 1]
  445. break
  446. }
  447. paste(c("linux", id, version), collapse = "-")
  448. }
  449. renv_bootstrap_library_root_name <- function(project) {
  450. # use project name as-is if requested
  451. asis <- Sys.getenv("RENV_PATHS_LIBRARY_ROOT_ASIS", unset = "FALSE")
  452. if (asis)
  453. return(basename(project))
  454. # otherwise, disambiguate based on project's path
  455. id <- substring(renv_bootstrap_hash_text(project), 1L, 8L)
  456. paste(basename(project), id, sep = "-")
  457. }
  458. renv_bootstrap_library_root <- function(project) {
  459. prefix <- renv_bootstrap_profile_prefix()
  460. path <- Sys.getenv("RENV_PATHS_LIBRARY", unset = NA)
  461. if (!is.na(path))
  462. return(paste(c(path, prefix), collapse = "/"))
  463. path <- renv_bootstrap_library_root_impl(project)
  464. if (!is.null(path)) {
  465. name <- renv_bootstrap_library_root_name(project)
  466. return(paste(c(path, prefix, name), collapse = "/"))
  467. }
  468. renv_bootstrap_paths_renv("library", project = project)
  469. }
  470. renv_bootstrap_library_root_impl <- function(project) {
  471. root <- Sys.getenv("RENV_PATHS_LIBRARY_ROOT", unset = NA)
  472. if (!is.na(root))
  473. return(root)
  474. type <- renv_bootstrap_project_type(project)
  475. if (identical(type, "package")) {
  476. userdir <- renv_bootstrap_user_dir()
  477. return(file.path(userdir, "library"))
  478. }
  479. }
  480. renv_bootstrap_validate_version <- function(version) {
  481. loadedversion <- utils::packageDescription("renv", fields = "Version")
  482. if (version == loadedversion)
  483. return(TRUE)
  484. # assume four-component versions are from GitHub; three-component
  485. # versions are from CRAN
  486. components <- strsplit(loadedversion, "[.-]")[[1]]
  487. remote <- if (length(components) == 4L)
  488. paste("rstudio/renv", loadedversion, sep = "@")
  489. else
  490. paste("renv", loadedversion, sep = "@")
  491. fmt <- paste(
  492. "renv %1$s was loaded from project library, but this project is configured to use renv %2$s.",
  493. "Use `renv::record(\"%3$s\")` to record renv %1$s in the lockfile.",
  494. "Use `renv::restore(packages = \"renv\")` to install renv %2$s into the project library.",
  495. sep = "\n"
  496. )
  497. msg <- sprintf(fmt, loadedversion, version, remote)
  498. warning(msg, call. = FALSE)
  499. FALSE
  500. }
  501. renv_bootstrap_hash_text <- function(text) {
  502. hashfile <- tempfile("renv-hash-")
  503. on.exit(unlink(hashfile), add = TRUE)
  504. writeLines(text, con = hashfile)
  505. tools::md5sum(hashfile)
  506. }
  507. renv_bootstrap_load <- function(project, libpath, version) {
  508. # try to load renv from the project library
  509. if (!requireNamespace("renv", lib.loc = libpath, quietly = TRUE))
  510. return(FALSE)
  511. # warn if the version of renv loaded does not match
  512. renv_bootstrap_validate_version(version)
  513. # load the project
  514. renv::load(project)
  515. TRUE
  516. }
  517. renv_bootstrap_profile_load <- function(project) {
  518. # if RENV_PROFILE is already set, just use that
  519. profile <- Sys.getenv("RENV_PROFILE", unset = NA)
  520. if (!is.na(profile) && nzchar(profile))
  521. return(profile)
  522. # check for a profile file (nothing to do if it doesn't exist)
  523. path <- renv_bootstrap_paths_renv("profile", profile = FALSE, project = project)
  524. if (!file.exists(path))
  525. return(NULL)
  526. # read the profile, and set it if it exists
  527. contents <- readLines(path, warn = FALSE)
  528. if (length(contents) == 0L)
  529. return(NULL)
  530. # set RENV_PROFILE
  531. profile <- contents[[1L]]
  532. if (!profile %in% c("", "default"))
  533. Sys.setenv(RENV_PROFILE = profile)
  534. profile
  535. }
  536. renv_bootstrap_profile_prefix <- function() {
  537. profile <- renv_bootstrap_profile_get()
  538. if (!is.null(profile))
  539. return(file.path("profiles", profile, "renv"))
  540. }
  541. renv_bootstrap_profile_get <- function() {
  542. profile <- Sys.getenv("RENV_PROFILE", unset = "")
  543. renv_bootstrap_profile_normalize(profile)
  544. }
  545. renv_bootstrap_profile_set <- function(profile) {
  546. profile <- renv_bootstrap_profile_normalize(profile)
  547. if (is.null(profile))
  548. Sys.unsetenv("RENV_PROFILE")
  549. else
  550. Sys.setenv(RENV_PROFILE = profile)
  551. }
  552. renv_bootstrap_profile_normalize <- function(profile) {
  553. if (is.null(profile) || profile %in% c("", "default"))
  554. return(NULL)
  555. profile
  556. }
  557. renv_bootstrap_path_absolute <- function(path) {
  558. substr(path, 1L, 1L) %in% c("~", "/", "\\") || (
  559. substr(path, 1L, 1L) %in% c(letters, LETTERS) &&
  560. substr(path, 2L, 3L) %in% c(":/", ":\\")
  561. )
  562. }
  563. renv_bootstrap_paths_renv <- function(..., profile = TRUE, project = NULL) {
  564. renv <- Sys.getenv("RENV_PATHS_RENV", unset = "renv")
  565. root <- if (renv_bootstrap_path_absolute(renv)) NULL else project
  566. prefix <- if (profile) renv_bootstrap_profile_prefix()
  567. components <- c(root, renv, prefix, ...)
  568. paste(components, collapse = "/")
  569. }
  570. renv_bootstrap_project_type <- function(path) {
  571. descpath <- file.path(path, "DESCRIPTION")
  572. if (!file.exists(descpath))
  573. return("unknown")
  574. desc <- tryCatch(
  575. read.dcf(descpath, all = TRUE),
  576. error = identity
  577. )
  578. if (inherits(desc, "error"))
  579. return("unknown")
  580. type <- desc$Type
  581. if (!is.null(type))
  582. return(tolower(type))
  583. package <- desc$Package
  584. if (!is.null(package))
  585. return("package")
  586. "unknown"
  587. }
  588. renv_bootstrap_user_dir <- function() {
  589. dir <- renv_bootstrap_user_dir_impl()
  590. path.expand(chartr("\\", "/", dir))
  591. }
  592. renv_bootstrap_user_dir_impl <- function() {
  593. # use local override if set
  594. override <- getOption("renv.userdir.override")
  595. if (!is.null(override))
  596. return(override)
  597. # use R_user_dir if available
  598. tools <- asNamespace("tools")
  599. if (is.function(tools$R_user_dir))
  600. return(tools$R_user_dir("renv", "cache"))
  601. # try using our own backfill for older versions of R
  602. envvars <- c("R_USER_CACHE_DIR", "XDG_CACHE_HOME")
  603. for (envvar in envvars) {
  604. root <- Sys.getenv(envvar, unset = NA)
  605. if (!is.na(root))
  606. return(file.path(root, "R/renv"))
  607. }
  608. # use platform-specific default fallbacks
  609. if (Sys.info()[["sysname"]] == "Windows")
  610. file.path(Sys.getenv("LOCALAPPDATA"), "R/cache/R/renv")
  611. else if (Sys.info()[["sysname"]] == "Darwin")
  612. "~/Library/Caches/org.R-project.R/R/renv"
  613. else
  614. "~/.cache/R/renv"
  615. }
  616. renv_json_read <- function(file = NULL, text = NULL) {
  617. # if jsonlite is loaded, use that instead
  618. if ("jsonlite" %in% loadedNamespaces())
  619. renv_json_read_jsonlite(file, text)
  620. else
  621. renv_json_read_default(file, text)
  622. }
  623. renv_json_read_jsonlite <- function(file = NULL, text = NULL) {
  624. text <- paste(text %||% read(file), collapse = "\n")
  625. jsonlite::fromJSON(txt = text, simplifyVector = FALSE)
  626. }
  627. renv_json_read_default <- function(file = NULL, text = NULL) {
  628. # find strings in the JSON
  629. text <- paste(text %||% read(file), collapse = "\n")
  630. pattern <- '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]'
  631. locs <- gregexpr(pattern, text, perl = TRUE)[[1]]
  632. # if any are found, replace them with placeholders
  633. replaced <- text
  634. strings <- character()
  635. replacements <- character()
  636. if (!identical(c(locs), -1L)) {
  637. # get the string values
  638. starts <- locs
  639. ends <- locs + attr(locs, "match.length") - 1L
  640. strings <- substring(text, starts, ends)
  641. # only keep those requiring escaping
  642. strings <- grep("[[\\]{}:]", strings, perl = TRUE, value = TRUE)
  643. # compute replacements
  644. replacements <- sprintf('"\032%i\032"', seq_along(strings))
  645. # replace the strings
  646. mapply(function(string, replacement) {
  647. replaced <<- sub(string, replacement, replaced, fixed = TRUE)
  648. }, strings, replacements)
  649. }
  650. # transform the JSON into something the R parser understands
  651. transformed <- replaced
  652. transformed <- gsub("{}", "`names<-`(list(), character())", transformed, fixed = TRUE)
  653. transformed <- gsub("[[{]", "list(", transformed, perl = TRUE)
  654. transformed <- gsub("[]}]", ")", transformed, perl = TRUE)
  655. transformed <- gsub(":", "=", transformed, fixed = TRUE)
  656. text <- paste(transformed, collapse = "\n")
  657. # parse it
  658. json <- parse(text = text, keep.source = FALSE, srcfile = NULL)[[1L]]
  659. # construct map between source strings, replaced strings
  660. map <- as.character(parse(text = strings))
  661. names(map) <- as.character(parse(text = replacements))
  662. # convert to list
  663. map <- as.list(map)
  664. # remap strings in object
  665. remapped <- renv_json_remap(json, map)
  666. # evaluate
  667. eval(remapped, envir = baseenv())
  668. }
  669. renv_json_remap <- function(json, map) {
  670. # fix names
  671. if (!is.null(names(json))) {
  672. lhs <- match(names(json), names(map), nomatch = 0L)
  673. rhs <- match(names(map), names(json), nomatch = 0L)
  674. names(json)[rhs] <- map[lhs]
  675. }
  676. # fix values
  677. if (is.character(json))
  678. return(map[[json]] %||% json)
  679. # handle true, false, null
  680. if (is.name(json)) {
  681. text <- as.character(json)
  682. if (text == "true")
  683. return(TRUE)
  684. else if (text == "false")
  685. return(FALSE)
  686. else if (text == "null")
  687. return(NULL)
  688. }
  689. # recurse
  690. if (is.recursive(json)) {
  691. for (i in seq_along(json)) {
  692. json[i] <- list(renv_json_remap(json[[i]], map))
  693. }
  694. }
  695. json
  696. }
  697. # load the renv profile, if any
  698. renv_bootstrap_profile_load(project)
  699. # construct path to library root
  700. root <- renv_bootstrap_library_root(project)
  701. # construct library prefix for platform
  702. prefix <- renv_bootstrap_platform_prefix()
  703. # construct full libpath
  704. libpath <- file.path(root, prefix)
  705. # attempt to load
  706. if (renv_bootstrap_load(project, libpath, version))
  707. return(TRUE)
  708. # load failed; inform user we're about to bootstrap
  709. prefix <- paste("# Bootstrapping renv", version)
  710. postfix <- paste(rep.int("-", 77L - nchar(prefix)), collapse = "")
  711. header <- paste(prefix, postfix)
  712. message(header)
  713. # perform bootstrap
  714. bootstrap(version, libpath)
  715. # exit early if we're just testing bootstrap
  716. if (!is.na(Sys.getenv("RENV_BOOTSTRAP_INSTALL_ONLY", unset = NA)))
  717. return(TRUE)
  718. # try again to load
  719. if (requireNamespace("renv", lib.loc = libpath, quietly = TRUE)) {
  720. message("* Successfully installed and loaded renv ", version, ".")
  721. return(renv::load())
  722. }
  723. # failed to download or load renv; warn the user
  724. msg <- c(
  725. "Failed to find an renv installation: the project will not be loaded.",
  726. "Use `renv::activate()` to re-initialize the project."
  727. )
  728. warning(paste(msg, collapse = "\n"), call. = FALSE)
  729. })