diff --git a/.gitignore b/.gitignore index fb33997b7..cab23c2e9 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ .Rproj.user /.Rhistory +tests/testthat/testthat-problems.rds + ## Qt Creator *.pro *.pro.user diff --git a/NEWS.md b/NEWS.md index fb8da8949..5cc17bd51 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,7 @@ # roxygen2 (development version) * `@family` lists are now ordered more carefully, "foo1" comes after "foo" (#1563, @krlmlr). +* Re-runs of `namespace_roclet()` fixed for packages using multi-line `@rawNamespace` directives (#1572, @MichaelChirico). # roxygen2 7.3.0 diff --git a/R/namespace.R b/R/namespace.R index d33e56b05..1a1b1e0fa 100644 --- a/R/namespace.R +++ b/R/namespace.R @@ -74,7 +74,7 @@ update_namespace_imports <- function(base_path) { } lines <- c(namespace_imports(base_path), namespace_exports(NAMESPACE)) - results <- c(made_by("#"), sort_c(unique(lines))) + results <- c(made_by("#"), sort_c(unique(trimws(lines)))) write_if_different(NAMESPACE, results, check = TRUE) invisible() @@ -109,12 +109,15 @@ namespace_imports_blocks <- function(srcref) { })) } +# NB: this is designed as the conjugate of namespace_imports(), so also +# includes @rawNamespace entries which may/may not also include import directives. namespace_exports <- function(path) { parsed <- as.list(parse(path, keep.source = TRUE)) is_import_directive <- function(x) is_call(x, import_directives) export_lines <- attr(parsed, "srcref")[!map_lgl(parsed, is_import_directive)] - unlist(lapply(export_lines, as.character)) + # Each multiline directives are a single element so they're sorted correctly + unlist(lapply(export_lines, function(x) paste(as.character(x), collapse = "\n"))) } # NAMESPACE generation ---------------------------------------------------- diff --git a/tests/testthat/test-namespace.R b/tests/testthat/test-namespace.R index c6322cdec..d5c30ba47 100644 --- a/tests/testthat/test-namespace.R +++ b/tests/testthat/test-namespace.R @@ -305,6 +305,17 @@ test_that("rawNamespace inserted unchanged", { expect_equal(out, "xyz\n abc") }) +test_that("rawNamespace does not break idempotency", { + test_pkg <- test_path("testRawNamespace") + NAMESPACE <- file.path(test_pkg, "NAMESPACE") + + lines_orig <- read_lines(NAMESPACE) + + expect_no_error(roxygenize(test_pkg, namespace_roclet())) + + # contents unchanged + expect_equal(read_lines(NAMESPACE), lines_orig) +}) # @evalNamespace ---------------------------------------------------------- @@ -391,7 +402,13 @@ test_that("can extract non-imports from namespace preserving source", { "export(b)" ) path <- withr::local_tempfile(lines = lines) - expect_equal(namespace_exports(path), lines[c(1:3, 5)]) + expect_equal( + namespace_exports(path), + c( + paste(lines[1:3], collapse = "\n"), + lines[5L] + ) + ) }) test_that("Invalid imports throw a helpful error", { diff --git a/tests/testthat/testRawNamespace/DESCRIPTION b/tests/testthat/testRawNamespace/DESCRIPTION new file mode 100644 index 000000000..633c5420f --- /dev/null +++ b/tests/testthat/testRawNamespace/DESCRIPTION @@ -0,0 +1,9 @@ +Package: testRawNamespace +Title: Check that re-running on multi-line @rawNamespace directive is OK +License: GPL-2 +Description: testRawNamespace. +Author: Hadley +Maintainer: Hadley +Encoding: UTF-8 +Version: 0.1 +RoxygenNote: 7.3.0.9000 diff --git a/tests/testthat/testRawNamespace/NAMESPACE b/tests/testthat/testRawNamespace/NAMESPACE new file mode 100644 index 000000000..74ea7af2a --- /dev/null +++ b/tests/testthat/testRawNamespace/NAMESPACE @@ -0,0 +1,10 @@ +# Generated by roxygen2: do not edit by hand + + +if (TRUE) { + import(grDevices) +} else { + import(methods) +} +import(graphics) +import(utils) diff --git a/tests/testthat/testRawNamespace/R/a.R b/tests/testthat/testRawNamespace/R/a.R new file mode 100644 index 000000000..a44b2fe03 --- /dev/null +++ b/tests/testthat/testRawNamespace/R/a.R @@ -0,0 +1,9 @@ +#' @import graphics +#' @rawNamespace +#' if (TRUE) { +#' import(grDevices) +#' } else { +#' import(methods) +#' } +#' @import utils +NULL