-
Notifications
You must be signed in to change notification settings - Fork 762
Use pak::local_install_deps() in install()
#2651
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
beadb4b
b411470
dcec15a
148a4d2
46be427
531df95
e23189b
2ace128
37702d8
2fe560b
c73c275
33849fa
95e25ad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,38 +1,33 @@ | ||
| #' Install a local development package | ||
| #' | ||
| #' Uses `R CMD INSTALL` to install the package. Will also try to install | ||
| #' dependencies of the package from CRAN, if they're not already installed. | ||
| #' | ||
| #' If `quick = TRUE`, installation takes place using the current package | ||
| #' directory. If you have compiled code, this means that artefacts of | ||
| #' compilation will be created in the `src/` directory. If you want to avoid | ||
| #' this, you can use `build = TRUE` to first build a package bundle and then | ||
| #' install it from a temporary directory. This is slower, but keeps the source | ||
| #' directory pristine. | ||
| #' | ||
| #' If the package is loaded, it will be reloaded after installation. This is | ||
| #' not always completely possible, see [reload()] for caveats. | ||
| #' @description | ||
| #' Uses `R CMD INSTALL` to install the package, after installing needed | ||
| #' dependencies with [pak::local_install_deps()]. | ||
| #' | ||
| #' To install a package in a non-default library, use [withr::with_libpaths()]. | ||
| #' To install to a non-default library, use [withr::with_libpaths()]. | ||
| #' | ||
| #' @template devtools | ||
| #' @inheritParams remotes::install_local | ||
| #' @param reload if `TRUE` (the default), will automatically reload the | ||
| #' package after installing. | ||
| #' @param quick if `TRUE` skips docs, multiple-architectures, | ||
| #' demos, and vignettes, to make installation as fast as possible. | ||
| #' @param build if `TRUE` [pkgbuild::build()]s the package first: | ||
| #' this ensures that the installation is completely clean, and prevents any | ||
| #' binary artefacts (like \file{.o}, `.so`) from appearing in your local | ||
| #' package directory, but is considerably slower, because every compile has | ||
| #' to start from scratch. | ||
| #' @param reload if `TRUE` (the default), will automatically attempt to reload | ||
| #' the package after installing. Reloading is not always completely possible | ||
| #' so see [pkgload::unregister()] for caveats. | ||
| #' @param quick if `TRUE`, skips some optional steps (e.g. help | ||
| #' pre-rendering and multi-arch builds) to make installation as fast | ||
| #' as possible. | ||
| #' @param build If `TRUE` (the default), first [pkgbuild::build()]s the | ||
| #' package. This ensures that the installation is completely clean, and | ||
| #' prevents any binary artefacts (like \file{.o}, `.so`) from appearing | ||
| #' in your local package directory, but is considerably slower, because | ||
| #' every compile has to start from scratch. | ||
| #' | ||
| #' One downside of installing from a built tarball is that the package is | ||
| #' installed from a temporary location. This means that any source references, | ||
| #' at R level or C/C++ level, will point to dangling locations. The debuggers | ||
| #' will not be able to find the sources for step-debugging. If you're | ||
| #' installing the package for development, consider setting `build` to | ||
| #' `FALSE`. | ||
| #' installed from a temporary location. This means that any source references | ||
| #' will point to dangling locations and debuggers won't have direct access to | ||
| #' the source for step-debugging. For development purposes, `build = FALSE` is | ||
| #' often the better choice. | ||
| #' | ||
| #' If `FALSE`, the package is installed directly from its source | ||
| #' directory. This is faster and can be favorable for preserving source | ||
| #' references for debugging (see `keep_source`). | ||
| #' @param args An optional character vector of additional command line | ||
| #' arguments to be passed to `R CMD INSTALL`. This defaults to the | ||
| #' value of the option `"devtools.install.args"`. | ||
|
|
@@ -41,112 +36,117 @@ | |
| #' sure vignettes are built even if a build never happens (i.e. because | ||
| #' `build = FALSE`). | ||
| #' @param keep_source If `TRUE` will keep the srcrefs from an installed | ||
| #' package. This is useful for debugging (especially inside of RStudio). | ||
| #' It defaults to the option `"keep.source.pkgs"`. | ||
| #' @param ... additional arguments passed to [remotes::install_deps()] | ||
| #' when installing dependencies. | ||
| #' package. This is useful for debugging (especially inside of RStudio or | ||
| #' Positron). Defaults to `getOption("keep.source.pkgs") || !build`, | ||
| #' since srcrefs are most useful when the package is installed from its | ||
| #' source directory, i.e. when `build = FALSE`. | ||
| #' @param quiet If `TRUE`, suppress output. | ||
| #' @param force `r lifecycle::badge("deprecated")` No longer used. | ||
| #' @family package installation | ||
| #' @seealso [update_packages()] to update installed packages from the | ||
| #' source location and [with_debug()] to install packages with | ||
| #' debugging flags set. | ||
| #' @inheritParams pak::local_install_deps | ||
| #' @seealso [with_debug()] to install packages with debugging flags set. | ||
| #' @export | ||
| install <- | ||
| function( | ||
| pkg = ".", | ||
| reload = TRUE, | ||
| quick = FALSE, | ||
| build = !quick, | ||
| args = getOption("devtools.install.args"), | ||
| quiet = FALSE, | ||
| dependencies = NA, | ||
| upgrade = "default", | ||
| build_vignettes = FALSE, | ||
| keep_source = getOption("keep.source.pkgs"), | ||
| force = FALSE, | ||
| ... | ||
| ) { | ||
| pkg <- as.package(pkg) | ||
|
|
||
| # Forcing all of the promises for the current namespace now will avoid lazy-load | ||
| # errors when the new package is installed overtop the old one. | ||
| # https://stat.ethz.ch/pipermail/r-devel/2015-December/072150.html | ||
| if (reload && is_loaded(pkg)) { | ||
| eapply(pkgload::ns_env(pkg$package), force, all.names = TRUE) | ||
| } | ||
| install <- function( | ||
| pkg = ".", | ||
| reload = TRUE, | ||
| quick = FALSE, | ||
| build = !quick, | ||
| args = getOption("devtools.install.args"), | ||
| quiet = FALSE, | ||
| dependencies = NA, | ||
| upgrade = FALSE, | ||
| build_vignettes = FALSE, | ||
| keep_source = getOption("keep.source.pkgs") || !build, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually changed this default! The previous default didn't make much sense to me, as it made it pretty hard for a developer to get srcrefs. You had to know to do I think this default is going to make default behaviour much more sensible.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was going to ask why we don't default to |
||
| force = deprecated() | ||
| ) { | ||
| if (!is.logical(upgrade) || length(upgrade) != 1) { | ||
| cli::cli_abort("{.arg upgrade} must be a single TRUE, FALSE, or NA") | ||
| } | ||
| if (lifecycle::is_present(force)) { | ||
| lifecycle::deprecate_warn("2.5.0", "install(force)") | ||
| } | ||
|
|
||
| if (isTRUE(build_vignettes)) { | ||
| # we likely need all Suggested dependencies if building vignettes | ||
| dependencies <- TRUE | ||
| build_opts <- c("--no-resave-data", "--no-manual") | ||
| pkg <- as.package(pkg) | ||
|
|
||
| # Forcing all of the promises for the current namespace now will avoid lazy-load | ||
| # errors when the new package is installed overtop the old one. | ||
| # https://stat.ethz.ch/pipermail/r-devel/2015-December/072150.html | ||
| if (reload && is_loaded(pkg)) { | ||
| eapply(pkgload::ns_env(pkg$package), base::force, all.names = TRUE) | ||
| } | ||
|
|
||
| # Install dependencies | ||
| local({ | ||
| if (quiet) { | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hack around lack of |
||
| withr::local_output_sink(withr::local_tempfile()) | ||
| withr::local_message_sink(withr::local_tempfile()) | ||
| } else { | ||
| build_opts <- c("--no-resave-data", "--no-manual", "--no-build-vignettes") | ||
| cli::cat_rule("pak::local_install_deps()", col = "cyan") | ||
| } | ||
|
|
||
| opts <- c( | ||
| if (keep_source) "--with-keep.source", | ||
| "--install-tests" | ||
| pak::local_install_deps( | ||
| pkg$path, | ||
| upgrade = upgrade, | ||
| dependencies = dependencies || isTRUE(build_vignettes) | ||
| ) | ||
| if (quick) { | ||
| opts <- c(opts, "--no-docs", "--no-multiarch", "--no-demo") | ||
| } | ||
| opts <- c(opts, args) | ||
| }) | ||
|
|
||
| check_dots_used(action = getOption("devtools.ellipsis_action", rlang::warn)) | ||
| if (isTRUE(build_vignettes)) { | ||
| build_opts <- c("--no-resave-data", "--no-manual") | ||
| } else { | ||
| build_opts <- c("--no-resave-data", "--no-manual", "--no-build-vignettes") | ||
| } | ||
| opts <- c( | ||
| if (keep_source) "--with-keep.source", | ||
| "--install-tests" | ||
| ) | ||
| if (quick) { | ||
| opts <- c(opts, "--no-docs", "--no-multiarch", "--no-demo") | ||
| } | ||
| opts <- c(opts, args) | ||
|
|
||
| remotes::install_deps( | ||
| if (build) { | ||
| install_path <- pkgbuild::build( | ||
| pkg$path, | ||
| build = build, | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a bit hard to see in the diff, but we're no longer passing any build or install options when we install the dependencies. I think this is likely to be fine, since you'd generally except these to coming from CRAN and not built locally. |
||
| build_opts = build_opts, | ||
| INSTALL_opts = opts, | ||
| dependencies = dependencies, | ||
| quiet = quiet, | ||
| force = force, | ||
| upgrade = upgrade, | ||
| ... | ||
| dest_path = tempdir(), | ||
| args = build_opts, | ||
| quiet = quiet | ||
| ) | ||
| on.exit(file_delete(install_path), add = TRUE) | ||
| } else { | ||
| install_path <- pkg$path | ||
| } | ||
|
|
||
| if (build) { | ||
| install_path <- pkgbuild::build( | ||
| pkg$path, | ||
| dest_path = tempdir(), | ||
| args = build_opts, | ||
| quiet = quiet | ||
| ) | ||
| on.exit(file_delete(install_path), add = TRUE) | ||
| } else { | ||
| install_path <- pkg$path | ||
| } | ||
|
|
||
| was_loaded <- is_loaded(pkg) | ||
| was_attached <- is_attached(pkg) | ||
| was_loaded <- is_loaded(pkg) | ||
| was_attached <- is_attached(pkg) | ||
|
|
||
| if (reload && was_loaded) { | ||
| pkgload::unregister(pkg$package) | ||
| } | ||
| if (reload && was_loaded) { | ||
| pkgload::unregister(pkg$package) | ||
| } | ||
|
|
||
| pkgbuild::with_build_tools( | ||
| required = FALSE, | ||
| callr::rcmd( | ||
| "INSTALL", | ||
| c(install_path, opts), | ||
| echo = !quiet, | ||
| show = !quiet, | ||
| spinner = FALSE, | ||
| stderr = "2>&1", | ||
| fail_on_status = TRUE | ||
| ) | ||
| if (!quiet) { | ||
| cli::cat_rule("R CMD INSTALL", col = "cyan") | ||
| } | ||
| pkgbuild::with_build_tools( | ||
| required = FALSE, | ||
| callr::rcmd( | ||
| "INSTALL", | ||
| c(install_path, opts), | ||
| echo = !quiet, | ||
| show = !quiet, | ||
| spinner = FALSE, | ||
| stderr = "2>&1", | ||
| fail_on_status = TRUE | ||
| ) | ||
| ) | ||
|
|
||
| if (reload && was_loaded) { | ||
| if (was_attached) { | ||
| require(pkg$package, quietly = TRUE, character.only = TRUE) | ||
| } else { | ||
| requireNamespace(pkg$package, quietly = TRUE) | ||
| } | ||
| if (reload && was_loaded) { | ||
| if (was_attached) { | ||
| require(pkg$package, quietly = TRUE, character.only = TRUE) | ||
| } else { | ||
| requireNamespace(pkg$package, quietly = TRUE) | ||
| } | ||
|
|
||
| invisible(TRUE) | ||
| } | ||
| } | ||
|
|
||
| #' Install package dependencies if needed | ||
| #' | ||
|
|
@@ -157,8 +157,9 @@ install <- | |
| #' * `pak::local_install_deps()` instead of `install_deps()` | ||
| #' * `pak::local_install_dev_deps()` instead of `install_dev_deps()` | ||
| #' | ||
| #' @inheritParams install | ||
| #' @inherit remotes::install_deps | ||
| #' @inheritParams install | ||
| #' @param ... Additional arguments passed to [remotes::install_deps()]. | ||
| #' @export | ||
| install_deps <- function( | ||
| pkg = ".", | ||
|
|
@@ -235,5 +236,5 @@ local_install <- function(pkg = ".", quiet = TRUE, env = parent.frame()) { | |
|
|
||
| cli::cli_inform(c(i = "Installing {.pkg {pkg$package}} in temporary library")) | ||
| withr::local_temp_libpaths(.local_envir = env) | ||
| install(pkg, upgrade = "never", reload = FALSE, quick = TRUE, quiet = quiet) | ||
| install(pkg, upgrade = FALSE, reload = FALSE, quick = TRUE, quiet = quiet) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -120,6 +120,8 @@ miniUI | |
| mnel | ||
| nchar | ||
| pak | ||
| objs | ||
| pak | ||
| pandoc | ||
| param | ||
| params | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I clarified this and had to do some research! I've never understood what it meant to "skip docs", because clearly help files are available after installation even with
install(quick = TRUE). So I've reworded to be more clear about what--no-docsactually does.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!