diff --git a/R/checker.R b/R/checker.R index 16adfb5..2dcac34 100644 --- a/R/checker.R +++ b/R/checker.R @@ -82,6 +82,8 @@ checker <- R6::R6Class( #' @param restore `logical` value, whether output directory should be #' unlinked before running checks. If `FALSE`, an attempt will me made to #' restore previous progress from the same `output`. + #' @param dependencies A vector of length one or a named list. + #' Compatible with [`as_pkg_dependencies`]. #' @param ... Additional arguments unused #' #' @return [checker]. @@ -95,6 +97,7 @@ checker <- R6::R6Class( lib.loc = .libPaths(), repos = getOption("repos"), restore = options::opt("restore"), + dependencies = TRUE, ... ) { check_past_output(output, restore, ask = interactive()) @@ -111,7 +114,7 @@ checker <- R6::R6Class( ) private$repos <- repos - self$graph <- task_graph(self$plan, repos) + self$graph <- task_graph(self$plan, repos, dependencies = dependencies) private$restore_complete_checks() }, diff --git a/R/pkg_origin.R b/R/pkg_origin.R index 40ebedd..8110742 100644 --- a/R/pkg_origin.R +++ b/R/pkg_origin.R @@ -214,7 +214,7 @@ pkg_deps.pkg_origin_local <- function( indirect_deps <- pkg_dependencies( packages = direct_deps$name, - dependencies = dependencies, + dependencies = "hard", db = db ) indirect_deps$depth <- rep.int("indirect", NROW(indirect_deps)) diff --git a/R/plan.R b/R/plan.R index eda9c32..44839e4 100644 --- a/R/plan.R +++ b/R/plan.R @@ -7,12 +7,16 @@ #' #' @param path path to the package source. #' @param repos repository used to identify reverse dependencies. +#' @param remotes_dependencies A vector of length one or a named list. +#' Compatible with [`as_pkg_dependencies`]. Used to filter out remotes +#' dependencies. #' #' @family plan #' @export plan_rev_dep_checks <- function( path, - repos = getOption("repos") + repos = getOption("repos"), + remotes_dependencies = TRUE ) { path <- check_path_is_pkg_source(path) ap <- available_packages(repos = repos) @@ -75,7 +79,7 @@ plan_rev_dep_checks <- function( g <- task_graph_class(g) if (remotes_permitted()) { - remotes_graph(g) + remotes_graph(g, dependencies = remotes_dependencies) } else { g } @@ -131,6 +135,9 @@ plan_rev_dep_release_check <- function(origin, revdep, repos) { #' @param package A path to either package, directory with packages or name #' of the package (details) #' @param repos repository used to identify packages when name is provided. +#' @param remotes_dependencies A vector of length one or a named list. +#' Compatible with [`as_pkg_dependencies`]. Used to filter out remotes +#' dependencies. #' #' @details #' `package` parameter has two different allowed values: @@ -145,7 +152,8 @@ plan_rev_dep_release_check <- function(origin, revdep, repos) { #' @export plan_local_checks <- function( package, - repos = getOption("repos") + repos = getOption("repos"), + remotes_dependencies = TRUE ) { task <- meta_task( @@ -179,7 +187,7 @@ plan_local_checks <- function( star_plan_template(c( list(task), local_checks_tasks - )) + ), remotes_dependencies) } @@ -189,11 +197,15 @@ plan_local_checks <- function( #' #' @param package A path to package source. #' @param repos repository used to identify packages when name is provided. +#' @param remotes_dependencies A vector of length one or a named list. +#' Compatible with [`as_pkg_dependencies`]. Used to filter out remotes +#' dependencies. #' #' @family plan plan_local_install <- function( package, - repos = getOption("repos") + repos = getOption("repos"), + remotes_dependencies = TRUE ) { m_task <- meta_task( @@ -208,10 +220,10 @@ plan_local_install <- function( star_plan_template(list( m_task, i_task - )) + ), remotes_dependencies) } -star_plan_template <- function(tasks) { +star_plan_template <- function(tasks, remotes_dependencies) { g <- star_graph( task = tasks ) @@ -221,7 +233,7 @@ star_plan_template <- function(tasks) { g <- task_graph_class(g) if (remotes_permitted()) { - remotes_graph(g) + remotes_graph(g, dependencies = remotes_dependencies) } else { g } diff --git a/R/remotes.R b/R/remotes.R index 198dafc..c9a4257 100644 --- a/R/remotes.R +++ b/R/remotes.R @@ -3,9 +3,14 @@ remotes_graph <- function(x, ...) { } #' @export -remotes_graph.task_graph <- function(x, ...) { +remotes_graph.task_graph <- function(x, ..., dependencies = TRUE) { vs <- V(x) - remotes_subgraphs <- lapply(vs, remotes_graph, vs = vs) + remotes_subgraphs <- lapply( + vs, + remotes_graph, + vs = vs, + dependencies = dependencies + ) task_graph_class( suppressWarningsRegex( @@ -23,13 +28,13 @@ remotes_graph.task_graph <- function(x, ...) { #' @export remotes_graph.integer <- function(x, ..., vs) { - remotes_graph(vs[[x]]) + remotes_graph(vs[[x]], ...) } #' @export #' @method remotes_graph igraph.vs remotes_graph.igraph.vs <- function(x, ...) { - remotes_graph(x$task) + remotes_graph(x$task, ...) } #' @export @@ -40,16 +45,19 @@ remotes_graph.task <- function(x, ...) { } #' @export -remotes_graph.install_task <- function(x, ...) { +remotes_graph.install_task <- function(x, ..., dependencies = TRUE) { remote_tasks <- get_remote_tasks(x) if (length(remote_tasks) == 0) return(igraph::make_empty_graph()) remote_tasks_names <- vcapply(remote_tasks, package) + dependencies <- as_pkg_dependencies(dependencies)$direct x_deps <- pkg_deps(x$origin) - x_remote_deps <- x_deps[ - x_deps$package == package(x) & x_deps$name %in% remote_tasks_names, - ] - + x_remote_deps <- x_deps[x_deps$package == package(x) & + x_deps$name %in% remote_tasks_names & + x_deps$type %in% dependencies, ] + + if (NROW(x_remote_deps) == 0) return(igraph::make_empty_graph()) + # Sort tasks according to same key remote_tasks <- remote_tasks[order(remote_tasks_names)] remote_tasks_types <- x_remote_deps[order(x_remote_deps$name), ]$type diff --git a/R/task_graph.R b/R/task_graph.R index 8f79499..e5d691d 100644 --- a/R/task_graph.R +++ b/R/task_graph.R @@ -14,6 +14,8 @@ #' @param x a `plan` object, containing a list of related steps. #' @param repos `repos`, as expected by [`tools::package_dependencies()`] to #' determine package relationships. +#' @param dependencies A vector of length one or a named list. Compatible with +#' [`as_pkg_dependencies`]. #' @param ... params passed to helper methods. #' @return A `data.frame` that can be used to build #' [`igraph::make_graph`] edges. @@ -26,13 +28,20 @@ #' @keywords internal #' #' @importFrom igraph V E -task_graph <- function(x, repos = getOption("repos"), ...) { +task_graph <- function( + x, repos = getOption("repos"), dependencies = TRUE, ... +) { UseMethod("task_graph") } #' @export -task_graph.task <- function(x, repos = getOption("repos"), ...) { - df <- pkg_deps(x$origin, repos = repos, dependencies = TRUE) +task_graph.task <- function( + x, + repos = getOption("repos"), + dependencies = TRUE, + ... +) { + df <- pkg_deps(x$origin, repos = repos, dependencies = dependencies) # Distinguish direct dependencies of the package form possible indirect # in the same data.frame which could come from suggested loops. This ensures # there is a separate node for the root task. @@ -47,17 +56,14 @@ task_graph.task <- function(x, repos = getOption("repos"), ...) { E(g_dep)$relation <- RELATION$dep E(g_dep)$type <- DEP[E(g_dep)$type] - V(g_dep)$task <- lapply( - V(g_dep)$name, - function(p) { - if (endsWith(p, "-root")) { - x - } else { - origin <- try_pkg_origin_repo(package = p, repos = repos) - install_task(origin = origin) - } + V(g_dep)$task <- lapply(V(g_dep)$name, function(p) { + if (endsWith(p, "-root")) { + x + } else { + origin <- try_pkg_origin_repo(package = p, repos = repos) + install_task(origin = origin) } - ) + }) V(g_dep)$name <- vcapply(V(g_dep)$task, as_vertex_name) @@ -65,7 +71,9 @@ task_graph.task <- function(x, repos = getOption("repos"), ...) { } #' @export -task_graph.task_graph <- function(x, repos = getOption("repos"), ...) { +task_graph.task_graph <- function( + x, repos = getOption("repos"), dependencies = TRUE, ... +) { # only use dependency edges when populating graph nodes <- V(x)[is_actionable_task(V(x)$task)] @@ -82,7 +90,7 @@ task_graph.task_graph <- function(x, repos = getOption("repos"), ...) { subtree <- igraph::induced_subgraph(x, nh) # build dependency graph, with fallback installation task - deps <- task_graph(nh[[1]]$task, repos = repos) + deps <- task_graph(nh[[1]]$task, repos = repos, dependencies = dependencies) # merge trees on package names # NOTE: attributes (tasks) are preserved in the order they appear @@ -94,8 +102,12 @@ task_graph.task_graph <- function(x, repos = getOption("repos"), ...) { deduplicate_task_graph(subtree) }) + # then merge all the full check task task trees into a single graph g <- graph_dedup_attrs(igraph::union(x, check_task_neighborhoods)) + # Make sure orphaned packages, so those that do not lead to any meta tasks, + # are skipped + g <- task_graph_removed_orphaned(g) E(g)$type <- DEP[E(g)$type] V(g)$status <- STATUS$pending @@ -138,8 +150,16 @@ deduplicate_task_graph <- function(g) { ) } } - isolated <- which(igraph::degree(g) == 0) - igraph::delete_vertices(g, isolated) + g +} + +task_graph_removed_orphaned <- function(g) { + # The only package without "in" vertex should be meta tasks + vs <- igraph::V(g) + + isolated <- igraph::degree(g, mode = "in") == 0 & !is_meta(vs$task) + + igraph::delete_vertices(g, vs[isolated]) } dep_edges <- function(edges, dependencies = TRUE) { diff --git a/R/utils-deps.R b/R/utils-deps.R index 3122dcd..cfc4b40 100644 --- a/R/utils-deps.R +++ b/R/utils-deps.R @@ -23,18 +23,17 @@ as_pkg_dependencies <- function(x) { x <- character(0L) } - deptypes <- c("Depends", "Imports", "LinkingTo", "Suggests", "Enhances") - if (is.character(x)) { + if (is.character(x) || is.factor(x)) { x <- switch(x, - "all" = deptypes, - "most" = c("Depends", "Imports", "LinkingTo", "Suggests"), - "hard" = c("Depends", "Imports", "LinkingTo"), - "soft" = c("Suggests", "Enhances"), - x + "all" = DEP, + "most" = DEP[c("Depends", "Imports", "LinkingTo", "Suggests")], + "hard" = DEP[c("Depends", "Imports", "LinkingTo")], + "soft" = DEP[c("Suggests", "Enhances")], + DEP[x] ) } - stopifnot(all(x %in% deptypes)) + stopifnot(all(x %in% DEP)) x } diff --git a/man/checker.Rd b/man/checker.Rd index f5cbff3..6270218 100644 --- a/man/checker.Rd +++ b/man/checker.Rd @@ -81,6 +81,7 @@ and installation order are embedded. lib.loc = .libPaths(), repos = getOption("repos"), restore = options::opt("restore"), + dependencies = TRUE, ... )}\if{html}{\out{}} } @@ -106,6 +107,9 @@ generating task graph and later pulling dependencies.} unlinked before running checks. If \code{FALSE}, an attempt will me made to restore previous progress from the same \code{output}.} +\item{\code{dependencies}}{A vector of length one or a named list. +Compatible with \code{\link{as_pkg_dependencies}}.} + \item{\code{...}}{Additional arguments unused} } \if{html}{\out{}} diff --git a/man/plan_local_checks.Rd b/man/plan_local_checks.Rd index 34f3e14..6916c83 100644 --- a/man/plan_local_checks.Rd +++ b/man/plan_local_checks.Rd @@ -4,13 +4,21 @@ \alias{plan_local_checks} \title{Plan R CMD Checks} \usage{ -plan_local_checks(package, repos = getOption("repos")) +plan_local_checks( + package, + repos = getOption("repos"), + remotes_dependencies = TRUE +) } \arguments{ \item{package}{A path to either package, directory with packages or name of the package (details)} \item{repos}{repository used to identify packages when name is provided.} + +\item{remotes_dependencies}{A vector of length one or a named list. +Compatible with \code{\link{as_pkg_dependencies}}. Used to filter out remotes +dependencies.} } \description{ Generates a plan for running R CMD check for a specified set of packages. diff --git a/man/plan_local_install.Rd b/man/plan_local_install.Rd index 8346cb5..fa95455 100644 --- a/man/plan_local_install.Rd +++ b/man/plan_local_install.Rd @@ -4,12 +4,20 @@ \alias{plan_local_install} \title{Plan source package installation} \usage{ -plan_local_install(package, repos = getOption("repos")) +plan_local_install( + package, + repos = getOption("repos"), + remotes_dependencies = TRUE +) } \arguments{ \item{package}{A path to package source.} \item{repos}{repository used to identify packages when name is provided.} + +\item{remotes_dependencies}{A vector of length one or a named list. +Compatible with \code{\link{as_pkg_dependencies}}. Used to filter out remotes +dependencies.} } \description{ Generates a plan for running installing a package from source. diff --git a/man/plan_rev_dep_checks.Rd b/man/plan_rev_dep_checks.Rd index a61a5a6..9708ecb 100644 --- a/man/plan_rev_dep_checks.Rd +++ b/man/plan_rev_dep_checks.Rd @@ -4,12 +4,20 @@ \alias{plan_rev_dep_checks} \title{Plan Reverse Dependency Checks} \usage{ -plan_rev_dep_checks(path, repos = getOption("repos")) +plan_rev_dep_checks( + path, + repos = getOption("repos"), + remotes_dependencies = TRUE +) } \arguments{ \item{path}{path to the package source.} \item{repos}{repository used to identify reverse dependencies.} + +\item{remotes_dependencies}{A vector of length one or a named list. +Compatible with \code{\link{as_pkg_dependencies}}. Used to filter out remotes +dependencies.} } \description{ Generates a plan for running reverse dependency check for certain diff --git a/man/task_graph.Rd b/man/task_graph.Rd index e01a62f..6a2b2df 100644 --- a/man/task_graph.Rd +++ b/man/task_graph.Rd @@ -4,7 +4,7 @@ \alias{task_graph} \title{Build task graph edges} \usage{ -task_graph(x, repos = getOption("repos"), ...) +task_graph(x, repos = getOption("repos"), dependencies = TRUE, ...) } \arguments{ \item{x}{a \code{plan} object, containing a list of related steps.} @@ -12,6 +12,9 @@ task_graph(x, repos = getOption("repos"), ...) \item{repos}{\code{repos}, as expected by \code{\link[tools:package_dependencies]{tools::package_dependencies()}} to determine package relationships.} +\item{dependencies}{A vector of length one or a named list. Compatible with +\code{\link{as_pkg_dependencies}}.} + \item{...}{params passed to helper methods.} } \value{