diff --git a/NEWS.md b/NEWS.md index 9c2d1d782..4f15aa4da 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,6 @@ # devtools (development version) +* `build_manual()` reports more details on failure (#2586). * New `check_mac_devel()` function to check a package using the macOS builder at https://mac.r-project.org/macbuilder/submit.html (@nfrerebeau, #2507) * `is_loading()` is now re-exported from pkgload (#2556). * `load_all()` now errors if called recursively, i.e. if you accidentally include a `load_all()` call in one of your R source files (#2617). diff --git a/R/build-manual.R b/R/build-manual.R index 804a58005..301e773e3 100644 --- a/R/build-manual.R +++ b/R/build-manual.R @@ -9,25 +9,28 @@ build_manual <- function(pkg = ".", path = NULL) { pkg <- as.package(pkg) path <- path %||% path_dir(pkg$path) - name <- paste0(pkg$package, "_", pkg$version, ".pdf", collapse = " ") - tryCatch( - msg <- callr::rcmd( - "Rd2pdf", - cmdargs = c( - "--force", - paste0("--output=", path, "/", name), - pkg$path - ), - fail_on_status = TRUE, - stderr = "2>&1", - spinner = FALSE - ), + name <- paste0(pkg$package, "_", pkg$version, ".pdf") + output <- file.path(path, name) + + cli::cli_inform("Saving manual to {.file {output}}") + withCallingHandlers( + invisible(rd2pdf(pkg$path, output)), error = function(e) { - cat(e$stdout) - cli::cli_abort("Failed to build manual") + cli::cli_abort( + c("Failed to build manual.", no_wrap(e$stderr)), + call = quote(build_manual()), + parent = e + ) } ) +} - cat(msg$stdout) - invisible(msg) +rd2pdf <- function(pkg_path, output_path) { + callr::rcmd( + "Rd2pdf", + cmdargs = c("--force", paste0("--output=", output_path), pkg_path), + stdout = "", + fail_on_status = TRUE, + spinner = FALSE + ) } diff --git a/R/utils.R b/R/utils.R index 3ea06488a..105a2c7d1 100644 --- a/R/utils.R +++ b/R/utils.R @@ -44,3 +44,10 @@ is_testing <- function() { is_rstudio_running <- function() { !is_testing() && rstudioapi::isAvailable() } + +# Suppress cli wrapping +no_wrap <- function(x) { + x <- gsub(" ", "\u00a0", x, fixed = TRUE) + x <- gsub("\n", "\f", x, fixed = TRUE) + x +} diff --git a/tests/testthat/_snaps/build-manual.md b/tests/testthat/_snaps/build-manual.md new file mode 100644 index 000000000..a8c212dd0 --- /dev/null +++ b/tests/testthat/_snaps/build-manual.md @@ -0,0 +1,13 @@ +# build_manual() shows stderr on failure + + Code + build_manual(pkg) + Message + Saving manual to '_0.0.0.9000.pdf' + Condition + Error in `build_manual()`: + ! Failed to build manual. + ! LaTeX Error: File `inconsolata.sty' not found. + Caused by error in `rd2pdf()`: + ! System command 'R' failed + diff --git a/tests/testthat/test-build-manual.R b/tests/testthat/test-build-manual.R new file mode 100644 index 000000000..75b64e7ae --- /dev/null +++ b/tests/testthat/test-build-manual.R @@ -0,0 +1,17 @@ +test_that("build_manual() shows stderr on failure", { + skip_on_os("windows") + + pkg <- local_package_create() + pkg <- normalizePath(pkg) + + # Too hard to replicate actual error, so we just simulate + local_mocked_bindings(rd2pdf = function(...) { + stderr <- "! LaTeX Error: File `inconsolata.sty' not found." + rlang::abort("System command 'R' failed", stderr = stderr) #nolint + }) + + expect_snapshot(build_manual(pkg), error = TRUE, transform = function(x) { + x <- gsub(pkg, "", x, fixed = TRUE) + x + }) +})