Skip to content

Commit

Permalink
Use better vignette workflow
Browse files Browse the repository at this point in the history
This installs the built vignettes to doc/ rather than inst/doc and
builds a vignette index in Meta/vignette.rds. This means that you can
view development vignettes by loading the package with `load_all()` and
calling `browseVignettes(pkgname)` or `vignette(vignName)` and the devel
vignettes are found. The `doc` and `Meta` directories are added to
.Rbuildignore; so they will not be included in the built package.

Fixes #1703
Fixes #1809
  • Loading branch information
jimhester committed Jul 20, 2018
1 parent 56ee182 commit 2e9f5b7
Show file tree
Hide file tree
Showing 14 changed files with 542 additions and 44 deletions.
3 changes: 2 additions & 1 deletion .Rbuildignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
^Meta$
^doc$
^\.github$
^devtools\.Rproj$
^\.Rproj\.user$
Expand All @@ -11,4 +13,3 @@
^codecov\.yml$
^_pkgdown\.yml$
^docs$

Binary file added Meta/vignette.rds
Binary file not shown.
7 changes: 7 additions & 0 deletions R/usethis.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,10 @@

#' @importFrom withr defer
local_proj <- withr::local_(usethis::proj_set)

usethis_use_directory <- function(pkg, dir, ignore = FALSE) {
capture.output({
local_proj(pkg$path)
usethis::use_directory(dir, ignore)
})
}
13 changes: 6 additions & 7 deletions R/vignette-r.r
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,29 @@
copy_vignettes <- function(pkg) {
pkg <- as.package(pkg)

doc_dir <- file.path(pkg$path, "inst", "doc")
if (!file.exists(doc_dir)) {
dir.create(doc_dir, recursive = TRUE, showWarnings = FALSE)
}
usethis_use_directory(pkg, "doc", ignore = TRUE)

doc_dir <- file.path(pkg$path, "doc")

vigns <- tools::pkgVignettes(dir = pkg$path, output = TRUE, source = TRUE)
if (length(vigns$docs) == 0) return(invisible())

out_mv <- c(vigns$outputs, unique(unlist(vigns$sources, use.names = FALSE)))
out_cp <- vigns$docs

message("Moving ", paste(basename(out_mv), collapse = ", "), " to inst/doc/")
message("Moving ", paste(basename(out_mv), collapse = ", "), " to doc/")
file.copy(out_mv, doc_dir, overwrite = TRUE)
file.remove(out_mv)

message("Copying ", paste(basename(out_cp), collapse = ", "), " to inst/doc/")
message("Copying ", paste(basename(out_cp), collapse = ", "), " to doc/")
file.copy(out_cp, doc_dir, overwrite = TRUE)

# Copy extra files, if needed
extra_files <- find_vignette_extras(pkg)
if (length(extra_files) == 0) return(invisible())

message("Copying extra files ", paste(basename(extra_files), collapse = ", "),
" to inst/doc/")
" to doc/")
file.copy(extra_files, doc_dir, recursive = TRUE)

invisible()
Expand Down
68 changes: 53 additions & 15 deletions R/vignettes.r
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,33 @@
#'
#' Builds package vignettes using the same algorithm that \code{R CMD build}
#' does. This means including non-Sweave vignettes, using makefiles (if
#' present), and copying over extra files. You need to ensure that these
#' files are not included in the built package - ideally they should not
#' be checked into source, or at least excluded with \code{.Rbuildignore}
#' present), and copying over extra files. The files are copied in the 'doc'
#' directory and an vignette index is created in 'Meta/vignette.rds', as they
#' would be in a built package. 'doc' and 'Meta' are added to
#' \code{.Rbuildignore}, so will not be included in the built package. These
#' files can be checked into version control, so they can be viewed with
#' \code{browseVignettes()} and \code{vignette()} if the package has been
#' loaded with \code{load_all()} without needing to re-build them locally.
#'
#' @param pkg package description, can be path or package name. See
#' \code{\link{as.package}} for more information
#' @param quiet If \code{TRUE}, suppresses most output. Set to \code{FALSE}
#' if you need to debug.
#' @param install If \code{TRUE}, install the package before building
#' vignettes.
#' @inheritParams tools::buildVignettes
#' @inheritParams install_deps
#' @keywords programming
#' @seealso \code{\link{clean_vignettes}} to remove the pdfs in
#' \file{inst/doc} created from vignettes
#' \file{doc} created from vignettes
#' @export
#' @seealso \code{\link{clean_vignettes}} to remove build tex/pdf files.
build_vignettes <- function(pkg = ".",
dependencies = "VignetteBuilder",
clean = TRUE,
upgrade = FALSE,
quiet = TRUE
quiet = TRUE,
install = TRUE
) {
pkg <- as.package(pkg)
vigns <- tools::pkgVignettes(dir = pkg$path)
Expand All @@ -31,24 +38,52 @@ build_vignettes <- function(pkg = ".",

message("Building ", pkg$package, " vignettes")

build <- function(pkg_path, clean, quiet) {
withr::with_temp_libpaths(action = "prefix", code = {
devtools::install(pkg_path, upgrade_dependencies = FALSE, reload = FALSE, quiet = quiet)
if (isTRUE(install)) {
build <- function(pkg_path, clean, quiet) {
withr::with_temp_libpaths(action = "prefix", {
devtools::install(pkg_path, upgrade_dependencies = FALSE, reload = FALSE, quiet = quiet)
tools::buildVignettes(dir = pkg_path, clean = clean, tangle = TRUE, quiet = quiet)
})
}
} else {
build <- function(pkg_path, clean, quiet) {
tools::buildVignettes(dir = pkg_path, clean = clean, tangle = TRUE, quiet = quiet)
})
}
}
callr::r(build,

callr::r(
build,
args = list(pkg_path = pkg$path, clean = clean, quiet = quiet),
show = TRUE, spinner = FALSE)
show = TRUE,
spinner = FALSE)

# We need to re-run pkgVignettes now that they are built to get the output
# files as well
vigns <- tools::pkgVignettes(dir = pkg$path, source = TRUE, output = TRUE)

copy_vignettes(pkg)

create_vignette_index(pkg, vigns)

invisible(TRUE)
}

create_vignette_index <- function(pkg, vigns) {

usethis_use_directory(pkg, "Meta", ignore = TRUE)

message("Building vignette index")

vignette_index <- ("tools" %:::% ".build_vignette_index")(vigns)

vignette_index_path <- file.path(pkg$path, "Meta", "vignette.rds")

saveRDS(vignette_index, vignette_index_path, version = 2L)
}

#' Clean built vignettes.
#'
#' This uses a fairly rudimentary algorithm where any files in \file{inst/doc}
#' This uses a fairly rudimentary algorithm where any files in \file{doc}
#' with a name that exists in \file{vignettes} are removed.
#'
#' @param pkg package description, can be path or package name. See
Expand All @@ -59,17 +94,20 @@ clean_vignettes <- function(pkg = ".") {
vigns <- tools::pkgVignettes(dir = pkg$path)
if (basename(vigns$dir) != "vignettes") return()

message("Cleaning built vignettes from ", pkg$package)
message("Cleaning built vignettes and index from ", pkg$package)

doc_path <- file.path(pkg$path, "inst", "doc")
doc_path <- file.path(pkg$path, "doc")

vig_candidates <- dir(doc_path, full.names = TRUE)
vig_rm <- vig_candidates[file_name(vig_candidates) %in% file_name(vigns$docs)]

extra_candidates <- file.path(doc_path, basename(find_vignette_extras(pkg)))
extra_rm <- extra_candidates[file.exists(extra_candidates)]

to_remove <- c(vig_rm, extra_rm)
vig_index_path <- file.path(pkg$path, "Meta", "vignette.rds")
vig_index_rm <- if (file.exists(vig_index_path)) vig_index_path

to_remove <- c(vig_rm, extra_rm, vig_index_rm)
if (length(to_remove) > 0) {
message("Removing ", paste(basename(to_remove), collapse = ", "))
file.remove(to_remove)
Expand Down
1 change: 1 addition & 0 deletions doc/dependencies.R
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

88 changes: 88 additions & 0 deletions doc/dependencies.Rmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
title: "Devtools dependencies"
author: "Jim Hester, Hadley Wickham"
date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Devtools dependencies}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---

# Package remotes

Devtools version 1.9 supports package dependency installation for packages not
yet in a standard package repository such as [CRAN](https://cran.r-project.org)
or [Bioconductor](http://bioconductor.org).

You can mark any regular dependency defined in the `Depends`, `Imports`,
`Suggests` or `Enhances` fields as being installed from a remote location by
adding the remote location to `Remotes` in your `DESCRIPTION` file. This will
cause devtools to download and install them prior to installing your package (so they won't be installed from CRAN).

The remote dependencies specified in `Remotes` should be described in the following form.

```
Remotes: [type::]<Repository>, [type2::]<Repository2>
```

The `type` is an optional parameter. If the type is missing the default is
to install from GitHub. Additional remote dependencies should be separated by
commas, just like normal dependencies elsewhere in the `DESCRIPTION` file.

### Github

Because github is the most commonly used unofficial package distribution in R, it's the default:

```yaml
Remotes: hadley/testthat
```
You can also specify a specific hash, tag, or pull request (using the same syntax as `install_github()` if you want a particular commit. Otherwise the latest commit on the master branch is used.

```yaml
Remotes: hadley/httr@v0.4,
klutometis/roxygen#142,
hadley/testthat@c67018fa4970
```

A type of 'github' can be specified, but is not required

```yaml
Remotes: github::hadley/ggplot2
```

### Other sources

All of the currently supported install sources are available, see the 'See
Also' section in `?install` for a complete list.

```yaml
# GitLab
Remotes: gitlab::jimhester/covr
# Git
Remotes: git::git@bitbucket.org:dannavarro/lsr-package.git
# Bitbucket
Remotes: bitbucket::sulab/mygene.r@default, dannavarro/lsr-package
# Bioconductor
Remotes: bioc::3.3/SummarizedExperiment#117513, bioc::release/Biobase
# SVN
Remotes: svn::https://github.com/hadley/stringr
# URL
Remotes: url::https://github.com/hadley/stringr/archive/master.zip
# Local
Remotes: local::/pkgs/testthat
# Gitorious
Remotes: gitorious::r-mpc-package/r-mpc-package
```

### CRAN submission

When you submit your package to CRAN, all of its dependencies must also be available on CRAN. For this reason, `release()` will warn you if you try to release a package with a `Remotes` field.
Loading

0 comments on commit 2e9f5b7

Please sign in to comment.