#! /bin/bash
##
## Maybe change --no-lock to e.g. --pkglock?
##
## Fully Qualified Domain Name of the system we use for checking.
FQDN=`hostname -f`
## Default flavor to use.
R_flavor=r-devel
## Location of the CRAN mirror root on the local file system.
case ${FQDN} in
aragorn.ci.tuwien.ac.at)
CRAN_rsync=/usr/local/src/apps/stat/R ;;
*.wu.ac.at)
CRAN_rsync=/srv/R/Repositories/CRAN ;;
*)
CRAN_rsync=/data/Repositories/CRAN ;;
esac
## Location gof CRAN's src/contrib on the local file system.
CRAN_dir=${CRAN_rsync}/src/contrib
## Default check args.
check_args_defaults=
## case ${FQDN} in
## xmgyges.wu.ac.at)
## check_args_defaults="--no-vignettes"
## ;;
## esac
## Where everything happens.
check_dir=~/tmp/R.check
## Check date in ISO 8601 format. GNU specific ...
check_date=`date -Idate`
check_results_mail_recipients="Kurt.Hornik@wu.ac.at"
case ${FQDN} in
gimli.wu.ac.at)
check_results_mail_recipients="Kurt.Hornik@wu.ac.at maechler@stat.math.ethz.ch Uwe.Ligges@R-project.org"
;;
esac
check_results_files="SUMMARY check.csv time_c.out time_i.out"
## Compilers to use.
## Use configure defaults (gcc/g++/gfortran).
compilers=
## Note that (in particular to achieve additional C++ strictness) we
## prefer to run checks with
## CFLAGS = -g -O2 -Wall -pedantic
## CXXFLAGS = -g -O2 -Wall -pedantic
## but use machine-local ~/.R/Makevars for setting this.
## Additional command line arguments to configure.
configure_args="--with-blas=no --enable-R-shlib LIBnn=lib"
## R scripts directory.
R_scripts_dir=~/lib/R/Scripts
## R profile for checking.
R_profile=${R_scripts_dir}/check_profile.R
## Shell scripts directory.
sh_scripts_dir=~/lib/bash
## Command line args.
while test -n "${1}"; do
case "${1}" in
-r)
R_flavor=r-release ;;
-p)
R_flavor=r-patched ;;
-v)
case "${2}" in
4.*)
compilers="CC=gcc-${2} CXX=g++-${2} F77=gfortran-${2}
FC=gfortran-${2} OBJC=gcc-${2} OBJCXX=gcc-${2}"
;;
3.*)
compilers="CC=gcc-${2} CXX=g++-${2} F77=g77-${2}"
;;
esac
shift ;;
esac
shift
done
## Start a virtual framebuffer X server and use this for DISPLAY so that
## we can run package tcltk and friends. We use the PID of the check
## process as the server number so that the checks for different flavors
## get different servers.
PATH=${PATH}:/usr/bin/X11
get_Xvfb_pid () {
ps auxw | grep "Xvfb :${$}" | grep -v grep | awk '{ print $2 }'
}
start_Xvfb () {
Xvfb :${$} -screen 0 1280x1024x24 &
pid=`get_Xvfb_pid`
echo ${pid} > ${check_dir}/${R_flavor}/Xvfb.pid
}
start_Xvfb_if_necessary () {
pid=`get_Xvfb_pid`
test -z "${pid}" && start_Xvfb
}
export DISPLAY=:${$}
if test -n "${BASH_VERSION}"; then
## No process is allowed more than 10 minutes
ulimit -t 600
fi
export R_BROWSER=false
export R_PDFVIEWER=false
export _R_SHLIB_BUILD_OBJECTS_SYMBOL_TABLES_=true
export _R_CHECK_CODETOOLS_PROFILE_="suppressPartialMatchArgs=false"
export _R_CHECK_FORCE_SUGGESTS_=false
export _R_CHECK_INSTALL_DEPENDS_=true
export _R_CHECK_SUGGESTS_ONLY_=true
export _R_CHECK_NO_RECOMMENDED_=true
export _R_CHECK_SUPPRESS_RANDR_MESSAGE_=true
# export _R_CHECK_WEAVE_VIGNETTES_=no
## And we definitely do not want package-local overrides:
export _R_CHECK_EXECUTABLES_EXCLUSIONS_=false
## And we want license checking ...
export _R_CHECK_LICENSE_=true
## And the additional timings added in R 2.15.0 ...
export _R_CHECK_TIMINGS_=0
export _R_CHECK_TEST_TIMING_=yes
export _R_CHECK_VIGNETTE_TIMING_=yes
## Try using a UTF-8 locale.
export LANG="en_US.UTF-8"
## But not for sorting ...
export LC_COLLATE=C
export R_GC_NGROWINCRFRAC=0.3
export R_GC_VGROWINCRFRAC=0.3
## Avoid hyperref problems with paper size 'letter'.
export R_PAPERSIZE=a4
do_cleanup_and_exit () {
lamwipe -sessionsuffix ${R_flavor} || true
rm -f ${check_dir}/${R_flavor}/check.pid
exit ${1-0}
}
test -d ${check_dir} || mkdir ${check_dir} || do_cleanup_and_exit 1
cd ${check_dir}
## Structure inside $check_dir: subdirs for each flavor. Within these,
## most of the work happens in 'Work', inside which R sources are in
## 'src', R is built in 'build', and packages are in 'PKGS'. When done,
## 'PKGS' is moved up for mirroring, and results are saved in 'Results'.
test -d ${R_flavor} || mkdir ${R_flavor} || do_cleanup_and_exit 1
cd ${R_flavor}
## If there is an old Xvfb/check process remaining, kill it:
test -f Xvfb.pid && kill -9 `cat Xvfb.pid`
test -f check.pid && kill -9 `cat check.pid`
## Start Xvfb.
start_Xvfb
echo ${$} > check.pid
test -d Work || mkdir Work || do_cleanup_and_exit 1
cd Work
## Update ${R_flavor} sources.
## Actually, we should check whether flavor of source and target agree.
test -d src || mkdir src || do_cleanup_and_exit 1
if test "`hostname`" = mithrandir; then
(cd ~/src/apps/R/${R_flavor}/R; tar cf - . ) | ( cd src; tar xf -)
else
## Argh, rsync is gone (at least for the time being ...).
## We could of course use svn checkout on https://svn.R-project.org/R,
## but how can one get "r-patched" and "r-release" without knowing the
## corresponding branch? Hence, we get things from CRAN (release) or
## ETHZ, but need to figure out the top-level source dir for the
## unpackaged version somehow (of course, we could also read this from
## the archive).
## (cd src; rsync -rC -t --delete rsync.r-project.org::${R_flavor} .)
##
## Maybe we should use svn checkout for r-devel?
##
case "${R_flavor}" in
r-devel)
url=ftp://ftp.stat.math.ethz.ch/Software/R/R-devel.tar.gz ;;
r-patched)
##
## Adjust as needed ...
url=ftp://ftp.stat.math.ethz.ch/Software/R/R-patched.tar.gz
## url=http://cran.at.r-project.org/src/base-prerelease/R-latest.tar.gz
##
;;
r-release)
url=http://cran.r-project.org/src/base/R-latest.tar.gz ;;
esac
mv src src.save
(mkdir tmp &&
cd tmp &&
touch stamp &&
wget -O - --retr-symlinks ${url} | tar zxmf - &&
entry=`find . -newer stamp -type d -mindepth 1 -maxdepth 1` &&
mv ${entry} ../src &&
cd .. &&
rm -rf src.save tmp) || (rm -rf tmp; mv src.save src)
fi
R_VERSION=`cut -f1 -d' ' src/VERSION`
if test "`cut -f2 -d' ' src/VERSION`" = "Patched"; then
R_VERSION=`echo ${R_VERSION} | sed 's/\.[0-9]*$//'`
R_VERSION="${R_VERSION}-patched"
fi
## Link recommended packages.
(cd src; \
CRAN_RSYNC="${CRAN_rsync}" ./tools/rsync-recommended)
## Rebuild R.
rm -rf build
mkdir build
(cd build && ../src/configure ${configure_args} ${compilers} \
&& make && make check && make pdf) || do_cleanup_and_exit 1
(cd build/doc/manual && make fullrefman.pdf) || do_cleanup_and_exit 1
mkdir build/Packages
R_HOME=`./build/bin/R RHOME`
R_exe="${R_HOME}/bin/R"
R_base_pkgs=
if test -f src/share/make/vars.mk; then
R_base_pkgs=`grep '^R_PKGS_BASE *=' src/share/make/vars.mk | \
sed 's/.*=//'`
fi
## Packages.
rm -rf PKGS # In case there are some leftovers ...
mkdir PKGS
cd PKGS
## Add check profile settings.
##
## This should work from 2.7.0 onwards ...
export R_PROFILE_USER=${R_profile}
##
## Old style:
## if test -r "${R_profile}"; then
## (echo; cat "${R_profile}") > .Rprofile
## fi
all_pkgs=
dir=${CRAN_dir}/${R_VERSION}/Recommended
if test -d ${dir}; then
for f in ${dir}/*.tar.gz; do tar zxf ${f}; done
CRAN_vspec_recommended_pkgs=`cd ${dir}; ls *.tar.gz | sed 's/_.*//'`
all_pkgs="${all_pkgs} ${CRAN_vspec_recommended_pkgs}"
fi
dir=${CRAN_dir}/${R_VERSION}/Other
if test -d ${dir}; then
for f in ${dir}/*.tar.gz; do tar zxf ${f}; done
CRAN_vspec_other_pkgs=`cd ${dir}; ls *.tar.gz | sed 's/_.*//'`
all_pkgs="${all_pkgs} ${CRAN_vspec_other_pkgs}"
fi
CRAN_main_pkgs=`cd ${CRAN_dir}; ls *.tar.gz`
for p in ${R_base_pkgs} \
${CRAN_vspec_recommended_pkgs} \
${CRAN_vspec_other_pkgs}; do
CRAN_main_pkgs=`echo "${CRAN_main_pkgs}" | sed "s/^${p}_.*//"`
done
for p in ${CRAN_main_pkgs}; do tar zxf ${CRAN_dir}/${p}; done
CRAN_main_pkgs=`echo "${CRAN_main_pkgs}" | sed 's/_.*//'`
all_pkgs="${all_pkgs} ${CRAN_main_pkgs}"
all_pkgs=`echo "${all_pkgs}" | tr ' ' '\n' | sort | uniq`
. ${sh_scripts_dir}/check_R_stoplists.sh
## For R 2.0.0 or better, we can compute an install order, but only for
## the CRAN packages in the main area. This is a *BUG*, and hopefully
## will be fixed soon. For the time being, the best we can do is to try
## installing the version specific packages first ...
##
## What is the problem?
##
CRAN_vspec_other_pkgs_install_yes=`echo "${CRAN_vspec_other_pkgs}" \
| egrep -v "${pkgs_install_fake_regexp}" \
| egrep -v "${pkgs_install_no_regexp}"`
CRAN_main_pkgs_install_yes=`echo "${CRAN_main_pkgs}" \
| egrep -v "${pkgs_install_fake_regexp}" \
| egrep -v "${pkgs_install_no_regexp}"`
pkgs_install_yes="${CRAN_vspec_recommended_pkgs} ${CRAN_vspec_other_pkgs_install_yes} ${CRAN_main_pkgs_install_yes}"
pkgs_install_fake=`echo "${all_pkgs}" | egrep "${pkgs_install_fake_regexp}"`
pkgs_install_no=`echo "${all_pkgs}" | egrep "${pkgs_install_no_regexp}"`
## Installation first ...
## Note that installing to the default library tree updates the HTML
## indices, which is very time consuming (as we install one package at a
## time to safeguard against limits on the size of the command line).
## Hence, we install the packages to a different library tree
## (${R_HOME}/Packages).
## Determine what needs to be installed, and the right order for this.
echo ${pkgs_install_yes} > pkgs_install_yes_by_names.dat
echo ${pkgs_install_fake} > pkgs_install_fake_by_names.dat
## The code below assumes that fake installs and their reverse depends
## are set up correctly. We could use R code for improving this, but
## then we would need to feed things back to the shell db with the check
## flags ...
## Hence, we first compute everything needed for installing the packages
## to be fully installed, and then what is *additionally* needed for the
## fake installs (which might include non-CRAN depends).
${R_exe} --vanilla --slave <<-EOF
source("${R_scripts_dir}/check.R")
dir <- file_path_as_absolute(getwd())
write_PACKAGES(dir, unpacked = TRUE)
available <- available_packages_in_local_repositories(dir)
p_yes <- scan("pkgs_install_yes_by_names.dat", character(),
quiet = TRUE)
o_yes <- find_install_order(p_yes, dir, available)
writeLines(o_yes[["out"]], "pkgs_install_yes_by_order.dat")
writeLines(o_yes[["bad"]], "pkgs_install_yes_uninstallable.dat")
p_fake <- scan("pkgs_install_fake_by_names.dat", character(),
quiet = TRUE)
o_fake <- find_install_order(p_fake, dir, available, FALSE)
writeLines(setdiff(o_fake[["out"]], o_yes[["out"]]),
"pkgs_install_fake_by_order.dat")
writeLines(o_fake[["bad"]], "pkgs_install_fake_uninstallable.dat")
EOF
pkgs_install_yes=`cat pkgs_install_yes_by_order.dat`
## Create install time stamps first.
for p in ${pkgs_install_yes}; do
case "${p}" in
/*) continue ;;
esac
touch ${p}/.install_timestamp
done
for p in ${pkgs_install_yes}; do
start_Xvfb_if_necessary
pname=`basename "${p}" | sed 's/_.*//'`
echo -n "${pname}: " >> ../time_i.out
/usr/bin/time -o ../time_i.out -a \
env R_LIBS="${R_HOME}/Packages" \
${R_exe} CMD INSTALL --no-lock ${p} >${pname}-install.out 2>&1
done
##
## Need to actually provide fake installs (otherwise, dependencies
## cannot be honored). Checking with --install=fake fake-installs
## again, which could perhaps be eliminated.
pkgs_install_fake=`cat pkgs_install_fake_by_order.dat`
## Create install time stamps first.
for p in ${pkgs_install_fake}; do
case "${p}" in
/*) continue ;;
esac
touch ${p}/.install_timestamp
done
for p in ${pkgs_install_fake}; do
pname=`basename "${p}" | sed 's/_.*//'`
echo -n "${pname}: " >> ../time_i.out
/usr/bin/time -o ../time_i.out -a \
env R_LIBS="${R_HOME}/Packages" \
${R_exe} CMD INSTALL --no-lock --fake ${p} >${pname}-install.out 2>&1
done
##
## And now the testing ...
## Start/customize distributed computing environments.
## Running Rmpi calls lamboot so that at the end we should clean up by
## calling lamwipe. Of course, the LAM RTE should be check process
## specific, which can be accomplished via LAM_MPI_SESSION_SUFFIX.
export LAM_MPI_SESSION_SUFFIX=${R_flavor}
##
## Could also try eliminating the non local packages which are not to be
## checked by something like
## pkgs_install_yes=`echo "${pkgs_install_yes}" | sed '/\/.*/d'`
## And similarly for pkgs_install_fake.
##
for p in ${pkgs_install_yes}; do
case "${p}" in
/*) continue ;;
esac
start_Xvfb_if_necessary
args=`get_check_args ${p}`
echo -n "${p}: " >> ../time_c.out
/usr/bin/time -o ../time_c.out -a \
env R_LIBS="${R_HOME}/Packages" \
${R_exe} CMD check \
--install="check:${p}-install.out" \
--library="${R_HOME}/Packages" \
${args} ${check_args_defaults} ${p}
## if test -n "${args}"; then
## echo "* using check arguments '${args}'" \
## >> ${p}.Rcheck/00check.log
## fi
rm -f ${p}-install.out
done
for p in ${pkgs_install_fake}; do
case "${p}" in
/*) continue ;;
esac
start_Xvfb_if_necessary
echo -n "${p}: " >> ../time_c.out
/usr/bin/time -o ../time_c.out -a \
env R_LIBS="${R_HOME}/Packages" \
${R_exe} CMD check --install=fake ${p}
## echo "* using check arguments '--install=fake'" \
## >> ${p}.Rcheck/00check.log
done
for p in ${pkgs_install_no}; do
start_Xvfb_if_necessary
echo -n "${p}: " >> ../time_c.out
/usr/bin/time -o ../time_c.out -a \
env R_LIBS="${R_HOME}/Packages" \
${R_exe} CMD check --install=no ${p}
## echo "* using check arguments '--install=no'" \
## >> ${p}.Rcheck/00check.log
done
## ... and copy the package/bundle DESCRIPTION metadata over to the
## directories with the check results.
for d in *.Rcheck; do
/usr/bin/install -m 644 `basename ${d} .Rcheck`/DESCRIPTION \
${d}/00package.dcf
done
## Summary and check db
get_dcf_field () {
## Get one field including all continuation lines from a DCF file.
## Usage:
## get_dcf_field FIELD FILE
ws="[ ]" # space and tab
(sed -n "/^${1}:/,/^[^ ]/{p;}" ${2} | \
sed -n "/^${1}:/{s/^${1}:${ws}*//;p;}
/^${ws}/{s/^${ws}*//;p;}") |
sed "s/[
]*$//"
}
## Summaries.
cd ${check_dir}/${R_flavor}
## Save old check results.
for f in ${check_results_files}; do
test -f "${f}.prev" && rm -f "${f}.prev"
test -f "${f}" && mv "${f}" "${f}.prev"
done
for d in Results Results/${check_date}; do
test -d ${d} || mkdir ${d} || do_cleanup_and_exit 1
done
test -d PKGS.prev && rm -rf PKGS.prev
test -d PKGS && mv PKGS PKGS.prev
mv Work/PKGS PKGS
## Move timings up.
mv Work/time_c.out time_c.out
mv Work/time_i.out time_i.out
cd ${check_dir}/${R_flavor}/PKGS
## Create check.csv.
(echo "Package,Version,Priority,Maintainer,Status,Comment"
for d in *.Rcheck; do
package=`basename ${d} .Rcheck`
version=`get_dcf_field Version ${package}/DESCRIPTION | head -1`
priority=`get_dcf_field Priority ${package}/DESCRIPTION | head -1`
maintainer=`get_dcf_field Maintainer ${package}/DESCRIPTION | \
head -1 | sed 's/\\"/""/g'`
warnings=`grep 'WARNING$' ${d}/00check.log`
errors=`grep 'ERROR$' ${d}/00check.log`
if test -n "${errors}"; then
status=ERROR
elif test -n "${warnings}"; then
status=WARN
else
status=OK
fi
args=`get_check_args ${package}`
if test -n "${args}"; then args="[${args}]"; fi
echo "${package},${version},${priority},\"${maintainer}\",${status},${args}"
done) | sed 's/
/ /' > ../check.csv
## Create SUMMARY.
(for d in *.Rcheck; do
package=`basename ${d} .Rcheck`
if test "${R_flavor}" = r-release; then
problems=`egrep -e 'ERROR$' ${d}/00check.log`
else
problems=`egrep -e \
'(^\* Rd files|^\* non-standard|(WARNING|ERROR)$)' \
${d}/00check.log`
fi
if test -n "${problems}"; then
echo "${package}"
egrep -e '^Maintainer:' "${package}/DESCRIPTION"
echo "${problems}"
fi
done) > ../SUMMARY
cd ${check_dir}/${R_flavor}
## Save result summaries.
for f in ${check_results_files}; do
cp "${f}" "Results/${check_date}"
done
## And notify of differences ...
for f in check.csv; do
if test -f "${f}.prev"; then
diff "${f}.prev" "${f}" > "${f}.diff"
test -s "${f}.diff" || rm -f "${f}.diff"
fi
if test -f "${f}.diff"; then
${R_exe} --vanilla --slave <<-EOF
source("${R_scripts_dir}/check.R")
db <- check_results_diffs(file.path("${check_dir}", "${R_flavor}"))
sink("${f}.diff")
writeLines(c("Changes in check status (S) and/or version (V)",
do.call(sprintf,
c(list("using R %s.%s (%s-%s-%s r%s):\n"),
R.version[c("major", "minor", "year",
"month", "day", "svn rev")]))))
print(db)
sink()
EOF
env REPLYTO=Kurt.Hornik@R-project.org \
mail -s "[CRAN-check] ${R_flavor}/`hostname` ${f} changes on `date -Iseconds`" \
${check_results_mail_recipients} < "${f}.diff"
rm -f "${f}.diff"
fi
done
## Manuals
test -d Manuals.prev && rm -rf Manuals.prev
test -d Manuals && mv Manuals Manuals.prev
mkdir Manuals
cp Work/build/doc/manual/*.html Manuals
cp Work/build/doc/manual/*.pdf Manuals
do_cleanup_and_exit
### Local Variables: ***
### mode: sh ***
### sh-basic-offset: 2 ***
### End: ***