diff --git a/Jenkinsfile b/Jenkinsfile
index 4f293248..14ee607a 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -34,6 +34,10 @@ pipeline {
             defaultValue: true,
             description: 'Attempt a build with docs in this run? (Note: corresponding tools are required in the build environment)',
             name: 'DO_BUILD_DOCS')
+        booleanParam (
+            defaultValue: false,
+            description: 'Publish as an archive a "dist" tarball from a build with docs in this run? (Note: corresponding tools are required in the build environment; enabling this enforces DO_BUILD_DOCS too)',
+            name: 'DO_DIST_DOCS')
         booleanParam (
             defaultValue: true,
             description: 'Attempt "make check" in this run?',
@@ -62,6 +66,10 @@ pipeline {
             defaultValue: true,
             description: 'Require that there are no files not discovered changed/untracked via .gitignore after builds and tests?',
             name: 'REQUIRE_GOOD_GITIGNORE')
+        string (
+            defaultValue: "30",
+            description: 'When running tests, use this timeout (in minutes; be sure to leave enough for double-job of a distcheck too)',
+            name: 'USE_TEST_TIMEOUT')
         booleanParam (
             defaultValue: true,
             description: 'When using temporary subdirs in build/test workspaces, wipe them after successful builds?',
@@ -73,20 +81,11 @@ pipeline {
 // Note: your Jenkins setup may benefit from similar setup on side of agents:
 //        PATH="/usr/lib64/ccache:/usr/lib/ccache:/usr/bin:/bin:${PATH}"
     stages {
-        stage ('cppcheck') {
-                    when { expression { return ( params.DO_CPPCHECK ) } }
-                    steps {
-                        dir("tmp") {
-                            deleteDir()
-                        }
-                        sh 'cppcheck --std=c++11 --enable=all --inconclusive --xml --xml-version=2 . 2>cppcheck.xml'
-                        archiveArtifacts artifacts: '**/cppcheck.xml'
-                        sh 'rm -f cppcheck.xml'
-                    }
-        }
         stage ('prepare') {
                     steps {
                         dir("tmp") {
+                            sh 'if [ -s Makefile ]; then make -k distclean || true ; fi'
+                            sh 'chmod -R u+w .'
                             deleteDir()
                         }
                         sh './autogen.sh'
@@ -132,12 +131,19 @@ pipeline {
                     }
                 }
                 stage ('build with DOCS') {
-                    when { expression { return ( params.DO_BUILD_DOCS ) } }
+                    when { expression { return ( params.DO_BUILD_DOCS || params.DO_DIST_DOCS ) } }
                     steps {
                       dir("tmp/build-DOCS") {
                         deleteDir()
                         unstash 'prepped'
                         sh 'CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; ./configure --enable-drafts=yes --with-docs=yes'
+                        script {
+                            if ( params.DO_DIST_DOCS ) {
+                                sh 'CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; make dist-gzip || exit ; DISTFILE="`ls -1tc *.tar.gz | head -1`" && [ -n "$DISTFILE" ] && [ -s "$DISTFILE" ] || exit ; mv -f "$DISTFILE" __dist.tar.gz'
+                                archiveArtifacts artifacts: '__dist.tar.gz'
+                                sh "rm -f __dist.tar.gz"
+                            }
+                        }
                         sh 'CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; make -k -j4 || make'
                         sh 'echo "Are GitIgnores good after make with docs? (should have no output below)"; git status -s || if [ "${params.REQUIRE_GOOD_GITIGNORE}" = false ]; then echo "WARNING GitIgnore tests found newly changed or untracked files" >&2 ; exit 0 ; else echo "FAILED GitIgnore tests" >&2 ; exit 1; fi'
                         stash (name: 'built-docs', includes: '**/*', excludes: '**/cppcheck.xml')
@@ -153,14 +159,42 @@ pipeline {
         }
         stage ('check') {
             parallel {
+                stage ('cppcheck') {
+                    when { expression { return ( params.DO_CPPCHECK ) } }
+                    steps {
+                        dir("tmp/test-cppcheck") {
+                            deleteDir()
+                            unstash 'prepped'
+                            sh 'cppcheck --std=c++11 --enable=all --inconclusive --xml --xml-version=2 . 2>cppcheck.xml'
+                            archiveArtifacts artifacts: '**/cppcheck.xml'
+                            sh 'rm -f cppcheck.xml'
+                            script {
+                                if ( params.DO_CLEANUP_AFTER_BUILD ) {
+                                    deleteDir()
+                                }
+                            }
+                        }
+                    }
+                }
                 stage ('check with DRAFT') {
                     when { expression { return ( params.DO_BUILD_WITH_DRAFT_API && params.DO_TEST_CHECK ) } }
                     steps {
                       dir("tmp/test-check-withDRAFT") {
                         deleteDir()
                         unstash 'built-draft'
-                        timeout (time: 20, unit: 'MINUTES') {
+                        retry(3) {
+                        timeout (time: "${params.USE_TEST_TIMEOUT}".toInteger(), unit: 'MINUTES') {
+                         script {
+                          try {
                             sh 'CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; LD_LIBRARY_PATH="`pwd`/src/.libs:$LD_LIBRARY_PATH"; export LD_LIBRARY_PATH; make LD_LIBRARY_PATH="$LD_LIBRARY_PATH" check'
+                          }
+                          catch (Exception e) {
+                            sh 'D="`pwd`"; B="`basename "$D"`" ; tar czf test-suite_"$B".tgz `find . -name '*.trs'` `find . -name '*.log'`'
+                            archiveArtifacts artifacts: "**/test-suite*.tgz", allowEmpty: true
+                            throw e
+                          }
+                         }
+                        }
                         }
                         sh 'echo "Are GitIgnores good after make check with drafts? (should have no output below)"; git status -s || if [ "${params.REQUIRE_GOOD_GITIGNORE}" = false ]; then echo "WARNING GitIgnore tests found newly changed or untracked files" >&2 ; exit 0 ; else echo "FAILED GitIgnore tests" >&2 ; exit 1; fi'
                         script {
@@ -177,8 +211,19 @@ pipeline {
                       dir("tmp/test-check-withoutDRAFT") {
                         deleteDir()
                         unstash 'built-nondraft'
-                        timeout (time: 20, unit: 'MINUTES') {
+                        retry(3) {
+                        timeout (time: "${params.USE_TEST_TIMEOUT}".toInteger(), unit: 'MINUTES') {
+                         script {
+                          try {
                             sh 'CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; LD_LIBRARY_PATH="`pwd`/src/.libs:$LD_LIBRARY_PATH"; export LD_LIBRARY_PATH; make LD_LIBRARY_PATH="$LD_LIBRARY_PATH" check'
+                          }
+                          catch (Exception e) {
+                            sh 'D="`pwd`"; B="`basename "$D"`" ; tar czf test-suite_"$B".tgz `find . -name '*.trs'` `find . -name '*.log'`'
+                            archiveArtifacts artifacts: "**/test-suite*.tgz", allowEmpty: true
+                            throw e
+                          }
+                         }
+                        }
                         }
                         sh 'echo "Are GitIgnores good after make check without drafts? (should have no output below)"; git status -s || if [ "${params.REQUIRE_GOOD_GITIGNORE}" = false ]; then echo "WARNING GitIgnore tests found newly changed or untracked files" >&2 ; exit 0 ; else echo "FAILED GitIgnore tests" >&2 ; exit 1; fi'
                         script {
@@ -195,8 +240,19 @@ pipeline {
                       dir("tmp/test-memcheck-withDRAFT") {
                         deleteDir()
                         unstash 'built-draft'
-                        timeout (time: 20, unit: 'MINUTES') {
+                        retry(3) {
+                        timeout (time: "${params.USE_TEST_TIMEOUT}".toInteger(), unit: 'MINUTES') {
+                         script {
+                          try {
                             sh 'CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; LD_LIBRARY_PATH="`pwd`/src/.libs:$LD_LIBRARY_PATH"; export LD_LIBRARY_PATH; make LD_LIBRARY_PATH="$LD_LIBRARY_PATH" memcheck && exit 0 ; echo "Re-running failed ($?) memcheck with greater verbosity" >&2 ; make LD_LIBRARY_PATH="$LD_LIBRARY_PATH" VERBOSE=1 memcheck-verbose'
+                          }
+                          catch (Exception e) {
+                            sh 'D="`pwd`"; B="`basename "$D"`" ; tar czf test-suite_"$B".tgz `find . -name '*.trs'` `find . -name '*.log'`'
+                            archiveArtifacts artifacts: "**/test-suite*.tgz", allowEmpty: true
+                            throw e
+                          }
+                         }
+                        }
                         }
                         sh 'echo "Are GitIgnores good after make memcheck with drafts? (should have no output below)"; git status -s || if [ "${params.REQUIRE_GOOD_GITIGNORE}" = false ]; then echo "WARNING GitIgnore tests found newly changed or untracked files" >&2 ; exit 0 ; else echo "FAILED GitIgnore tests" >&2 ; exit 1; fi'
                         script {
@@ -213,8 +269,19 @@ pipeline {
                       dir("tmp/test-memcheck-withoutDRAFT") {
                         deleteDir()
                         unstash 'built-nondraft'
-                        timeout (time: 20, unit: 'MINUTES') {
+                        retry(3) {
+                        timeout (time: "${params.USE_TEST_TIMEOUT}".toInteger(), unit: 'MINUTES') {
+                         script {
+                          try {
                             sh 'CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; LD_LIBRARY_PATH="`pwd`/src/.libs:$LD_LIBRARY_PATH"; export LD_LIBRARY_PATH; make LD_LIBRARY_PATH="$LD_LIBRARY_PATH" memcheck && exit 0 ; echo "Re-running failed ($?) memcheck with greater verbosity" >&2 ; make LD_LIBRARY_PATH="$LD_LIBRARY_PATH" VERBOSE=1 memcheck-verbose'
+                          }
+                          catch (Exception e) {
+                            sh 'D="`pwd`"; B="`basename "$D"`" ; tar czf test-suite_"$B".tgz `find . -name '*.trs'` `find . -name '*.log'`'
+                            archiveArtifacts artifacts: "**/test-suite*.tgz", allowEmpty: true
+                            throw e
+                          }
+                         }
+                        }
                         }
                         sh 'echo "Are GitIgnores good after make memcheck without drafts? (should have no output below)"; git status -s || if [ "${params.REQUIRE_GOOD_GITIGNORE}" = false ]; then echo "WARNING GitIgnore tests found newly changed or untracked files" >&2 ; exit 0 ; else echo "FAILED GitIgnore tests" >&2 ; exit 1; fi'
                         script {
@@ -231,8 +298,19 @@ pipeline {
                       dir("tmp/test-distcheck-withDRAFT") {
                         deleteDir()
                         unstash 'built-draft'
-                        timeout (time: 30, unit: 'MINUTES') {
+                        retry(3) {
+                        timeout (time: "${params.USE_TEST_TIMEOUT}".toInteger(), unit: 'MINUTES') {
+                         script {
+                          try {
                             sh 'CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; LD_LIBRARY_PATH="`pwd`/src/.libs:$LD_LIBRARY_PATH"; export LD_LIBRARY_PATH; DISTCHECK_CONFIGURE_FLAGS="--enable-drafts=yes --with-docs=no" ; export DISTCHECK_CONFIGURE_FLAGS; make DISTCHECK_CONFIGURE_FLAGS="$DISTCHECK_CONFIGURE_FLAGS" LD_LIBRARY_PATH="$LD_LIBRARY_PATH" distcheck'
+                          }
+                          catch (Exception e) {
+                            sh 'D="`pwd`"; B="`basename "$D"`" ; tar czf test-suite_"$B".tgz `find . -name '*.trs'` `find . -name '*.log'`'
+                            archiveArtifacts artifacts: "**/test-suite*.tgz", allowEmpty: true
+                            throw e
+                          }
+                         }
+                        }
                         }
                         sh 'echo "Are GitIgnores good after make distcheck with drafts? (should have no output below)"; git status -s || if [ "${params.REQUIRE_GOOD_GITIGNORE}" = false ]; then echo "WARNING GitIgnore tests found newly changed or untracked files" >&2 ; exit 0 ; else echo "FAILED GitIgnore tests" >&2 ; exit 1; fi'
                         script {
@@ -249,8 +327,19 @@ pipeline {
                       dir("tmp/test-distcheck-withoutDRAFT") {
                         deleteDir()
                         unstash 'built-nondraft'
-                        timeout (time: 30, unit: 'MINUTES') {
+                        retry(3) {
+                        timeout (time: "${params.USE_TEST_TIMEOUT}".toInteger(), unit: 'MINUTES') {
+                         script {
+                          try {
                             sh 'CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; LD_LIBRARY_PATH="`pwd`/src/.libs:$LD_LIBRARY_PATH"; export LD_LIBRARY_PATH; DISTCHECK_CONFIGURE_FLAGS="--enable-drafts=no --with-docs=no" ; export DISTCHECK_CONFIGURE_FLAGS; make DISTCHECK_CONFIGURE_FLAGS="$DISTCHECK_CONFIGURE_FLAGS" LD_LIBRARY_PATH="$LD_LIBRARY_PATH" distcheck'
+                          }
+                          catch (Exception e) {
+                            sh 'D="`pwd`"; B="`basename "$D"`" ; tar czf test-suite_"$B".tgz `find . -name '*.trs'` `find . -name '*.log'`'
+                            archiveArtifacts artifacts: "**/test-suite*.tgz", allowEmpty: true
+                            throw e
+                          }
+                         }
+                        }
                         }
                         sh 'echo "Are GitIgnores good after make distcheck without drafts? (should have no output below)"; git status -s || if [ "${params.REQUIRE_GOOD_GITIGNORE}" = false ]; then echo "WARNING GitIgnore tests found newly changed or untracked files" >&2 ; exit 0 ; else echo "FAILED GitIgnore tests" >&2 ; exit 1; fi'
                         script {
@@ -267,9 +356,11 @@ pipeline {
                       dir("tmp/test-install-withDRAFT") {
                         deleteDir()
                         unstash 'built-draft'
-                        timeout (time: 20, unit: 'MINUTES') {
+                        retry(3) {
+                        timeout (time: "${params.USE_TEST_TIMEOUT}".toInteger(), unit: 'MINUTES') {
                             sh """CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; LD_LIBRARY_PATH="`pwd`/src/.libs:\${LD_LIBRARY_PATH}"; export LD_LIBRARY_PATH; make LD_LIBRARY_PATH="\${LD_LIBRARY_PATH}" DESTDIR="${params.USE_TEST_INSTALL_DESTDIR}/withDRAFT" install"""
                         }
+                        }
                         sh """cd "${params.USE_TEST_INSTALL_DESTDIR}/withDRAFT" && find . -ls"""
                         sh 'echo "Are GitIgnores good after make install with drafts? (should have no output below)"; git status -s || if [ "${params.REQUIRE_GOOD_GITIGNORE}" = false ]; then echo "WARNING GitIgnore tests found newly changed or untracked files" >&2 ; exit 0 ; else echo "FAILED GitIgnore tests" >&2 ; exit 1; fi'
                         script {
@@ -286,9 +377,11 @@ pipeline {
                       dir("tmp/test-install-withoutDRAFT") {
                         deleteDir()
                         unstash 'built-nondraft'
-                        timeout (time: 20, unit: 'MINUTES') {
+                        retry(3) {
+                        timeout (time: "${params.USE_TEST_TIMEOUT}".toInteger(), unit: 'MINUTES') {
                             sh """CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; LD_LIBRARY_PATH="`pwd`/src/.libs:\${LD_LIBRARY_PATH}"; export LD_LIBRARY_PATH; make LD_LIBRARY_PATH="\${LD_LIBRARY_PATH}" DESTDIR="${params.USE_TEST_INSTALL_DESTDIR}/withoutDRAFT" install"""
                         }
+                        }
                         sh """cd "${params.USE_TEST_INSTALL_DESTDIR}/withoutDRAFT" && find . -ls"""
                         sh 'echo "Are GitIgnores good after make install without drafts? (should have no output below)"; git status -s || if [ "${params.REQUIRE_GOOD_GITIGNORE}" = false ]; then echo "WARNING GitIgnore tests found newly changed or untracked files" >&2 ; exit 0 ; else echo "FAILED GitIgnore tests" >&2 ; exit 1; fi'
                         script {
@@ -305,9 +398,11 @@ pipeline {
                       dir("tmp/test-install-withDOCS") {
                         deleteDir()
                         unstash 'built-docs'
-                        timeout (time: 20, unit: 'MINUTES') {
+                        retry(3) {
+                        timeout (time: "${params.USE_TEST_TIMEOUT}".toInteger(), unit: 'MINUTES') {
                             sh """CCACHE_BASEDIR="`pwd`" ; export CCACHE_BASEDIR; LD_LIBRARY_PATH="`pwd`/src/.libs:\${LD_LIBRARY_PATH}"; export LD_LIBRARY_PATH; make LD_LIBRARY_PATH="\${LD_LIBRARY_PATH}" DESTDIR="${params.USE_TEST_INSTALL_DESTDIR}/withDOCS" install"""
                         }
+                        }
                         sh """cd "${params.USE_TEST_INSTALL_DESTDIR}/withDOCS" && find . -ls"""
                         sh 'echo "Are GitIgnores good after make install with Docs? (should have no output below)"; git status -s || if [ "${params.REQUIRE_GOOD_GITIGNORE}" = false ]; then echo "WARNING GitIgnore tests found newly changed or untracked files" >&2 ; exit 0 ; else echo "FAILED GitIgnore tests" >&2 ; exit 1; fi'
                         script {
@@ -332,10 +427,13 @@ pipeline {
                         if ( env.BRANCH_NAME =~ myDEPLOY_BRANCH_PATTERN ) {
                             def GIT_URL = sh(returnStdout: true, script: """git remote -v | egrep '^origin' | awk '{print \$2}' | head -1""").trim()
                             def GIT_COMMIT = sh(returnStdout: true, script: 'git rev-parse --verify HEAD').trim()
+                            def DIST_ARCHIVE = ""
+                            if ( params.DO_DIST_DOCS ) { DIST_ARCHIVE = env.BUILD_URL + "artifact/__dist.tar.gz" }
                             build job: "${myDEPLOY_JOB_NAME}", parameters: [
                                 string(name: 'DEPLOY_GIT_URL', value: "${GIT_URL}"),
                                 string(name: 'DEPLOY_GIT_BRANCH', value: env.BRANCH_NAME),
-                                string(name: 'DEPLOY_GIT_COMMIT', value: "${GIT_COMMIT}")
+                                string(name: 'DEPLOY_GIT_COMMIT', value: "${GIT_COMMIT}"),
+                                string(name: 'DEPLOY_DIST_ARCHIVE', value: "${DIST_ARCHIVE}")
                                 ], quietPeriod: 0, wait: myDEPLOY_REPORT_RESULT, propagate: myDEPLOY_REPORT_RESULT
                         } else {
                             echo "Not deploying because branch '${env.BRANCH_NAME}' did not match filter '${myDEPLOY_BRANCH_PATTERN}'"