#! /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: ***