yetus-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From a.@apache.org
Subject [08/17] yetus git commit: YETUS-15. build environment
Date Sun, 11 Nov 2018 22:19:24 GMT
http://git-wip-us.apache.org/repos/asf/yetus/blob/6ebaa111/precommit/src/main/shell/test-patch.sh
----------------------------------------------------------------------
diff --git a/precommit/src/main/shell/test-patch.sh b/precommit/src/main/shell/test-patch.sh
new file mode 100755
index 0000000..967c76b
--- /dev/null
+++ b/precommit/src/main/shell/test-patch.sh
@@ -0,0 +1,3345 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Make sure that bash version meets the pre-requisite
+
+if [[ -z "${BASH_VERSINFO[0]}" ]] \
+   || [[ "${BASH_VERSINFO[0]}" -lt 3 ]] \
+   || [[ "${BASH_VERSINFO[0]}" -eq 3 && "${BASH_VERSINFO[1]}" -lt 2 ]]; then
+  echo "bash v3.2+ is required. Sorry."
+  exit 1
+fi
+
+this="${BASH_SOURCE-$0}"
+BINDIR=$(cd -P -- "$(dirname -- "${this}")" >/dev/null && pwd -P)
+BINNAME=${this##*/}
+BINNAME=${BINNAME%.sh}
+STARTINGDIR=$(pwd)
+USER_PARAMS=("$@")
+#shellcheck disable=SC2034
+QATESTMODE=false
+
+# global arrays
+declare -a CHANGED_FILES
+declare -a CHANGED_MODULES
+declare -a TP_HEADER
+declare -a TP_VOTE_TABLE
+declare -a TP_TEST_TABLE
+declare -a TP_FOOTER_TABLE
+declare -a MODULE
+declare -a MODULE_BACKUP_STATUS
+declare -a MODULE_BACKUP_STATUS_TIMER
+declare -a MODULE_BACKUP_STATUS_MSG
+declare -a MODULE_BACKUP_STATUS_LOG
+declare -a MODULE_BACKUP_COMPILE_LOG
+declare -a MODULE_STATUS
+declare -a MODULE_STATUS_TIMER
+declare -a MODULE_STATUS_MSG
+declare -a MODULE_STATUS_LOG
+declare -a MODULE_COMPILE_LOG
+declare -a USER_MODULE_LIST
+
+TP_HEADER_COUNTER=0
+TP_VOTE_COUNTER=0
+TP_TEST_COUNTER=0
+TP_FOOTER_COUNTER=0
+
+## @description  Setup the default global variables
+## @audience     public
+## @stability    stable
+## @replaceable  no
+function setup_defaults
+{
+  declare version="in-progress"
+
+  common_defaults
+  GLOBALTIMER=$("${AWK}" 'BEGIN {srand(); print srand()}')
+
+  set_yetus_version
+
+  if [[ ${VERSION} =~ SNAPSHOT$ ]]; then
+    version="in-progress"
+  fi
+
+  PATCH_NAMING_RULE="https://yetus.apache.org/documentation/${version}/precommit-patchnames"
+  INSTANCE=${RANDOM}
+  RELOCATE_PATCH_DIR=false
+
+  ALLOWSUMMARIES=true
+
+  BUILD_NATIVE=${BUILD_NATIVE:-true}
+
+  BUILD_URL_ARTIFACTS=artifact/patchprocess
+  BUILD_URL_CONSOLE=console
+  BUILDTOOLCWD=module
+
+  # shellcheck disable=SC2034
+  CHANGED_UNION_MODULES=""
+
+  GIT_OFFLINE=false
+  PROC_LIMIT=1000
+  REEXECED=false
+  RESETREPO=false
+  BUILDMODE=patch
+  # shellcheck disable=SC2034
+  BUILDMODEMSG="The patch"
+  ISSUE=""
+  TIMER=$("${AWK}" 'BEGIN {srand(); print srand()}')
+  JVM_REQUIRED=true
+  yetus_add_entry JDK_TEST_LIST compile
+  yetus_add_entry JDK_TEST_LIST unit
+}
+
+## @description  Convert the given module name to a file fragment
+## @audience     public
+## @stability    stable
+## @replaceable  no
+## @param        module
+function module_file_fragment
+{
+  local mod=$1
+  if [[ ${mod} = \. ]]; then
+    echo root
+  else
+    echo "$1" | tr '/' '_' | tr '\\' '_'
+  fi
+}
+
+## @description  Convert time in seconds to m + s
+## @audience     public
+## @stability    stable
+## @replaceable  no
+## @param        seconds
+function clock_display
+{
+  local -r elapsed=$1
+
+  if [[ ${elapsed} -lt 0 ]]; then
+    echo "N/A"
+  else
+    printf  "%3sm %02ss" $((elapsed/60)) $((elapsed%60))
+  fi
+}
+
+## @description  Activate the local timer
+## @audience     public
+## @stability    stable
+## @replaceable  no
+function start_clock
+{
+  yetus_debug "Start clock"
+  TIMER=$(date +"%s")
+}
+
+## @description  Print the elapsed time in seconds since the start of the local timer
+## @audience     public
+## @stability    stable
+## @replaceable  no
+function stop_clock
+{
+  local -r stoptime=$(date +"%s")
+  local -r elapsed=$((stoptime-TIMER))
+  yetus_debug "Stop clock"
+
+  echo ${elapsed}
+}
+
+## @description  Print the elapsed time in seconds since the start of the global timer
+## @audience     private
+## @stability    stable
+## @replaceable  no
+function stop_global_clock
+{
+  local -r stoptime=$(date +"%s")
+  local -r elapsed=$((stoptime-GLOBALTIMER))
+  yetus_debug "Stop global clock"
+
+  echo ${elapsed}
+}
+
+## @description  Add time to the local timer
+## @audience     public
+## @stability    stable
+## @replaceable  no
+## @param        seconds
+function offset_clock
+{
+  declare off=$1
+
+  yetus_debug "offset clock by ${off}"
+
+  if [[ -n ${off} ]]; then
+    ((TIMER=TIMER-off))
+  else
+    yetus_error "ASSERT: no offset passed to offset_clock: ${index}"
+    generate_stack
+  fi
+}
+
+## @description generate a stack trace when in debug mode
+## @audience     public
+## @stability    stable
+## @replaceable  no
+## @return       exits
+function generate_stack
+{
+  declare frame
+
+  if [[ "${YETUS_SHELL_SCRIPT_DEBUG}" = true ]]; then
+    while caller "${frame}"; do
+      ((frame++));
+    done
+  fi
+  exit 1
+}
+
+## @description  Add to the header of the display
+## @audience     public
+## @stability    stable
+## @replaceable  no
+## @param        string
+function add_header_line
+{
+  # shellcheck disable=SC2034
+  TP_HEADER[${TP_HEADER_COUNTER}]="$*"
+  ((TP_HEADER_COUNTER=TP_HEADER_COUNTER+1 ))
+}
+
+## @description  Add to the output table. If the first parameter is a number
+## @description  that is the vote for that column and calculates the elapsed time
+## @description  based upon the last start_clock().  The second parameter is the reporting
+## @description  subsystem (or test) that is providing the vote.  The second parameter
+## @description  is always required.  The third parameter is any extra verbage that goes
+## @description  with that subsystem.
+## @description  if the vote is H, then that designates that "subsystem" should be a
+## @description  header in the vote table comment output. The other parameters are
+## @description  ignored
+## @audience     public
+## @stability    stable
+## @replaceable  no
+## @param        +1/0/-1/H
+## @param        subsystem
+## @param        string
+function add_vote_table
+{
+  declare value=$1
+  declare subsystem=$2
+  shift 2
+
+  # apparently shellcheck doesn't know about declare -r
+  #shellcheck disable=SC2155
+  declare -r elapsed=$(stop_clock)
+  declare filt
+
+  yetus_debug "add_vote_table ${value} ${subsystem} ${elapsed} ${*}"
+
+  if [[ "${value}" = H ]]; then
+    TP_VOTE_TABLE[${TP_VOTE_COUNTER}]="|${value}| | | ${subsystem} |"
+    ((TP_VOTE_COUNTER=TP_VOTE_COUNTER+1))
+    return
+  fi
+
+  if [[ ${value} == "1" ]]; then
+    value="+1"
+  fi
+
+  for filt in "${VOTE_FILTER[@]}"; do
+    if [[ "${subsystem}" == "${filt}" && "${value}" == -1 ]]; then
+      value=-0
+    fi
+  done
+
+  # shellcheck disable=SC2034
+  TP_VOTE_TABLE[${TP_VOTE_COUNTER}]="| ${value} | ${subsystem} | ${elapsed} | $* |"
+  ((TP_VOTE_COUNTER=TP_VOTE_COUNTER+1))
+
+  if [[ "${value}" = -1 ]]; then
+    ((RESULT = RESULT + 1))
+  fi
+}
+
+## @description  Report the JVM version of the given directory
+## @stability    stable
+## @audience     private
+## @replaceable  yes
+## @param        directory
+## @return       version
+function report_jvm_version
+{
+  #shellcheck disable=SC2016
+  "${1}/bin/java" -version 2>&1 | head -1 | ${AWK} '{print $NF}' | tr -d \"
+}
+
+## @description  Verify if a given test is multijdk
+## @audience     public
+## @stability    stable
+## @replaceable  yes
+## @param        test
+## @return       0 = yes
+## @return       1 = no
+function verify_multijdk_test
+{
+  local i=$1
+
+  if [[ "${JDK_DIR_LIST}" == "${JAVA_HOME}" ]]; then
+    yetus_debug "MultiJDK not configured."
+    return 1
+  fi
+
+  if [[ ${JDK_TEST_LIST} =~ $i ]]; then
+    yetus_debug "${i} is in ${JDK_TEST_LIST} and MultiJDK configured."
+    return 0
+  fi
+  return 1
+}
+
+## @description  Put the opening environment information at the bottom
+## @description  of the footer table
+## @stability    stable
+## @audience     private
+## @replaceable  yes
+function prepopulate_footer
+{
+  # shellcheck disable=SC2155
+  declare -r unamea=$(uname -a)
+
+  add_footer_table "uname" "${unamea}"
+  add_footer_table "Build tool" "${BUILDTOOL}"
+
+  if [[ -n ${REEXECPERSONALITY} ]]; then
+    add_footer_table "Personality" "${REEXECPERSONALITY}"
+  elif [[ -n ${PERSONALITY} ]]; then
+    add_footer_table "Personality" "${PERSONALITY}"
+  fi
+
+  gitrev=$(${GIT} rev-parse --verify --short HEAD)
+
+  add_footer_table "git revision" "${PATCH_BRANCH} / ${gitrev}"
+}
+
+## @description  Last minute entries on the footer table
+## @audience     private
+## @stability    stable
+## @replaceable  no
+function finish_footer_table
+{
+  declare counter
+
+  if [[ -f "${PATCH_DIR}/threadcounter.txt" ]]; then
+    counter=$(cat "${PATCH_DIR}/threadcounter.txt")
+    add_footer_table "Max. process+thread count" "${counter} (vs. ulimit of ${PROC_LIMIT})"
+  fi
+
+  add_footer_table "modules" "C: ${CHANGED_MODULES[*]} U: ${CHANGED_UNION_MODULES}"
+}
+
+## @description  Put the final elapsed time at the bottom of the table.
+## @audience     private
+## @stability    stable
+## @replaceable  no
+function finish_vote_table
+{
+
+  local -r elapsed=$(stop_global_clock)
+  local calctime
+
+  calctime=$(clock_display "${elapsed}")
+
+  echo ""
+  echo "Total Elapsed time: ${calctime}"
+  echo ""
+
+  # shellcheck disable=SC2034
+  TP_VOTE_TABLE[${TP_VOTE_COUNTER}]="| | | ${elapsed} | |"
+  ((TP_VOTE_COUNTER=TP_VOTE_COUNTER+1 ))
+}
+
+## @description  Add to the footer of the display. @@BASE@@ will get replaced with the
+## @description  correct location for the local filesystem in dev mode or the URL for
+## @description  Jenkins mode.
+## @audience     public
+## @stability    stable
+## @replaceable  no
+## @param        subsystem
+## @param        string
+function add_footer_table
+{
+  local subsystem=$1
+  shift 1
+
+  # shellcheck disable=SC2034
+  TP_FOOTER_TABLE[${TP_FOOTER_COUNTER}]="| ${subsystem} | $* |"
+  ((TP_FOOTER_COUNTER=TP_FOOTER_COUNTER+1 ))
+}
+
+## @description  Special table just for unit test failures
+## @audience     public
+## @stability    stable
+## @replaceable  no
+## @param        failurereason
+## @param        testlist
+function add_test_table
+{
+  local failure=$1
+  shift 1
+
+  # shellcheck disable=SC2034
+  TP_TEST_TABLE[${TP_TEST_COUNTER}]="| ${failure} | $* |"
+  ((TP_TEST_COUNTER=TP_TEST_COUNTER+1 ))
+}
+
+## @description  Large display for the user console
+## @audience     public
+## @stability    stable
+## @replaceable  no
+## @param        string
+## @return       large chunk of text
+function big_console_header
+{
+  local text="$*"
+  local spacing=$(( (75+${#text}) /2 ))
+  printf "\n\n"
+  echo "============================================================================"
+  echo "============================================================================"
+  printf "%*s\n"  ${spacing} "${text}"
+  echo "============================================================================"
+  echo "============================================================================"
+  printf "\n\n"
+}
+
+## @description  Find the largest size of a column of an array
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+## @return       size
+function findlargest
+{
+  local column=$1
+  shift
+  local a=("$@")
+  local sizeofa=${#a[@]}
+  local i=0
+  local string
+  local maxlen=0
+
+  until [[ ${i} -eq ${sizeofa} ]]; do
+    # shellcheck disable=SC2086
+    string=$( echo ${a[$i]} | cut -f$((column + 1)) -d\| )
+    if [[ ${#string} -gt ${maxlen} ]]; then
+      maxlen=${#string}
+    fi
+    i=$((i+1))
+  done
+  echo "${maxlen}"
+}
+
+## @description Write the contents of a file to all of the bug systems
+## @description (so content should avoid special formatting)
+## @param     filename
+## @stability stable
+## @audience  public
+function write_comment
+{
+  local -r commentfile=${1}
+  declare bug
+
+  for bug in ${BUGCOMMENTS}; do
+    if declare -f ${bug}_write_comment >/dev/null; then
+       "${bug}_write_comment" "${commentfile}"
+    fi
+  done
+}
+
+## @description Verify that the patch directory is still in working order
+## @description since bad actors on some systems wipe it out. If not,
+## @description recreate it and then exit
+## @audience    private
+## @stability   evolving
+## @replaceable yes
+## @return      may exit on failure
+function verify_patchdir_still_exists
+{
+  local -r commentfile=/tmp/testpatch.$$.${RANDOM}
+  local extra=""
+
+  if [[ ! -d ${PATCH_DIR} ]]; then
+    rm "${commentfile}" 2>/dev/null
+
+    echo "(!) The patch artifact directory has been removed! " > "${commentfile}"
+    echo "This is a fatal error for test-patch.sh.  Aborting. " >> "${commentfile}"
+    echo
+    cat ${commentfile}
+    echo
+    if [[ ${JENKINS} == true ]]; then
+      if [[ -n ${NODE_NAME} ]]; then
+        extra=" (Jenkins node ${NODE_NAME})"
+      fi
+      echo "Jenkins${extra} information at ${BUILD_URL}${BUILD_URL_CONSOLE} may provide some hints. " >> "${commentfile}"
+
+      write_comment ${commentfile}
+    fi
+
+    rm "${commentfile}"
+    cleanup_and_exit "${RESULT}"
+  fi
+}
+
+## @description generate a list of all files and line numbers in $GITDIFFLINES that
+## @description that were added/changed in the source repo.  $GITDIFFCONTENT
+## @description is same file, but also includes the content of those lines
+## @audience    private
+## @stability   stable
+## @replaceable no
+function compute_gitdiff
+{
+  local file
+  local line
+  local startline
+  local counter
+  local numlines
+  local actual
+  local content
+  local outfile="${PATCH_DIR}/computegitdiff.${RANDOM}"
+
+  pushd "${BASEDIR}" >/dev/null
+  ${GIT} add --all --intent-to-add
+  while read -r line; do
+    if [[ ${line} =~ ^\+\+\+ ]]; then
+      file="./"$(echo "${line}" | cut -f2- -d/)
+      continue
+    elif [[ ${line} =~ ^@@ ]]; then
+      startline=$(echo "${line}" | cut -f3 -d' ' | cut -f1 -d, | tr -d + )
+      numlines=$(echo "${line}" | cut -f3 -d' ' | cut -s -f2 -d, )
+      # if this is empty, then just this line
+      # if it is 0, then no lines were added and this part of the patch
+      # is strictly a delete
+      if [[ ${numlines} == 0 ]]; then
+        continue
+      elif [[ -z ${numlines} ]]; then
+        numlines=1
+      fi
+      counter=0
+      # it isn't obvious, but on MOST platforms under MOST use cases,
+      # this is faster than using sed, and definitely faster than using
+      # awk.
+      # http://unix.stackexchange.com/questions/47407/cat-line-x-to-line-y-on-a-huge-file
+      # has a good discussion w/benchmarks
+      #
+      # note that if tail is still sending data through the pipe, but head gets enough
+      # to do what was requested, head will exit, leaving tail with a broken pipe.
+      # we're going to send stderr to /dev/null and ignore the error since head's
+      # output is really what we're looking for
+      tail -n "+${startline}" "${file}" 2>/dev/null | head -n ${numlines} > "${outfile}"
+      oldifs=${IFS}
+      IFS=''
+      while read -r content; do
+          ((actual=counter+startline))
+          echo "${file}:${actual}:" >> "${GITDIFFLINES}"
+          printf "%s:%s:%s\n" "${file}" "${actual}" "${content}" >> "${GITDIFFCONTENT}"
+          ((counter=counter+1))
+      done < "${outfile}"
+      rm "${outfile}"
+      IFS=${oldifs}
+    fi
+  done < <("${GIT}" diff --unified=0 --no-color)
+
+  if [[ ! -f "${GITDIFFLINES}" ]]; then
+    touch "${GITDIFFLINES}"
+  fi
+
+  if [[ ! -f "${GITDIFFCONTENT}" ]]; then
+    touch "${GITDIFFCONTENT}"
+  fi
+
+  if [[ -s "${GITDIFFLINES}" ]]; then
+    compute_unidiff
+  else
+    touch "${GITUNIDIFFLINES}"
+  fi
+
+  popd >/dev/null
+}
+
+## @description generate an index of unified diff lines vs. modified/added lines
+## @description ${GITDIFFLINES} must exist.
+## @audience    private
+## @stability   stable
+## @replaceable no
+function compute_unidiff
+{
+  declare fn
+  declare filen
+  declare tmpfile="${PATCH_DIR}/tmp.$$.${RANDOM}"
+
+  # now that we know what lines are where, we can deal
+  # with github's pain-in-the-butt API. It requires
+  # that the client provides the line number of the
+  # unified diff on a per file basis.
+
+  # First, build a per-file unified diff, pulling
+  # out the 'extra' lines, grabbing the adds with
+  # the line number in the diff file along the way,
+  # finally rewriting the line so that it is in
+  # './filename:diff line:content' format
+
+  for fn in "${CHANGED_FILES[@]}"; do
+    filen=${fn##./}
+
+    if [[ -f "${filen}" ]]; then
+      ${GIT} diff "${filen}" \
+        | tail -n +6 \
+        | ${GREP} -n '^+' \
+        | ${GREP} -vE '^[0-9]*:\+\+\+' \
+        | ${SED} -e 's,^\([0-9]*:\)\+,\1,g' \
+          -e "s,^,./${filen}:,g" \
+              >>  "${tmpfile}"
+    fi
+  done
+
+  # at this point, tmpfile should be in the same format
+  # as gitdiffcontent, just with different line numbers.
+  # let's do a merge (using gitdifflines because it's easier)
+
+  # ./filename:real number:diff number
+  # shellcheck disable=SC2016
+  paste -d: "${GITDIFFLINES}" "${tmpfile}" \
+    | ${AWK} -F: '{print $1":"$2":"$5":"$6}' \
+    >> "${GITUNIDIFFLINES}"
+
+  rm "${tmpfile}"
+}
+
+
+## @description  Print the command to be executing to the screen. Then
+## @description  run the command, sending stdout and stderr to the given filename
+## @description  This will also ensure that any directories in ${BASEDIR} have
+## @description  the exec bit set as a pre-exec step.
+## @audience     public
+## @stability    stable
+## @param        filename
+## @param        command
+## @param        [..]
+## @replaceable  no
+## @return       $?
+function echo_and_redirect
+{
+  declare logfile=$1
+  shift
+
+  verify_patchdir_still_exists
+
+  find "${BASEDIR}" -type d -exec chmod +x {} \;
+  # to the screen
+  echo "cd $(pwd)"
+  echo "${*} > ${logfile} 2>&1"
+
+  if [[ ${BASH_VERSINFO[0]} -gt 3 ]]; then
+
+    # use a coprocessor with the
+    # lower proc limit so that yetus can
+    # do stuff unimpacted by it
+
+    e_a_r_helper "${logfile}" "${@}" >> "${COPROC_LOGFILE}" 2>&1
+
+    # now that it's off as a separate process, we need to wait
+    # for it to finish. wait will either return 0, exit code
+    # of the coproc, or 127. all of which is
+    # perfectly fine for us.
+
+
+    # shellcheck disable=SC2154,SC2086
+    wait ${yrr_coproc_PID}
+
+  else
+
+    # if bash < 4 (e.g., OS X), just run it
+    # the ulimit was set earlier
+
+    yetus_run_and_redirect "${logfile}" "${@}"
+  fi
+
+}
+
+## @description is a given directory relative to BASEDIR?
+## @audience    public
+## @stability   stable
+## @replaceable yes
+## @param       path
+## @return      1 - no, path
+## @return      0 - yes, path - BASEDIR
+function relative_dir
+{
+  local p=${1#${BASEDIR}}
+
+  if [[ ${#p} -eq ${#1} ]]; then
+    echo "${p}"
+    return 1
+  fi
+  p=${p#/}
+  echo "${p}"
+  return 0
+}
+
+## @description  Print the usage information
+## @audience     public
+## @stability    stable
+## @replaceable  no
+function yetus_usage
+{
+  declare bugsys
+  declare jdktlist
+
+  importplugins
+
+  # shellcheck disable=SC2116,SC2086
+  bugsys=$(echo ${BUGSYSTEMS})
+  bugsys=${bugsys// /,}
+
+  # shellcheck disable=SC2116,SC2086
+  jdktlist=$(echo ${JDK_TEST_LIST})
+  jdktlist=${jdktlist// /,}
+
+  if [[ "${BUILDMODE}" = patch ]]; then
+    echo "${BINNAME} [OPTIONS] patch"
+    echo ""
+    echo "Where:"
+    echo "  patch is a file, URL, or bugsystem-compatible location of the patch file"
+  else
+    echo "${BINNAME} [OPTIONS]"
+  fi
+  echo ""
+  echo "Options:"
+  echo ""
+  yetus_add_option "--archive-list=<list>" "Comma delimited list of pattern matching notations to copy to patch-dir"
+  yetus_add_option "--basedir=<dir>" "The directory to apply the patch to (default current directory)"
+  yetus_add_option "--branch=<ref>" "Forcibly set the branch"
+  yetus_add_option "--branch-default=<ref>" "If the branch isn't forced and we don't detect one in the patch name, use this branch (default 'master')"
+  yetus_add_option "--build-native=<bool>" "If true, then build native components (default 'true')"
+  # shellcheck disable=SC2153
+  yetus_add_option "--build-tool=<tool>" "Pick which build tool to focus around (one of ${BUILDTOOLS})"
+  yetus_add_option "--bugcomments=<bug>" "Only write comments to the screen and this comma delimited list (default: ${bugsys})"
+  yetus_add_option "--contrib-guide=<url>" "URL to point new users towards project conventions. (default: ${PATCH_NAMING_RULE} )"
+  yetus_add_option "--debug" "If set, then output some extra stuff to stderr"
+  yetus_add_option "--dirty-workspace" "Allow the local git workspace to have uncommitted changes"
+  yetus_add_option "--empty-patch" "Create a summary of the current source tree"
+  yetus_add_option "--java-home=<path>" "Set JAVA_HOME (In Docker mode, this should be local to the image)"
+  yetus_add_option "--linecomments=<bug>" "Only write line comments to this comma delimited list (defaults to bugcomments)"
+  yetus_add_option "--list-plugins" "List all installed plug-ins and then exit"
+  yetus_add_option "--multijdkdirs=<paths>" "Comma delimited lists of JDK paths to use for multi-JDK tests"
+  yetus_add_option "--multijdktests=<list>" "Comma delimited tests to use when multijdkdirs is used. (default: '${jdktlist}')"
+  yetus_add_option "--modulelist=<list>" "Specify additional modules to test (comma delimited)"
+  yetus_add_option "--offline" "Avoid connecting to the Internet"
+  yetus_add_option "--patch-dir=<dir>" "The directory for working and output files (default '/tmp/test-patch-${PROJECT_NAME}/pid')"
+  yetus_add_option "--personality=<file>" "The personality file to load"
+  yetus_add_option "--proclimit=<num>" "Limit on the number of processes (default: ${PROC_LIMIT})"
+  yetus_add_option "--project=<name>" "The short name for project currently using test-patch (default 'yetus')"
+  yetus_add_option "--plugins=<list>" "Specify which plug-ins to add/delete (comma delimited; use 'all' for all found) e.g. --plugins=all,-ant,-scalac (all plugins except ant and scalac)"
+  yetus_add_option "--resetrepo" "Forcibly clean the repo"
+  yetus_add_option "--run-tests" "Run all relevant tests below the base directory"
+  yetus_add_option "--skip-dirs=<list>" "Skip following directories for module finding"
+  yetus_add_option "--skip-system-plugins" "Do not load plugins from ${BINDIR}/test-patch.d"
+  yetus_add_option "--summarize=<bool>" "Allow tests to summarize results"
+  yetus_add_option "--test-parallel=<bool>" "Run multiple tests in parallel (default false in developer mode, true in Jenkins mode)"
+  yetus_add_option "--test-threads=<int>" "Number of tests to run in parallel (default defined in ${PROJECT_NAME} build)"
+  yetus_add_option "--unit-test-filter-file=<file>" "The unit test filter file to load"
+  yetus_add_option "--tests-filter=<list>" "Lists of tests to turn failures into warnings"
+  yetus_add_option "--user-plugins=<dir>" "A directory of user provided plugins. see test-patch.d for examples (default empty)"
+  yetus_add_option "--version" "Print release version information and exit"
+
+  yetus_generic_columnprinter "${YETUS_OPTION_USAGE[@]}"
+  yetus_reset_usage
+
+  echo ""
+  echo "Shell binary overrides:"
+  yetus_add_option "--awk-cmd=<cmd>" "The 'awk' command to use (default 'awk')"
+  yetus_add_option "--curl-cmd=<cmd>" "The 'curl' command to use (default 'curl')"
+  yetus_add_option "--diff-cmd=<cmd>" "The GNU-compatible 'diff' command to use (default 'diff')"
+  yetus_add_option "--file-cmd=<cmd>" "The 'file' command to use (default 'file')"
+  yetus_add_option "--git-cmd=<cmd>" "The 'git' command to use (default 'git')"
+  yetus_add_option "--grep-cmd=<cmd>" "The 'grep' command to use (default 'grep')"
+  yetus_add_option "--patch-cmd=<cmd>" "The 'patch' command to use (default 'patch')"
+  yetus_add_option "--sed-cmd=<cmd>" "The 'sed' command to use (default 'sed')"
+
+  yetus_generic_columnprinter "${YETUS_OPTION_USAGE[@]}"
+  yetus_reset_usage
+
+  echo ""
+  echo "Automation options:"
+  yetus_add_option "--build-url=<url>" "Set the build location web page (Default: '${BUILD_URL}')"
+  yetus_add_option "--build-url-console=<location>" "Location relative to --build-url of the console (Default: '${BUILD_URL_CONSOLE}')"
+  yetus_add_option "--build-url-artifacts=<location>" "Location relative to --build-url of the --patch-dir (Default: '${BUILD_URL_ARTIFACTS}')"
+  yetus_add_option "--console-report-file=<file>" "Save the final console-based report to a file in addition to the screen"
+  yetus_add_option "--console-urls" "Use the build URL instead of path on the console report"
+  yetus_add_option "--instance=<string>" "Parallel execution identifier string"
+  yetus_add_option "--jenkins" "Enable Jenkins-specifc handling (auto: --robot)"
+  yetus_add_option "--mv-patch-dir" "Move the patch-dir into the basedir during cleanup"
+  yetus_add_option "--robot" "Assume this is an automated run"
+  yetus_add_option "--sentinel" "A very aggressive robot (auto: --robot)"
+
+  yetus_generic_columnprinter "${YETUS_OPTION_USAGE[@]}"
+  yetus_reset_usage
+
+
+  echo ""
+  echo "Docker options:"
+  docker_usage
+  yetus_generic_columnprinter "${YETUS_OPTION_USAGE[@]}"
+  yetus_reset_usage
+
+  echo ""
+  echo "Reaper options:"
+  reaper_usage
+  yetus_generic_columnprinter "${YETUS_OPTION_USAGE[@]}"
+  yetus_reset_usage
+
+  for plugin in ${BUILDTOOLS} ${TESTTYPES} ${BUGSYSTEMS} ${TESTFORMATS}; do
+    if declare -f ${plugin}_usage >/dev/null 2>&1; then
+      echo ""
+      echo "'${plugin}' plugin usage options:"
+      "${plugin}_usage"
+      yetus_generic_columnprinter "${YETUS_OPTION_USAGE[@]}"
+      yetus_reset_usage
+    fi
+  done
+}
+
+## @description  Interpret the command line parameters
+## @audience     private
+## @stability    stable
+## @replaceable  no
+## @param        $@
+## @return       May exit on failure
+function parse_args
+{
+  declare i
+  declare j
+
+  common_args "$@"
+
+  for i in "$@"; do
+    case ${i} in
+      --archive-list=*)
+        yetus_comma_to_array ARCHIVE_LIST "${i#*=}"
+        yetus_debug "Set to archive: ${ARCHIVE_LIST[*]}"
+      ;;
+      --bugcomments=*)
+        BUGCOMMENTS=${i#*=}
+        BUGCOMMENTS=${BUGCOMMENTS//,/ }
+      ;;
+      --build-native=*)
+        BUILD_NATIVE=${i#*=}
+      ;;
+      --build-tool=*)
+        BUILDTOOL=${i#*=}
+      ;;
+      --build-url=*)
+        BUILD_URL=${i#*=}
+      ;;
+      --build-url-artifacts=*)
+        # shellcheck disable=SC2034
+        BUILD_URL_ARTIFACTS=${i#*=}
+      ;;
+      --build-url-console=*)
+        # shellcheck disable=SC2034
+        BUILD_URL_CONSOLE=${i#*=}
+      ;;
+      --console-report-file=*)
+        CONSOLE_REPORT_FILE=${i#*=}
+      ;;
+      --console-urls)
+        # shellcheck disable=SC2034
+        CONSOLE_USE_BUILD_URL=true
+      ;;
+      --contrib-guide=*)
+        PATCH_NAMING_RULE=${i#*=}
+      ;;
+      --dirty-workspace)
+        DIRTY_WORKSPACE=true
+      ;;
+      --instance=*)
+        INSTANCE=${i#*=}
+      ;;
+      --empty-patch)
+        BUILDMODE=full
+        # shellcheck disable=SC2034
+        BUILDMODEMSG="The source tree"
+      ;;
+      --java-home=*)
+        JAVA_HOME=${i#*=}
+      ;;
+      --jenkins)
+        JENKINS=true
+      ;;
+      --linecomments=*)
+        BUGLINECOMMENTS=${i#*=}
+        BUGLINECOMMENTS=${BUGLINECOMMENTS//,/ }
+      ;;
+      --modulelist=*)
+        yetus_comma_to_array USER_MODULE_LIST "${i#*=}"
+        yetus_debug "Manually forcing modules ${USER_MODULE_LIST[*]}"
+      ;;
+      --multijdkdirs=*)
+        JDK_DIR_LIST=${i#*=}
+        JDK_DIR_LIST=${JDK_DIR_LIST//,/ }
+        yetus_debug "Multi-JDK mode activated with ${JDK_DIR_LIST}"
+        yetus_add_entry EXEC_MODES MultiJDK
+      ;;
+      --multijdktests=*)
+        JDK_TEST_LIST=${i#*=}
+        JDK_TEST_LIST=${JDK_TEST_LIST//,/ }
+        yetus_debug "Multi-JDK test list: ${JDK_TEST_LIST}"
+      ;;
+      --mv-patch-dir)
+        RELOCATE_PATCH_DIR=true;
+      ;;
+      --personality=*)
+        PERSONALITY=${i#*=}
+      ;;
+      --proclimit=*)
+        PROC_LIMIT=${i#*=}
+      ;;
+      --reexec)
+        REEXECED=true
+      ;;
+      --resetrepo)
+        RESETREPO=true
+      ;;
+      --robot)
+        ROBOT=true
+      ;;
+      --run-tests)
+        RUN_TESTS=true
+      ;;
+      --sentinel)
+        # shellcheck disable=SC2034
+        SENTINEL=true
+        yetus_add_entry EXEC_MODES Sentinel
+      ;;
+      --skip-dirs=*)
+        MODULE_SKIPDIRS=${i#*=}
+        MODULE_SKIPDIRS=${MODULE_SKIPDIRS//,/ }
+        yetus_debug "Setting skipdirs to ${MODULE_SKIPDIRS}"
+      ;;
+      --summarize=*)
+        ALLOWSUMMARIES=${i#*=}
+      ;;
+      --test-parallel=*)
+        # shellcheck disable=SC2034
+        TEST_PARALLEL=${i#*=}
+      ;;
+      --test-threads=*)
+        # shellcheck disable=SC2034
+        TEST_THREADS=${i#*=}
+      ;;
+      --unit-test-filter-file=*)
+        UNIT_TEST_FILTER_FILE=${i#*=}
+      ;;
+      --tests-filter=*)
+        yetus_comma_to_array VOTE_FILTER "${i#*=}"
+      ;;
+      --tpglobaltimer=*)
+        GLOBALTIMER=${i#*=}
+      ;;
+      --tpinstance=*)
+        INSTANCE=${i#*=}
+        EXECUTOR_NUMBER=${INSTANCE}
+      ;;
+      --tpperson=*)
+        REEXECPERSONALITY=${i#*=}
+      ;;
+      --tpreexectimer=*)
+        REEXECLAUNCHTIMER=${i#*=}
+      ;;
+      --*)
+        ## PATCH_OR_ISSUE can't be a --.  So this is probably
+        ## a plugin thing.
+        continue
+      ;;
+      *)
+        PATCH_OR_ISSUE=${i}
+      ;;
+    esac
+  done
+
+  docker_parse_args "$@"
+
+  reaper_parse_args "$@"
+
+  if [[ -z "${PATCH_OR_ISSUE}"
+       && "${BUILDMODE}" = patch ]]; then
+    yetus_usage
+    exit 1
+  fi
+
+  if [[ ${JENKINS} = true ]]; then
+    ROBOT=true
+    INSTANCE=${EXECUTOR_NUMBER}
+    yetus_add_entry EXEC_MODES Jenkins
+  fi
+
+  if [[ ${ROBOT} = true ]]; then
+    # shellcheck disable=SC2034
+    TEST_PARALLEL=true
+    RESETREPO=true
+    RUN_TESTS=true
+    ISSUE=${PATCH_OR_ISSUE}
+    yetus_add_entry EXEC_MODES Robot
+  fi
+
+  if [[ -n $UNIT_TEST_FILTER_FILE ]]; then
+    if [[ -f $UNIT_TEST_FILTER_FILE ]]; then
+      UNIT_TEST_FILTER_FILE=$(yetus_abs "${UNIT_TEST_FILTER_FILE}")
+    else
+      yetus_error "ERROR: Unit test filter file (${UNIT_TEST_FILTER_FILE}) does not exist!"
+      cleanup_and_exit 1
+    fi
+  fi
+
+  if [[ -n ${REEXECLAUNCHTIMER} ]]; then
+    TIMER=${REEXECLAUNCHTIMER};
+  else
+    start_clock
+  fi
+
+  if [[ "${DOCKERMODE}" = true || "${DOCKERSUPPORT}" = true ]]; then
+    if [[ "${DOCKER_DESTRCUTIVE}" = true ]]; then
+      yetus_add_entry EXEC_MODES DestructiveDocker
+    else
+      yetus_add_entry EXEC_MODES Docker
+    fi
+    add_vote_table 0 reexec "Docker mode activated."
+    start_clock
+  elif [[ "${REEXECED}" = true ]]; then
+    yetus_add_entry EXEC_MODES Re-exec
+    add_vote_table 0 reexec "Precommit patch detected."
+    start_clock
+  fi
+
+  # we need absolute dir for ${BASEDIR}
+  cd "${STARTINGDIR}" || cleanup_and_exit 1
+  BASEDIR=$(yetus_abs "${BASEDIR}")
+
+  if [[ -n ${USER_PATCH_DIR} ]]; then
+    PATCH_DIR="${USER_PATCH_DIR}"
+  fi
+
+  # we need absolute dir for PATCH_DIR
+  cd "${STARTINGDIR}" || cleanup_and_exit 1
+  if [[ ! -d ${PATCH_DIR} ]]; then
+    mkdir -p "${PATCH_DIR}"
+    if [[ $? == 0 ]] ; then
+      echo "${PATCH_DIR} has been created"
+    else
+      echo "Unable to create ${PATCH_DIR}"
+      cleanup_and_exit 1
+    fi
+  fi
+  PATCH_DIR=$(yetus_abs "${PATCH_DIR}")
+  COPROC_LOGFILE="${PATCH_DIR}/coprocessors.txt"
+
+  # we need absolute dir for ${CONSOLE_REPORT_FILE}
+  if [[ -n "${CONSOLE_REPORT_FILE}" ]]; then
+    if : > "${CONSOLE_REPORT_FILE}"; then
+      CONSOLE_REPORT_FILE_ORIG="${CONSOLE_REPORT_FILE}"
+      CONSOLE_REPORT_FILE=$(yetus_abs "${CONSOLE_REPORT_FILE_ORIG}")
+    else
+      yetus_error "ERROR: cannot write to ${CONSOLE_REPORT_FILE}. Disabling console report file."
+      unset CONSOLE_REPORT_FILE
+    fi
+  fi
+
+  if [[ ${RESETREPO} == "true" ]] ; then
+    yetus_add_entry EXEC_MODES ResetRepo
+  fi
+
+  if [[ ${RUN_TESTS} == "true" ]] ; then
+    yetus_add_entry EXEC_MODES UnitTests
+  fi
+
+  if [[ -n "${USER_PLUGIN_DIR}" ]]; then
+    USER_PLUGIN_DIR=$(yetus_abs "${USER_PLUGIN_DIR}")
+  fi
+
+  GITDIFFLINES="${PATCH_DIR}/gitdifflines.txt"
+  GITDIFFCONTENT="${PATCH_DIR}/gitdiffcontent.txt"
+  GITUNIDIFFLINES="${PATCH_DIR}/gitdiffunilines.txt"
+
+  if [[ "${REEXECED}" = true
+     && -f "${PATCH_DIR}/precommit/personality/provided.sh" ]]; then
+    REEXECPERSONALITY="${PERSONALITY}"
+    PERSONALITY="${PATCH_DIR}/precommit/personality/provided.sh"
+  fi
+}
+
+## @description  Locate the build file for a given directory
+## @audience     private
+## @stability    stable
+## @replaceable  no
+## @return       directory containing the buildfile. Nothing returned if not found.
+## @param        buildfile
+## @param        directory
+function find_buildfile_dir
+{
+  local buildfile=$1
+  local dir=$2
+
+  yetus_debug "Find ${buildfile} dir for: ${dir}"
+
+  while builtin true; do
+    if [[ -f "${dir}/${buildfile}" ]];then
+      echo "${dir}"
+      yetus_debug "Found: ${dir}"
+      return 0
+    elif [[ ${dir} == "." || ${dir} == "/" ]]; then
+      yetus_debug "ERROR: ${buildfile} is not found."
+      return 1
+    else
+      dir=$(faster_dirname "${dir}")
+    fi
+  done
+}
+
+## @description  List of files that ${PATCH_DIR}/patch modifies
+## @audience     private
+## @stability    stable
+## @replaceable  no
+## @return       None; sets ${CHANGED_FILES[@]}
+function find_changed_files
+{
+  declare line
+  declare oldifs
+
+  case "${BUILDMODE}" in
+    full)
+      echo "Building a list of all files in the source tree"
+      oldifs=${IFS}
+      IFS=$'\n'
+      CHANGED_FILES=($(git ls-files))
+      IFS=${oldifs}
+    ;;
+    patch)
+      # get a list of all of the files that have been changed,
+      # except for /dev/null (which would be present for new files).
+      # Additionally, remove any a/ b/ patterns at the front of the patch filenames.
+      # shellcheck disable=SC2016
+      while read -r line; do
+        CHANGED_FILES=("${CHANGED_FILES[@]}" "${line}")
+      done < <(
+        ${AWK} 'function p(s){sub("^[ab]/","",s); if(s!~"^/dev/null"){print s}}
+        /^diff --git /   { p($3); p($4) }
+        /^(\+\+\+|---) / { p($2) }' "${PATCH_DIR}/patch" | sort -u)
+      ;;
+    esac
+}
+
+## @description Check for directories to skip during
+## @description changed module calcuation
+## @audience    private
+## @stability   stable
+## @replaceable no
+## @param       directory
+## @return      0 for use
+## @return      1 for skip
+function module_skipdir
+{
+  local dir=${1}
+  local i
+
+  yetus_debug "Checking skipdirs for ${dir}"
+
+  if [[ -z ${MODULE_SKIPDIRS} ]]; then
+    yetus_debug "Skipping skipdirs"
+    return 0
+  fi
+
+  while builtin true; do
+    for i in ${MODULE_SKIPDIRS}; do
+      if [[ ${dir} = "${i}" ]];then
+        yetus_debug "Found a skip: ${dir}"
+        return 1
+      fi
+    done
+    if [[ ${dir} == "." || ${dir} == "/" ]]; then
+      return 0
+    else
+      dir=$(faster_dirname "${dir}")
+      yetus_debug "Trying to skip: ${dir}"
+    fi
+  done
+}
+
+## @description  Find the modules of the build that ${PATCH_DIR}/patch modifies
+## @audience     private
+## @stability    stable
+## @replaceable  no
+## @param        repostatus
+## @return       None; sets ${CHANGED_MODULES[@]}
+function find_changed_modules
+{
+  declare repostatus=$1
+  declare i
+  declare builddir
+  declare module
+  declare prev_builddir
+  declare i=1
+  declare dir
+  declare dirt
+  declare buildfile
+  declare -a tmpmods
+
+  buildfile=$("${BUILDTOOL}_buildfile")
+
+  if [[ $? != 0 ]]; then
+    yetus_error "ERROR: Unsupported build tool."
+    bugsystem_finalreport 1
+    cleanup_and_exit 1
+  fi
+
+  #  Empty string indicates the build system wants to disable module detection
+  if [[ -z ${buildfile} ]]; then
+    tmpmods=(".")
+  else
+
+    # Now find all the modules that were changed
+    for i in "${CHANGED_FILES[@]}"; do
+
+      # TODO: optimize this
+      if [[ "${BUILDMODE}" = full && ! "${i}" =~ ${buildfile} ]]; then
+        continue
+      fi
+
+      dirt=$(dirname "${i}")
+
+      module_skipdir "${dirt}"
+      if [[ $? != 0 ]]; then
+        continue
+      fi
+
+      builddir=$(find_buildfile_dir "${buildfile}" "${dirt}")
+      if [[ -z ${builddir} ]]; then
+        yetus_error "ERROR: ${buildfile} is not found. Make sure the target is a ${BUILDTOOL}-based project."
+        bugsystem_finalreport 1
+        cleanup_and_exit 1
+      fi
+      tmpmods=("${tmpmods[@]}" "${builddir}")
+    done
+  fi
+
+  tmpmods=("${tmpmods[@]}" "${USER_MODULE_LIST[@]}")
+
+  CHANGED_MODULES=($(printf "%s\n" "${tmpmods[@]}" | sort -u))
+
+  yetus_debug "Locate the union of ${CHANGED_MODULES[*]}"
+  count=${#CHANGED_MODULES[@]}
+  if [[ ${count} -lt 2 ]]; then
+    yetus_debug "Only one entry, so keeping it ${CHANGED_MODULES[0]}"
+    # shellcheck disable=SC2034
+    CHANGED_UNION_MODULES="${CHANGED_MODULES[0]}"
+  else
+    i=1
+    while [[ ${i} -lt 100 ]]
+    do
+      tmpmods=()
+      for j in "${CHANGED_MODULES[@]}"; do
+        tmpmods=("${tmpmods[@]}" $(echo "${j}" | cut -f1-${i} -d/))
+      done
+      tmpmods=($(printf "%s\n" "${tmpmods[@]}" | sort -u))
+
+      module=${tmpmods[0]}
+      count=${#tmpmods[@]}
+      if [[ ${count} -eq 1
+        && -f ${module}/${buildfile} ]]; then
+        prev_builddir=${module}
+      elif [[ ${count} -gt 1 ]]; then
+        builddir=${prev_builddir}
+        break
+      fi
+      ((i=i+1))
+    done
+
+    if [[ -z ${builddir} ]]; then
+      builddir="."
+    fi
+
+    yetus_debug "Finding union of ${builddir}"
+    builddir=$(find_buildfile_dir "${buildfile}" "${builddir}" || true)
+
+    #shellcheck disable=SC2034
+    CHANGED_UNION_MODULES="${builddir}"
+  fi
+
+  # some build tools may want to change these and/or
+  # make other changes based upon these results
+  if declare -f ${BUILDTOOL}_reorder_modules >/dev/null; then
+    "${BUILDTOOL}_reorder_modules" "${repostatus}"
+  fi
+}
+
+## @description  check if repo requires ssh creds to pull
+## @audience     private
+## @stability    stable
+## @replaceable  no
+## @return       0 = no
+## @return       1 = yes
+function git_requires_creds
+{
+  declare status
+  declare -a remotes
+
+  # shellcheck disable=SC2207
+  remotes=( $("${GIT}" remote -v show -n) )
+
+  for r in "${remotes[@]}"; do
+    if [[ ${r} =~ /@/ ]] || [[ ${r} =~ git:// ]]; then
+      return 1
+    fi
+  done
+  return 0
+}
+
+## @description  git checkout the appropriate branch to test.  Additionally, this calls
+## @description  'determine_branch' based upon the context provided
+## @description  in ${PATCH_DIR} and in git after checkout.
+## @audience     private
+## @stability    stable
+## @replaceable  no
+## @return       0 on success.  May exit on failure.
+function git_checkout
+{
+  declare currentbranch
+  declare exemptdir
+  declare status
+  declare pullmayfail=false
+
+  big_console_header "Confirming git environment"
+
+  cd "${BASEDIR}" || cleanup_and_exit 1
+  if [[ ! -e .git ]]; then
+    yetus_error "ERROR: ${BASEDIR} is not a git repo."
+    cleanup_and_exit 1
+  fi
+
+  if git_requires_creds; then
+    pullmayfail=true
+  fi
+
+  if [[ ${RESETREPO} == "true" ]] ; then
+
+    if [[ -d .git/rebase-apply ]]; then
+      yetus_error "ERROR: a previous rebase failed. Aborting it."
+      ${GIT} rebase --abort
+    fi
+
+    if ! ${GIT} reset --hard; then
+      yetus_error "ERROR: git reset is failing"
+      cleanup_and_exit 1
+    fi
+
+    # if PATCH_DIR is in BASEDIR, then we don't want
+    # git wiping it out.
+    exemptdir=$(relative_dir "${PATCH_DIR}")
+    if [[ $? == 1 ]]; then
+      ${GIT} clean -xdf
+      status=$?
+
+    else
+      # we do, however, want it emptied of all _files_.
+      # we need to leave _directories_ in case we are in
+      # re-exec mode (which places a directory full of stuff in it)
+      yetus_debug "Exempting ${exemptdir} from clean"
+      rm "${PATCH_DIR}/*" 2>/dev/null
+      ${GIT} clean -xdf -e "${exemptdir}"
+      status=$?
+    fi
+
+    if [[ ${status} != 0 ]]; then
+      yetus_error "ERROR: git clean is failing"
+      cleanup_and_exit 1
+    fi
+
+    if ! ${GIT} checkout --force "${PATCH_BRANCH_DEFAULT}"; then
+      yetus_error "ERROR: git checkout --force ${PATCH_BRANCH_DEFAULT} is failing"
+      cleanup_and_exit 1
+    fi
+
+    determine_branch
+
+    # we need to explicitly fetch in case the
+    # git ref hasn't been brought in tree yet
+    if [[ ${OFFLINE} == false ]] && [[ ${GIT_OFFLINE} == false ]]; then
+
+      if ! ${GIT} pull --rebase; then
+        if [[ ${pullmayfail} == true ]]; then
+          yetus_error "WARNING: Noted that pull failed, will treat git as offline from here on out"
+          GIT_OFFLINE=true
+        else
+          yetus_error "ERROR: git pull is failing"
+          cleanup_and_exit 1
+        fi
+      fi
+    fi
+
+    # forcibly checkout this branch or git ref
+
+    if ! ${GIT} checkout --force "${PATCH_BRANCH}"; then
+      yetus_error "ERROR: git checkout ${PATCH_BRANCH} is failing"
+      cleanup_and_exit 1
+    fi
+
+    # if we've selected a feature branch that has new changes
+    # since our last build, we'll need to reset to the latest FETCH_HEAD.
+    if [[ ${OFFLINE} == false ]]; then
+
+      # previous clause where GIT_OFFLINE would get set is also
+      # protected by OFFLINE == false
+
+      if [[ "${GIT_OFFLINE}" == false ]]; then
+        if ! ${GIT} fetch; then
+          yetus_error "ERROR: git fetch is failing"
+          cleanup_and_exit 1
+        fi
+
+        if ! ${GIT} reset --hard FETCH_HEAD; then
+          yetus_error "ERROR: git reset is failing"
+          cleanup_and_exit 1
+        fi
+      fi
+
+      if ! ${GIT} clean -df; then
+        yetus_error "ERROR: git clean is failing"
+        cleanup_and_exit 1
+      fi
+    fi
+
+  else
+
+    status=$(${GIT} status --porcelain)
+    if [[ "${status}" != "" && -z ${DIRTY_WORKSPACE} ]] ; then
+      yetus_error "ERROR: --dirty-workspace option not provided."
+      yetus_error "ERROR: can't run in a workspace that contains the following modifications"
+      yetus_error "${status}"
+      cleanup_and_exit 1
+    fi
+
+    determine_branch
+
+    currentbranch=$(${GIT} rev-parse --abbrev-ref HEAD)
+    if [[ "${currentbranch}" != "${PATCH_BRANCH}" ]];then
+      if [[ "${BUILDMODE}" = patch ]]; then
+        echo "WARNING: Current git branch is ${currentbranch} but patch is built for ${PATCH_BRANCH}."
+        echo "WARNING: Continuing anyway..."
+      fi
+      PATCH_BRANCH=${currentbranch}
+    fi
+  fi
+
+  return 0
+}
+
+## @description  Confirm the given branch is a git reference
+## @descriptoin  or a valid gitXYZ commit hash
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+## @param        branch
+## @return       0 on success, if gitXYZ was passed, PATCH_BRANCH=xyz
+## @return       1 on failure
+function verify_valid_branch
+{
+  local check=$1
+  local i
+  local hash
+
+  # shortcut some common
+  # non-resolvable names
+  if [[ -z ${check} ]]; then
+    return 1
+  fi
+
+  if [[ ${check} =~ ^git ]]; then
+    hash=$(echo "${check}" | cut -f2- -dt)
+    if [[ -n ${hash} ]]; then
+      ${GIT} cat-file -t "${hash}" >/dev/null 2>&1
+      if [[ $? -eq 0 ]]; then
+        PATCH_BRANCH=${hash}
+        return 0
+      fi
+      return 1
+    else
+      return 1
+    fi
+  fi
+
+  ${GIT} show-ref "${check}" >/dev/null 2>&1
+  return $?
+}
+
+## @description  Try to guess the branch being tested using a variety of heuristics
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+## @return       0 on success, with PATCH_BRANCH updated appropriately
+## @return       1 on failure, with PATCH_BRANCH updated to PATCH_BRANCH_DEFAULT
+function determine_branch
+{
+  declare bugs
+  declare retval=1
+
+  # something has already set this, so move on
+  if [[ -n ${PATCH_BRANCH} ]]; then
+    return
+  fi
+
+  pushd "${BASEDIR}" > /dev/null
+
+  yetus_debug "Determine branch"
+
+  # something has already set this, so move on
+  if [[ -n ${PATCH_BRANCH} ]]; then
+    return
+  fi
+
+  # developer mode, existing checkout, whatever
+  if [[ "${DIRTY_WORKSPACE}" == true ]];then
+    PATCH_BRANCH=$(${GIT} rev-parse --abbrev-ref HEAD)
+    echo "dirty workspace mode; applying against existing branch"
+    return
+  fi
+
+  for bugs in ${BUGSYSTEMS}; do
+    if declare -f ${bugs}_determine_branch >/dev/null;then
+      "${bugs}_determine_branch"
+      retval=$?
+      if [[ ${retval} == 0 ]]; then
+        break
+      fi
+    fi
+  done
+
+  if [[ ${retval} != 0 ]]; then
+    PATCH_BRANCH="${PATCH_BRANCH_DEFAULT}"
+  fi
+  popd >/dev/null
+}
+
+## @description  Try to guess the issue being tested using a variety of heuristics
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+## @return       0 on success, with ISSUE updated appropriately
+## @return       1 on failure, with ISSUE updated to "Unknown"
+function determine_issue
+{
+  declare bugsys
+
+  yetus_debug "Determine issue"
+
+  for bugsys in ${BUGSYSTEMS}; do
+    if declare -f ${bugsys}_determine_issue >/dev/null; then
+      "${bugsys}_determine_issue" "${PATCH_OR_ISSUE}"
+      if [[ $? == 0 ]]; then
+        yetus_debug "${bugsys} says ${ISSUE}"
+        return 0
+      fi
+    fi
+  done
+  return 1
+}
+
+## @description  Use some heuristics to determine which long running
+## @description  tests to run
+## @audience     private
+## @stability    stable
+## @replaceable  no
+function determine_needed_tests
+{
+  declare i
+  declare plugin
+
+  big_console_header "Determining needed tests"
+  echo "(Depending upon input size and number of plug-ins, this may take a while)"
+
+  for i in "${CHANGED_FILES[@]}"; do
+    yetus_debug "Determining needed tests for ${i}"
+    personality_file_tests "${i}"
+
+    for plugin in ${TESTTYPES} ${BUILDTOOL}; do
+      if declare -f ${plugin}_filefilter >/dev/null 2>&1; then
+        "${plugin}_filefilter" "${i}"
+      fi
+    done
+  done
+
+  add_footer_table "Optional Tests" "${NEEDED_TESTS}"
+}
+
+## @description  Given ${PATCH_DIR}/patch, apply the patch
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+## @return       0 on success
+## @return       exit on failure
+function apply_patch_file
+{
+  big_console_header "Applying patch to ${PATCH_BRANCH}"
+
+  patchfile_apply_driver "${PATCH_DIR}/patch"
+  if [[ $? != 0 ]] ; then
+    echo "PATCH APPLICATION FAILED"
+    ((RESULT = RESULT + 1))
+    add_vote_table -1 patch "${PATCH_OR_ISSUE} does not apply to ${PATCH_BRANCH}. Rebase required? Wrong Branch? See ${PATCH_NAMING_RULE} for help."
+    bugsystem_finalreport 1
+    cleanup_and_exit 1
+  fi
+  return 0
+}
+
+## @description  copy the test-patch binary bits to a new working dir,
+## @description  setting USER_PLUGIN_DIR and PERSONALITY to the new
+## @description  locations.
+## @description  this is used for test-patch in docker and reexec mode
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+function copytpbits
+{
+  declare dockerdir
+  declare dockfile
+  declare lines
+
+  # we need to copy/consolidate all the bits that might have changed
+  # that are considered part of test-patch.  This *might* break
+  # things that do off-path includes, but there isn't much we can
+  # do about that, I don't think.
+
+  # if we've already copied, then don't bother doing it again
+  if [[ ${STARTINGDIR} == ${PATCH_DIR}/precommit ]]; then
+    yetus_debug "Skipping copytpbits; already copied once"
+    return
+  fi
+
+  pushd "${STARTINGDIR}" >/dev/null
+  mkdir -p "${PATCH_DIR}/precommit/user-plugins"
+  mkdir -p "${PATCH_DIR}/precommit/personality"
+  mkdir -p "${PATCH_DIR}/precommit/test-patch-docker"
+
+  # copy our entire universe, preserving links, etc.
+  yetus_debug "copying '${BINDIR}' over to '${PATCH_DIR}/precommit'"
+  # shellcheck disable=SC2164
+  (cd "${BINDIR}"; tar cpf - . ) | (cd "${PATCH_DIR}/precommit"; tar xpf - )
+
+  echo "${VERSION}" > "${PATCH_DIR}/precommit/VERSION"
+
+  if [[ -n "${USER_PLUGIN_DIR}"
+    && -d "${USER_PLUGIN_DIR}"  ]]; then
+    yetus_debug "copying '${USER_PLUGIN_DIR}' over to ${PATCH_DIR}/precommit/user-plugins"
+    cp -pr "${USER_PLUGIN_DIR}"/. \
+      "${PATCH_DIR}/precommit/user-plugins"
+  fi
+  # Set to be relative to ${PATCH_DIR}/precommit
+  USER_PLUGIN_DIR="${PATCH_DIR}/precommit/user-plugins"
+
+  if [[ -n ${PERSONALITY}
+    && -f ${PERSONALITY} ]]; then
+    yetus_debug "copying '${PERSONALITY}' over to '${PATCH_DIR}/precommit/personality/provided.sh'"
+    cp -pr "${PERSONALITY}" "${PATCH_DIR}/precommit/personality/provided.sh"
+  fi
+
+  if [[ -n ${UNIT_TEST_FILTER_FILE}
+    && -f ${UNIT_TEST_FILTER_FILE} ]]; then
+    yetus_debug "copying '${UNIT_TEST_FILTER_FILE}' over to '${PATCH_DIR}/precommit/unit_test_filter_file.txt'"
+    cp -pr "${UNIT_TEST_FILTER_FILE}" "${PATCH_DIR}/precommit/unit_test_filter_file.txt"
+  fi
+
+  if [[ -n ${DOCKERFILE}
+      && -f ${DOCKERFILE} ]]; then
+    yetus_debug "copying '${DOCKERFILE}' over to '${PATCH_DIR}/precommit/test-patch-docker/Dockerfile'"
+    dockerdir=$(dirname "${DOCKERFILE}")
+    dockfile=$(basename "${DOCKERFILE}")
+    pushd "${dockerdir}" >/dev/null
+    gitfilerev=$("${GIT}" log -n 1 --pretty=format:%h -- "${dockfile}" 2>/dev/null)
+    popd >/dev/null
+    if [[ -z ${gitfilerev} ]]; then
+      gitfilerev=$(date "+%F")
+      gitfilerev="date${gitfilerev}"
+    fi
+    (
+      echo "### YETUS_PRIVATE: dockerfile=${DOCKERFILE}"
+      echo "### YETUS_PRIVATE: gitrev=${gitfilerev}"
+      lines=$(${GREP} -n 'YETUS CUT HERE' "${DOCKERFILE}" | cut -f1 -d:)
+      if [[ -z "${lines}" ]]; then
+        cat "${DOCKERFILE}"
+      else
+        head -n "${lines}" "${DOCKERFILE}"
+      fi
+      # make sure we put some space between, just in case last
+      # line isn't an empty line or whatever
+      printf "\n\n"
+      echo "### YETUS_PRIVATE: start test-patch-bootstrap"
+      cat "${BINDIR}/test-patch-docker/Dockerfile-endstub"
+
+      printf "\n\n"
+    ) > "${PATCH_DIR}/precommit/test-patch-docker/Dockerfile"
+    DOCKERFILE="${PATCH_DIR}/precommit/test-patch-docker/Dockerfile"
+  fi
+
+  popd >/dev/null
+}
+
+## @description  change the working directory to execute the buildtool
+## @audience     public
+## @stability    evolving
+## @replaceable  no
+## @param        MODULE_ index
+function buildtool_cwd
+{
+  declare modindex=$1
+
+  BUILDTOOLCWD="${BUILDTOOLCWD//@@@BASEDIR@@@/${BASEDIR}}"
+  BUILDTOOLCWD="${BUILDTOOLCWD//@@@MODULEDIR@@@/${BASEDIR}/${MODULE[${modindex}]}}"
+
+  if [[ "${BUILDTOOLCWD}" =~ ^/ ]]; then
+    yetus_debug "buildtool_cwd: ${BUILDTOOLCWD}"
+    if [[ ! -e "${BUILDTOOLCWD}" ]]; then
+      mkdir -p "${BUILDTOOLCWD}"
+    fi
+    pushd "${BUILDTOOLCWD}" >/dev/null
+    return $?
+  fi
+
+  case "${BUILDTOOLCWD}" in
+    basedir)
+      pushd "${BASEDIR}" >/dev/null
+    ;;
+    module)
+      pushd "${BASEDIR}/${MODULE[${modindex}]}" >/dev/null
+    ;;
+    *)
+      pushd "$(pwd)"
+    ;;
+  esac
+}
+
+## @description  If this patches actually patches test-patch.sh, then
+## @description  run with the patched version for the test.
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+## @return       none; otherwise relaunches
+function check_reexec
+{
+  declare commentfile=${PATCH_DIR}/tp.${RANDOM}
+  declare tpdir
+  declare copy=false
+  declare testdir
+  declare plugin
+
+  if [[ ${REEXECED} == true ]]; then
+    big_console_header "Re-exec mode detected. Continuing."
+    return
+  fi
+
+  # determine if the patch hits
+  # any test-patch sensitive bits
+  # if so, we need to copy the universe
+  # after patching it (copy=true)
+  for testdir in "${BINDIR}" \
+      "${PERSONALITY}" \
+      "${USER_PLUGIN_DIR}" \
+      "${DOCKERFILE}"; do
+    tpdir=$(relative_dir "${testdir}")
+    if [[ $? == 0
+        && "${CHANGED_FILES[*]}" =~ ${tpdir} ]]; then
+      copy=true
+    fi
+  done
+
+  if [[ ${copy} == true && "${BUILDMODE}" != full ]]; then
+    big_console_header "precommit patch detected"
+
+    if [[ ${RESETREPO} == false ]]; then
+      ((RESULT = RESULT + 1))
+      yetus_debug "can't destructively change the working directory. run with '--resetrepo' please. :("
+      add_vote_table -1 precommit "Couldn't test precommit changes because we aren't configured to destructively change the working directory."
+    else
+
+      apply_patch_file
+
+      if [[ ${ROBOT} == true ]]; then
+        rm "${commentfile}" 2>/dev/null
+        echo "(!) A patch to the testing environment has been detected. " > "${commentfile}"
+        echo "Re-executing against the patched versions to perform further tests. " >> "${commentfile}"
+        echo "The console is at ${BUILD_URL}${BUILD_URL_CONSOLE} in case of problems." >> "${commentfile}"
+        write_comment "${commentfile}"
+        rm "${commentfile}"
+      fi
+    fi
+  fi
+
+  if [[ ${DOCKERSUPPORT} == false
+     && ${copy} == false ]]; then
+    return
+  fi
+
+  if [[ ${DOCKERSUPPORT} == true
+    && ${copy} == false ]]; then
+      big_console_header "Re-execing under Docker"
+  fi
+
+  # copy our universe
+  copytpbits
+
+  if [[ ${DOCKERSUPPORT} == true ]]; then
+    # if we are doing docker, then we re-exec, but underneath the
+    # container
+
+    determine_user
+
+    # need to call this explicitly
+    console_docker_support
+
+    for plugin in ${PROJECT_NAME} ${BUILDTOOL} ${BUGSYSTEMS} ${TESTTYPES} ${TESTFORMATS}; do
+      if declare -f "${plugin}_docker_support" >/dev/null; then
+        "${plugin}_docker_support"
+      fi
+    done
+
+    TESTPATCHMODE="${USER_PARAMS[*]}"
+    if [[ -n "${BUILD_URL}" ]]; then
+      TESTPATCHMODE="--build-url=${BUILD_URL} ${TESTPATCHMODE}"
+    fi
+
+    if [[ -f "${PERSONALITY}" ]]; then
+      TESTPATCHMODE="--tpperson=${PERSONALITY} ${TESTPATCHMODE}"
+    fi
+
+    TESTPATCHMODE="--tpglobaltimer=${GLOBALTIMER} ${TESTPATCHMODE}"
+    TESTPATCHMODE="--tpreexectimer=${TIMER} ${TESTPATCHMODE}"
+    TESTPATCHMODE="--tpinstance=${INSTANCE} ${TESTPATCHMODE}"
+    TESTPATCHMODE="--plugins=${ENABLED_PLUGINS// /,} ${TESTPATCHMODE}"
+    TESTPATCHMODE=" ${TESTPATCHMODE}"
+    export TESTPATCHMODE
+
+    #shellcheck disable=SC2164
+    cd "${BASEDIR}"
+    #shellcheck disable=SC2093
+    docker_handler
+  else
+
+    # if we aren't doing docker, then just call ourselves
+    # but from the new path with the new flags
+    #shellcheck disable=SC2164
+    cd "${PATCH_DIR}/precommit/"
+    exec "${PATCH_DIR}/precommit/test-patch.sh" \
+      "${USER_PARAMS[@]}" \
+      --reexec \
+      --basedir="${BASEDIR}" \
+      --branch="${PATCH_BRANCH}" \
+      --patch-dir="${PATCH_DIR}" \
+      --tpglobaltimer="${GLOBALTIMER}" \
+      --tpreexectimer="${TIMER}" \
+      --personality="${PERSONALITY}" \
+      --tpinstance="${INSTANCE}" \
+      --user-plugins="${USER_PLUGIN_DIR}"
+  fi
+}
+
+## @description  Save file names and directory to the patch dir
+## @audience     public
+## @stability    evolving
+## @replaceable  no
+function archive
+{
+  declare pmn
+  declare fn
+  declare line
+  declare srcdir
+  declare tmpfile="${PATCH_DIR}/tmp.$$.${RANDOM}"
+
+  if [[ ${#ARCHIVE_LIST[@]} -eq 0 ]]; then
+    return
+  fi
+
+  if ! verify_command "rsync" "${RSYNC}"; then
+    yetus_error "WARNING: Cannot use the archive function"
+    return
+  fi
+
+  yetus_debug "Starting archiving process"
+  # get the list of files. these will be with
+  # the full path
+  # (this is pretty expensive)
+
+  rm "${tmpfile}" 2>/dev/null
+  for pmn in "${ARCHIVE_LIST[@]}"; do
+    find "${BASEDIR}" -name "${pmn}" >> "${tmpfile}"
+  done
+
+  # read the list, stripping of both
+  # the BASEDIR and any leading /.
+  # with our filename fragment,
+  # call faster_dirname with a prepended /
+  while read -r line; do
+    yetus_debug "Archiving: ${line}"
+    srcdir=$(faster_dirname "/${line}")
+    mkdir -p "${PATCH_DIR}/archiver${srcdir}"
+    "${RSYNC}" -av "${BASEDIR}/${line}" "${PATCH_DIR}/archiver${srcdir}" >/dev/null 2>&1
+  done < <("${SED}" -e "s,${BASEDIR},,g" \
+      -e "s,^/,,g" "${tmpfile}")
+  rm "${tmpfile}" 2>/dev/null
+  yetus_debug "Ending archiving process"
+
+}
+
+## @description  Reset the test results
+## @audience     public
+## @stability    evolving
+## @replaceable  no
+function modules_reset
+{
+  MODULE_STATUS=()
+  MODULE_STATUS_TIMER=()
+  MODULE_STATUS_MSG=()
+  MODULE_STATUS_LOG=()
+  MODULE_COMPILE_LOG=()
+}
+
+## @description  Backup the MODULE globals prior to loop processing
+## @audience     public
+## @stability    evolving
+## @replaceable  no
+function modules_backup
+{
+  MODULE_BACKUP_STATUS=("${MODULE_STATUS[@]}")
+  MODULE_BACKUP_STATUS_TIMER=("${MODULE_STATUS_TIMER[@]}")
+  MODULE_BACKUP_STATUS_MSG=("${MODULE_STATUS_MSG[@]}")
+  MODULE_BACKUP_STATUS_LOG=("${MODULE_STATUS_LOG[@]}")
+  MODULE_BACKUP_COMPILE_LOG=("${MODULE_COMPILE_LOG[@]}")
+}
+
+## @description  Restore the backup
+## @audience     public
+## @stability    evolving
+## @replaceable  no
+function modules_restore
+{
+  MODULE_STATUS=("${MODULE_BACKUP_STATUS[@]}")
+  MODULE_STATUS_TIMER=("${MODULE_BACKUP_STATUS_TIMER[@]}")
+  MODULE_STATUS_MSG=("${MODULE_BACKUP_STATUS_MSG[@]}")
+  MODULE_STATUS_LOG=("${MODULE_BACKUP_STATUS_LOG[@]}")
+  MODULE_COMPILE_LOG=("${MODULE_BACKUP_COMPILE_LOG[@]}")
+}
+
+## @description  Utility to print standard module errors
+## @audience     public
+## @stability    evolving
+## @replaceable  no
+## @param        repostatus
+## @param        testtype
+## @param        summarymode
+function modules_messages
+{
+  declare repostatus=$1
+  declare testtype=$2
+  declare summarymode=$3
+  shift 3
+  declare modindex=0
+  declare repo
+  declare goodtime=0
+  declare failure=false
+  declare oldtimer
+  declare statusjdk
+  declare multijdkmode=false
+
+  if [[ "${BUILDMODE}" == full ]]; then
+    repo="the source"
+  elif [[ "${repostatus}" == branch ]]; then
+    repo=${PATCH_BRANCH}
+  else
+    repo="the patch"
+  fi
+
+  if verify_multijdk_test "${testtype}"; then
+    multijdkmode=true
+  fi
+
+  oldtimer=${TIMER}
+
+  if [[ ${summarymode} == true
+    && ${ALLOWSUMMARIES} == true ]]; then
+
+    until [[ ${modindex} -eq ${#MODULE[@]} ]]; do
+
+      if [[ ${multijdkmode} == true ]]; then
+        statusjdk=${MODULE_STATUS_JDK[${modindex}]}
+      fi
+
+      if [[ "${MODULE_STATUS[${modindex}]}" == '+1' ]]; then
+        ((goodtime=goodtime + ${MODULE_STATUS_TIMER[${modindex}]}))
+      else
+        failure=true
+        start_clock
+        echo ""
+        echo "${MODULE_STATUS_MSG[${modindex}]}"
+        echo ""
+        offset_clock "${MODULE_STATUS_TIMER[${modindex}]}"
+        add_vote_table "${MODULE_STATUS[${modindex}]}" "${testtype}" "${MODULE_STATUS_MSG[${modindex}]}"
+        if [[ ${MODULE_STATUS[${modindex}]} == -1
+          && -n "${MODULE_STATUS_LOG[${modindex}]}" ]]; then
+          add_footer_table "${testtype}" "@@BASE@@/${MODULE_STATUS_LOG[${modindex}]}"
+        fi
+      fi
+      ((modindex=modindex+1))
+    done
+
+    if [[ ${failure} == false ]]; then
+      start_clock
+      offset_clock "${goodtime}"
+      add_vote_table +1 "${testtype}" "${repo} passed${statusjdk}"
+    fi
+  else
+    until [[ ${modindex} -eq ${#MODULE[@]} ]]; do
+      start_clock
+      echo ""
+      echo "${MODULE_STATUS_MSG[${modindex}]}"
+      echo ""
+      offset_clock "${MODULE_STATUS_TIMER[${modindex}]}"
+      add_vote_table "${MODULE_STATUS[${modindex}]}" "${testtype}" "${MODULE_STATUS_MSG[${modindex}]}"
+      if [[ ${MODULE_STATUS[${modindex}]} == -1
+        && -n "${MODULE_STATUS_LOG[${modindex}]}" ]]; then
+        add_footer_table "${testtype}" "@@BASE@@/${MODULE_STATUS_LOG[${modindex}]}"
+      fi
+      ((modindex=modindex+1))
+    done
+  fi
+  TIMER=${oldtimer}
+}
+
+## @description  Add or update a test result. Update requires
+## @description  at least the first two parameters.
+## @description  WARNING: If the message is updated,
+## @description  then the JDK version is also calculated to match
+## @description  the current JAVA_HOME.
+## @audience     public
+## @stability    evolving
+## @replaceable  no
+## @param        moduleindex
+## @param        -1-0|0|+1
+## @param        logvalue
+## @param        message
+function module_status
+{
+  declare index=$1
+  declare value=$2
+  shift 2
+  declare log=$1
+  shift
+
+  declare jdk
+
+  jdk=$(report_jvm_version "${JAVA_HOME}")
+
+  if [[ -n ${index}
+    && ${index} =~ ^[0-9]+$ ]]; then
+    MODULE_STATUS[${index}]="${value}"
+    if [[ -n ${log} ]]; then
+      MODULE_STATUS_LOG[${index}]="${log}"
+    fi
+    if [[ -n $1 ]]; then
+      MODULE_STATUS_JDK[${index}]=" with JDK v${jdk}"
+      MODULE_STATUS_MSG[${index}]="${*}"
+    fi
+  else
+    yetus_error "ASSERT: module_status given bad index: ${index}"
+    yetus_error "ASSERT: module_stats $*"
+    generate_stack
+    exit 1
+  fi
+}
+
+## @description  run the tests for the queued modules
+## @audience     public
+## @stability    evolving
+## @replaceable  no
+## @param        repostatus
+## @param        testtype
+## @param        mvncmdline
+function modules_workers
+{
+  declare repostatus=$1
+  declare testtype=$2
+  shift 2
+  declare modindex=0
+  declare fn
+  declare savestart=${TIMER}
+  declare savestop
+  declare repo
+  declare modulesuffix
+  declare jdk=""
+  declare jdkindex=0
+  declare statusjdk
+  declare result=0
+  declare argv
+  declare execvalue
+
+  if [[ "${BUILDMODE}" = full ]]; then
+    repo="the source"
+  elif [[ ${repostatus} == branch ]]; then
+    repo=${PATCH_BRANCH}
+  else
+    repo="the patch"
+  fi
+
+  modules_reset
+
+  if verify_multijdk_test "${testtype}"; then
+    jdk=$(report_jvm_version "${JAVA_HOME}")
+    statusjdk=" with JDK v${jdk}"
+    jdk="-jdk${jdk}"
+    jdk=${jdk// /}
+    yetus_debug "Starting MultiJDK mode${statusjdk} on ${testtype}"
+  fi
+
+  until [[ ${modindex} -eq ${#MODULE[@]} ]]; do
+    start_clock
+
+    fn=$(module_file_fragment "${MODULE[${modindex}]}")
+    fn="${fn}${jdk}"
+    modulesuffix=$(basename "${MODULE[${modindex}]}")
+    buildtool_cwd "${modindex}"
+
+    if [[ ${modulesuffix} = \. ]]; then
+      modulesuffix="root"
+    fi
+
+    if [[ $? != 0 ]]; then
+      echo "${BASEDIR}/${MODULE[${modindex}]} no longer exists. Skipping."
+      ((modindex=modindex+1))
+      continue
+    fi
+
+    argv=("${@//@@@MODULEFN@@@/${fn}}")
+    argv=("${argv[@]//@@@MODULEDIR@@@/${BASEDIR}/${MODULE[${modindex}]}}")
+
+    # shellcheck disable=2086,2046
+    echo_and_redirect "${PATCH_DIR}/${repostatus}-${testtype}-${fn}.txt" \
+      $("${BUILDTOOL}_executor" "${testtype}") \
+      ${MODULEEXTRAPARAM[${modindex}]//@@@MODULEFN@@@/${fn}} \
+      "${argv[@]}"
+    execvalue=$?
+
+    reaper_post_exec "${modulesuffix}" "${repostatus}-${testtype}-${fn}"
+    ((execvalue = execvalue + $? ))
+
+    if [[ ${execvalue} == 0 ]] ; then
+      module_status \
+        ${modindex} \
+        +1 \
+        "${repostatus}-${testtype}-${fn}.txt" \
+        "${modulesuffix} in ${repo} passed${statusjdk}."
+    else
+      module_status \
+        ${modindex} \
+        -1 \
+        "${repostatus}-${testtype}-${fn}.txt" \
+        "${modulesuffix} in ${repo} failed${statusjdk}."
+      ((result = result + 1))
+    fi
+
+    # compile is special
+    if [[ ${testtype} = compile ]]; then
+      MODULE_COMPILE_LOG[${modindex}]="${PATCH_DIR}/${repostatus}-${testtype}-${fn}.txt"
+      yetus_debug "Compile log set to ${MODULE_COMPILE_LOG[${modindex}]}"
+    fi
+
+    savestop=$(stop_clock)
+    MODULE_STATUS_TIMER[${modindex}]=${savestop}
+    # shellcheck disable=SC2086
+    echo "Elapsed: $(clock_display ${savestop})"
+    popd >/dev/null
+    ((modindex=modindex+1))
+  done
+
+  TIMER=${savestart}
+
+  if [[ ${result} -gt 0 ]]; then
+    return 1
+  fi
+  return 0
+}
+
+## @description  Reset the queue for tests
+## @audience     public
+## @stability    evolving
+## @replaceable  no
+function clear_personality_queue
+{
+  yetus_debug "Personality: clear queue"
+  MODCOUNT=0
+  MODULE=()
+}
+
+## @description  Build the queue for tests
+## @audience     public
+## @stability    evolving
+## @replaceable  no
+## @param        module
+## @param        profiles/flags/etc
+function personality_enqueue_module
+{
+  yetus_debug "Personality: enqueue $*"
+  local module=$1
+  shift
+
+  MODULE[${MODCOUNT}]=${module}
+  MODULEEXTRAPARAM[${MODCOUNT}]=${*}
+  ((MODCOUNT=MODCOUNT+1))
+}
+
+## @description  Remove a module
+## @audience     public
+## @stability    evolving
+## @replaceable  no
+## @param        modulenames
+function dequeue_personality_module
+{
+  declare modname=$1
+  declare oldmodule=("${MODULE[@]}")
+  declare oldmodparams=("${MODULEESXTRAPARAM[@]}")
+  declare modindex=0
+
+  yetus_debug "Personality: dequeue $*"
+
+  clear_personality_queue
+
+  until [[ ${modindex} -eq ${#oldmodule[@]} ]]; do
+    if [[ "${oldmodule[${modindex}]}" = "${modname}" ]]; then
+      yetus_debug "Personality: removing ${modindex}, ${oldmodule[${modindex}]} = ${modname}"
+    else
+      personality_enqueue_module "${oldmodule[${modindex}]}" "${oldmodparams[${modindex}]}"
+    fi
+    ((modindex=modindex+1))
+  done
+}
+
+## @description  Utility to push many tests into the failure list
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+## @param        testdesc
+## @param        testlist
+function populate_test_table
+{
+  local reason=$1
+  shift
+  local first=""
+  local i
+
+  for i in "$@"; do
+    if [[ -z "${first}" ]]; then
+      add_test_table "${reason}" "${i}"
+      first="${reason}"
+    else
+      add_test_table " " "${i}"
+    fi
+  done
+}
+
+## @description  Run and verify the output of the appropriate unit tests
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+## @return       0 on success
+## @return       1 on failure
+function check_unittests
+{
+  declare i
+  declare testsys
+  declare test_logfile
+  declare result=0
+  declare -r savejavahome=${JAVA_HOME}
+  declare multijdkmode
+  declare jdk=""
+  declare jdkindex=0
+  declare jdklist
+  declare statusjdk
+  declare formatresult=0
+  declare needlog
+
+  if ! verify_needed_test unit; then
+    return 0
+  fi
+
+  big_console_header "Running unit tests"
+
+  if verify_multijdk_test unit; then
+    multijdkmode=true
+    jdklist=${JDK_DIR_LIST}
+  else
+    multijdkmode=false
+    jdklist=${JAVA_HOME}
+  fi
+
+  for jdkindex in ${jdklist}; do
+    if [[ ${multijdkmode} == true ]]; then
+      JAVA_HOME=${jdkindex}
+      jdk=$(report_jvm_version "${JAVA_HOME}")
+      statusjdk="JDK v${jdk} "
+      jdk="-jdk${jdk}"
+      jdk=${jdk// /}
+    fi
+
+    personality_modules patch unit
+    "${BUILDTOOL}_modules_worker" patch unit
+
+    ((result=result+$?))
+
+    i=0
+    until [[ $i -eq ${#MODULE[@]} ]]; do
+      module=${MODULE[${i}]}
+      fn=$(module_file_fragment "${module}")
+      fn="${fn}${jdk}"
+      test_logfile="${PATCH_DIR}/patch-unit-${fn}.txt"
+
+      buildtool_cwd "${i}"
+
+      needlog=0
+      for testsys in ${TESTFORMATS}; do
+        if declare -f ${testsys}_process_tests >/dev/null; then
+          yetus_debug "Calling ${testsys}_process_tests"
+          "${testsys}_process_tests" "${module}" "${test_logfile}" "${fn}"
+          formatresult=$?
+          ((result=result+formatresult))
+          if [[ "${formatresult}" != 0 ]]; then
+            needlog=1
+          fi
+        fi
+      done
+
+      if [[ ${needlog} == 1 ]]; then
+        module_status ${i} -1 "patch-unit-${fn}.txt"
+      fi
+
+      popd >/dev/null
+
+      ((i=i+1))
+    done
+
+    for testsys in ${TESTFORMATS}; do
+      if declare -f ${testsys}_finalize_results >/dev/null; then
+        yetus_debug "Calling ${testsys}_finalize_results"
+        "${testsys}_finalize_results" "${statusjdk}"
+      fi
+    done
+
+  done
+  JAVA_HOME=${savejavahome}
+
+  modules_messages patch unit false
+
+  if [[ ${JENKINS} == true ]]; then
+    add_footer_table "${statusjdk} Test Results" "${BUILD_URL}testReport/"
+  fi
+
+  if [[ ${result} -gt 0 ]]; then
+    return 1
+  fi
+  return 0
+}
+
+## @description  Write comments onto bug systems that have code review support.
+## @description  File should be in the form of "file:line:comment"
+## @audience     public
+## @stability    evolving
+## @replaceable  no
+## @param        filename
+function bugsystem_linecomments
+{
+  declare title=$1
+  declare fn=$2
+  declare line
+  declare bugs
+  declare realline
+  declare text
+  declare idxline
+  declare uniline
+
+  if [[ ! -f "${GITUNIDIFFLINES}" ]]; then
+    return
+  fi
+
+  while read -r line;do
+    file=$(echo "${line}" | cut -f1 -d:)
+    realline=$(echo "${line}" | cut -f2 -d:)
+    text=$(echo "${line}" | cut -f3- -d:)
+    idxline="${file}:${realline}:"
+    uniline=$(${GREP} "${idxline}" "${GITUNIDIFFLINES}" | cut -f3 -d: )
+
+    for bugs in ${BUGLINECOMMENTS}; do
+      if declare -f ${bugs}_linecomments >/dev/null;then
+        "${bugs}_linecomments" "${title}" "${file}" "${realline}" "${uniline}" "${text}"
+      fi
+    done
+  done < "${fn}"
+}
+
+## @description  Write the final output to the selected bug system
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+function bugsystem_finalreport
+{
+  declare version
+  declare bugs
+
+  if [[ "${ROBOT}" = true &&
+        -n "${BUILD_URL}" &&
+        -n "${BUILD_URL_CONSOLE}" ]]; then
+    add_footer_table "Console output" "${BUILD_URL}${BUILD_URL_CONSOLE}"
+  fi
+  add_footer_table "Powered by" "Apache Yetus ${VERSION} http://yetus.apache.org"
+
+  for bugs in ${BUGCOMMENTS}; do
+    if declare -f ${bugs}_finalreport >/dev/null;then
+      "${bugs}_finalreport" "${@}"
+    fi
+  done
+}
+
+## @description  Clean the filesystem as appropriate and then exit
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+## @param        runresult
+function cleanup_and_exit
+{
+  local result=$1
+
+  if [[ ${ROBOT} == "true" && ${RELOCATE_PATCH_DIR} == "true" && \
+      -e ${PATCH_DIR} && -d ${PATCH_DIR} ]] ; then
+    # if PATCH_DIR is already inside BASEDIR, then
+    # there is no need to move it since we assume that
+    # Jenkins or whatever already knows where it is at
+    # since it told us to put it there!
+    relative_dir "${PATCH_DIR}" >/dev/null
+    if [[ $? == 1 ]]; then
+      yetus_debug "mv ${PATCH_DIR} ${BASEDIR}"
+      mv "${PATCH_DIR}" "${BASEDIR}"
+    fi
+  fi
+  big_console_header "Finished build."
+
+  # shellcheck disable=SC2086
+  exit ${result}
+}
+
+## @description  Driver to execute _tests routines
+## @audience     private
+## @stability    evolving
+## @replaceable  no
+function runtests
+{
+  local plugin
+
+  if [[ ${RUN_TESTS} == "true" ]] ; then
+
+    verify_patchdir_still_exists
+    check_unittests
+  fi
+
+  for plugin in ${TESTTYPES}; do
+    verify_patchdir_still_exists
+    if declare -f ${plugin}_tests >/dev/null 2>&1; then
+      modules_reset
+      yetus_debug "Running ${plugin}_tests"
+      #shellcheck disable=SC2086
+      ${plugin}_tests
+    fi
+  done
+  archive
+}
+
+## @description  Calculate the differences between the specified files
+## @description  using just the column+ messages (third+ column in a
+## @descriptoin  colon delimated flie) and output it to stdout.
+## @audience     public
+## @stability    evolving
+## @replaceable  no
+## @param        branchlog
+## @param        patchlog
+## @return       differences
+function column_calcdiffs
+{
+  declare branch=$1
+  declare patch=$2
+  declare tmp=${PATCH_DIR}/pl.$$.${RANDOM}
+  declare j
+
+  # first, strip filenames:line:
+  # this keeps column: in an attempt to increase
+  # accuracy in case of multiple, repeated errors
+  # since the column number shouldn't change
+  # if the line of code hasn't been touched
+  # shellcheck disable=SC2016
+  cut -f3- -d: "${branch}" > "${tmp}.branch"
+  # shellcheck disable=SC2016
+  cut -f3- -d: "${patch}" > "${tmp}.patch"
+
+  # compare the errors, generating a string of line
+  # numbers. Sorry portability: GNU diff makes this too easy
+  ${DIFF} --unchanged-line-format="" \
+     --old-line-format="" \
+     --new-line-format="%dn " \
+     "${tmp}.branch" \
+     "${tmp}.patch" > "${tmp}.lined"
+
+  # now, pull out those lines of the raw output
+  # shellcheck disable=SC2013
+  for j in $(cat "${tmp}.lined"); do
+    # shellcheck disable=SC2086
+    head -${j} "${patch}" | tail -1
+  done
+
+  rm "${tmp}.branch" "${tmp}.patch" "${tmp}.lined" 2>/dev/null
+}
+
+## @description  Calculate the differences between the specified files
+## @description  using just the error messages (last column in a
+## @descriptoin  colon delimated flie) and output it to stdout.
+## @audience     public
+## @stability    evolving
+## @replaceable  no
+## @param        branchlog
+## @param        patchlog
+## @return       differences
+function error_calcdiffs
+{
+  declare branch=$1
+  declare patch=$2
+  declare tmp=${PATCH_DIR}/pl.$$.${RANDOM}
+  declare j
+
+  # first, pull out just the errors
+  # shellcheck disable=SC2016
+  ${AWK} -F: '{print $NF}' "${branch}" > "${tmp}.branch"
+
+  # shellcheck disable=SC2016
+  ${AWK} -F: '{print $NF}' "${patch}" > "${tmp}.patch"
+
+  # compare the errors, generating a string of line
+  # numbers. Sorry portability: GNU diff makes this too easy
+  ${DIFF} --unchanged-line-format="" \
+     --old-line-format="" \
+     --new-line-format="%dn " \
+     "${tmp}.branch" \
+     "${tmp}.patch" > "${tmp}.lined"
+
+  # now, pull out those lines of the raw output
+  # shellcheck disable=SC2013
+  for j in $(cat "${tmp}.lined"); do
+    # shellcheck disable=SC2086
+    head -${j} "${patch}" | tail -1
+  done
+
+  rm "${tmp}.branch" "${tmp}.patch" "${tmp}.lined" 2>/dev/null
+}
+
+## @description  Wrapper to call specific version of calcdiffs if available
+## @description  otherwise calls error_calcdiffs
+## @audience     public
+## @stability    evolving
+## @replaceable  no
+## @param        branchlog
+## @param        patchlog
+## @param        testtype
+## @return       differences
+function calcdiffs
+{
+  declare branchlog=$1
+  declare patchlog=$2
+  declare testtype=$3
+
+  # ensure that both log files exist
+  if [[ ! -f "${branchlog}" ]]; then
+    touch "${branchlog}"
+  fi
+  if [[ ! -f "${patchlog}" ]]; then
+    touch "${patchlog}"
+  fi
+
+  if declare -f ${PROJECT_NAME}_${testtype}_calcdiffs >/dev/null; then
+    "${PROJECT_NAME}_${testtype}_calcdiffs" "${branchlog}" "${patchlog}"
+  elif declare -f ${BUILDTOOL}_${testtype}_calcdiffs >/dev/null; then
+    "${BUILDTOOL}_${testtype}_calcdiffs" "${branchlog}" "${patchlog}"
+  elif declare -f ${testtype}_calcdiffs >/dev/null; then
+    "${testtype}_calcdiffs" "${branchlog}" "${patchlog}"
+  else
+    error_calcdiffs "${branchlog}" "${patchlog}"
+  fi
+}
+
+## @description generate a standarized calcdiff status message
+## @audience    public
+## @stability   evolving
+## @replaceable no
+## @param       totalbranchissues
+## @param       totalpatchissues
+## @param       newpatchissues
+## @return      errorstring
+function generic_calcdiff_status
+{
+  declare -i numbranch=$1
+  declare -i numpatch=$2
+  declare -i addpatch=$3
+  declare -i samepatch
+  declare -i fixedpatch
+
+  ((samepatch=numpatch-addpatch))
+  ((fixedpatch=numbranch-numpatch+addpatch))
+
+  if [[ "${BUILDMODE}" = full ]]; then
+    printf "has %i issues." "${addpatch}"
+  else
+    printf "generated %i new + %i unchanged - %i fixed = %i total (was %i)" \
+      "${addpatch}" \
+      "${samepatch}" \
+      "${fixedpatch}" \
+      "${numpatch}" \
+      "${numbranch}"
+  fi
+}
+
+## @description  Helper routine for plugins to ask projects, etc
+## @description  to count problems in a log file
+## @description  and output it to stdout.
+## @audience     public
+## @stability    evolving
+## @replaceable  no
+## @return       number of issues
+function generic_logfilter
+{
+  declare testtype=$1
+  declare input=$2
+  declare output=$3
+
+  if declare -f ${PROJECT_NAME}_${testtype}_logfilter >/dev/null; then
+    "${PROJECT_NAME}_${testtype}_logfilter" "${input}" "${output}"
+  elif declare -f ${BUILDTOOL}_${testtype}_logfilter >/dev/null; then
+    "${BUILDTOOL}_${testtype}_logfilter" "${input}" "${output}"
+  elif declare -f ${testtype}_logfilter >/dev/null; then
+    "${testtype}_logfilter" "${input}" "${output}"
+  else
+    yetus_error "ERROR: ${testtype}: No function defined to filter problems."
+    echo 0
+  fi
+}
+
+## @description  Helper routine for plugins to do a pre-patch prun
+## @audience     public
+## @stability    evolving
+## @replaceable  no
+## @param        testype
+## @param        multijdk
+## @return       1 on failure
+## @return       0 on success
+function generic_pre_handler
+{
+  declare testtype=$1
+  declare multijdkmode=$2
+  declare result=0
+  declare -r savejavahome=${JAVA_HOME}
+  declare multijdkmode
+  declare jdkindex=0
+  declare jdklist
+
+  if ! verify_needed_test "${testtype}"; then
+     return 0
+  fi
+
+  big_console_header "Pre-patch ${testtype} verification on ${PATCH_BRANCH}"
+
+  if verify_multijdk_test "${testtype}"; then
+    multijdkmode=true
+    jdklist=${JDK_DIR_LIST}
+  else
+    multijdkmode=false
+    jdklist=${JAVA_HOME}
+  fi
+
+  for jdkindex in ${jdklist}; do
+    if [[ ${multijdkmode} == true ]]; then
+      JAVA_HOME=${jdkindex}
+    fi
+
+    personality_modules branch "${testtype}"
+    "${BUILDTOOL}_modules_worker" branch "${testtype}"
+
+    ((result=result + $?))
+    modules_messages branch "${testtype}" true
+
+  done
+  JAVA_HOME=${savejavahome}
+
+  if [[ ${result} -gt 0 ]]; then
+    return 1
+  fi
+  return 0
+}
+
+## @description  Generic post-patch log handler
+## @audience     public
+## @stability    evolving
+## @replaceable  no
+## @return       0 on success
+## @return       1 on failure
+## @param        origlog
+## @param        testtype
+## @param        multijdkmode
+function generic_postlog_compare
+{
+  declare origlog=$1
+  declare testtype=$2
+  declare multijdk=$3
+  declare result=0
+  declare i
+  declare fn
+  declare jdk
+  declare statusjdk
+  declare -i numbranch=0
+  declare -i numpatch=0
+  declare -i addpatch=0
+  declare -i samepatch=0
+  declare -i fixedpatch=0
+  declare summarize=true
+
+  if [[ ${multijdk} == true ]]; then
+    jdk=$(report_jvm_version "${JAVA_HOME}")
+    statusjdk=" with JDK v${jdk}"
+    jdk="-jdk${jdk}"
+    jdk=${jdk// /}
+  fi
+
+  i=0
+  until [[ ${i} -eq ${#MODULE[@]} ]]; do
+    if [[ ${MODULE_STATUS[${i}]} == -1 ]]; then
+      ((result=result+1))
+      ((i=i+1))
+      continue
+    fi
+
+    fn=$(module_file_fragment "${MODULE[${i}]}")
+    fn="${fn}${jdk}"
+    module_suffix=$(basename "${MODULE[${i}]}")
+    if [[ ${module_suffix} == \. ]]; then
+      module_suffix=root
+    fi
+
+    yetus_debug "${testtype}: branch-${origlog}-${fn}.txt vs. patch-${origlog}-${fn}.txt"
+
+    # if it was a new module, this won't exist.
+    if [[ ! -f "${PATCH_DIR}/branch-${origlog}-${fn}.txt" ]]; then
+      touch "${PATCH_DIR}/branch-${origlog}-${fn}.txt"
+    fi
+
+    if [[ ! -f "${PATCH_DIR}/patch-${origlog}-${fn}.txt" ]]; then
+      touch "${PATCH_DIR}/patch-${origlog}-${fn}.txt"
+    fi
+
+    generic_logfilter "${testtype}" "${PATCH_DIR}/branch-${origlog}-${fn}.txt" "${PATCH_DIR}/branch-${origlog}-${testtype}-${fn}.txt"
+    generic_logfilter "${testtype}" "${PATCH_DIR}/patch-${origlog}-${fn}.txt" "${PATCH_DIR}/patch-${origlog}-${testtype}-${fn}.txt"
+
+    # shellcheck disable=SC2016
+    numbranch=$(wc -l "${PATCH_DIR}/branch-${origlog}-${testtype}-${fn}.txt" | ${AWK} '{print $1}')
+    # shellcheck disable=SC2016
+    numpatch=$(wc -l "${PATCH_DIR}/patch-${origlog}-${testtype}-${fn}.txt" | ${AWK} '{print $1}')
+
+    calcdiffs \
+      "${PATCH_DIR}/branch-${origlog}-${testtype}-${fn}.txt" \
+      "${PATCH_DIR}/patch-${origlog}-${testtype}-${fn}.txt" \
+      "${testtype}" \
+      > "${PATCH_DIR}/diff-${origlog}-${testtype}-${fn}.txt"
+
+    # shellcheck disable=SC2016
+    addpatch=$(wc -l "${PATCH_DIR}/diff-${origlog}-${testtype}-${fn}.txt" | ${AWK} '{print $1}')
+
+    ((fixedpatch=numbranch-numpatch+addpatch))
+
+    statstring=$(generic_calcdiff_status "${numbranch}" "${numpatch}" "${addpatch}" )
+
+    if [[ ${addpatch} -gt 0 ]]; then
+      ((result = result + 1))
+      module_status "${i}" -1 "diff-${origlog}-${testtype}-${fn}.txt" "${fn}${statusjdk} ${statstring}"
+    elif [[ ${fixedpatch} -gt 0 ]]; then
+      module_status "${i}" +1 "${MODULE_STATUS_LOG[${i}]}" "${fn}${statusjdk} ${statstring}"
+      summarize=false
+    fi
+    ((i=i+1))
+  done
+
+  modules_messages patch "${testtype}" "${summarize}"
+  if [[ ${result} -gt 0 ]]; then
+    return 1
+  fi
+  return 0
+}
+
+## @description  Generic post-patch handler
+## @audience     public
+## @stability    evolving
+## @replaceable  no
+## @return       0 on success
+## @return       1 on failure
+## @param        origlog
+## @param        testtype
+## @param        multijdkmode
+## @param        run commands
+function generic_post_handler
+{
+  declare origlog=$1
+  declare testtype=$2
+  declare multijdkmode=$3
+  declare need2run=$4
+  declare i
+  declare result=0
+  declare fn
+  declare -r savejavahome=${JAVA_HOME}
+  declare jdk=""
+  declare jdkindex=0
+  declare statusjdk
+  declare -i numbranch=0
+  declare -i numpatch=0
+
+  if ! verify_needed_test "${testtype}"; then
+    yetus_debug "${testtype} not needed"
+    return 0
+  fi
+
+  big_console_header "${testtype} verification: ${BUILDMODE}"
+
+  for jdkindex in ${JDK_DIR_LIST}; do
+    if [[ ${multijdkmode} == true ]]; then
+      JAVA_HOME=${jdkindex}
+      yetus_debug "Using ${JAVA_HOME} to run this set of tests"
+    fi
+
+    if [[ ${need2run} = true ]]; then
+      personality_modules "${codebase}" "${testtype}"
+      "${BUILDTOOL}_modules_worker" "${codebase}" "${testtype}"
+
+      if [[ ${UNSUPPORTED_TEST} = true ]]; then
+        return 0
+      fi
+    fi
+
+    generic_postlog_compare "${origlog}" "${testtype}" "${multijdkmode}"
+    ((result=result+$?))
+  done
+  JAVA_HOME=${savejavahome}
+
+  if [[ ${result} -gt 0 ]]; then
+    return 1
+  fi
+  return 0
+}
+
+## @description  Execute the compile phase. This will callout
+## @description  to _compile
+## @audience     public
+## @stability    evolving
+## @replaceable  no
+## @param        branch|patch
+## @return       0 on success
+## @return       1 on failure
+function compile_jvm
+{
+  declare codebase=$1
+  declare result=0
+  declare -r savejavahome=${JAVA_HOME}
+  declare multijdkmode
+  declare jdkindex=0
+  declare jdklist
+
+  if verify_multijdk_test compile; then
+    multijdkmode=true
+    jdklist=${JDK_DIR_LIST}
+  else
+    multijdkmode=false
+    jdklist=${JAVA_HOME}
+  fi
+
+  for jdkindex in ${jdklist}; do
+    if [[ ${multijdkmode} == true ]]; then
+      JAVA_HOME=${jdkindex}
+    fi
+
+    compile_nonjvm "${codebase}" "${multijdkmode}"
+
+  done
+  JAVA_HOME=${savejavahome}
+
+  if [[ ${result} -gt 0 ]]; then
+    return 1
+  fi
+  return 0
+}
+
+## @description  Execute the compile phase. This will callout
+## @description  to _compile
+## @audience     public
+## @stability    evolving
+## @replaceable  no
+## @param        branch|patch
+## @return       0 on success
+## @return       1 on failure
+function compile_nonjvm
+{
+  declare codebase=$1
+  declare result=0
+  declare -r savejavahome=${JAVA_HOME}
+  declare multijdkmode=${2:-false}
+  declare jdkindex=0
+
+  personality_modules "${codebase}" compile
+  "${BUILDTOOL}_modules_worker" "${codebase}" compile
+  modules_messages "${codebase}" compile true
+
+  modules_backup
+
+  for plugin in ${TESTTYPES}; do
+    modules_restore
+    verify_patchdir_still_exists
+    if declare -f ${plugin}_compile >/dev/null 2>&1; then
+      yetus_debug "Running ${plugin}_compile ${codebase} ${multijdkmode}"
+      "${plugin}_compile" "${codebase}" "${multijdkmode}"
+      ((result = result + $?))
+      archive
+    fi
+  done
+
+  if [[ ${result} -gt 0 ]]; then
+    return 1
+  fi
+  return 0
+}
+
+## @description  Execute the compile phase. This will callout
+## @description  to _compile
+## @audience     public
+## @stability    evolving
+## @replaceable  no
+## @param        branch|patch
+## @return       0 on success
+## @return       1 on failure
+function compile
+{
+  declare codebase=$1
+
+  if ! verify_needed_test compile; then
+     return 0
+  fi
+
+  if [[ ${codebase} = "branch" ]]; then
+    big_console_header "${PATCH_BRANCH} compilation: pre-patch"
+  else
+    big_console_header "${PATCH_BRANCH} compilation: ${BUILDMODE}"
+  fi
+
+  yetus_debug "Is JVM Required? ${JVM_REQUIRED}"
+  if [[ "${JVM_REQUIRED}" = true ]]; then
+    compile_jvm "${codebase}"
+  else
+    compile_nonjvm "${codebase}"
+  fi
+}
+
+## @description  Execute the static analysis test cycle.
+## @description  This will callout to _precompile, compile, _postcompile and _rebuild
+## @audience     public
+## @stability    evolving
+## @replaceable  no
+## @param        branch|patch
+## @return       0 on success
+## @return       1 on failure
+function compile_cycle
+{
+  declare codebase=$1
+  declare result=0
+  declare plugin
+
+  find_changed_modules "${codebase}"
+
+  for plugin in ${PROJECT_NAME} ${BUILDTOOL} ${TESTTYPES} ${TESTFORMATS}; do
+    if declare -f ${plugin}_precompile >/dev/null 2>&1; then
+      yetus_debug "Running ${plugin}_precompile"
+      #shellcheck disable=SC2086
+      ${plugin}_precompile ${codebase}
+      if [[ $? -gt 0 ]]; then
+        ((result = result+1))
+      fi
+      archive
+    fi
+  done
+
+  compile "${codebase}"
+
+  for plugin in ${PROJECT_NAME} ${BUILDTOOL} ${TESTTYPES} ${TESTFORMATS}; do
+    if declare -f ${plugin}_postcompile >/dev/null 2>&1; then
+      yetus_debug "Running ${plugin}_postcompile"
+      #shellcheck disable=SC2086
+      ${plugin}_postcompile ${codebase}
+      if [[ $? -gt 0 ]]; then
+        ((result = result+1))
+      fi
+      archive
+    fi
+  done
+
+  for plugin in ${PROJECT_NAME} ${BUILDTOOL} ${TESTTYPES} ${TESTFORMATS}; do
+    if declare -f ${plugin}_rebuild >/dev/null 2>&1; then
+      yetus_debug "Running ${plugin}_rebuild"
+      #shellcheck disable=SC2086
+      ${plugin}_rebuild ${codebase}
+      if [[ $? -gt 0 ]]; then
+        ((result = result+1))
+      fi
+      archive
+    fi
+  done
+
+  if [[ ${result} -gt 0 ]]; then
+    return 1
+  fi
+  return 0
+}
+
+## @description  Execute the patch file test phase. Calls out to
+## @description  to _patchfile
+## @audience     public
+## @stability    evolving
+## @replaceable  no
+## @param        branch|patch
+## @return       0 on success
+## @return       1 on failure
+function patchfiletests
+{
+  declare plugin
+  declare result=0
+
+  for plugin in ${BUILDTOOL} ${TESTTYPES} ${TESTFORMATS}; do
+    if declare -f ${plugin}_patchfile >/dev/null 2>&1; then
+      yetus_debug "Running ${plugin}_patchfile"
+      #shellcheck disable=SC2086
+      ${plugin}_patchfile "${PATCH_DIR}/patch"
+      if [[ $? -gt 0 ]]; then
+        ((result = result+1))
+      fi
+      archive
+    fi
+  done
+
+  if [[ ${result} -gt 0 ]]; then
+    return 1
+  fi
+  return 0
+}
+
+
+## @description  Wipe the repo clean to not invalidate tests
+## @audience     public
+## @stability    evolving
+## @replaceable  no
+## @return       0 on success
+## @return       1 on failure
+function distclean
+{
+  declare result=0
+  declare plugin
+
+  big_console_header "Cleaning the source tree"
+
+  for plugin in ${TESTTYPES} ${TESTFORMATS}; do
+    if declare -f ${plugin}_clean >/dev/null 2>&1; then
+      yetus_debug "Running ${plugin}_distclean"
+      #shellcheck disable=SC2086
+      ${plugin}_clean
+      if [[ $? -gt 0 ]]; then
+        ((result = result+1))
+      fi
+    fi
+  done
+
+  personality_modules branch distclean
+  "${BUILDTOOL}_modules_worker" branch distclean
+  (( result = result + $? ))
+
+  if [[ ${result} -gt 0 ]]; then
+    return 1
+  fi
+  return 0
+}
+
+## @description  Start any coprocessors
+## @audience     private
+## @stability    evolving
+## @replaceable  yes
+function start_coprocessors
+{
+
+  declare 

<TRUNCATED>

Mime
View raw message