# install.packages('pak')
::pak('mjfrigaard/shinypak') pak
31 Dependency hell
Mastering dependency management
This chapter covers a few packages and tools to help explore, understand and keep track of your app-package dependencies.1 It’s not likely you’ll build an application that only relies on shiny
, so it’s important to 1) know the packages and versions required for your application to function, and 2) ensure these packages are included in the correct DESCRIPTION
field (or NAMESPACE
).
31.1 Exploring dependencies
The first package we’ll cover is pak
, which is, “A Fresh Approach to R Package Installation.” pak
includes two tools I’ve found to be incredibly helpful for understanding the dependencies in a given package (or a package I’m building): dependency trees and the dependency explainer.
Launch app with the shinypak
package:
launch('28_dependency-hell')
31.1.1 Trees
pak::pkg_deps_tree()
shows us the dependencies for a particular package. To demonstrate how this function works, we’ll explore the dependencies in three packages:
rlang
: “Functions for Base Types and Core R and ‘Tidyverse’ Features”lifecycle
: “Manage the Life Cycle of your Package Functions”, andvctrs
: “Vector Helpers”
Let’s start with the rlang
package:
::pkg_deps_tree("rlang") pak
rlang 1.1.1 ✨
Key: ✨ new
rlang
is “a collection of frameworks and APIs for programming with R” and it’s built with only base R packages (that’s why it’s DESCRIPTION
file only Imports
the utils
package):
Imports: utils
Now lets look at lifecycle
:
::pkg_deps_tree(pkg = "lifecycle") pak
lifecycle 1.0.3 ✨ ⬇ (123.60 kB)
├─cli 3.6.1 ✨
├─glue 1.6.2 ✨ └─rlang 1.1.1 ✨
lifecycle
depends on cli
, glue
, and rlang
.
If we look at the DESCRIPTION
file for lifecycle
, it also imports cli
, glue
, and rlang
(and specifies versions for cli
and rlang
)
Imports:
cli (>= 3.4.0),
glue, rlang (>= 1.1.0)
Finally, lets look at the dependencies in the vctrs
package. The DESCRIPTION
file for vctrs
imports cli
, glue
, lifecycle
, and rlang
Imports:
cli (>= 3.4.0),
glue,
lifecycle (>= 1.0.3), rlang (>= 1.1.0)
If we check the dependency tree, we see the cli
, glue
, and rlang
are listed twice (once for vctrs
, and again for lifecycle
):
::pkg_deps_tree(pkg = "vctrs") pak
vctrs 0.6.4 ✨
├─cli 3.6.1 ✨
├─glue 1.6.2 ✨
├─lifecycle 1.0.3 ✨ ⬇ (123.60 kB)
│ ├─cli
│ ├─glue
│ └─rlang 1.1.1 ✨
└─rlang
Key: ✨ new | ⬇ download
vctrs
depends on cli
, glue
, rlang
, and lifecycle
(which also depends on cli
, glue
, and rlang
)
31.1.2 Explain
We can show dependency relationships with pak::pkg_deps_explain()
. For example,
How does lifecycle
depend on rlang
?
::pkg_deps_explain("lifecycle", "rlang") pak
lifecycle -> rlang
How does vctrs
depend on rlang
?
::pkg_deps_explain("vctrs", "rlang") pak
vctrs -> lifecycle -> rlang vctrs -> rlang
vctrs
directly depends on rlang
and lifecycle
(which also depends on rlang
).
31.1.3 Depends
So far we’ve been including add-on functions to the Imports
field in the DESCRIPTION
, which ensures the package is installed with our app-package, but not attached to the search list. However, if we include a package in the Depends
field, it’s installed and attached.
This is rarely needed, but a great example is the relationship between devtools
usethis
:
::pkg_deps_explain("devtools", "usethis") pak
devtools -> usethis
In the DECRIPTION
file for devtools
, usethis
is listed with a version number under Depends
:
Depends: usethis (>= 2.1.6)
31.1.4 Case study: devtools
The conscious uncoupling of devtools
split package development across multiple packages. Let’s see how this works, starting with the commonly used devtools
function load_all()
31.1.4.1 pkgload
load_all()
is handled by the pkgload package, which “Simulate[s] Package Installation and Attach”.
How does devtools
depend on pkgload
?
::pkg_deps_explain("devtools", "pkgload") pak
devtools -> pkgload
devtools -> roxygen2 -> pkgload devtools -> testthat -> pkgload
This relationship shows the three actions that call load_all()
during package development:
devtools::load_all()
actually callspkgload::load_all()
devtools::document()
anddevtools::test()
also callpkgload::load_all()
::pkg_deps_explain("devtools", "roxygen2") pak
devtools -> roxygen2
::pkg_deps_explain("devtools", "testthat") pak
devtools -> testthat
31.2 Tracking dependencies
The following packages will help keep your app-package dependencies managed in the DESCRIPTION
file and the code below R/
:
31.2.1 attachment
attachment
was introduced in the golem
chapter, but you don’t have to use the golem
framework to take advantage of it’s functions. att_amend_desc()
will update the package dependencies in the DESCRIPTION
file.
::att_amend_desc() attachment
Saving attachment parameters to yaml config file
Updating sap documentation
ℹ Loading sap
Writing NAMESPACE
Writing NAMESPACE
ℹ Loading sap
[+] 6 package(s) added: cli, tools, fst, ggplot2movies, glue, waldo.
attachment::att_amend_desc()
will automatically create a dev/
folder with a YAML configuration file:
dev
└── config_attachment.yaml
1 directory, 1 file
config_attachment.yaml
contents:
path.n: NAMESPACE
path.d: DESCRIPTION
dir.r: R
dir.v: vignettes
dir.t: tests
extra.suggests: ~
pkg_ignore: ~
document: yes
normalize: yes
inside_rmd: no
must.exist: yes
check_if_suggests_is_installed: yes
This can be deleted, but if you’re going to continue using attachment
it’s worth customizing some of the options for your app-package.
31.2.2 sinew
The sinew
package also warrants mentioning because it can help ensure you’re namespacing functions from add-on packages, although it’s not automated like attachment::att_amend_desc()
. The primary function in sinew
is pretty_namespace()
.
::pretty_namespace(con = "app.R") sinew
All Shiny app-packages will inherently depend on shiny
, so including more dependencies can make developers justifiably uneasy. In this appendix, we’ll explore the package dependencies using the dependency lookup features from the pak
package
31.2.3 desc
The desc
package provides functions for creating, reading, writing, and manipulating DESCRIPTION
files. You can include additional dependencies to your DESCRIPTION using the desc_set_dep()
function.
library(desc)
desc_set_dep("glue", "Imports")
desc_get("Imports")
Imports:
bslib,
cli,
glue,
ggplot2,
logger,
rlang,
sass,
shiny,
shinythemes,
stringr, tools
Try to avoid dependency hell.↩︎