Nothing
#' Roxygenize a package, clean up and build/check the package
#'
#' After the source package is roxygenized, this function will build the
#' package. Optionally it also installs or checks the package, reformats the
#' code in the example sections. Note \code{\link{rab}} is an alias of
#' \code{\link{roxygen_and_build}}.
#' @param pkg the root directory of the source package
#' @param build whether to build the package
#' @param build.opts options to be passed to \command{R CMD build}
#' @param install whether to install the package
#' @param install.opts options to be passed to \command{R CMD INSTALL}
#' @param check whether to check the package
#' @param check.opts options to check the package (e.g. \code{"--no-examples"})
#' @param remove.check whether to remove the directory generated by \command{R
#' CMD check}
#' @param reformat whether to reformat the example code; see
#' \code{\link{reformat_code}}
#' @param before an R expression to be evaluated under the package root
#' directory before the package is roxygenized and built
#' @param ... other arguments passed to \code{\link[roxygen2]{roxygenize}}
#' @return \code{NULL}
#' @author Yihui Xie <\url{http://yihui.org}>
#' @rdname roxygen_and_build
#' @import utils
#' @export
#' @examples \dontrun{
#' roxygen_and_build("Rd2roxygen", install = TRUE)
#' ## or simply
#' rab('Rd2roxygen', install = TRUE)
#' }
roxygen_and_build = function(
pkg, build = TRUE, build.opts = '--no-manual',
install = FALSE, install.opts = if (build) '' else '--with-keep.source',
check = FALSE, check.opts = '--as-cran --no-manual', remove.check = TRUE,
reformat = TRUE, before = NULL, ...
) {
if (missing(pkg)) pkg = head(commandArgs(TRUE), 1)
if (length(pkg) != 1) stop('The package directory must be one character string')
xfun::in_dir(pkg, before)
xfun::Rscript_call(roxygen2::roxygenize, list(pkg, ...))
desc = file.path(pkg, 'DESCRIPTION')
pv = read.dcf(desc, fields = c('Package', 'Version'))
if (reformat) {
message('Reformatting examples')
rd.list = list.files(
file.path(pkg, 'man'), '.*\\.Rd$', all.files = TRUE, full.names = TRUE
)
for (f in rd.list) reformat_code(f)
}
# delete existing tarballs
unlink(sprintf('%s_*.tar.gz', pv[1, 1]))
if (build) {
system(sprintf('%s CMD build %s %s', Rbin(), build.opts, pkg))
}
res = sprintf('%s_%s.tar.gz', pv[1, 1], pv[1, 2])
if (install) {
if (!build) res = pkg
system(sprintf('%s CMD INSTALL %s %s ', Rbin(), install.opts, res))
# generate Rd for objects imported and exported from other packages
importRd(pkg, pv[1, 1])
}
if (check) {
if (!build) stop('You must build the source package before running R CMD check')
if ((system(sprintf('%s CMD check %s %s', Rbin(), res, check.opts)) == 0) &&
remove.check) unlink(sprintf('%s.Rcheck', pv[1, 1]), TRUE)
}
}
#' @rdname roxygen_and_build
#' @export
rab = roxygen_and_build
#' Format the code in the usage and examples sections
#'
#' The function \code{\link[formatR]{tidy_source}} in the \pkg{formatR} package
#' is used to polish the Rd files generated by \pkg{roxygen2} in the usage and
#' examples sections.
#' @param path the path of the Rd file
#' @param ... other arguments passed to \code{tidy_source}
#' @return \code{NULL}; as a side effect, the original Rd file will be updated
#' @export
#' @author Yihui Xie <\url{http://yihui.org}>
#' @seealso \code{\link[formatR]{tidy_source}}
#' @note If the usage or examples code is not syntactically correct, it will not
#' be reformatted and a message will be printed on screen. One possible
#' situation is the percent symbol \code{\%}, which should be escaped even in
#' the examples code (cf Writing R Extensions), and this can make the code
#' syntactically incorrect, e.g. \code{a \%in\% b} should be \code{a
#' \\\%in\\\% b} but the latter is not valid R code. Anyway, this function
#' will try to unescape the percent symbols before reformating the code, then
#' escape them.
#' @examples
#' rd.file = system.file('examples', 'reformat_code_demo.Rd', package = 'Rd2roxygen')
#' file.copy(rd.file, tempdir())
#' fmt.file = file.path(tempdir(), 'reformat_code_demo.Rd')
#'
#' file.show(fmt.file) ## show the raw Rd
#'
#' reformat_code(fmt.file)
#' file.show(fmt.file) ## the formatted Rd
reformat_code = function(path, ...) {
rd = readLines(path)
tags = tags_possible
if (length(idx0 <- grep('^\\\\examples\\{', rd))) {
# tags after \examples?
idx1 = grep(tags, rd)
if (length(idx1) && any(idx1 > idx0))
idx1 = min(idx1[idx1 > idx0]) - 1 else idx1 = tail(grep('\\}$', rd), 1)
rd = tidy_examples(rd, idx0, idx1, ..., path = path)
}
writeLines(strip_white(rd), path)
flush.console()
}
# strip beginning and trailing white spaces
strip_white = function(x) gsub('^\\s+|\\s+$', '', paste(x, collapse = '\n'))
# possible tags after \example{} or \usage{}
tags_possible = sprintf('^\\\\(%s)\\{', paste(c(
'docType', 'name', 'alias', 'title', 'format', 'source', 'usage', 'arguments',
'value', 'description', 'details', 'note', 'section', 'examples', 'author',
'references', 'seealso', 'concept', 'keyword', 'subsection'
), collapse = '|'))
tidy_code = function(code, ...) {
tidy = function(w) {
formatR::tidy_source(text = code, output = FALSE, width.cutoff = w, ...)$text.tidy
}
res = try(tidy(80))
if (inherits(res, 'try-error')) return(res)
i = 1
# R CMD check used to require code width to be less than 90
while (any(nchar(unlist(strsplit(res, '\n'))) >= 90) && i <= 40) {
res = tidy(90 - i)
i = i + 1
}
if (i > 40) code else res
}
#' Generate R doc for functions imported from other packages and re-exported
#'
#' This function searches for exported functions in a package that are not
#' really created in this package, and generate documentation for them so that R
#' CMD check will not complain about the missing documentation for exported
#' functions.
#' @param path the path to the source package
#' @param package the package name
#' @noRd
importRd = function(path, package) {
escape = getFromNamespace('escape.character', 'roxygen2')
import = getNamespaceImports(package); import$base = NULL
pkgs = .packages(TRUE)
link = lapply(getNamespaceExports(package), function(name) {
fun = getExportedValue(package, name)
pkg = if (is.function(fun)) {
packageName(environment(fun))
} else {
which_package(name, import)
}
if (is.null(pkg) || pkg == package || !(pkg %in% pkgs)) return()
# if name is not exported there, you have to document it by yourself
if (!(name %in% getNamespaceExports(pkg))) return()
topic = basename(do.call(help, list(name, pkg))[[1]])
name = as.character(escape(name))
c(
package = pkg, name = name,
link = sprintf('\\code{\\link[%s:%s]{%s}}', pkg, topic, name)
)
})
link = do.call(rbind, link)
if (NROW(link) == 0) return()
link = link[order(link[, 'package'], link[, 'name']), , drop = FALSE]
alias = sprintf('\\alias{%s}', link[, 'name'])
link = sapply(split(link[, 'link'], link[, 'package']), paste, collapse = ', ')
doc = c(
sprintf('\\name{%s-imports}', package),
alias,
'\\docType{import}',
'\\title{Objects imported from other packages}',
'\\description{',
'These objects are imported from other packages. Follow the links to their documentation.',
'\\describe{',
sprintf(' \\item{%s}{%s}', names(link), link),
'}}'
)
Rd = file.path(path, 'man', sprintf('%s-imports.Rd', package))
if (file.exists(Rd) && !any(readLines(Rd) == '\\docType{import}')) {
warning(
'The Rd file ', Rd, ' already exists. It seems I should not overwrite it. ',
'You may want to manually save the Rd below:', immediate. = TRUE
)
cat(doc, sep = '\n')
return()
}
writeLines(doc, Rd)
}
which_package = function(name, exports) {
for (i in names(exports)) {
if (name %in% exports[[i]]) return(i)
}
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.