Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 94 additions & 17 deletions R/rxp_make.R
Original file line number Diff line number Diff line change
@@ -1,25 +1,91 @@
#' Validate verbose parameter and handle backward compatibility
#'
#' @param verbose The verbose parameter value to validate
#' @return A single non-negative integer
#' @keywords internal
.rxp_validate_verbose <- function(verbose) {
if (is.logical(verbose)) {
warning(
"logical values for 'verbose' are deprecated. ",
"Use integer values instead: verbose = 0 (FALSE) or verbose = 1 (TRUE).",
call. = FALSE
)
return(as.integer(verbose))
}

if (!is.numeric(verbose) || length(verbose) != 1) {
stop("verbose must be a single numeric or integer value", call. = FALSE)
}

verbose <- as.integer(verbose)

if (verbose < 0) {
stop("rxp_make(): verbose must be a non-negative integer", call. = FALSE)
}

verbose
}

#' Prepare nix-store command arguments
#'
#' @param max_jobs Integer, number of derivations to be built in parallel
#' @param cores Integer, number of cores a derivation can use during build
#' @param drv_paths Character vector of derivation paths
#' @param verbose Integer, verbosity level (0 = silent, >=1 = verbose)
#' @return Character vector of command arguments
#' @keywords internal
.rxp_prepare_nix_store_args <- function(max_jobs, cores, drv_paths, verbose) {
args <- c(
"--realise",
"--keep-going",
"--max-jobs",
max_jobs,
"--cores",
cores
)

# Add --verbose flags based on verbosity level
if (verbose > 0) {
verbose_flags <- rep("--verbose", verbose)
args <- c(args[1], verbose_flags, args[-1])
}

# Add derivation paths at the end
c(args, drv_paths)
}

#' Build pipeline using Nix
#'
#' Runs `nix-build` with a quiet flag, outputting to `_rixpress/result`.
#' @family pipeline functions
#' @param verbose Logical, defaults to FALSE. Set to TRUE to see nix's
#' standard output, can be useful to check what is happening if the
#' build process takes long.
#' @param verbose Integer, defaults to 0L. Verbosity level: 0 = silent build
#' (no live output), 1+ = show nix output with increasing verbosity. 0:
#' "Errors only", 1: "Informational", 2: "Talkative", 3: "Chatty", 4: "Debug",
#' 5: "Vomit". Values higher than 5 are capped to 5
#' Each level adds one --verbose flag to nix-store command.
#' For backward compatibility, logical TRUE/FALSE are accepted with a
#' deprecation warning (TRUE → 1, FALSE → 0).
#' @param max_jobs Integer, number of derivations to be built in parallel.
#' @param cores Integer, number of cores a derivation can use during build.
#' @importFrom processx run
#' @importFrom utils capture.output
#' @return A character vector of paths to the built outputs.
#' @examples
#' \dontrun{
#' # Build the pipeline with default settings
#' # Build the pipeline with default settings (silent)
#' rxp_make()
#'
#' # Build with verbose output and parallel execution
#' rxp_make(verbose = TRUE, max_jobs = 4, cores = 2)
#' rxp_make(verbose = 2, max_jobs = 4, cores = 2)
#'
#' # Maximum verbosity
#' rxp_make(verbose = 3)
#' }
#' @export
rxp_make <- function(verbose = FALSE, max_jobs = 1, cores = 1) {
rxp_make <- function(verbose = 0L, max_jobs = 1, cores = 1) {
# Validate and normalize verbose parameter
verbose <- .rxp_validate_verbose(verbose)

message("Build process started...\n", "\n")

instantiate <- processx::run(
Expand All @@ -38,24 +104,35 @@ rxp_make <- function(verbose = FALSE, max_jobs = 1, cores = 1) {
# Extract derivation paths from stdout (split into lines)
drv_paths <- strsplit(instantiate$stdout, "\n")[[1]]

if (verbose) {
# Set up callbacks based on verbosity level
if (verbose >= 1) {
cb <- function(line, proc) cat(line, "\n")
} else {
cb <- NULL
}

if (verbose > 5L) {
warning(
sprintf(
"Argument 'verbose' (%d) exceeds the maximum of 5; using 5.",
verbose
),
call. = FALSE
)
verbose <- 5L
}

# Prepare nix-store arguments using helper function
nix_store_args <- .rxp_prepare_nix_store_args(
max_jobs,
cores,
drv_paths,
verbose
)

build_process <- processx::run(
command = "nix-store",
args = c(
"--realise",
"--verbose",
"--keep-going",
"--max-jobs",
max_jobs,
"--cores",
cores,
drv_paths
),
args = nix_store_args,
stdout_line_callback = cb,
stderr_line_callback = cb,
error_on_status = FALSE,
Expand Down
12 changes: 6 additions & 6 deletions data-raw/gen_pipeline.R
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ d0 <- rxp_r_file(
)
d1 <- rxp_r(
mtcars_am,
filter(mtcars, am == 1),
filter(mtcars, mpg = 100),
nix_env = "default2.nix"
)

Expand Down Expand Up @@ -40,13 +40,13 @@ derivs <- list(d0, d1, d2, d3, d4, doc)

rixpress(derivs, project_path = ".", build = FALSE)

#rxp_make()
rxp_make(verbose = 5)

dag_obj <- plot_dag(return_igraph = TRUE)
#dag_obj <- plot_dag(return_igraph = TRUE)

dag_obj <- set_vertex_attr(dag_obj, "label", value = V(dag_obj)$name)
#dag_obj <- set_vertex_attr(dag_obj, "label", value = V(dag_obj)$name)

# Step 2: Delete the "name" attribute
dag_obj <- delete_vertex_attr(dag_obj, "name")
#dag_obj <- delete_vertex_attr(dag_obj, "name")

igraph::write_graph(dag_obj, file = "dag.dot", format = "dot")
#igraph::write_graph(dag_obj, file = "dag.dot", format = "dot")
104 changes: 104 additions & 0 deletions tests/testthat/test-rxp_make-verbosity.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
test_that(".rxp_prepare_nix_store_args works with verbose = 0", {
args <- .rxp_prepare_nix_store_args(
max_jobs = 1,
cores = 1,
drv_paths = c("/nix/store/test.drv"),
verbose = 0
)

# Should not contain --verbose flag
expect_false("--verbose" %in% args)

# Should contain other expected args
expect_true("--realise" %in% args)
expect_true("--keep-going" %in% args)
expect_true("--max-jobs" %in% args)
expect_true("--cores" %in% args)
})

test_that(".rxp_prepare_nix_store_args works with verbose = 1", {
args <- .rxp_prepare_nix_store_args(
max_jobs = 2,
cores = 3,
drv_paths = c("/nix/store/test.drv"),
verbose = 1
)

# Should contain exactly one --verbose flag
verbose_count <- sum(args == "--verbose")
expect_equal(verbose_count, 1)

# Should contain other expected args
expect_true("--realise" %in% args)
expect_true("--keep-going" %in% args)
expect_equal(args[which(args == "--max-jobs") + 1], "2")
expect_equal(args[which(args == "--cores") + 1], "3")
})

test_that(".rxp_prepare_nix_store_args works with verbose = 2", {
args <- .rxp_prepare_nix_store_args(
max_jobs = 1,
cores = 1,
drv_paths = c("/nix/store/test.drv"),
verbose = 2
)

# Should contain exactly two --verbose flags
verbose_count <- sum(args == "--verbose")
expect_equal(verbose_count, 2)
})

test_that(".rxp_prepare_nix_store_args works with verbose = 3", {
args <- .rxp_prepare_nix_store_args(
max_jobs = 1,
cores = 1,
drv_paths = c("/nix/store/test.drv"),
verbose = 3
)

# Should contain exactly three --verbose flags
verbose_count <- sum(args == "--verbose")
expect_equal(verbose_count, 3)
})

test_that("verbose parameter validation works", {
# Test that negative verbose triggers an error
expect_error(
.rxp_validate_verbose(-1),
"verbose must be a non-negative integer"
)

# Test that non-integer triggers an error
expect_error(
.rxp_validate_verbose("invalid"),
"verbose must be a single numeric or integer value"
)

# Test that multiple values trigger an error
expect_error(
.rxp_validate_verbose(c(1, 2)),
"verbose must be a single numeric or integer value"
)

# Test that valid integers work
expect_equal(.rxp_validate_verbose(0), 0L)
expect_equal(.rxp_validate_verbose(1), 1L)
expect_equal(.rxp_validate_verbose(2L), 2L)
expect_equal(.rxp_validate_verbose(3.0), 3L)
})

test_that("logical verbose backward compatibility works with deprecation warning", {
# Test TRUE maps to 1 with warning
expect_warning(
result <- .rxp_validate_verbose(TRUE),
"logical values for 'verbose' are deprecated"
)
expect_equal(result, 1L)

# Test FALSE maps to 0 with warning
expect_warning(
result <- .rxp_validate_verbose(FALSE),
"logical values for 'verbose' are deprecated"
)
expect_equal(result, 0L)
})
Loading