The rprojroot
package solves a seemingly trivial but annoying problem that occurs sooner or later in any largish project: How to find files in subdirectories? Relative file paths are almost always preferable to absolute paths, but relative to what should they be? Unfortunately, we cannot always be sure about the value of the working directory: For instance, in RStudio, sometimes it’s the project root (when running R scripts), sometimes a subdirectory (when building vignettes), sometimes again the project root (when executing chunks of a vignette):
basename(getwd())
## [1] "vignettes"
If we could only get hold of our…
The root of a project is defined as a directory that contains a regular file whose name matches a given pattern and whose contents optionally match another pattern. Thus, the following method reliably finds our project root:
The Git version control system (and probably many other tools) use a similar approach: A Git command can be executed from within any subdirectory of a repository.
Note that the following code produces identical results when building the vignette and when sourcing the chunk, provided that the project root is the current working directory or a parent thereof:
library(rprojroot)
basename(find_root("DESCRIPTION"))
## [1] "rprojroot"
file.exists(find_root_file("R", "root.R", filename = "DESCRIPTION"))
## [1] TRUE
You can save some time for RStudio projects and packages:
print(find_package_root_file)
## function (..., path = getwd())
## {
## find_root_file(..., filename = "^DESCRIPTION$", contents = "^Package: ",
## n = 1L, path = path)
## }
## <environment: 0x3bb73e8>
head(readLines(find_package_root_file("vignettes", "rprojroot.Rmd")))
## [1] "---" "title: \"Finding files reliably\""
## [3] "author: \"Kirill Müller\"" "date: \"`r Sys.Date()`\""
## [5] "output: rmarkdown::html_vignette" "vignette: >"
If you’re lazy, define a shortcut:
P <- find_package_root_file
file.exists(P("vignettes", "rprojroot.Rmd"))
## [1] TRUE
Legacy code can benefit immediately if the file.path
function is overwritten.
You might also want to define your project root differently (this fails here – we look for a file named LICENSE
which is absent):
R <- make_find_root_file(glob2rx("LICENSE"))
R
## function (..., path = getwd())
## {
## find_root_file(..., filename = "^LICENSE$", contents = NULL,
## n = -1L, path = path)
## }
## <environment: 0x29de7b0>
class(try(dir.exists(R("man"))))
## [1] "try-error"
If there is only one root, things get even simpler: We can create a function that computes a path relative to the root at creation time.
F <- make_fix_root_file(glob2rx("DESCRIPTION"))
formals(F)
## $...
readLines(F("NAMESPACE"))
## [1] "# Generated by roxygen2 (4.1.1): do not edit by hand"
## [2] ""
## [3] "export(find_package_root_file)"
## [4] "export(find_root)"
## [5] "export(find_root_file)"
## [6] "export(find_rstudio_root_file)"
## [7] "export(make_find_root_file)"
## [8] "export(make_fix_root_file)"
This even works if we later change the working directory to somewhere outside the project:
local({
oldwd <- setwd("../..")
on.exit(setwd(oldwd), add = TRUE)
file.size(F("NAMESPACE"))
})
## [1] 212
The rprojroot
package allows easy access to files below a project root which is supposed to be the only directory in the whole hierarchy that contains a specific file. This is a robust solution for finding project files in largish projects with a subdirectory hierarchy if the current working directory cannot be assumed fixed. (However, at least initially, the current working directory must be somewhere below the project root.)
This package was inspired by the “Stop working directory insanity” gist by Jennifer Bryan, and by the way Git knows where its files are.