Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Tree: 551a97dc63
Fetching contributors…

Cannot retrieve contributors at this time

3699 lines (3466 sloc) 168.617 kB
# File src/library/tools/R/check.R
# Part of the R package, http://www.R-project.org
#
# Copyright (C) 1995-2012 The R Core Team
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# A copy of the GNU General Public License is available at
# http://www.r-project.org/Licenses/
###- R based engine for R CMD check
## R developers can use this to debug the function by running it
## directly as tools:::.check_packages(args), where the args should
## be what commandArgs(TRUE) would return, that is a character vector
## of (space-delimited) terms that would be passed to R CMD checks.
## Used for INSTALL and Rd2pdf
run_Rcmd <- function(args, out = "", env = "")
{
if(.Platform$OS.type == "windows")
system2(file.path(R.home("bin"), "Rcmd.exe"), args, out, out)
else
system2(file.path(R.home("bin"), "R"), c("CMD", args), out, out,
env = env)
}
R_runR <- function(cmd = NULL, Ropts = "", env = "",
stdout = TRUE, stderr = TRUE, stdin = NULL,
arch = "")
{
if (.Platform$OS.type == "windows") {
## workaround Windows problem with input = cmd
if (!is.null(cmd)) {
## In principle this should escape \
Rin <- tempfile("Rin"); on.exit(unlink(Rin)); writeLines(cmd, Rin)
} else Rin <- stdin
suppressWarnings(system2(if(nzchar(arch)) file.path(R.home(), "bin", arch, "Rterm.exe")
else file.path(R.home("bin"), "Rterm.exe"),
c(Ropts, paste("-f", Rin)), stdout, stderr, env = env))
} else {
suppressWarnings(system2(file.path(R.home("bin"), "R"),
c(if(nzchar(arch)) paste0("--arch=", arch), Ropts),
stdout, stderr, stdin, input = cmd, env = env))
}
}
setRlibs <- function(lib0 = "", pkgdir = ".", suggests = FALSE,
libdir = NULL, self = FALSE, self2 = TRUE)
{
WINDOWS <- .Platform$OS.type == "windows"
useJunctions <- WINDOWS && !nzchar(Sys.getenv("R_WIN_NO_JUNCTIONS"))
flink <- function(from, to) {
res <- if(WINDOWS) {
if(useJunctions) Sys.junction(from, to)
else file.copy(from, to, recursive = TRUE)
} else file.symlink(from, to)
if (!res) stop(gettextf("cannot link from %s", from), domain = NA)
}
pi <- .split_description(.read_description(file.path(pkgdir, "DESCRIPTION")))
thispkg <- unname(pi$DESCRIPTION["Package"])
## We need to make some assumptions about layout: this version
## assumes .Library contains standard and recommended packages
## and nothing else.
tmplib <- tempfile("RLIBS_")
dir.create(tmplib)
## Since this is under the session directory and only contains
## symlinks and dummies (hence will be small) we never clean it up.
test_recommended <-
config_val_to_logical(Sys.getenv("_R_CHECK_NO_RECOMMENDED_", "FALSE"))
if(test_recommended) {
## Now add dummies for recommended packages (removed later if declared)
recommended <- .get_standard_package_names()$recommended
## grDevices has :: to KernSmooth
## stats has ::: to Matrix, Matrix depends on lattice
## which gives false positives in MASS and Rcpp
## codetools is really part of tools
exceptions <- "codetools"
if (thispkg %in% c("MASS", "Rcpp"))
exceptions <- c(exceptions, "Matrix", "lattice")
if (thispkg %in%
c("Modalclust", "aroma.core", "iWebPlots",
"openair", "oce", "pcalg", "tileHMM"))
exceptions <- c(exceptions, "KernSmooth")
recommended <- recommended[!recommended %in% exceptions]
for(pkg in recommended) {
if(pkg == thispkg) next
dir.create(pd <- file.path(tmplib, pkg))
file.copy(file.path(.Library, pkg, "DESCRIPTION"), pd)
## to make sure find.package throws an error:
close(file(file.path(pd, "dummy_for_check"), "w"))
}
}
deps <- unique(c(names(pi$Depends), names(pi$Imports), names(pi$LinkingTo),
if(suggests) names(pi$Suggests)))
if(length(libdir) && self2) flink(file.path(libdir, thispkg), tmplib)
## .Library is not necessarily canonical, but the .libPaths version is.
lp <- .libPaths()
poss <- c(lp[length(lp)], .Library)
already <- thispkg
more <- unique(deps[!deps %in% already]) # should not depend on itself ...
while(length(more)) {
m0 <- more; more <- character()
for (pkg in m0) {
if (test_recommended) {
if (pkg %in% recommended) unlink(file.path(tmplib, pkg), TRUE)
## hard-code dependencies for now.
if (pkg == "mgcv")
unlink(file.path(tmplib, c("Matrix", "lattice", "nlme")), TRUE)
if (pkg == "Matrix") unlink(file.path(tmplib, "lattice"), TRUE)
if (pkg == "class") unlink(file.path(tmplib, "MASS"), TRUE)
if (pkg == "nlme") unlink(file.path(tmplib, "lattice"), TRUE)
}
where <- find.package(pkg, quiet = TRUE)
if(length(where)) {
if (!(dirname(where) %in% poss))
flink(where, tmplib)
else if (!test_recommended)
# If the package is in the standard library we can
# assume dependencies have been met, but we can
# only skip the traversal if we aren't testing recommended
# packages, because loading will fail if there is
# an indirect dependency to one that has been hidden
# by a dummy in tmplib.
next
pi <- readRDS(file.path(where, "Meta", "package.rds"))
more <- c(more, names(pi$Depends), names(pi$Imports),
names(pi$LinkingTo))
}
}
already <- c(already, m0)
more <- unique(more[!more %in% already])
}
if (self) flink(normalizePath(pkgdir), tmplib)
# print(dir(tmplib))
rlibs <- tmplib
if (nzchar(lib0)) rlibs <- c(lib0, rlibs)
rlibs <- paste(rlibs, collapse = .Platform$path.sep)
c(paste("R_LIBS", rlibs, sep = "="),
if(WINDOWS) " R_ENVIRON_USER='no_such_file'" else "R_ENVIRON_USER=''",
if(WINDOWS) " R_LIBS_USER='no_such_dir'" else "R_LIBS_USER=''",
" R_LIBS_SITE='no_such_dir'")
}
###- The main function for "R CMD check" {currently extends all the way to the end-of-file}
.check_packages <- function(args = NULL)
{
WINDOWS <- .Platform$OS.type == "windows"
## this requires on Windows: file.exe (optional)
wrapLog <- function(...) {
text <- paste(..., collapse = " ")
## strwrap expects paras separated by blank lines.
## Perl's wrap split on \n
text <- strsplit(text, "\n", useBytes = TRUE)[[1L]]
printLog(Log, paste(strwrap(text), collapse = "\n"), "\n")
}
## Used for
## .check_packages_used
## .check_packages_used_in_examples
## .check_packages_used_in_tests
## .check_packages_used_in_vignettes
## checkS3methods
## checkReplaceFuns
## checkFF
## .check_code_usage_in_package (with full set)
## .check_T_and_F (with full set)
## .check_dotInternal (with full set)
## undoc, codoc, codocData, codocClasses
## checkDocFiles, checkDocStyle
## The default set of packages here are as they are because
## .get_S3_generics_as_seen_from_package needs utils,graphics,stats
## Used by checkDocStyle (which needs the generic visible) and checkS3methods.
R_runR2 <-
if(WINDOWS) {
function(cmd,
env = "R_DEFAULT_PACKAGES=utils,grDevices,graphics,stats")
{
out <- R_runR(cmd, R_opts2, env)
## pesky gdata ....
grep("^(ftype: not found|File type)", out,
invert = TRUE, value = TRUE)
}
} else
function(cmd,
env = "R_DEFAULT_PACKAGES='utils,grDevices,graphics,stats'")
{
out <- R_runR(cmd, R_opts2, env)
if (R_check_suppress_RandR_message)
grep('^Xlib: *extension "RANDR" missing on display', out,
invert = TRUE, value = TRUE)
else out
}
dir.exists <- function(x) !is.na(isdir <- file.info(x)$isdir) & isdir
td0 <- Inf # updated below
print_time <- function(t1, t2, Log)
{
td <- t2 - t1
if(td[3L] < td0) return()
td2 <- if (td[3L] > 600) {
td <- td/60
if(WINDOWS) sprintf(" [%dm]", round(td[3L]))
else sprintf(" [%dm/%dm]", round(sum(td[-3L])), round(td[3L]))
} else {
if(WINDOWS) sprintf(" [%ds]", round(td[3L]))
else sprintf(" [%ds/%ds]", round(sum(td[-3L])), round(td[3L]))
}
cat(td2)
if (!is.null(Log) && Log$con > 0L) cat(td2, file = Log$con)
}
parse_description_field <- function(desc, field, default=TRUE)
{
tmp <- desc[field]
if (is.na(tmp)) default
else switch(tmp,
"yes"=, "Yes" =, "true" =, "True" =, "TRUE" = TRUE,
"no" =, "No" =, "false" =, "False" =, "FALSE" = FALSE,
default)
}
check_pkg <- function(pkg, pkgname, pkgoutdir, startdir, libdir, desc,
is_base_pkg, is_rec_pkg, subdirs, extra_arch)
{
## pkg is the argument we received from the main loop.
## pkgdir is the corresponding absolute path,
checkingLog(Log, "package directory")
setwd(startdir)
pkg <- sub("/$", "", pkg)
if (dir.exists(pkg)) {
setwd(pkg) ## wrap in try()?
pkgdir <- getwd()
resultLog(Log, "OK")
} else {
errorLog(Log, "Package directory ", sQuote(pkg), "does not exist.")
do_exit(1L)
}
haveR <- dir.exists("R") && !extra_arch
if (!extra_arch) {
allfiles <- check_file_names()
if (R_check_permissions) check_permissions(allfiles)
check_meta() # Check DESCRIPTION meta-information.
check_top_level()
check_detritus()
check_indices()
check_subdirectories(haveR, subdirs)
## Check R code for non-ASCII chars which
## might be syntax errors in some locales.
if (!is_base_pkg && haveR && R_check_ascii_code) check_non_ASCII()
} # end of !extra_arch
## Check we can actually load the package: base is always loaded
if (do_install && pkgname != "base") {
if (this_multiarch) {
Log$stars <<- "**"
for (arch in inst_archs) {
printLog(Log, "* loading checks for arch ", sQuote(arch), "\n")
check_loading(arch)
}
Log$stars <<- "*"
} else {
check_loading()
}
}
if (haveR) {
check_R_code() # unstated dependencies, S3 methods, replacement, foreign
check_R_files(is_rec_pkg) # codetools etc
}
check_Rd_files(haveR)
check_data() # 'data' dir and sysdata.rda
if (!is_base_pkg && dir.exists("src") && !extra_arch) check_src_dir()
if(do_install &&
dir.exists("src") &&
length(so_symbol_names_table)) # suitable OS
check_sos()
miss <- file.path("inst", "doc", c("Rplots.ps", "Rplots.pdf"))
if (any(f <- file.exists(miss))) {
checkingLog(Log, "for left-overs from vignette generation")
warningLog(Log)
printLog(Log,
paste(" file", paste(sQuote(miss[f]), collapse = ", "),
"will not be installed: please remove it\n"))
}
if (dir.exists("inst/doc")) {
if (R_check_doc_sizes) check_doc_size()
else if (as_cran)
warningLog(Log, "'qpdf' is needed for checks on size reduction of PDFs")
}
if (dir.exists("inst/doc") && do_install) check_doc_contents()
setwd(pkgoutdir)
## Run the examples: this will be skipped if installation was
if (dir.exists(file.path(libdir, pkgname, "help"))) {
run_examples()
} else if (dir.exists(file.path(pkgdir, "man"))) {
checkingLog(Log, "examples")
resultLog(Log, "SKIPPED")
}
## Run the package-specific tests.
tests_dir <- file.path(pkgdir, "tests")
if (dir.exists(tests_dir) && # trackObjs has only *.Rin
length(dir(tests_dir, pattern = "\\.(R|Rin)$")))
run_tests()
## Check package vignettes.
setwd(pkgoutdir)
run_vignettes(desc)
} ## end{ check_pkg }
check_file_names <- function()
{
## Check for portable file names.
checkingLog(Log, "for portable file names")
## Build list of exclude patterns.
ignore <- get_exclude_patterns()
ignore_file <- ".Rbuildignore"
if (file.exists(ignore_file))
ignore <- c(ignore, readLines(ignore_file))
## Ensure that the names of the files in the package are valid
## for at least the supported OS types. Under Unix, we
## definitely cannot have '/'. Under Windows, the control
## characters as well as " * : < > ? \ | (i.e., ASCII
## characters 1 to 31 and 34, 36, 58, 60, 62, 63, 92, and 124)
## are or can be invalid. (In addition, one cannot have
## one-character file names consisting of just ' ', '.', or
## '~'.) Based on information by Uwe Ligges, Duncan Murdoch,
## and Brian Ripley.
## In addition, Windows does not allow the following DOS type
## device names (by themselves or with possible extensions),
## see e.g.
## http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/naming_a_file.asp
## http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx#naming_conventions
## and http://en.wikipedia.org/wiki/Filename (which as of
## 2007-04-22 is wrong about claiming that COM0 and LPT0 are
## disallowed):
##
## CON: Keyboard and display
## PRN: System list device, usually a parallel port
## AUX: Auxiliary device, usually a serial port
## NUL: Bit-bucket device
## CLOCK$: System real-time clock
## COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9:
## Serial communications ports 1-9
## LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9:
## parallel printer ports 1-9
## In addition, the names of help files get converted to HTML
## file names and so should be valid in URLs. We check that
## they are ASCII and do not contain %, which is what is known
## to cause troubles.
allfiles <- dir(".", all.files = TRUE,
full.names = TRUE, recursive = TRUE)
allfiles <- c(allfiles, unique(dirname(allfiles)))
allfiles <- sub("^./", "", allfiles)
ignore_re <- paste0("(", paste(ignore, collapse = "|"), ")")
allfiles <- grep(ignore_re, allfiles, invert = TRUE, value = TRUE)
bad_files <- allfiles[grepl("[[:cntrl:]\"*/:<>?\\|]",
basename(allfiles))]
is_man <- grepl("man$", dirname(allfiles))
bad <- sapply(strsplit(basename(allfiles[is_man]), ""),
function(x) any(grepl("[^ -~]|%", x)))
if (length(bad))
bad_files <- c(bad_files, (allfiles[is_man])[bad])
bad <- tolower(basename(allfiles))
## remove any extension(s) (see 'Writing R Extensions')
bad <- sub("[.].*", "", bad)
bad <- grepl("^(con|prn|aux|clock[$]|nul|lpt[1-9]|com[1-9])$", bad)
bad_files <- c(bad_files, allfiles[bad])
if (nb <- length(bad_files)) {
errorLog(Log)
msg <- ngettext(nb,
"Found the following file with a non-portable file name:\n",
"Found the following files with non-portable file names:\n",
domain = NA)
wrapLog(msg)
printLog(Log, .format_lines_with_indent(bad_files), "\n")
wrapLog("These are not valid file names",
"on all R platforms.\n",
"Please rename the files and try again.\n",
"See section 'Package structure'",
"in the 'Writing R Extensions' manual.\n")
do_exit(1L)
}
## Next check for name clashes on case-insensitive file systems
## (that is on Windows and (by default) on Mac OS X).
dups <- unique(allfiles[duplicated(tolower(allfiles))])
if (nb <- length(dups)) {
errorLog(Log)
wrapLog("Found the following files with duplicate lower-cased file names:\n")
printLog(Log, .format_lines_with_indent(dups), "\n")
wrapLog("File names must not differ just by case",
"to be usable on all R platforms.\n",
"Please rename the files and try again.\n",
"See section 'Package structure'",
"in the 'Writing R Extensions' manual.\n")
do_exit(1L)
}
## NB: the omission of ' ' is deliberate.
non_ASCII_files <-
allfiles[grepl("[^-A-Za-z0-9._!#$%&+,;=@^(){}\'[\\]]", #
basename(allfiles), perl = TRUE)]
if (nb <-length(non_ASCII_files)) {
warningLog(Log)
msg <- ngettext(nb,
"Found the following file with a non-portable file name:\n",
"Found the following files with non-portable file names:\n",
domain = NA)
wrapLog(msg)
printLog(Log, .format_lines_with_indent(non_ASCII_files), "\n")
wrapLog("These are not fully portable file names.\n",
"See section 'Package structure'",
"in the 'Writing R Extensions' manual.\n")
} else resultLog(Log, "OK")
allfiles
}
check_permissions <- function(allfiles)
{
checkingLog(Log, "for sufficient/correct file permissions")
## This used to be much more 'aggressive', requiring that dirs
## and files have mode >= 00755 and 00644, respectively (with
## an error if not), and that files know to be 'text' have
## mode 00644 (with a warning if not). We now only require
## that dirs and files have mode >= 00700 and 00400,
## respectively, and try to fix insufficient permission in the
## INSTALL code (Unix only).
##
## In addition, we check whether files 'configure' and
## 'cleanup' exists in the top-level directory but are not
## executable, which is most likely not what was intended.
## Phase A. Directories at least 700, files at least 400.
bad_files <- character()
## allfiles <- dir(".", all.files = TRUE,
## full.names = TRUE, recursive = TRUE)
## allfiles <- sub("^./", "", allfiles)
if(length(allfiles)) {
mode <- file.info(allfiles)$mode
bad_files <- allfiles[(mode & "400") < as.octmode("400")]
}
if(length(alldirs <- unique(dirname(allfiles)))) {
mode <- file.info(alldirs)$mode
bad_files <- c(bad_files,
alldirs[(mode & "700") < as.octmode("700")])
}
if (length(bad_files)) {
errorLog(Log)
wrapLog("Found the following files with insufficient permissions:\n")
printLog(Log, .format_lines_with_indent(bad_files), "\n")
wrapLog("Permissions should be at least 700 for directories and 400 for files.\nPlease fix permissions and try again.\n")
do_exit(1L)
}
## Phase B. Top-level scripts 'configure' and 'cleanup'
## should really be mode at least 500, or they will not be
## necessarily be used (or should we rather change *that*?)
bad_files <- character()
for (f in c("configure", "cleanup")) {
if (!file.exists(f)) next
mode <- file.info(f)$mode
if ((mode & "500") < as.octmode("500"))
bad_files <- c(bad_files, f)
}
if (length(bad_files)) {
warningLog(Log)
wrapLog("The following files should most likely be executable (for the owner):\n")
printLog(Log, .format_lines_with_indent(bad_files), "\n")
printLog(Log, "Please fix their permissions\n")
} else resultLog(Log, "OK")
}
check_meta <- function()
{
## If we just installed the package (via R CMD INSTALL), we already
## validated most of the package DESCRIPTION metadata. Otherwise,
## let us be defensive about this ...
checkingLog(Log, "DESCRIPTION meta-information")
dfile <- if (is_base_pkg) "DESCRIPTION.in" else "DESCRIPTION"
## FIXME: this does not need to be run in another process
Rcmd <- sprintf("tools:::.check_package_description(\"%s\")", dfile)
out <- R_runR(Rcmd, R_opts2, "R_DEFAULT_PACKAGES=NULL")
if (length(out)) {
errorLog(Log)
printLog(Log, paste(out, collapse="\n"), "\n")
do_exit(1L)
}
any <- FALSE
## Check the encoding.
Rcmd <- sprintf("tools:::.check_package_description_encoding(\"%s\")", dfile)
out <- R_runR(Rcmd, R_opts2, "R_DEFAULT_PACKAGES=NULL")
if (length(out)) {
warningLog(Log)
any <- TRUE
printLog(Log, paste(out, collapse="\n"), "\n")
}
## Check the license.
## For base packages, the DESCRIPTION.in files have non-canonical
## License: Part of R @VERSION@
## entries because these really are a part of R: hence, skip the
## check.
check_license <- if (!is_base_pkg) {
Check_license <- Sys.getenv("_R_CHECK_LICENSE_", NA)
if(is.na(Check_license)) {
## The check code conditionalizes *output* on _R_CHECK_LICENSE_.
Sys.setenv('_R_CHECK_LICENSE_' = "TRUE")
TRUE
} else config_val_to_logical(Check_license)
} else FALSE
if (!identical(check_license, FALSE)) {
Rcmd <- sprintf("tools:::.check_package_license(\"%s\", \"%s\")",
dfile, pkgdir)
## FIXME: this does not need to be run in another process
out <- R_runR(Rcmd, R_opts2, "R_DEFAULT_PACKAGES=NULL")
if (length(out)) {
if (check_license == "maybe") {
if (!any) warningLog(Log)
} else if (any(grepl("^(Standardizable: FALSE|Invalid license file pointers:)",
out))) {
if (!any) warningLog(Log)
} else {
if (!any) noteLog(Log)
}
any <- TRUE
printLog(Log, paste(out, collapse="\n"), "\n")
}
}
## Check Authors@R in case it was not checked as part of
## .check_package_description().
db <- .read_description(dfile)
if(!is.na(aar <- db["Authors@R"]) &&
!is.na(db["Author"]) &&
!is.na(db["Maintainer"])) {
out <- .check_package_description_authors_at_R_field(aar)
if(length(out)) {
if(!any) noteLog(Log)
any <- TRUE
.show_check_package_description_authors_at_R_field_results(out)
}
}
if (!any) resultLog(Log, "OK")
}
check_top_level <- function()
{
checkingLog(Log, "top-level files")
topfiles <- Sys.glob(c("install.R", "R_PROFILE.R"))
if (length(topfiles)) {
warningLog(Log)
printLog(Log, .format_lines_with_indent(topfiles), "\n")
wrapLog("These files are defunct.",
"See manual 'Writing R Extensions'.\n")
} else resultLog(Log, "OK")
}
check_detritus <- function()
{
checkingLog(Log, "for left-over files")
files <- dir(".", full.names = TRUE, recursive = TRUE)
bad <- grep("svn-commit[.].*tmp$", files, value = TRUE)
if (length(bad)) {
bad <- sub("^[.]/", "", bad)
noteLog(Log)
printLog(Log,
"The following files look like leftovers:\n",
paste(strwrap(paste(sQuote(bad), collapse = ", "),
indent = 2, exdent = 2), collapse = "\n"),
"\nPlease remove them from your package.\n")
} else resultLog(Log, "OK")
}
check_indices <- function()
{
## Check index information.
checkingLog(Log, "index information")
any <- FALSE
if (file.exists("INDEX") &&
!length(readLines("INDEX", warn = FALSE))) {
any <- TRUE
warningLog(Log, "Empty file 'INDEX'.")
}
if (dir.exists("demo")) {
index <- file.path("demo", "00Index")
if (!file.exists(index) ||
!length(readLines(index, warn = FALSE))) {
if(!any) warningLog(Log)
any <- TRUE
printLog(Log,
sprintf("Empty or missing file %s.\n",
sQuote(index)))
} else {
Rcmd <- "options(warn=1)\ntools:::.check_demo_index(\"demo\")\n"
## FIXME: this does not need to be run in another process
out <- R_runR(Rcmd, R_opts2, "R_DEFAULT_PACKAGES=NULL")
if(length(out)) {
if(!any) warningLog(Log)
any <- TRUE
printLog(Log, paste(c(out, ""), collapse="\n"))
}
}
}
if (dir.exists(file.path("inst", "doc"))) {
Rcmd <- "options(warn=1)\ntools:::.check_vignette_index(\"inst/doc\")\n"
## FIXME: this does not need to be run in another process
out <- R_runR(Rcmd, R_opts2, "R_DEFAULT_PACKAGES=NULL")
if(length(out)) {
if(!any) warningLog(Log)
any <- TRUE
printLog(Log, paste(c(out, ""), collapse="\n"))
}
}
if (any)
wrapLog("See the information on INDEX files and package",
"subdirectories in the chapter 'Creating R packages'",
"of the 'Writing R Extensions' manual.\n")
else resultLog(Log, "OK")
}
check_subdirectories <- function(haveR, subdirs)
{
checkingLog(Log, "package subdirectories")
any <- FALSE
if (haveR && !length(list_files_with_type("R", "code"))) {
haveR <- FALSE
warningLog(Log, "Found directory 'R' with no source files.")
any <- TRUE
}
if (R_check_subdirs_nocase) {
## Argh. We often get submissions where 'R' comes out as 'r',
## or 'man' comes out as 'MAN', and we've just ran into 'DATA'
## instead of 'data' (2007-03-31). Maybe we should warn about
## this unconditionally ...
## <FIXME>
## Actually, what we should really do is check whether there is
## any directory with lower-cased name matching a lower-cased
## name of a standard directory, while differing in name.
## </FIXME>
## Watch out for case-insensitive file systems
if ("./r" %in% list.dirs(recursive = FALSE)) {
if (!any) warningLog(Log)
any <- TRUE
printLog(Log, "Found subdirectory 'r'.\n",
"Most likely, this should be 'R'.\n")
}
if ("./MAN" %in% list.dirs(recursive = FALSE)) {
if (!any) warningLog(Log)
any <- TRUE
printLog(Log, "Found subdirectory 'MAN'.\n",
"Most likely, this should be 'man'.\n")
}
if ("./DATA" %in% list.dirs(recursive = FALSE)) {
if (!any) warningLog(Log)
any <- TRUE
printLog(Log, "Found subdirectory 'DATA'.\n",
"Most likely, this should be 'data'.\n")
}
}
all_dirs <- list.dirs(".")
## several packages have had check dirs in the sources, e.g.
## ./languageR/languageR.Rcheck
## ./locfdr/man/locfdr.Rcheck
## ./clustvarsel/inst/doc/clustvarsel.Rcheck
## ./bicreduc/OldFiles/bicreduc.Rcheck
## ./waved/man/waved.Rcheck
## ./waved/..Rcheck
ind <- grepl("\\.Rcheck$", all_dirs)
if(any(ind)) {
if(!any) warningLog(Log)
any <- TRUE
msg <- ngettext(sum(ind),
"Found the following directory with the name of a check directory:\n",
"Found the following directories with names of check directories:\n", domain = NA)
printLog(Log, msg,
.format_lines_with_indent(all_dirs[ind]),
"\n",
"Most likely, these were included erroneously.\n")
}
## Several packages had leftover Rd2dvi build directories in
## their sources
ind <- grepl("^\\.Rd2(dvi|pdf)", basename(all_dirs))
if(any(ind)) {
if(!any) warningLog(Log)
any <- TRUE
msg <- ngettext(sum(ind),
"Found the following directory with the name of a Rd2pdf directory:\n",
"Found the following directories with names of Rd2pdf directories:\n", domain = NA)
printLog(Log, msg,
.format_lines_with_indent(all_dirs[ind]),
"\n",
"Most likely, these were included erroneously.\n")
}
if(!is_base_pkg && (istar || R_check_vc_dirs)) {
## Packages also should not contain version control subdirs
## provided that we check a .tar.gz or know we unpacked one.
ind <- basename(all_dirs) %in% .vc_dir_names
if(any(ind)) {
if(!any) warningLog(Log)
any <- TRUE
msg <- ngettext(sum(ind),
"Found the following directory with the name of a version control directory:\n",
"Found the following directories with names of version control directories:\n", domain = NA)
printLog(Log, msg,
.format_lines_with_indent(all_dirs[ind]),
"\n",
"These should not be in a package tarball.\n")
}
}
if (subdirs != "no") {
Rcmd <- "tools:::.check_package_subdirs(\".\")\n"
## We don't run this in the C locale, as we only require
## certain filenames to start with ASCII letters/digits, and not
## to be entirely ASCII.
out <- R_runR(Rcmd, R_opts2, "R_DEFAULT_PACKAGES=NULL")
if(length(out)) {
if(!any) warningLog(Log)
any <- TRUE
printLog(Log, paste(c(out, ""), collapse="\n"))
wrapLog("Please remove or rename the files.\n",
"See section 'Package subdirectories'",
"in the 'Writing R Extensions' manual.\n")
}
}
## Subdirectory 'data' without data sets?
if (dir.exists("data") &&
!length(list_files_with_type("data", "data"))) {
if (!any) warningLog(Log)
any <- TRUE
printLog(Log, "Subdirectory 'data' contains no data sets.\n")
}
## Subdirectory 'demo' without demos?
if (dir.exists("demo")) {
demos <- list_files_with_type("demo", "demo")
if(!length(demos)) {
if (!any) warningLog(Log)
any <- TRUE
printLog(Log, "Subdirectory 'demo' contains no demos.\n")
} else {
## check for non-ASCII code in each demo
bad <- character()
for(d in demos) {
x <- readLines(d, warn = FALSE)
asc <- iconv(x, "latin1", "ASCII")
ind <- is.na(asc) | asc != x
if (any(ind)) bad <- c(bad, basename(d))
}
if (length(bad)) {
if (!any) warningLog(Log)
any <- TRUE
printLog(Log, "Demos with non-ASCII characters:")
if(length(bad) > 1L)
printLog(Log, "\n",
.format_lines_with_indent(bad), "\n")
else printLog(Log, " ", bad, "\n")
wrapLog("Portable packages must use only ASCII",
"characters in their demos.\n",
"Use \\uxxxx escapes for other characters.\n")
demos <- demos[! basename(demos) %in% bad]
}
## check we can parse each demo.
bad <- character()
for(d in demos)
tryCatch(parse(file = d),
error = function(e) bad <<- c(bad, basename(d)))
if (length(bad)) {
if (!any) warningLog(Log)
any <- TRUE
printLog(Log, "Demos which do not contain valid R code:")
if(length(bad) > 1L)
printLog(Log, "\n",
.format_lines_with_indent(bad), "\n")
else printLog(Log, " ", bad, "\n")
}
}
}
## Subdirectory 'exec' without files?
if (dir.exists("exec") && !length(dir("exec"))) {
if (!any) warningLog(Log)
any <- TRUE
printLog(Log, "Subdirectory 'exec' contains no files.\n")
}
## Subdirectory 'inst' without files?
if (dir.exists("inst") && !length(dir("inst", recursive = TRUE))) {
if (!any) warningLog(Log)
any <- TRUE
printLog(Log, "Subdirectory 'inst' contains no files.\n")
}
## Subdirectory 'src' without sources?
if (dir.exists("src")) {
## <NOTE>
## If there is a Makefile (or a Makefile.win), we cannot assume
## that source files have the predefined extensions.
## </NOTE>
if (!any(file.exists(file.path("src",
c("Makefile", "Makefile.win"))))) {
if (!length(dir("src", pattern = "\\.([cfmM]|cc|cpp|f90|f95|mm)"))) {
if (!any) warningLog(Log)
printLog(Log, "Subdirectory 'src' contains no source files.\n")
any <- TRUE
}
}
}
## Do subdirectories of 'inst' interfere with R package system
## subdirectories?
if (dir.exists("inst")) {
## These include pre-2.10.0 ones
R_system_subdirs <-
c("Meta", "R", "data", "demo", "exec", "libs",
"man", "help", "html", "latex", "R-ex")
allfiles <- dir("inst", full.names = TRUE)
alldirs <- allfiles[file.info(allfiles)$isdir]
suspect <- basename(alldirs) %in% R_system_subdirs
if (any(suspect)) {
## check they are non-empty
suspect <- alldirs[suspect]
suspect <- suspect[sapply(suspect, function(x) {
length(dir(x, all.files = TRUE)) > 2L
})]
if (length(suspect)) {
if (!any) warningLog(Log)
any <- TRUE
wrapLog("Found the following non-empty",
"subdirectories of 'inst' also",
"used by R:\n")
printLog(Log, paste(c(suspect, ""), collapse="\n"))
wrapLog("It is recommended not to interfere",
"with package subdirectories used by R.\n")
}
}
}
## Valid NEWS.Rd?
nfile <- file.path("inst", "NEWS.Rd")
if(file.exists(nfile)) {
## Catch all warning and error messages.
## We use the same construction in at least another place,
## so maybe factor out a common utility function
## .try_catch_all_warnings_and_errors
## eventually.
## For testing package NEWS.Rd files, we really need a real
## QC check function eventually ...
.warnings <- NULL
.error <- NULL
withCallingHandlers(tryCatch(.build_news_db_from_package_NEWS_Rd(nfile),
error = function(e)
.error <<- conditionMessage(e)),
warning = function(e) {
.warnings <<- c(.warnings,
conditionMessage(e))
invokeRestart("muffleWarning")
})
msg <- c(.warnings, .error)
if(length(msg)) {
if(!any) warningLog(Log)
any <- TRUE
printLog(Log, "Problems with news in 'inst/NEWS.Rd':\n")
printLog(Log,
paste(" ",
unlist(strsplit(msg, "\n", fixed = TRUE)),
sep = "", collapse = "\n"),
"\n")
}
}
## Valid CITATION metadata?
if (file.exists(file.path("inst", "CITATION"))) {
Rcmd <- "tools:::.check_citation(\"inst/CITATION\")\n"
out <- R_runR(Rcmd, R_opts2, "R_DEFAULT_PACKAGES=utils")
if(length(out)) {
if(!any) warningLog(Log)
any <- TRUE
printLog(Log,
"Invalid citation information in 'inst/CITATION':\n")
printLog(Log, .format_lines_with_indent(out), "\n")
}
}
## CITATION files in non-standard places?
## Common problems: rather than inst/CITATION, have
## CITATION
## CITATION.txt
## inst/doc/CITATION
## Of course, everything in inst is justifiable, so only give a
## note for now.
files <- dir(".", pattern = "^CITATION.*", recursive = TRUE)
files <- files[file_path_sans_ext(basename(files)) == "CITATION" &
files != file.path("inst", "CITATION")]
if(length(files)) {
if(!any) noteLog(Log)
any <- TRUE
msg <- ngettext(length(files),
"Found the following CITATION file in a non-standard place:\n",
"Found the following CITATION files in a non-standard place:\n", domain = NA)
wrapLog(msg)
printLog(Log, .format_lines_with_indent(files), "\n")
wrapLog("Most likely 'inst/CITATION' should be used instead.\n")
}
if(!any) resultLog(Log, "OK")
}
check_non_ASCII <- function()
{
checkingLog(Log, "R files for non-ASCII characters")
out <- R_runR("tools:::.check_package_ASCII_code('.')",
R_opts2, "R_DEFAULT_PACKAGES=NULL")
if (length(out)) {
warningLog(Log)
msg <- ngettext(length(out),
"Found the following file with non-ASCII characters:\n",
"Found the following files with non-ASCII characters:\n",
domain = NA)
wrapLog(msg)
printLog(Log, .format_lines_with_indent(out), "\n")
wrapLog("Portable packages must use only ASCII",
"characters in their R code,\n",
"except perhaps in comments.\n",
"Use \\uxxxx escapes for other characters.\n")
} else resultLog(Log, "OK")
checkingLog(Log, "R files for syntax errors")
Rcmd <- "options(warn=1);tools:::.check_package_code_syntax(\"R\")"
out <- R_runR(Rcmd, R_opts2, "R_DEFAULT_PACKAGES=NULL")
if (any(grepl("^Error", out))) {
errorLog(Log)
printLog(Log, paste(c(out, ""), collapse = "\n"))
do_exit(1L)
} else if (length(out)) {
warningLog(Log)
printLog(Log, paste(c(out, ""), collapse = "\n"))
} else resultLog(Log, "OK")
}
check_R_code <- function()
{
if (!is_base_pkg) {
checkingLog(Log, "for unstated dependencies in R code")
if (do_install) {
Rcmd <- paste("options(warn=1, showErrorCalls=FALSE)\n",
sprintf("tools:::.check_packages_used(package = \"%s\")\n", pkgname))
out <- R_runR2(Rcmd, "R_DEFAULT_PACKAGES=NULL")
if (length(out)) {
warningLog(Log)
printLog(Log, paste(c(out, ""), collapse = "\n"))
wrapLog(msg_DESCRIPTION)
} else resultLog(Log, "OK")
} else {
## this needs to read the package code, and will fail on
## syntax errors such as non-ASCII code.
Rcmd <- paste("options(warn=1, showErrorCalls=FALSE)\n",
sprintf("tools:::.check_packages_used(dir = \"%s\")\n", pkgdir))
out <- R_runR(Rcmd, R_opts2, "R_DEFAULT_PACKAGES=NULL")
if (length(out)) {
warningLog(Log)
printLog(Log, paste(c(out, ""), collapse = "\n"))
wrapLog(msg_DESCRIPTION)
} else resultLog(Log, "OK")
}
}
## Check whether methods have all arguments of the corresponding
## generic.
checkingLog(Log, "S3 generic/method consistency")
Rcmd <- paste("options(warn=1)\n",
"options(expressions=1000)\n",
if (do_install)
sprintf("tools::checkS3methods(package = \"%s\")\n", pkgname)
else
sprintf("tools::checkS3methods(dir = \"%s\")\n", pkgdir))
out <- R_runR2(Rcmd)
if (length(out)) {
warningLog(Log)
printLog(Log, paste(c(out, ""), collapse = "\n"))
wrapLog("See section 'Generic functions and methods'",
"of the 'Writing R Extensions' manual.\n")
} else resultLog(Log, "OK")
## Check whether replacement functions have their final argument
## named 'value'.
checkingLog(Log, "replacement functions")
Rcmd <- paste("options(warn=1)\n",
if (do_install)
sprintf("tools::checkReplaceFuns(package = \"%s\")\n", pkgname)
else
sprintf("tools::checkReplaceFuns(dir = \"%s\")\n", pkgdir))
out <- R_runR2(Rcmd)
if (length(out)) {
## <NOTE>
## We really want to stop if we find offending replacement
## functions. But we cannot use error() because output may
## contain warnings ...
warningLog(Log)
## </NOTE>
printLog(Log, paste(c(out, ""), collapse = "\n"))
wrapLog("The argument of a replacement function",
"which corresponds to the right hand side",
"must be named 'value'.\n")
} else resultLog(Log, "OK")
## Check foreign function calls.
## The neverending story ...
## For the time being, allow to turn this off by setting the environment
## variable _R_CHECK_FF_CALLS_ to an empty value.
if (nzchar(Sys.getenv("_R_CHECK_FF_CALLS_", "true"))) {
checkingLog(Log, "foreign function calls")
Rcmd <- paste("options(warn=1)\n",
if (do_install)
sprintf("tools::checkFF(package = \"%s\")\n", pkgname)
else
sprintf("tools::checkFF(dir = \"%s\")\n", pkgdir))
out <- R_runR2(Rcmd)
if (length(out)) {
if(any(grepl("^Foreign function calls? with(out| empty)", out)) ||
(!is_base_pkg && any(grepl("in a base package:", out))) ||
any(grepl("^Undeclared packages? in", out))
) warningLog(Log)
else noteLog(Log)
printLog(Log, paste(c(out, ""), collapse = "\n"))
if(!is_base_pkg && any(grepl("in a base package:", out)))
wrapLog("Packages should not make .C/.Call/.Fortran",
"calls to base packages.",
"They are not part of the API,",
"for use only by R itself",
"and subject to change without notice.")
else
wrapLog("See the chapter 'System and foreign language interfaces' of the 'Writing R Extensions' manual.\n")
} else resultLog(Log, "OK")
}
}
check_R_files <- function(is_rec_pkg)
{
checkingLog(Log, "R code for possible problems")
if (!is_base_pkg) {
Rcmd <- "options(warn=1);tools:::.check_package_code_shlib(\"R\")"
out <- R_runR(Rcmd, R_opts2, "R_DEFAULT_PACKAGES=NULL")
if (length(out)) {
errorLog(Log)
wrapLog("Incorrect (un)loading of package",
"shared object.\n")
printLog0(Log, paste(c(out, ""), collapse = "\n"))
wrapLog("The system-specific extension for",
"shared objects must not be added.\n",
"See ?library.dynam.\n")
do_exit(1L)
}
}
Rcmd <- paste("options(warn=1)\n",
sprintf("tools:::.check_package_code_startup_functions(dir = \"%s\")\n",
pkgdir))
out1 <- R_runR(Rcmd, R_opts2, "R_DEFAULT_PACKAGES=")
out2 <- out3 <- out4 <- NULL
if (!is_base_pkg && R_check_unsafe_calls) {
Rcmd <- "options(warn=1);tools:::.check_package_code_tampers(\"R\")"
out2 <- R_runR(Rcmd, R_opts2, "R_DEFAULT_PACKAGES=NULL")
}
if (R_check_use_codetools && do_install) {
Rcmd <-
paste("options(warn=1)\n",
sprintf("tools:::.check_code_usage_in_package(package = \"%s\")\n", pkgname))
out3 <- R_runR2(Rcmd, "R_DEFAULT_PACKAGES=")
}
if(!is_base_pkg && R_check_use_codetools && R_check_dot_internal) {
details <- pkgname != "relax" # has .Internal in a 10,000 line fun
Rcmd <- paste("options(warn=1)\n",
if (do_install)
sprintf("tools:::.check_dotInternal(package = \"%s\",details=%s)\n", pkgname, details)
else
sprintf("tools:::.check_dotInternal(dir = \"%s\",details=%s)\n", pkgdir, details))
out4 <- R_runR2(Rcmd, "R_DEFAULT_PACKAGES=")
## Hmisc, gooJSON, quantmod give spurious output
if (!any(grepl("^Found.* .Internal call", out4))) out4 <- NULL
}
if (length(out1) || length(out2) || length(out3) || length(out4)) {
if (length(out4)) warningLog(Log) else noteLog(Log)
if (length(out1))
printLog0(Log, paste(c(out1, ""), collapse = "\n"))
if (length(out2))
printLog0(Log,
paste(c("Found the following possibly unsafe calls:",
out2, ""), collapse = "\n"))
if (length(out3))
printLog0(Log, paste(c(out3, ""), collapse = "\n"))
if (length(out4)) {
first <- grep("^Found.* .Internal call", out4)[1L]
if(first > 1L) out4 <- out4[-seq_len(first-1)]
printLog0(Log, paste(c(out4, "", ""), collapse = "\n"))
wrapLog(c("Packages should not call .Internal():",
"it is not part of the API,",
"for use only by R itself",
"and subject to change without notice."))
}
} else resultLog(Log, "OK")
}
check_Rd_files <- function(haveR)
{
msg_writing_Rd <-
c("See the chapter 'Writing R documentation files'",
"in the 'Writing R Extensions' manual.\n")
if (dir.exists("man") && !extra_arch) {
checkingLog(Log, "Rd files")
minlevel <- Sys.getenv("_R_CHECK_RD_CHECKRD_MINLEVEL_", "-1")
Rcmd <- paste("options(warn=1)\n",
sprintf("tools:::.check_package_parseRd('.', minlevel=%s)\n", minlevel))
out <- R_runR(Rcmd, R_opts2, "R_DEFAULT_PACKAGES=NULL")
if (length(out)) {
if(length(grep("^prepare.*Dropping empty section", out,
invert = TRUE)))
warningLog(Log)
else noteLog(Log)
printLog0(Log, paste(c(out, ""), collapse = "\n"))
} else resultLog(Log, "OK")
checkingLog(Log, "Rd metadata")
Rcmd <- paste("options(warn=1)\n",
if (do_install)
sprintf("tools:::.check_Rd_metadata(package = \"%s\")\n", pkgname)
else
sprintf("tools:::.check_Rd_metadata(dir = \"%s\")\n", pkgdir))
out <- R_runR(Rcmd, R_opts2, "R_DEFAULT_PACKAGES=NULL")
if (length(out)) {
warningLog(Log)
printLog0(Log, paste(c(out, ""), collapse = "\n"))
} else resultLog(Log, "OK")
}
## Check cross-references in R documentation files.
## <NOTE>
## Installing a package warns about missing links (and hence R CMD
## check knows about this too provided an install log is used).
## However, under Windows the install-time check verifies the links
## against what is available in the default library, which might be
## considerably more than what can be assumed to be available.
##
## The formulations in section "Cross-references" of R-exts are not
## quite clear about this, but CRAN policy has for a long time
## enforced anchoring links to targets (aliases) from non-base
## packages.
## </NOTE>
if (dir.exists("man") && R_check_Rd_xrefs) {
checkingLog(Log, "Rd cross-references")
Rcmd <- paste("options(warn=1)\n",
if (do_install)
sprintf("tools:::.check_Rd_xrefs(package = \"%s\")\n", pkgname)
else
sprintf("tools:::.check_Rd_xrefs(dir = \"%s\")\n", pkgdir))
out <- R_runR(Rcmd, R_opts2, "R_DEFAULT_PACKAGES=NULL")
if (length(out)) {
if (!all(grepl("Package[s]? unavailable to check", out)))
warningLog(Log)
else noteLog(Log)
printLog(Log, paste(c(out, ""), collapse = "\n"))
} else resultLog(Log, "OK")
}
## Check for missing documentation entries.
if (!extra_arch && (haveR || dir.exists("data"))) {
checkingLog(Log, "for missing documentation entries")
Rcmd <- paste("options(warn=1)\n",
if (do_install)
sprintf("tools::undoc(package = \"%s\")\n", pkgname)
else
sprintf("tools::undoc(dir = \"%s\")\n", pkgdir))
## This is needed to pick up undocumented S4 classes.
## even for packages which only import methods.
## But as that check needs to run get() on all the lazy-loaded
## promises, avoid if possible.
## desc exists in the body of this function.
use_methods <- if(pkgname == "methods") TRUE else {
pi <- .split_description(desc)
"methods" %in% c(names(pi$Depends), names(pi$Imports))
}
out <- if (use_methods) {
env <- if(WINDOWS) "R_DEFAULT_PACKAGES=utils,grDevices,graphics,stats,methods" else "R_DEFAULT_PACKAGES='utils,grDevices,graphics,stats,methods'"
R_runR2(Rcmd, env = env)
} else R_runR2(Rcmd)
## Grr, get() in undoc can change the search path
## Current example is TeachingDemos
out <- grep("^Loading required package:", out,
invert = TRUE, value = TRUE)
err <- grep("^Error", out)
if (length(err)) {
errorLog(Log)
printLog0(Log, paste(c(out, ""), collapse = "\n"))
do_exit(1L)
} else if (length(out)) {
warningLog(Log)
printLog0(Log, paste(c(out, ""), collapse = "\n"))
wrapLog("All user-level objects",
"in a package",
if (any(grepl("^Undocumented S4", out)))
"(including S4 classes and methods)",
"should have documentation entries.\n")
wrapLog(msg_writing_Rd)
} else resultLog(Log, "OK")
}
## Check for code/documentation mismatches.
if (dir.exists("man") && !extra_arch) {
checkingLog(Log, "for code/documentation mismatches")
if (!do_codoc) resultLog(Log, "SKIPPED")
else {
any <- FALSE
## Check for code/documentation mismatches in functions.
if (haveR) {
Rcmd <- paste("options(warn=1)\n",
if (do_install)
sprintf("tools::codoc(package = \"%s\")\n", pkgname)
else
sprintf("tools::codoc(dir = \"%s\")\n", pkgdir))
out <- R_runR2(Rcmd)
if (length(out)) {
any <- TRUE
warningLog(Log)
printLog0(Log, paste(c(out, ""), collapse = "\n"))
}
}
## Check for code/documentation mismatches in data sets.
if (do_install) {
Rcmd <- paste("options(warn=1)\n",
sprintf("tools::codocData(package = \"%s\")\n", pkgname))
out <- R_runR2(Rcmd)
if (length(out)) {
if (!any) warningLog(Log)
any <- TRUE
printLog0(Log, paste(c(out, ""), collapse = "\n"))
}
}
## Check for code/documentation mismatches in S4 classes.
if (do_install && haveR) {
Rcmd <- paste("options(warn=1)\n",
sprintf("tools::codocClasses(package = \"%s\")\n", pkgname))
out <- R_runR2(Rcmd)
if (length(out)) {
if (!any) warningLog(Log)
any <- TRUE
printLog0(Log, paste(c(out, ""), collapse = "\n"))
}
}
if (!any) resultLog(Log, "OK")
}
}
## Check Rd files, for consistency of \usage with \arguments (are
## all arguments shown in \usage documented in \arguments?) and
## aliases (do all functions shown in \usage have an alias?)
if (dir.exists("man") && !extra_arch) {
checkingLog(Log, "Rd \\usage sections")
msg_doc_files <-
c("Functions with \\usage entries",
"need to have the appropriate \\alias entries,",
"and all their arguments documented.\n",
"The \\usage entries must correspond to syntactically",
"valid R code.\n")
any <- FALSE
Rcmd <- paste("options(warn=1)\n",
if (do_install)
sprintf("tools::checkDocFiles(package = \"%s\")\n", pkgname)
else
sprintf("tools::checkDocFiles(dir = \"%s\")\n", pkgdir))
out <- R_runR2(Rcmd)
if (length(out)) {
any <- TRUE
warningLog(Log)
printLog0(Log, paste(c(out, ""), collapse = "\n"))
wrapLog(msg_doc_files)
wrapLog(msg_writing_Rd)
}
if (R_check_Rd_style && haveR) {
msg_doc_style <-
c("The \\usage entries for S3 methods should use",
"the \\method markup and not their full name.\n")
Rcmd <- paste("options(warn=1)\n",
if (do_install)
sprintf("tools::checkDocStyle(package = \"%s\")\n", pkgname)
else
sprintf("tools::checkDocStyle(dir = \"%s\")\n", pkgdir))
out <- R_runR2(Rcmd)
if (length(out)) {
if (!any) noteLog(Log)
any <- TRUE
printLog0(Log, paste(c(out, ""), collapse = "\n"))
wrapLog(msg_doc_style)
wrapLog(msg_writing_Rd)
}
}
if (!any) resultLog(Log, "OK")
}
## Check Rd contents
if (dir.exists("man") && R_check_Rd_contents && !extra_arch) {
checkingLog(Log, "Rd contents")
Rcmd <- paste("options(warn=1)\n",
if (do_install)
sprintf("tools:::.check_Rd_contents(package = \"%s\")\n", pkgname)
else
sprintf("tools:::.check_Rd_contents(dir = \"%s\")\n", pkgdir))
out <- R_runR(Rcmd, R_opts2, "R_DEFAULT_PACKAGES=NULL")
if (length(out)) {
warningLog(Log)
printLog0(Log, paste(c(out, ""), collapse = "\n"))
} else resultLog(Log, "OK")
}
## Check undeclared dependencies in examples (if any)
if (dir.exists("man") && do_install && !extra_arch && !is_base_pkg) {
checkingLog(Log, "for unstated dependencies in examples")
Rcmd <- paste("options(warn=1, showErrorCalls=FALSE)\n",
sprintf("tools:::.check_packages_used_in_examples(package = \"%s\")\n", pkgname))
out <- R_runR2(Rcmd, "R_DEFAULT_PACKAGES=NULL")
if (length(out)) {
warningLog(Log)
printLog(Log, paste(c(out, ""), collapse = "\n"))
# wrapLog(msg_DESCRIPTION)
} else resultLog(Log, "OK")
} ## FIXME, what if no install?
}
check_data <- function()
{
## Check contents of 'data'
if (!is_base_pkg && dir.exists("data")) {
checkingLog(Log, "contents of 'data' directory")
fi <- list.files("data")
if (!any(grepl("\\.[Rr]$", fi))) { # code files can do anything
dataFiles <- basename(list_files_with_type("data", "data"))
odd <- fi[! fi %in% c(dataFiles, "datalist")]
if (length(odd)) {
warningLog(Log)
msg <- c("Files not of a type allowed in a 'data' directory:\n",
paste0(.pretty_format(odd), "\n"),
"Please use e.g. 'inst/extdata' for non-R data files\n")
printLog(Log, msg)
} else resultLog(Log, "OK")
} else resultLog(Log, "OK")
}
## Check for non-ASCII characters in 'data'
if (!is_base_pkg && R_check_ascii_data && dir.exists("data")) {
checkingLog(Log, "data for non-ASCII characters")
out <- R_runR("tools:::.check_package_datasets('.')", R_opts2)
out <- grep("Loading required package", out,
invert = TRUE, value = TRUE)
out <- grep("Warning: running .First.lib()", out,
invert = TRUE, value = TRUE, fixed = TRUE)
out <- grep("using .First.lib()", out,
invert = TRUE, value = TRUE, fixed = TRUE)
out <- grep("Warning: changing locked binding", out,
invert = TRUE, value = TRUE, fixed = TRUE)
if (length(out)) {
bad <- grep("^Warning:", out)
if (length(bad)) warningLog(Log) else noteLog(Log)
printLog0(Log, .format_lines_with_indent(out), "\n")
} else resultLog(Log, "OK")
}
## Check for ASCII and uncompressed/unoptimized saves in 'data'
if (!is_base_pkg && R_check_compact_data && dir.exists("data")) {
checkingLog(Log, "data for ASCII and uncompressed saves")
out <- R_runR("tools:::.check_package_compact_datasets('.', TRUE)",
R_opts2)
out <- grep("Warning: changing locked binding", out,
invert = TRUE, value = TRUE, fixed = TRUE)
if (length(out)) {
warningLog(Log)
printLog0(Log, .format_lines_with_indent(out), "\n")
} else resultLog(Log, "OK")
}
## Check for ASCII and uncompressed/unoptimized saves in 'sysdata':
## no base package has this
if (R_check_compact_data && file.exists(file.path("R", "sysdata.rda"))) {
checkingLog(Log, "R/sysdata.rda")
out <- R_runR("tools:::.check_package_compact_sysdata('.', TRUE)",
R_opts2)
if (length(out)) {
bad <- grep("^Warning:", out)
if (length(bad)) warningLog(Log) else noteLog(Log)
printLog0(Log, .format_lines_with_indent(out), "\n")
} else resultLog(Log, "OK")
}
}
check_doc_contents <- function()
{
## Have already checked that inst/doc exists
doc_dir <- file.path(libdir, pkgname, "doc")
if (!dir.exists(doc_dir)) return()
checkingLog(Log, "installed files from 'inst/doc'")
## special case common problems.
any <- FALSE
files <- dir(file.path(pkgdir, "inst", "doc"))
already <- c("jss.cls", "jss.bst", "Rd.sty", "Sweave.sty")
bad <- files[files %in% already]
if (length(bad)) {
noteLog(Log)
any <- TRUE
printLog(Log,
"The following files are already in R: ",
paste(sQuote(bad), collapse = ", "), "\n",
"Please remove them from your package.\n")
}
files2 <- dir(file.path(pkgdir, "inst", "doc"), recursive = TRUE,
pattern = "[.](cls|sty|drv)$", full.names = TRUE)
## Skip Rnews.sty and RJournal.sty for now
files2 <- files2[! basename(files2) %in%
c("jss.cls", "jss.drv", "Rnews.sty", "RJournal.sty")]
bad <- character()
for(f in files2) {
pat <- "%% (This generated file may be distributed as long as the|original source files, as listed above, are part of the|same distribution.)"
if(length(grep(pat, readLines(f, warn = FALSE), useBytes = TRUE))
== 3L) bad <- c(bad, basename(f))
}
if (length(bad)) {
if(!any) noteLog(Log)
any <- TRUE
printLog(Log,
"The following files contain a license that requires\n",
"distribution of original sources:\n",
" ", paste(sQuote(bad), collapse = ", "), "\n",
"Please ensure that you have complied with it.\n")
}
## Now look for TeX leftovers (and soiltexture, Amelia ...).
bad <- grepl("[.](log|aux|bbl|blg|dvi|toc|out|Rd|Rout|dbj|drv|ins)$",
files, ignore.case = TRUE)
if (any(bad)) {
if(!any) noteLog(Log)
any <- TRUE
printLog(Log,
"The following files look like leftovers/mistakes:\n",
paste(strwrap(paste(sQuote(files[bad]), collapse = ", "),
indent = 2, exdent = 2), collapse = "\n"),
"\nPlease remove them from your package.\n")
}
files <- dir(doc_dir)
files <- files[! files %in% already]
bad <- grepl("[.](tex|lyx|png|jpg|jpeg|gif|ico|bst|cls|sty|ps|eps|img)$",
files, ignore.case = TRUE)
bad <- bad | grepl("(Makefile|~$)", files)
## How about any pdf files which look like figures files from vignettes?
vigns <- pkgVignettes(dir = pkgdir)
if (!is.null(vigns) && length(vigns$docs)) {
vf <- sub("[.][RSrs](nw|tex)", "", basename(vigns$docs))
pat <- paste(vf, collapse="|")
pat <- paste0("^(", pat, ")-[0-9]+[.]pdf")
bad <- bad | grepl(pat, files)
}
bad <- bad | grepl("^fig.*[.]pdf$", files)
badf <- files[bad]
dirs <- basename(list.dirs(doc_dir, recursive = FALSE))
badd <- dirs[dirs %in% c("auto", "Bilder", "fig", "figs", "figures",
"Figures", "img", "images", "JSSstyle",
"jssStyle", "screenshots2", "src", "tex", "tmp")]
if (length(c(badf, badd))) {
if(!any) noteLog(Log)
any <- TRUE
if(length(badf))
printLog(Log,
"The following files should probably not be installed:\n",
paste(strwrap(paste(sQuote(badf), collapse = ", "),
indent = 2, exdent = 2), collapse = "\n"),
"\n")
if(length(badd))
printLog(Log,
"The following directories should probably not be installed:\n",
paste(strwrap(paste(sQuote(badd), collapse = ", "),
indent = 2, exdent = 2), collapse = "\n"),
"\n")
printLog(Log, "\nConsider the use of a .Rinstignore file: see ",
sQuote("Writing R Extensions"), ",\n",
"or move the vignette sources from ",
sQuote("inst/doc"), " to ", sQuote("vignettes"), ".\n")
}
if (!any) resultLog(Log, "OK")
}
check_doc_size <- function()
{
## Have already checked that inst/doc exists and qpdf can be found
pdfs <- dir('inst/doc', pattern="\\.pdf",
recursive = TRUE, full.names = TRUE)
pdfs <- pdfs %w/o% "inst/doc/Rplots.pdf"
if (length(pdfs)) {
checkingLog(Log, "sizes of PDF files under 'inst/doc'")
any <- FALSE
td <- tempfile('pdf')
dir.create(td)
file.copy(pdfs, td)
res <- compactPDF(td, gs_quality = "none") # use qpdf
res <- format(res, diff = 1e5)
if(length(res)) {
noteLog(Log)
any <- TRUE
printLog(Log,
" 'qpdf' made some significant size reductions:\n",
paste(" ", res, collapse = "\n"),
"\n",
" consider running tools::compactPDF() on these files\n")
}
if (R_check_doc_sizes2) {
gs_cmd <- find_gs_cmd(Sys.getenv("R_GSCMD", ""))
if (nzchar(gs_cmd)) {
res <- compactPDF(td, gs_cmd = gs_cmd, gs_quality = "ebook")
res <- format(res, diff = 2.5e5) # 250 KB for now
if(length(res)) {
if (!any) warningLog(Log)
any <- TRUE
printLog(Log,
" 'gs+qpdf' made some significant size reductions:\n",
paste(" ", res, collapse = "\n"),
"\n",
' consider running tools::compactPDF(gs_quality = "ebook") on these files\n')
}
} else {
if (!any) noteLog(Log)
any <- TRUE
printLog(Log, "Unable to find GhostScript executable to run checks on size reduction\n")
}
}
if (!any) resultLog(Log, "OK")
}
}
check_src_dir <- function()
{
## Check C/C++/Fortran sources/headers for CRLF line endings.
## <FIXME>
## Does ISO C really require LF line endings? (Reference?)
## We know that some versions of Solaris cc and f77/f95
## will not accept CRLF or CR line endings.
## (Sun Studio 12 definitely objects to CR in both C and Fortran).
## </FIXME>
checkingLog(Log, "line endings in C/C++/Fortran sources/headers")
## pattern is "([cfh]|cc|cpp)"
files <- dir("src", pattern = "\\.([cfh]|cc|cpp)$",
full.names = TRUE, recursive = TRUE)
## exclude dirs starting src/win, e.g for tiff
files <- grep("^src/[Ww]in", files, invert = TRUE, value = TRUE)
bad_files <- character()
for(f in files) {
contents <- readChar(f, file.info(f)$size, useBytes = TRUE)
if (grepl("\r", contents, fixed = TRUE, useBytes = TRUE))
bad_files <- c(bad_files, f)
}
if (length(bad_files)) {
warningLog(Log, "Found the following sources/headers with CR or CRLF line endings:")
printLog(Log, .format_lines_with_indent(bad_files), "\n")
printLog(Log, "Some Unix compilers require LF line endings.\n")
} else resultLog(Log, "OK")
## Check src/Make* for LF line endings, as Sun make does not accept CRLF
checkingLog(Log, "line endings in Makefiles")
bad_files <- character()
## .win files are not checked, as CR/CRLF work there
all_files <-
dir("src",
pattern = "^(Makevars|Makevars.in|Makefile|Makefile.in)$",
full.names = TRUE, recursive = TRUE)
for(f in all_files) {
if (!file.exists(f)) next
contents <- readChar(f, file.info(f)$size, useBytes = TRUE)
if (grepl("\r", contents, fixed = TRUE, useBytes = TRUE))
bad_files <- c(bad_files, f)
}
if (length(bad_files)) {
warningLog(Log, "Found the following Makefiles with CR or CRLF line endings:")
printLog(Log, .format_lines_with_indent(bad_files), "\n")
printLog(Log, "Some Unix 'make' programs require LF line endings.\n")
} else resultLog(Log, "OK")
## Check src/Makevars[.in] for portable compilation flags.
if (any(file.exists(file.path("src", c("Makevars", "Makevars.in")))) ) {
checkingLog(Log, "for portable compilation flags in Makevars")
Rcmd <- "tools:::.check_make_vars(\"src\")\n"
out <- R_runR(Rcmd, R_opts2, "R_DEFAULT_PACKAGES=NULL")
if (length(out)) {
warningLog(Log)
printLog(Log, paste(c(out, ""), collapse = "\n"))
} else resultLog(Log, "OK")
}
## check src/Makevar*, src/Makefile* for correct use of BLAS_LIBS
## FLIBS is not needed on Windows, at least currently (as it is
## statically linked).
checkingLog(Log, "for portable use of $(BLAS_LIBS) and $(LAPACK_LIBS)")
makefiles <- Sys.glob(file.path("src",
c("Makevars", "Makevars.in",
"Makefile", "Makefile.win")))
any <- FALSE
for (f in makefiles) {
lines <- readLines(f, warn = FALSE)
## Combine lines ending in escaped newlines.
if(any(ind <- grepl("[\\]$", lines, useBytes = TRUE))) {
## Eliminate escape.
lines[ind] <-
sub("[\\]$", "", lines[ind], useBytes = TRUE)
## Determine ids of blocks that need to be joined.
ind <- seq_along(ind) - c(0, cumsum(ind)[-length(ind)])
## And join.
lines <- unlist(lapply(split(lines, ind), paste,
collapse = " "))
}
c1 <- grepl("^[[:space:]]*PKG_LIBS", lines, useBytes = TRUE)
c2l <- grepl("\\$[{(]{0,1}LAPACK_LIBS", lines, useBytes = TRUE)
c2b <- grepl("\\$[{(]{0,1}BLAS_LIBS", lines, useBytes = TRUE)
c3 <- grepl("\\$[{(]{0,1}FLIBS", lines, useBytes = TRUE)
if (any(c1 & c2l & !c2b)) {
if (!any) warningLog(Log)
any <- TRUE
printLog(Log,
" apparently using $(LAPACK_LIBS) without $(BLAS_LIBS) in ",
sQuote(f), "\n")
}
if (any(c1 & (c2b | c2l) & !c3)) {
if (!any) warningLog(Log)
any <- TRUE
printLog(Log, " apparently PKG_LIBS is missing $(FLIBS) in ",
sQuote(f), "\n")
}
}
if (!any) resultLog(Log, "OK")
}
check_sos <- function() {
checkingLog(Log, "compiled code")
## from sotools.R
Rcmd <- paste("options(warn=1)\n",
sprintf("tools:::check_compiled_code(\"%s\")",
file.path(libdir, pkgname)))
out <- R_runR(Rcmd, R_opts2, "R_DEFAULT_PACKAGES=NULL")
if(length(out) == 1L && grepl("^Note:", out)) {
## This will be a note about symbols.rds not being available
if(!is_base_pkg) {
noteLog(Log)
printLog0(Log, c(out, "\n"))
} else resultLog(Log, "OK")
} else if(length(out)) {
## If we have named objects then we have symbols.rds and
## will not be picking up symbols just in system libraries.
haveObjs <- any(grepl("^ *Object", out))
if(haveObjs && any(grepl("(abort|assert|exit)", out)) &&
!pkgname %in% c("multicore", "parallel")) # need to call exit
warningLog(Log)
else noteLog(Log)
printLog0(Log, paste(c(out, ""), collapse = "\n"))
if(haveObjs)
wrapLog("\nCompiled code should not call functions which",
"might terminate R nor write to stdout/stderr instead",
"of to the console.\n" ,
"\n",
"See 'Writing portable packages'",
"in the 'Writing R Extensions' manual.\n")
else
wrapLog("\nCompiled code should not call functions which",
"might terminate R nor write to stdout/stderr instead",
"of to the console. The detected symbols are linked",
"into the code but might come from libraries",
"and not actually be called.\n",
"\n",
"See 'Writing portable packages'",
"in the 'Writing R Extensions' manual.\n")
} else resultLog(Log, "OK")
}
check_loading <- function(arch = "")
{
checkingLog(Log, "whether the package can be loaded")
Rcmd <- sprintf("library(%s)", pkgname)
opts <- if(nzchar(arch)) R_opts4 else R_opts2
env <- "R_DEFAULT_PACKAGES=NULL"
env1 <- if(nzchar(arch)) env0 else character()
out <- R_runR(Rcmd, opts, env1, arch = arch)
if(length(st <- attr(out, "status"))) {
errorLog(Log)
wrapLog("Loading this package had a fatal error",
"status code ", st, "\n")
if(length(out))
printLog(Log, paste(c("Loading log:", out, ""),
collapse = "\n"))
do_exit()
}
if (any(grepl("^Error", out))) {
errorLog(Log)
printLog(Log, paste(c(out, ""), collapse = "\n"))
wrapLog("\nIt looks like this package",
"has a loading problem: see the messages",
"for details.\n")
do_exit()
} else resultLog(Log, "OK")
checkingLog(Log, "whether the package can be loaded with stated dependencies")
out <- R_runR(Rcmd, opts, c(env, env1), arch = arch)
if (any(grepl("^Error", out)) || length(attr(out, "status"))) {
warningLog(Log)
printLog(Log, paste(c(out, ""), collapse = "\n"))
wrapLog("\nIt looks like this package",
"(or one of its dependent packages)",
"has an unstated dependence on a standard",
"package. All dependencies must be",
"declared in DESCRIPTION.\n")
wrapLog(msg_DESCRIPTION)
} else resultLog(Log, "OK")
checkingLog(Log, "whether the package can be unloaded cleanly")
Rcmd <- sprintf("suppressMessages(library(%s)); cat('\n---- unloading\n'); detach(\"package:%s\")", pkgname, pkgname)
out <- R_runR(Rcmd, opts, c(env, env1), arch = arch)
if (any(grepl("^(Error|\\.Last\\.lib failed)", out)) ||
length(attr(out, "status"))) {
warningLog(Log)
ll <- grep("---- unloading", out)
if(length(ll)) {
ll <- ll[length(ll)]
out <- out[ll:length(out)]
}
printLog(Log, paste(c(out, ""), collapse = "\n"))
} else resultLog(Log, "OK")
## and if it has a namespace, that we can load/unload just
## the namespace
if (file.exists(file.path(pkgdir, "NAMESPACE"))) {
checkingLog(Log, "whether the namespace can be loaded with stated dependencies")
Rcmd <- sprintf("loadNamespace(\"%s\")", pkgname)
out <- R_runR(Rcmd, opts, c(env, env1), arch = arch)
if (any(grepl("^Error", out)) || length(attr(out, "status"))) {
warningLog(Log)
printLog(Log, paste(c(out, ""), collapse = "\n"))
wrapLog("\nA namespace must be able to be loaded",
"with just the base namespace loaded:",
"otherwise if the namespace gets loaded by a",
"saved object, the session will be unable",
"to start.\n\n",
"Probably some imports need to be declared",
"in the NAMESPACE file.\n")
} else resultLog(Log, "OK")
checkingLog(Log,
"whether the namespace can be unloaded cleanly")
Rcmd <- sprintf("invisible(suppressMessages(loadNamespace(\"%s\"))); cat('\n---- unloading\n'); unloadNamespace(\"%s\")",
pkgname, pkgname)
out <- if (is_base_pkg && pkgname != "stats4")
R_runR(Rcmd, opts, "R_DEFAULT_PACKAGES=NULL", arch = arch)
else R_runR(Rcmd, opts, env1)
if (any(grepl("^(Error|\\.onUnload failed)", out)) ||
length(attr(out, "status"))) {
warningLog(Log)
ll <- grep("---- unloading", out)
if(length(ll)) {
ll <- ll[length(ll)]
out <- out[ll:length(out)]
}
printLog(Log, paste(c(out, ""), collapse = "\n"))
} else resultLog(Log, "OK")
}
## No point in this test if already installed in .Library
if (!pkgname %in% dir(.Library)) {
checkingLog(Log, "loading without being on the library search path")
Rcmd <- sprintf("library(%s, lib.loc = '%s')", pkgname, libdir)
opts <- if(nzchar(arch)) R_opts4 else R_opts2
env <- setRlibs(pkgdir = pkgdir, libdir = libdir, self2 = FALSE)
if(nzchar(arch)) env <- c(env, "R_DEFAULT_PACKAGES=NULL")
out <- R_runR(Rcmd, opts, env, arch = arch)
if (any(grepl("^Error", out))) {
warningLog(Log)
printLog(Log, paste(c(out, ""), collapse = "\n"))
wrapLog("\nIt looks like this package",
"has a loading problem when not on .libPaths:",
"see the messages for details.\n")
} else resultLog(Log, "OK")
}
}
run_examples <- function()
{
run_one_arch <- function(exfile, exout, arch = "")
{
Ropts <- if (nzchar(arch)) R_opts3 else R_opts
if (use_valgrind) Ropts <- paste(Ropts, "-d valgrind")
t1 <- proc.time()
## might be diff-ing results against tests/Examples later
## so force LANGUAGE=en
status <- R_runR(NULL, c(Ropts, enc),
c("LANGUAGE=en", "_R_CHECK_INTERNALS2_=1",
if(nzchar(arch)) env0,
jitstr, elibs),
stdout = exout, stderr = exout,
stdin = exfile, arch = arch)
t2 <- proc.time()
if (status) {
errorLog(Log, "Running examples in ",
sQuote(basename(exfile)),
" failed")
## Try to spot the offending example right away.
txt <- paste(readLines(exout, warn = FALSE),
collapse = "\n")
## Look for the header section anchored by a
## subsequent call to flush(): needs to be kept in
## sync with the code in massageExamples (in
## testing.R). Should perhaps also be more
## defensive about the prompt ...
chunks <- strsplit(txt,
"> ### \\* [^\n]+\n> \n> flush[^\n]+\n> \n", useBytes = TRUE)[[1L]]
if((ll <- length(chunks)) >= 2) {
printLog(Log,
"The error most likely occurred in:\n\n")
printLog0(Log, chunks[ll], "\n")
} else {
## most likely error before the first example
## so show all the output.
printLog(Log, "The error occurred in:\n\n")
printLog0(Log, txt, "\n")
}
return(FALSE)
}
print_time(t1, t2, Log)
## Look at the output from running the examples. For
## the time being, report warnings about use of
## deprecated functions, as the next release will make
## them defunct and hence using them an error.
any <- FALSE
lines <- readLines(exout, warn = FALSE)
bad_lines <- grep("^Warning: .*is deprecated.$", lines,
useBytes = TRUE, value = TRUE)
if(length(bad_lines)) {
any <- TRUE
warningLog(Log, "Found the following significant warnings:\n")
printLog(Log, .format_lines_with_indent(bad_lines), "\n")
wrapLog("Deprecated functions may be defunct as",
"soon as of the next release of R.\n",
"See ?Deprecated.\n")
}
if (!any) resultLog(Log, "OK")
## Try to compare results from running the examples to
## a saved previous version.
exsave <- file.path(pkgdir, "tests", "Examples",
paste0(pkgname, "-Ex.Rout.save"))
if (file.exists(exsave)) {
checkingLog(Log, "differences from ",
sQuote(basename(exout)),
" to ", sQuote(basename(exsave)))
cmd <- paste("invisible(tools::Rdiff('",
exout, "', '", exsave, "',TRUE,TRUE))",
sep = "")
out <- R_runR(cmd, R_opts2)
if(length(out))
printLog0(Log, paste(c("", out, ""), collapse = "\n"))
resultLog(Log, "OK")
}
TRUE
}
checkingLog(Log, "examples")
if (!do_examples) resultLog(Log, "SKIPPED")
else {
pkgtopdir <- file.path(libdir, pkgname)
cmd <- sprintf('tools:::.createExdotR("%s", "%s", silent = TRUE, use_gct = %s, addTiming = %s)', pkgname, pkgtopdir, use_gct, do_timings)
Rout <- tempfile("Rout")
## any arch will do here
status <- R_runR(cmd, R_opts2, "LC_ALL=C",
stdout = Rout, stderr = Rout)
if (status) {
errorLog(Log,
paste("Running massageExamples to create",
sQuote(exfile), "failed"))
printLog(Log, paste(readLines(Rout, warn = FALSE),
collapse = "\n"), "\n")
do_exit(1L)
}
## It ran, but did it create any examples?
exfile <- paste0(pkgname, "-Ex.R")
if (file.exists(exfile)) {
enc <- if (!is.na(e <- desc["Encoding"])) {
if (is_ascii)
warningLog(Log,
paste("checking a package with encoding ",
sQuote(e), " in an ASCII locale\n"))
paste("--encoding", e, sep="=")
} else ""
if (!this_multiarch) {
exout <- paste0(pkgname, "-Ex.Rout")
if(!run_one_arch(exfile, exout)) do_exit(1L)
} else {
printLog(Log, "\n")
Log$stars <<- "**"
res <- TRUE
for (arch in inst_archs) {
printLog(Log, "** running examples for arch ",
sQuote(arch), " ...")
if (arch %in% R_check_skip_examples_arch) {
resultLog(Log, "SKIPPED")
} else {
tdir <- paste0("examples_", arch)
dir.create(tdir)
if (!dir.exists(tdir)) {
errorLog(Log,
"unable to create examples directory")
do_exit(1L)
}
od <- setwd(tdir)
exout <- paste0(pkgname, "-Ex_", arch, ".Rout")
res <- res & run_one_arch(file.path("..", exfile),
file.path("..", exout),
arch)
setwd(od)
}
}
Log$stars <<- "*"
if (!res) do_exit(1L)
}
} else resultLog(Log, "NONE")
}
}
run_tests <- function()
{
if (!extra_arch && !is_base_pkg) {
checkingLog(Log, "for unstated dependencies in tests")
Rcmd <- paste("options(warn=1, showErrorCalls=FALSE)\n",
sprintf("tools:::.check_packages_used_in_tests(\"%s\")\n", pkgdir))
out <- R_runR2(Rcmd, "R_DEFAULT_PACKAGES=NULL")
if (length(out)) {
warningLog(Log)
printLog(Log, paste(c(out, ""), collapse = "\n"))
# wrapLog(msg_DESCRIPTION)
} else resultLog(Log, "OK")
}
checkingLog(Log, "tests")
run_one_arch <- function(arch = "")
{
testsrcdir <- file.path(pkgdir, "tests")
testdir <- file.path(pkgoutdir, "tests")
if(nzchar(arch)) testdir <- paste(testdir, arch, sep = "_")
if(!dir.exists(testdir)) dir.create(testdir, mode = "0755")
if(!dir.exists(testdir)) {
errorLog(Log,
sprintf("unable to create %s", sQuote(testdir)))
do_exit(1L)
}
file.copy(Sys.glob(paste0(testsrcdir, "/*")),
testdir, recursive = TRUE)
setwd(testdir)
extra <- character()
if (use_gct) extra <- c(extra, "use_gct = TRUE")
if (use_valgrind) extra <- c(extra, "use_valgrind = TRUE")
tf <- gsub("\\", "/", tempfile(), fixed=TRUE)
extra <- c(extra, paste0('Log="', tf, '"'))
## might be diff-ing results against tests/*.R.out.save
## so force LANGUAGE=en
cmd <- paste("tools:::.runPackageTestsR(",
paste(extra, collapse=", "),
")", sep = "")
t1 <- proc.time()
status <- R_runR(cmd,
if(nzchar(arch)) R_opts4 else R_opts2,
env = c("LANGUAGE=en",
"_R_CHECK_INTERNALS2_=1",
if(nzchar(arch)) env0,
jitstr, elibs),
stdout = "", stderr = "", arch = arch)
t2 <- proc.time()
if (status) {
errorLog(Log)
## Don't just fail: try to log where the problem occurred.
## First, find the test which failed.
## (Maybe there was an error without a failing test.)
bad_files <- dir(".", pattern="\\.Rout\\.fail")
if (length(bad_files)) {
## Read in output from the (first) failed test
## and retain at most the last 13 lines
## (13? why not?).
file <- bad_files[1L]
lines <- readLines(file, warn = FALSE)
file <- file.path("tests", sub("out\\.fail", "", file))
ll <- length(lines)
lines <- lines[max(1, ll-12):ll]
if (R_check_suppress_RandR_message)
lines <- grep('^Xlib: *extension "RANDR" missing on display',
lines, invert = TRUE, value = TRUE)
printLog(Log, sprintf("Running the tests in %s failed.\n", sQuote(file)))
printLog(Log, "Last 13 lines of output:\n")
printLog0(Log, .format_lines_with_indent(lines), "\n")
}
return(FALSE)
} else {
print_time(t1, t2, Log)
resultLog(Log, "OK")
if (Log$con > 0L && file.exists(tf)) {
## write results only to 00check.log
lines <- readLines(tf, warn = FALSE)
cat(lines, sep="\n", file = Log$con)
unlink(tf)
}
}
setwd(pkgoutdir)
TRUE
}
if (do_install && do_tests) {
if (!this_multiarch) {
res <- run_one_arch()
} else {
printLog(Log, "\n")
res <- TRUE
for (arch in inst_archs)
if (!(arch %in% R_check_skip_tests_arch)) {
printLog(Log, "** running tests for arch ", sQuote(arch))
res <- res & run_one_arch(arch)
}
}
if (!res) do_exit(1L)
} else resultLog(Log, "SKIPPED")
}
run_vignettes <- function(desc)
{
vigns <- pkgVignettes(dir = pkgdir)
if (is.null(vigns) || !length(vigns$docs)) return()
vf <- vigns$docs
if(do_install && !spec_install && !is_base_pkg && !extra_arch) {
## fake installs don't install inst/doc
checkingLog(Log, "for unstated dependencies in vignettes")
Rcmd <- paste("options(warn=1, showErrorCalls=FALSE)\n",
sprintf("tools:::.check_packages_used_in_vignettes(package = \"%s\")\n", pkgname))
out <- R_runR2(Rcmd, "R_DEFAULT_PACKAGES=NULL")
if (length(out)) {
noteLog(Log)
printLog(Log, paste(c(out, ""), collapse = "\n"))
} else resultLog(Log, "OK")
}
checkingLog(Log, "package vignettes in ", sQuote("inst/doc"))
any <- FALSE
## Do PDFs exist for all package vignettes?
## A base source package need not have PDFs to avoid
## frequently-changing binary files in the SVN archive.
if (!is_base_pkg) {
pdfs <- file.path(pkgdir, "inst", "doc",
sub("\\.[[:alpha:]]+$", ".pdf", basename(vf)))
bad_vignettes <- vf[!file.exists(pdfs)]
if(nb <- length(bad_vignettes)) {
any <- TRUE
warningLog(Log)
msg <- ngettext(nb,
"Package vignette( without corresponding PDF:\n",
"Package vignettes without corresponding PDFs:\n", domain = NA)
printLog(Log, msg)
printLog(Log,
paste(c(paste(" ",
sQuote(basename(bad_vignettes))),
"", ""), collapse = "\n"))
}
encs <- vapply(vf, getVignetteEncoding, "")
bad_vignettes <- vf[encs == "non-ASCII"]
if(nb <- length(bad_vignettes)) {
if(!any) warningLog(Log)
any <- TRUE
msg <- ngettext(nb,
"Non-ASCII package vignette without specified encoding:\n",
"Non-ASCII package vignettes without specified encoding:\n", domain = NA)
printLog(Log, " ", msg)
printLog(Log,
paste(c(paste(" ",
sQuote(basename(bad_vignettes))),
"", ""), collapse = "\n"))
}
}
## Do any of the .R files which will be generated
## exist in inst/doc? If so the latter will be ignored,
sources <-
basename(list_files_with_exts(file.path(pkgdir, "inst/doc"), "R"))
if (length(sources)) {
new_sources <- sub("\\.[RrSs](nw|tex)$", ".R", basename(vf))
dups <- sources[sources %in% new_sources]
if(nb <- length(dups)) {
if(!any) warningLog(Log)
any <- TRUE
msg <- ngettext(nb,
"Unused file in 'inst/doc' which is pointless or misleading",
"Unused files in 'inst/doc' which are pointless or misleading", domain = NA)
printLog(Log, " ",
paste(msg,
" as they will be re-created from the vignettes:", "",
sep = "\n"))
printLog(Log,
paste(c(paste(" ", dups), "", ""),
collapse = "\n"))
}
}
## avoid case-insensitive matching
if ("makefile" %in% dir(vigns$dir)) {
if(!any) warningLog(Log)
any <- TRUE
printLog(Log,
" Found 'inst/doc/makefile': should be 'Makefile' and will be ignored\n")
}
if ("Makefile" %in% dir(vigns$dir)) {
lines <- readLines(file.path(vigns$dir, "Makefile"), warn = FALSE)
## remove comment lines
lines <- grep("^[[:space:]]*#", lines, invert = TRUE, value = TRUE)
if(any(grepl("[^/]R +CMD", lines))) {
if(!any) warningLog(Log)
any <- TRUE
printLog(Log,
" Found 'R CMD' in 'inst/doc/Makefile': should be '\"$(R_HOME)/bin/R\" CMD'\n")
}
if(any(grepl("[^/]Rscript", lines))) {
if(!any) warningLog(Log)
any <- TRUE
printLog(Log,
" Found 'Rscript' in 'inst/doc/Makefile': should be '\"$(R_HOME)/bin/Rscript\"'\n")
}
}
## If the vignettes declare an encoding, are they actually in it?
## (We don't check the .tex, though)
bad_vignettes <- character()
for (v in vigns$docs) {
enc <- getVignetteEncoding(v, TRUE)
if (enc %in% c("", "non-ASCII", "unknown")) next
lines <- readLines(v, warn = FALSE) # some miss final NA
lines2 <- iconv(lines, enc, "UTF-16LE", toRaw = TRUE)
if(any(vapply(lines2, is.null, TRUE)))
bad_vignettes <- c(bad_vignettes, v)
if(nb <- length(bad_vignettes)) {
if(!any) warningLog(Log)
any <- TRUE
msg <- ngettext(nb,
"Package vignette which is not in its specified encoding:\n",
"Package vignettes which are not in their specified encoding:\n", domain = NA)
printLog(Log, " ", msg)
printLog(Log,
paste(c(paste(" ",
sQuote(basename(bad_vignettes))),
"", ""), collapse = "\n"))
}
}
if (!any) resultLog(Log, "OK")
if (do_install && do_vignettes) {
## Can we run the code in the vignettes?
## Should checking the vignettes assume the system default
## packages, or just base?
## FIXME: should we do this for multiple sub-archs?
checkingLog(Log, "running R code from vignettes")
vigns <- pkgVignettes(dir = pkgdir)
problems <- list()
res <- character()
cat("\n")
def_enc <- desc["Encoding"]
if( (is.na(def_enc))) def_enc <- ""
t1 <- proc.time()
for(v in vigns$docs) {
enc <- getVignetteEncoding(v, TRUE)
if(enc %in% c("non-ASCII", "unknown")) enc <- def_enc
cat(" ", sQuote(basename(v)),
if(nzchar(enc)) paste("using", sQuote(enc)),
"...")
Rcmd <- paste("options(warn=1)\ntools:::.run_one_vignette('",
basename(v), "', '", vigns$dir, "'",
if (nzchar(enc))
paste0(", encoding = '", enc, "'"),
")", sep = "")
outfile <- paste0(basename(v), ".log")
t1b <- proc.time()
status <- R_runR(Rcmd,
if (use_valgrind) paste(R_opts2, "-d valgrind") else R_opts2,
## add timing as footer, as BATCH does
env = c(jitstr, "R_BATCH=1234", elibs,
"_R_CHECK_INTERNALS2_=1"),
stdout = outfile, stderr = outfile)
t2b <- proc.time()
out <- readLines(outfile, warn = FALSE)
savefile <- sub("\\.[RrSs](nw|tex)$", ".Rout.save", v)
if(length(grep("^ When (tangling|sourcing)", out,
useBytes = TRUE))) {
cat(" failed\n")
res <- c(res,
paste("when running code in", sQuote(basename(v))),
" ...",
utils::tail(out, as.numeric(Sys.getenv("_R_CHECK_VIGNETTES_NLINES_", 10))))
} else if(status || ! " *** Run successfully completed ***" %in% out) {
## (Need not be the final line if running under valgrind)
cat(" failed to complete the test\n")
out <- c(out, "", "... incomplete output. Crash?")
res <- c(res,
paste("when running code in", sQuote(basename(v))),
" ...",
utils::tail(out, as.numeric(Sys.getenv("_R_CHECK_VIGNETTES_NLINES_", 10))))
} else if (file.exists(savefile)) {
cmd <- paste("invisible(tools::Rdiff('",
outfile, "', '", savefile, "',TRUE,TRUE))",
sep = "")
out2 <- R_runR(cmd, R_opts2)
if(length(out2)) {
print_time(t1b, t2b, NULL)
cat(" differences from ", sQuote(basename(savefile)),
"\n", sep = "")
writeLines(c(out2, ""))
} else {
print_time(t1b, t2b, NULL)
cat(" OK\n")
if (!config_val_to_logical(Sys.getenv("_R_CHECK_ALWAYS_LOG_VIGNETTE_OUTPUT_", use_valgrind)))
unlink(outfile)
}
} else {
print_time(t1b, t2b, NULL)
cat(" OK\n")
if (!config_val_to_logical(Sys.getenv("_R_CHECK_ALWAYS_LOG_VIGNETTE_OUTPUT_", use_valgrind)))
unlink(outfile)
}
}
t2 <- proc.time()
print_time(t1, t2, Log)
if (R_check_suppress_RandR_message)
res <- grep('^Xlib: *extension "RANDR" missing on display', res,
invert = TRUE, value = TRUE, useBytes = TRUE)
if(length(res)) {
if(length(grep("there is no package called", res,
useBytes = TRUE))) {
warningLog(Log, "Errors in running code in vignettes:")
printLog0(Log, paste(c(res, "", ""), collapse = "\n"))
} else {
errorLog(Log, "Errors in running code in vignettes:")
printLog0(Log, paste(c(res, "", ""), collapse = "\n"))
do_exit(1L)
}
} else resultLog(Log, "OK")
if (do_rebuild_vignettes &&
parse_description_field(desc, "BuildVignettes", TRUE)) {
checkingLog(Log, "re-building of vignette PDFs")
## copy the whole pkg directory to check directory
## so we can work in place, and allow ../../foo references.
dir.create(vd2 <- "vign_test")
if (!dir.exists(vd2)) {
errorLog(Log, "unable to create 'vign_test'")
do_exit(1L)
}
file.copy(pkgdir, vd2, recursive = TRUE)
## since so many people use 'R CMD' in Makefiles,
oPATH <- Sys.getenv("PATH")
Sys.setenv(PATH = paste(R.home("bin"), oPATH,
sep = .Platform$path.sep))
on.exit(Sys.setenv(PATH = oPATH))
## And too many inst/doc/Makefile are not safe for
## parallel makes
Sys.setenv(MAKEFLAGS="")
## we could use clean = FALSE, but that would not be
## testing what R CMD build uses.
Rcmd <- "options(warn=1)\nlibrary(tools)\n"
Rcmd <- paste(Rcmd, "buildVignettes(dir = '",
file.path(pkgoutdir, "vign_test", pkgname0),
"')", sep = "")
t1 <- proc.time()
outfile <- tempfile()
status <- R_runR(Rcmd, R_opts2, jitstr,
stdout = outfile, stderr = outfile)
t2 <- proc.time()
if (status) {
noteLog(Log)
out <- readLines(outfile, warn = FALSE)
if (R_check_suppress_RandR_message)
out <- grep('^Xlib: *extension "RANDR" missing on display', out,
invert = TRUE, value = TRUE, useBytes = TRUE)
out <- utils::tail(out, 25)
printLog0(Log,
paste(c("Error in re-building vignettes:",
" ...", out, "", ""), collapse = "\n"))
} else {
## clean up
if (config_val_to_logical(Sys.getenv("_R_CHECK_CLEAN_VIGN_TEST_", "true")))
unlink(vd2, recursive = TRUE)
print_time(t1, t2, Log)
resultLog(Log, "OK")
}
} else {
checkingLog(Log, "re-building of vignettes")
resultLog(Log, "SKIPPED")
}
} else {
checkingLog(Log, "running R code from vignettes")
resultLog(Log, "SKIPPED")
checkingLog(Log, "re-building of vignettes")
resultLog(Log, "SKIPPED")
}
}
check_pkg_manual <- function(pkgdir, pkgname)
{
## Run Rd2pdf on the manual, if there are man pages
## If it is installed there is a 'help' dir
## and for a source package, there is a 'man' dir
if (dir.exists(file.path(pkgdir, "help")) ||
dir.exists(file.path(pkgdir, "man"))) {
topdir <- pkgdir
Rd2pdf_opts <- "--batch --no-preview"
checkingLog(Log, "PDF version of manual")
build_dir <- gsub("\\", "/", tempfile("Rd2pdf"), fixed = TRUE)
man_file <- paste0(pkgname, "-manual.pdf ")
## precautionary remove in case some other attempt left it behind
if(file.exists(man_file)) unlink(man_file)
args <- c( "Rd2pdf ", Rd2pdf_opts,
paste0("--build-dir=", shQuote(build_dir)),
"--no-clean", "-o ", man_file , topdir)
res <- run_Rcmd(args, "Rdlatex.log")
latex_log <- file.path(build_dir, "Rd2.log")
if (file.exists(latex_log))
file.copy(latex_log, paste0(pkgname, "-manual.log"))
if (res == 11) { ## return code from Rd2pdf
errorLog(Log, "Rd conversion errors:")
lines <- readLines("Rdlatex.log", warn = FALSE)
lines <- grep("^(Hmm|Execution)", lines,
invert = TRUE, value = TRUE)
printLog0(Log, paste(c(lines, ""), collapse = "\n"))
unlink(build_dir, recursive = TRUE)
do_exit(1L)
} else if (res > 0) {
latex_file <- file.path(build_dir, "Rd2.tex")
if (file.exists(latex_file))
file.copy(latex_file, paste0(pkgname, "-manual.tex"))
warningLog(Log)
printLog(Log,
paste("LaTeX errors when creating PDF version.\n",
"This typically indicates Rd problems.\n",
sep = ""))
## If possible, indicate the problems found.
if (file.exists(latex_log)) {
lines <- .get_LaTeX_errors_from_log_file(latex_log)
printLog(Log, "LaTeX errors found:\n")
printLog0(Log, paste(c(lines, ""), collapse="\n"))
}
unlink(build_dir, recursive = TRUE)
## for Windows' sake: errors can make it unwritable
build_dir <- gsub("\\", "/", tempfile("Rd2pdf"), fixed = TRUE)
checkingLog(Log, "PDF version of manual without hyperrefs or index")
## Also turn off hyperrefs.
Sys.setenv(R_RD4PDF = "times")
args <- c( "Rd2pdf ", Rd2pdf_opts,
paste0("--build-dir=", shQuote(build_dir)),
"--no-clean", "--no-index",
"-o ", man_file, topdir)
if (run_Rcmd(args, "Rdlatex.log")) {
## FIXME: the info is almost certainly in Rdlatex.log
errorLog(Log)
latex_log <- file.path(build_dir, "Rd2.log")
if (file.exists(latex_log))
file.copy(latex_log, paste0(pkgname, "-manual.log"))
else {
## No log file and thus no chance to find out
## what went wrong. Hence, re-run without
## redirecting stdout/stderr and hope that this
## gives the same problem ...
# printLog(Log, "Error when running command:\n")
# cmd <- paste(c("R CMD", args), collapse = " ")
# printLog(Log, strwrap(cmd, indent = 2, exdent = 4), "\n")
printLog(Log, "Re-running with no redirection of stdout/stderr.\n")
unlink(build_dir, recursive = TRUE)
build_dir <- gsub("\\", "/", tempfile("Rd2pdf"), fixed = TRUE)
args <- c( "Rd2pdf ", Rd2pdf_opts,
paste0("--build-dir=", shQuote(build_dir)),
"--no-clean", "--no-index",
"-o ", paste0(pkgname, "-manual.pdf "),
topdir)
run_Rcmd(args)
}
unlink(build_dir, recursive = TRUE)
do_exit(1L)
} else {
unlink(build_dir, recursive = TRUE)
resultLog(Log, "OK")
}
} else {
unlink(build_dir, recursive = TRUE)
resultLog(Log, "OK")
}
}
}
check_executables <- function()
{
owd <- setwd(pkgdir)
allfiles <- dir(".", all.files = TRUE, full.names = TRUE,
recursive = TRUE)
allfiles <- sub("^./","", allfiles)
## this is tailored to the FreeBSD/Linux 'file',
## see http://www.darwinsys.com/file/
## (Solaris has a different 'file' without --version)
## Most systems are now on 5.03/7, but Mac OS 10.5 is 4.17
## version 4.21 writes to stdout,
## 4.23 to stderr and sets an error status code
lines <- suppressWarnings(tryCatch(system2("file", "--version", TRUE, TRUE), error = function(e) "error"))
## a reasonable check -- it does not identify itself well
have_free_file <-
any(grepl("^(file-[45]|magic file from)", lines))
if (have_free_file) {
checkingLog(Log, "for executable files")
## Watch out for spaces in file names here
## Do in parallel for speed on Windows, but in batches
## since there may be a line-length limit.
execs <- character()
files <- allfiles
while(ll <- length(files)) {
chunk <- seq_len(min(100, ll))
these <- files[chunk]
files <- files[-chunk]
lines <- suppressWarnings(system2("file", shQuote(these), TRUE, TRUE))
## avoid match to is_executable.Rd
ex <- grepl(" executable", lines, useBytes=TRUE)
ex2 <- grepl("script", lines, useBytes=TRUE) &
grepl("text", lines, useBytes=TRUE)
execs <- c(execs, lines[ex & !ex2])
}
if(length(execs)) {
execs <- sub(":[[:space:]].*$", "", execs, useBytes = TRUE)
known <- rep(FALSE, length(execs))
pexecs <- file.path(pkgname, execs)
## known false positives
for(fp in c("foreign/tests/datefactor.dta",
"msProcess/inst/data[12]/.*.txt",
"WMBrukerParser/inst/Examples/C3ValidationExtractSmall/RobotRun1/2-100kDa/0_B1/1/1SLin/fid") )
known <- known | grepl(fp, pexecs)
execs <- execs[!known]
}
} else {
## no 'file', so just check extensions
checkingLog(Log, "for .dll and .exe files")
execs <- grep("\\.(exe|dll)$", allfiles, value = TRUE)
}
if (R_check_executables_exclusions && file.exists("BinaryFiles")) {
excludes <- readLines("BinaryFiles")
execs <- execs[!execs %in% excludes]
}
if (grepl("^check", install) && file.exists(".install_timestamp"))
execs <- execs[file_test("-ot", execs, ".install_timestamp")]
if (nb <- length(execs)) {
msg <- ngettext(nb,
"Found the following executable file:",
"Found the following executable files:",
domain = NA)
warningLog(Log, msg)
printLog(Log, .format_lines_with_indent(execs), "\n")
wrapLog("Source packages should not contain undeclared executable files.\n",
"See section 'Package structure'",
"in the 'Writing R Extensions' manual.\n")
} else resultLog(Log, "OK")
setwd(owd)
}
## CRAN-pack knows about
.hidden_file_exclusions <-
c(".Renviron", ".Rprofile", ".Rproj.user",
".Rhistory", ".Rapp.history",
".tex", ".log", ".aux", ".pdf", ".png",
".backups", ".cvsignore", ".cproject", ".directory",
".dropbox", ".exrc", ".gdb.history",
".gitattributes", ".gitignore", ".gitmodules",
".hgignore", ".hgtags",
".project", ".seed", ".settings", ".tm_properties")
check_dot_files <- function(cran = FALSE)
{
checkingLog(Log, "for hidden files and directories")
owd <- setwd(pkgdir)
dots <- dir(".", all.files = TRUE, full.names = TRUE,
recursive = TRUE, pattern = "^[.]")
dots <- sub("^./","", dots)
allowed <-
c(".Rbuildignore", ".Rinstignore", "vignettes/.install_extras",
".install_timestamp") # Kurt uses this
dots <- dots[!dots %in% allowed]
alldirs <- list.dirs(".", full.names = TRUE, recursive = TRUE)
alldirs <- sub("^./","", alldirs)
alldirs <- alldirs[alldirs != "."]
bases <- basename(alldirs)
dots <- c(dots, alldirs[grepl("^[.]", bases)])
if (length(dots)) {
noteLog(Log, "Found the following hidden files and directories:")
printLog(Log, .format_lines_with_indent(dots), "\n")
wrapLog("These were most likely included in error.",
"See section 'Package structure'",
"in the 'Writing R Extensions' manual.\n")
if(cran) {
known <- basename(dots) %in% .hidden_file_exclusions
known <- known | grepl("^.Rbuildindex[.]", dots) |
grepl("inst/doc/[.](Rinstignore|build[.]timestamp)$", dots) |
grepl("vignettes/[.]Rinstignore$", dots) |
grepl("^src.*/[.]deps$", dots)
if (all(known))
printLog(Log, "\nCRAN-pack knows about all of these\n")
else if (any(!known)) {
printLog(Log, "\nCRAN-pack does not know about\n")
printLog(Log, .format_lines_with_indent(dots[!known]), "\n")
}
}
} else resultLog(Log, "OK")
setwd(owd)
}
check_install <- function()
{
## Option '--no-install' turns off installation and the tests
## which require the package to be installed. When testing
## recommended packages bundled with R we can skip
## installation, and do so if '--install=skip' was given. If
## command line option '--install' is of the form
## 'check:FILE', it is assumed that installation was already
## performed with stdout/stderr redirected to FILE, the
## contents of which need to be checked (without repeating the
## installation). In this case, one also needs to specify
## *where* the package was installed to using command line
## option '--library'.
if (install == "skip")
messageLog(Log, "skipping installation test")
else {
use_install_log <-
(grepl("^check", install) || R_check_use_install_log
|| !isatty(stdout()))
INSTALL_opts <- install_args
## don't use HTML, checkRd goes over the same ground.
INSTALL_opts <- c(INSTALL_opts, "--no-html")
if (install == "fake")
INSTALL_opts <- c(INSTALL_opts, "--fake")
else if (!multiarch)
INSTALL_opts <- c(INSTALL_opts, "--no-multiarch")
INSTALL_opts <- paste(INSTALL_opts, collapse = " ")
args <- c("INSTALL", "-l", shQuote(libdir), INSTALL_opts,
shQuote(if (WINDOWS) shortPathName(pkgdir) else pkgdir))
if (!use_install_log) {
## Case A: No redirection of stdout/stderr from installation.
## This is very rare: needs _R_CHECK_USE_INSTALL_LOG_ set
## to false.
message("")
## Rare use of R CMD INSTALL
if (run_Rcmd(args)) {
errorLog(Log, "Installation failed.")
do_exit(1L)
}
message("")
} else {
## Case B. All output from installation redirected,
## or already available in the log file.
checkingLog(Log,
"whether package ",
sQuote(desc["Package"]),
" can be installed")
outfile <- file.path(pkgoutdir, "00install.out")
if (grepl("^check", install)) {
if (!nzchar(arg_libdir))
printLog(Log, "\nWarning: --install=check... specified without --library\n")
thislog <- substr(install, 7L, 1000L)
#owd <- setwd(startdir)
if (!file.exists(thislog)) {
errorLog(Log,
sprintf("install log %s does not exist", sQuote(thislog)))
do_exit(2L)
}
file.copy(thislog, outfile)
#setwd(owd)
install <- "check"
lines <- readLines(outfile, warn = FALSE)
## <NOTE>
## We used to have
## $install_error = ($lines[$#lines] !~ /^\* DONE/);
## but what if there is output from do_cleanup
## in (Unix) R CMD INSTALL?
## </NOTE>
install_error <- !any(grepl("^\\* DONE", lines))
} else {
## record in the log what options were used
cat("* install options ", sQuote(INSTALL_opts),
"\n\n", sep = "", file = outfile)
env <- ""
## Normal use of R CMD INSTALL
t1 <- proc.time()
install_error <- run_Rcmd(args, outfile)
t2 <- proc.time()
print_time(t1, t2, Log)
lines <- readLines(outfile, warn = FALSE)
}
if (install_error) {
errorLog(Log, "Installation failed.")
printLog(Log, "See ", sQuote(outfile),
" for details.\n")
do_exit(1L)
}
## There could still be some important warnings that
## we'd like to report. For the time being, start
## with compiler warnings about non ISO C code (or
## at least, what looks like it), and also include
## warnings resulting from the const char * CHAR()
## change in R 2.6.0. (In theory, we should only do
## this when using GCC ...)
if (install != "check")
lines <- readLines(outfile, warn = FALSE)
lines0 <- lines
warn_re <- c("^WARNING:",
"^Warning:",
## <FIXME>
## New style Rd conversion
## which may even show errors:
"^Rd (warning|error): ",
## </FIXME>
": warning: .*ISO C",
": warning: .* discards qualifiers from pointer target type",
": warning: .* is used uninitialized",
": warning: .* set but not used",
": warning: unused",
# these are from era of static HTML
"missing links?:")
## Warnings spotted by gcc with
## '-Wimplicit-function-declaration', which is
## implied by '-Wall'. Currently only accessible
## via an internal environment variable.
check_src_flag <- Sys.getenv("_R_CHECK_SRC_MINUS_W_IMPLICIT_", "FALSE")
## (Not quite perfect, as the name should really
## include 'IMPLICIT_FUNCTION_DECLARATION'.)
if (config_val_to_logical(check_src_flag)) {
warn_re <- c(warn_re,
": warning: implicit declaration of function",
": warning: incompatible implicit declaration of built-in function")
}
warn_re <- paste("(",
paste(warn_re, collapse = "|"),
")", sep = "")
lines <- grep(warn_re, lines, value = TRUE, useBytes = TRUE)