Skip to content
1 change: 1 addition & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Imports:
lifecycle (>= 1.0.4),
memoise (>= 2.0.1),
miniUI (>= 0.1.2),
pak,
pkgbuild (>= 1.4.8),
pkgdown (>= 2.1.3),
pkgload (>= 1.4.1),
Expand Down
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* `dev_sitrep()` now uses cli for user-facing messages instead of deprecated usethis UI functions.
* `dev_sitrep()` now works correctly in Positron (#2618).
* `is_loading()` is now re-exported from pkgload (#2556).
* `install()` now installs dependencies with `pak::local_install_deps()` instead of `remotes::install_deps()`. This lets us default to `upgrade = FALSE`, so that existing dependencies are only upgraded when a newer version is actually required (#2486). `keep_source` now defaults to `TRUE` when `build = FALSE`, so that source references are automatically preserved during development installs.
* Package installation functions are now deprecated: `install_bioc()`, `install_bitbucket()`, `install_cran()`, `install_deps()`, `install_dev()`, `install_dev_deps()`, `install_git()`, `install_github()`, `install_gitlab()`, `install_local()`, `install_svn()`, `install_url()`, `install_version()`, `update_packages()`, `dev_package_deps()`, `github_pull()`, and `github_release()`. We now recommend pak (<https://pak.r-lib.org/>) for general package installation. See `?install-deprecated` for migration guidance.
* `load_all()` now errors if called recursively, i.e. if you accidentally include a `load_all()` call in one of your R source files (#2617).
* `release()` is deprecated in favour of `usethis::use_release_issue()`.
Expand Down
239 changes: 120 additions & 119 deletions R/install.R
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.
Comment on lines +13 to +15
Copy link
Member

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-docs actually does.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

#' @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"`.
Expand All @@ -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,
Copy link
Member

Choose a reason for hiding this comment

The 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 build = FALSE AND keep_source = TRUE.

I think this default is going to make default behaviour much more sensible.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to ask why we don't default to keep_source = TRUE but reading the documentation made it clear 😄.

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) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hack around lack of quiet argument or option for pak.

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,
Copy link
Member Author

Choose a reason for hiding this comment

The 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
#'
Expand All @@ -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 = ".",
Expand Down Expand Up @@ -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)
}
2 changes: 2 additions & 0 deletions inst/WORDLIST
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ miniUI
mnel
nchar
pak
objs
pak
pandoc
param
params
Expand Down
Loading