1#' Detect Application Dependencies 2#' 3#' Recursively detect all package dependencies for an application. This function 4#' parses all .R files in the application directory to determine what packages 5#' the application depends on; and for each of those packages what other 6#' packages they depend on. 7#' @inheritParams deployApp 8#' @param appDir Directory containing application. Defaults to current working 9#' directory. 10#' @return Returns a data frame listing the package 11#' dependencies detected for the application: \tabular{ll}{ \code{package} 12#' \tab Name of package \cr \code{version} \tab Version of package\cr } 13#' @details Dependencies are determined by parsing application source code and 14#' looking for calls to \code{library}, \code{require}, \code{::}, and 15#' \code{:::}. 16#' 17#' Recursive dependencies are detected by examining the \code{Depends}, 18#' \code{Imports}, and \code{LinkingTo} fields of the packages immediately 19#' dependend on by the application. 20#' 21#' @note Since the \code{Suggests} field is not included when determining 22#' recursive dependencies of packages, it's possible that not every package 23#' required to run your application will be detected. 24#' 25#' In this case, you can force a package to be included dependency by 26#' inserting call(s) to \code{require} within your source directory. This code 27#' need not actually execute, for example you could create a standalone file 28#' named \code{dependencies.R} with the following code: \cr \cr 29#' \code{require(xts)} \cr \code{require(colorspace)} \cr 30#' 31#' This will force the \code{xts} and \code{colorspace} packages to be 32#' installed along with the rest of your application when it is deployed. 33#' @examples 34#' \dontrun{ 35#' 36#' # dependencies for the app in the current working dir 37#' appDependencies() 38#' 39#' # dependencies for an app in another directory 40#' appDependencies("~/projects/shiny/app1") 41#' } 42#' @seealso \link[rsconnect:rsconnectPackages]{Using Packages with rsconnect} 43#' @export 44appDependencies <- function(appDir = getwd(), appFiles=NULL) { 45 # if the list of files wasn't specified, generate it 46 if (is.null(appFiles)) { 47 appFiles <- bundleFiles(appDir) 48 } 49 bundleDir <- bundleAppDir(appDir, appFiles) 50 deps <- snapshotDependencies(bundleDir) 51 data.frame(package = deps[,"Package"], 52 version = deps[,"Version"], 53 source = deps[,"Source"], 54 row.names = c(1:length(deps[,"Package"])), 55 stringsAsFactors=FALSE) 56} 57 58snapshotDependencies <- function(appDir, implicit_dependencies=c()) { 59 60 # create a packrat "snapshot" 61 addPackratSnapshot(appDir, implicit_dependencies) 62 63 # TODO: should we care about lockfile version or packrat version? 64 lockFilePath <- snapshotLockFile(appDir) 65 df <- as.data.frame(read.dcf(lockFilePath), stringsAsFactors = FALSE) 66 67 # get repos defined in the lockfile 68 repos <- gsub("[\r\n]", " ", df[1, 'Repos']) 69 repos <- strsplit(unlist(strsplit(repos, "\\s*,\\s*", perl = TRUE)), "=", fixed = TRUE) 70 repos <- setNames( 71 sapply(repos, "[[", 2), 72 sapply(repos, "[[", 1) 73 ) 74 75 # get Bioconductor repos if any 76 biocRepos = repos[grep('BioC', names(repos), perl=TRUE, value=TRUE)] 77 if (length(biocRepos) > 0) { 78 biocPackages = available.packages(contriburl=contrib.url(biocRepos, type="source")) 79 } else { 80 biocPackages = c() 81 } 82 83 # get packages records defined in the lockfile 84 records <- utils::tail(df, -1) 85 86 # if the package is in a named CRAN-like repository capture it 87 tmp <- sapply(seq.int(nrow(records)), function(i) { 88 pkg <- records[i, "Package"] 89 source <- records[i, "Source"] 90 repository <- NA 91 # capture Bioconcutor repository 92 if (identical(source, "Bioconductor")) { 93 if (pkg %in% biocPackages) { 94 repository <- biocPackages[pkg, 'Repository'] 95 } 96 } else { 97 # capture CRAN-like repository 98 repository <- if (source %in% names(repos)) repos[[source]] else NA 99 } 100 repository 101 }) 102 records[, "Repository"] <- tmp 103 return(records) 104} 105 106# get source packages from CRAN 107availableCRANSourcePackages <- function() { 108 available.packages("https://cran.rstudio.com/src/contrib", type = "source") 109} 110