There’s a more elaborated vignette coming with package packager
. Please see packager’s vignette.
First, we need to create a sample package, so we create a package skeleton:
pkg_path <- file.path(tempdir(), "fakepack")
unlink(pkg_path, force = TRUE, recursive = TRUE)
usethis::create_package(pkg_path)
And add a minimal R code file:
## [1] TRUE
This package does not make any sense. It is just a minimal working example (in the sense that it passes R CMD build
and a simple R CMD check
). It does not provide any functionality apart from a single internal function that is not exported via the package’s NAMESPACE. It is just there to exemplify the usage of fakemake.
Then we get a package makelist:
## Warning: 'package_makelist' is deprecated.
## Use 'packager::get_package_makelist' instead.
## See help("Deprecated")
And we have a look at it:
Obviously the tarball depends on many files and the only target that’s no other target’s prerequisite is “log/check.Rout”. If you are more into hierarchical depictions, you can use the terminal target as root:
But then you might be interested in this python program, it would leave with this graph:
I regularly use it to visualize complex Makefiles.
Let’s take a look at the target rule that builds the tarball:
## $alias
## [1] "build"
##
## $target
## [1] "get_pkg_archive_path(absolute = FALSE)"
##
## $code
## [1] "print(pkgbuild::build(path = \".\", dest_path = \".\", vignettes = FALSE))"
##
## $sink
## [1] "log/build.Rout"
##
## $prerequisites
## [1] ".log.Rout"
## [2] "list.files(\"R\", full.names = TRUE, recursive = TRUE)"
## [3] "list.files(\"man\", full.names = TRUE, recursive = TRUE)"
## [4] "DESCRIPTION"
## [5] "file.path(\"log\", \"lintr.Rout\")"
## [6] "file.path(\"log\", \"cleanr.Rout\")"
## [7] "file.path(\"log\", \"spell.Rout\")"
## [8] "file.path(\"log\", \"covr.Rout\")"
## [9] "file.path(\"log\", \"roxygen2.Rout\")"
Note that some of its items are strings giving file names, some are strings that parse as R expressions, and prerequisites
is a mix of both. Obviously, fakemake parses and evaluates these character strings dynamically.
Let us take a look at the prerequisites for roxygen2
:
## [1] ".log.Rout"
## [2] "list.files(\"R\", full.names = TRUE, recursive = TRUE)"
Now we build and check the package in one go:
## [1] ".log.Rout"
## [2] "log/cleanr.Rout"
## [3] "log/covr.Rout"
## [4] "log/lintr.Rout"
## [5] "log/roxygen2.Rout"
## [6] "log/spell.Rout"
## [7] "get_pkg_archive_path(absolute = FALSE)"
## [8] "log/check.Rout"
We should now detach the package to prevent the coverage target to crash in subsequent calls.
We see the files created in the log directory correspond to the names given by make:
## [1] "build.Rout" "check.Rout" "cleanr.Rout" "covr.Rout"
## [5] "lintr.Rout" "roxygen2.Rout" "spell.Rout"
and we can take a look at one:
## * using log directory ‘/tmp/RtmpZABf0G/fakepack/fakepack.Rcheck’
## * using R Under development (unstable) (2020-11-20 r79451)
## * using platform: x86_64-pc-linux-gnu (64-bit)
## * using session charset: UTF-8
## * using option ‘--as-cran’
## * checking for file ‘fakepack/DESCRIPTION’ ... OK
## * this is package ‘fakepack’ version ‘0.0.0.9000’
## * package encoding: UTF-8
## Error in readRDS(con) : error reading from connection
## Execution halted
Rebuilding the package does not do anything (NULL is returned instead of the names of targets above), you save quite some CPU time compared to unconditionally rerunning the codes in the makelist:
## NULL
## user system elapsed
## 0.097 0.000 0.098
Let us take a look at our testing coverage:
## filename functions line value
## 1 R/throw.R throw 18 0
## 2 R/throw.R throw 19 0
## 3 R/throw.R throw 20 0
## 4 R/throw.R throw 21 0
## fakepack Coverage: 0.00%
## R/throw.R: 0.00%
Well, poor. So we add a test file:
dir.create(file.path(pkg_path, "tests", "testthat"), recursive = TRUE)
file.copy(system.file("templates", "testthat.R", package = "fakemake"),
file.path(pkg_path, "tests"))
## [1] TRUE
file.copy(system.file("templates", "test-throw.R", package = "fakemake"),
file.path(pkg_path, "tests", "testthat"))
## [1] TRUE
Now we re-build the package’s tarball again (of course we could make("check", ml)
again, but for the sake of (CRAN’s) CPU time, I skip the check):
## [1] "log/cleanr.Rout"
## [2] "log/covr.Rout"
## [3] "log/lintr.Rout"
## [4] "get_pkg_archive_path(absolute = FALSE)"
We see that most of the build chain is rerun, except roxygenising, since the files under “tests/” are not prerequisites to log/royxgen2.Rout. Ah, and the test coverage is improved:
## [1] filename functions line value
## <0 rows> (or 0-length row.names)
## fakepack Coverage: 100.00%
## R/throw.R: 100.00%