#!/bin/bash

############################################################################
#
# Script for running those SCST regression tests that can be run automatically.
#
# Copyright (C) 2008-2009 Bart Van Assche <bvanassche@acm.org>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation, version 2
# of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
############################################################################

############################################################################
# This script performs the following actions:
# - Creates a temporary directory for storing the output of the regression
#   tests. No existing files are modified by this script.
# - Verifies whether the top-level *.patch files apply cleanly to the SCST
#   tree.
# - Duplicates the entire source tree to the temporary directory and
#   compiles the SCST source code.
# - Duplicates the entire source tree to the temporary directory, runs
#   'make debug2release' and again compiles the SCST source code.
# - Checks whether the specified kernel version is present
#   in the directory specified through option -c.
# - If the source code of the specified kernel version is not present,
#   download it.
# - Convert the SCST source code into a kernel patch.
# - Extract the kernel sources.
# - Run checkpatch on the SCST kernel patch.
# - Apply the SCST kernel patch to the kernel tree.
# - Run 'make allmodconfig'.
# - Run the sparse source code checker on the SCST directory.
# - Run 'make headers_check'.
# - Compile the kernel tree.
# - Run 'make checkstack'.
# - Run 'make namespacecheck'.
# - Run 'make htmldocs'.
#
# Note: the results of the individual steps are not verified by this script
# -- the output generated by the individual steps has to be verified by
# reviewing the output files written into the temporary directory.
############################################################################


########################
# Function definitions #
########################

source $(dirname $0)/kernel-version

function usage {
  echo "Usage: $0 [-c <dir>] [-d <dir>] [-f] [-h] [-j <jobs>] [-p <patchdir>] [-q] <kver1> <kver2> ..."
  echo "        -c - cache directory for Linux kernel tarballs."
  echo "        -d - directory for temporary regression test files."
  echo "        -h - display this help information."
  echo "        -j - number of jobs that 'make' should run simultaneously."
  echo "        -k - remove temporary files before exiting."
  echo "        -q - download kernel sources silently."
  echo "        <kver1> <kver2> ... - kernel versions to test."
}

# Create a linux-$1 tree in the current directory, where $1 is a kernel
# version number with either three or four components.
function extract_kernel_tree {
  local kver="$(kernel_version $1)"
  local plevel="$(patchlevel $1)"
  local tmpdir=kernel-tree-tmp-$$

  rm -rf "linux-$1" "${tmpdir}"
  mkdir "${tmpdir}" || return $?
  (
    cd "${tmpdir}" || return $?
    tar xjf "${kernel_sources}/linux-${kver}.tar.bz2" || return $?
    cd "linux-${kver}" || return $?
    if [ "${plevel}" != "" ]; then
      bzip2 -cd "${kernel_sources}/patch-$1.bz2" \
        | patch -p1 -f -s || return $?
    fi
    cd ..
    mv "linux-${kver}" "../linux-$1" || return $?
    if [ "$1" = "2.6.29" -o "$1" = "2.6.29.1" -o "$1" = "2.6.29.2" -o "$1" = "2.6.29.3" ]
    then
      cd "../linux-$1" || return $?
      patch -f -s -p1 <<'EOF'
Make sure that branch profiling does not trigger sparse warnings.
See also http://bugzilla.kernel.org/show_bug.cgi?id=12925
---
See also http://lkml.org/lkml/2009/4/5/120
--- orig/linux-2.6.29/include/linux/compiler.h	2009-03-23 19:12:14.000000000 -0400
+++ linux-2.6.29/include/linux/compiler.h	2009-03-24 08:46:46.000000000 -0400
@@ -75,7 +75,8 @@ struct ftrace_branch_data {
  * Note: DISABLE_BRANCH_PROFILING can be used by special lowlevel code
  * to disable branch tracing on a per file basis.
  */
-#if defined(CONFIG_TRACE_BRANCH_PROFILING) && !defined(DISABLE_BRANCH_PROFILING)
+#if defined(CONFIG_TRACE_BRANCH_PROFILING) \
+    && !defined(DISABLE_BRANCH_PROFILING) && !defined(__CHECKER__)
 void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect);
 
 #define likely_notrace(x)	__builtin_expect(!!(x), 1)
EOF
    fi
    if [ "$1" = "2.6.31" -o "${1#2.6.31.}" != "$1" ]
    then
      cd "../linux-$1" || return $?
      patch -f -s -p1 <<'EOF'
Checking a 2.6.31.1 kernel configured with allyesconfig/allmodconfig
with sparse (make C=2) triggers a sparse warning on code that uses the
kmemcheck_annotate_bitfield() macro. An example of such a warning:

include/net/inet_sock.h:208:17: warning: do-while statement is not a compound statement

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
Cc: Vegard Nossum <vegardno@ifi.uio.no>
Cc: Andrew Morton <akpm@linux-foundation.org>

---
See also http://lkml.org/lkml/2009/9/26/51

--- linux-2.6.31.1/include/linux/kmemcheck-orig.h	2009-09-26 13:53:44.000000000 +0200
+++ linux-2.6.31.1/include/linux/kmemcheck.h	2009-09-26 13:53:56.000000000 +0200
@@ -137,13 +137,13 @@ static inline void kmemcheck_mark_initia
 	int name##_end[0];
 
 #define kmemcheck_annotate_bitfield(ptr, name)				\
-	do if (ptr) {							\
+	do { if (ptr) {							\
 		int _n = (long) &((ptr)->name##_end)			\
 			- (long) &((ptr)->name##_begin);		\
 		BUILD_BUG_ON(_n < 0);					\
 									\
 		kmemcheck_mark_initialized(&((ptr)->name##_begin), _n);	\
-	} while (0)
+	} } while (0)
 
 #define kmemcheck_annotate_variable(var)				\
 	do {								\
EOF
    fi
    if [ "$1" = 2.6.32 -o "${1#2.6.32.}" != "$1" -o "$1" = "2.6.33" \
	 -o "${1#2.6.33.}" != "$1" ]
    then
      cd "../linux-$1" || return $?
      patch -f -s -p1 <<'EOF'
Get rid of sparse errors on sk_buff.protocol.

--- linux/include/linux/skbuff-orig.h	2010-12-07 13:40:51.000000000 -0500
+++ linux/include/linux/skbuff.h	2010-12-07 13:41:05.000000000 -0500
@@ -349,8 +349,8 @@ struct sk_buff {
 				ipvs_property:1,
 				peeked:1,
 				nf_trace:1;
-	__be16			protocol:16;
 	kmemcheck_bitfield_end(flags1);
+	__be16			protocol;
 
 	void			(*destructor)(struct sk_buff *skb);
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
EOF
    fi
  )
  rmdir "${tmpdir}"
}

# Test whether the *.patch files in the SCST top-level directory apply cleanly
# to the SCST tree. Does not modify any files nor produce any output files.
function test_scst_tree_patches {
  local rc=0
  echo "Testing whether the SCST patches apply cleanly to the SCST tree ..."
  for p in *.patch srpt/patches/scst*.patch
  do
    if [ -e "$p" ] && ! patch -p0 -f --dry-run -s <$p &>/dev/null; then
      echo "ERROR: patch $p does not apply cleanly."
      rc=1
    fi
  done
  if [ "${rc}" = 0 ]; then
    echo "OK"
  fi
}

# Copy the entire SCST source code tree from "$1" into the current directory.
# Only copy those files which are administered by Subversion.
function duplicate_scst_source_tree {
  if [ -e "$1/AskingQuestions" ]; then
    ( cd "$1" && svn status -v | grep -v '^[D?]' | cut -c3- | awk '{print $4}' \
      | while read f; do [ ! -d "$f" ] && echo "$f"; done ) \
    | tar -C "$1" --files-from=- -c -f - | tar -x -f -
  else
    return 1
  fi
}

# Compile the unpatched SCST source code.
function compile_scst_unpatched {
  local scst="$PWD"
  local outputfile="${outputdir}/compilation-output-unpatched.txt"
  local workingdirectory="${outputdir}/scst-unpatched"

  echo "Testing whether the SCST tree compiles fine ..."
  (
    if mkdir -p "${workingdirectory}"  \
       && cd "${workingdirectory}"     \
       && duplicate_scst_source_tree "${scst}"  \
       && (export KCFLAGS=-DCONFIG_SCST_MEASURE_LATENCY; \
           make -s clean \
           && make -s scst iscsi-scst  \
           && if "${scst_local}" = "true" ; then make -C scst_local clean; fi \
           && if "${scst_local}" = "true" ; then make -C scst_local -s   ; fi \
           && if "${mpt_scst}"   = "true" ; then make -C mpt clean; fi \
           && if "${mpt_scst}"   = "true" ; then make -C mpt -s   ; fi \
           && make -C srpt -s clean     \
           && make -C srpt -s )         \
           &> "${outputfile}"
    then
      echo "OK"
    else
      echo "FAILED"
    fi
  )
}

# Test out-of-tree compilation agains the kernel header files in
# /lib/modules/$(uname -r)/build.
function compile_scst_patched {
  local scst="$PWD"
  local outputfile="${outputdir}/compilation-output-$1.txt"
  local workingdirectory="${outputdir}/scst-$1"

  echo "Testing whether the $1 SCST tree compiles fine ..."
  (
    if mkdir -p "${workingdirectory}"  \
       && cd "${workingdirectory}"     \
       && duplicate_scst_source_tree "${scst}"  \
       && make -s "$1"                 \
       && (export KCFLAGS=-DCONFIG_SCST_MEASURE_LATENCY; \
           make -s clean               \
           && make -s scst iscsi-scst  \
           && if "${scst_local}" = "true" ; then make -C scst_local clean; fi \
           && if "${scst_local}" = "true" ; then make -C scst_local -s   ; fi \
           && if "${mpt_scst}"   = "true" ; then make -C mpt clean; fi \
           && if "${mpt_scst}"   = "true" ; then make -C mpt -s   ; fi \
           && make -C srpt -s clean     \
           && make -C srpt -s )         \
          &> "${outputfile}"
    then
      echo "OK"
    else
      echo "FAILED"
    fi
  )
}

# Download the file from URL $1 and save it in the current directory.
function download_file {
  if [ ! -e "$(basename "$1")" ]; then
    if [ "${quiet_download}" = "false" ]; then
      echo "Downloading $1 ..."
    fi
    if ! wget -q -nc "$1"; then
      echo "Downloading $1 failed."
      return 1
    fi
  fi
}

# Make sure the kernel tarball and patch file are present in directory
# ${kernel_sources}. Download any missing files from ${kernel_mirror}.
function download_kernel {
  local kver="$(kernel_version $1)"
  local plevel="$(patchlevel $1)"
  local series="$1"

  if [ "${series:0:2}" = "3." ]; then
    series="3.x"
  else
    series="${series:0:3}"
  fi
  mkdir -p "${kernel_sources}" || return $?
  test -w "${kernel_sources}" || return $?
  (
    cd "${kernel_sources}" || return $?
    download_file "${kernel_mirror}/v$series/linux-$kver.tar.bz2" \
      || return $?
    if [ "${plevel}" != "" ]; then
      download_file "${kernel_mirror}/v$series/patch-$1.bz2" \
        || download_file "${kernel_longterm}/v$series/longterm/patch-$1.bz2" \
        || return $?
    fi
  )
}

# Generate a kernel patch from the SCST source tree for kernel version $1
# and with generate-kernel-patch options $2.
function generate_kernel_patch {
  local scst_dir="${PWD}"
  local kver="$(kernel_version $1)"
  local patchfile="${outputdir}/scst-$1-kernel.patch"
  local patchfile_m="${outputdir}/scst-$1-kernel-matching-line-numbers.patch"
  local driver_options=""

  driver_options="$([ "${scst_local}" = "true" ] && echo "-l") \
                  $([ "${mpt_scst}"   = "true" ] && echo "-m") \
                  $([ "${qla2x00t}"   = "true" ] && echo "-q")"

  scripts/generate-kernel-patch ${driver_options} $2 $1 > "${patchfile}"

  scripts/generate-kernel-patch ${driver_options} -n $2 $1 > "${patchfile_m}"

  scripts/generate-kernel-patch ${driver_options} -p "${outputdir}/${patchdir}" $2 $1
}

# Run checkpatch on the generated kernel patch. Assumes that there is a
# vanilla kernel tree present in directory "${outputdir}/linux-$1", and leaves
# this kernel tree clean.
function run_checkpatch  {
  local kver="$(kernel_version $1)"
  local plevel="$(patchlevel $1)"
  local outputfile="${outputdir}/checkpatch-$1-output.txt"
  local patchfile="${outputdir}/scst-$1-kernel.patch"

  if [ -e "${outputdir}/linux-$1/scripts/checkpatch.pl" ]; then
    if [ "${multiple_patches}" = "false" ]; then
      echo "Running checkpatch on the SCST kernel patch ..."
      ( cd "${outputdir}/linux-$1" \
        && scripts/checkpatch.pl --no-tree --no-signoff - < "${patchfile}" &> "${outputfile}")
    else
      echo "Running checkpatch on the SCST kernel patches ..."
      rm -f "${outputfile}"
      ( cd "${outputdir}/linux-$1" \
        && for p in "${outputdir}/${patchdir}"/*
           do
             echo "==== $p" >>"${outputfile}"
             scripts/checkpatch.pl --no-tree --no-signoff - < "$p" >> "${outputfile}" 2>&1
           done
      )
    fi
    local errors=$(grep -c '^ERROR' "${outputfile}")
    local warnings=$(grep -c '^WARNING' "${outputfile}")
    echo "${errors} errors / ${warnings} warnings."
    grep -E '^WARNING|^ERROR' "${outputfile}" | sort | uniq -c
  else
    echo "Skipping checkpatch step for kernel $1."
  fi
  return 0
}

function patch_and_configure_kernel {
  local kver="$(kernel_version $1)"
  local patchfile="${outputdir}/scst-$1-kernel-matching-line-numbers.patch"
  local patchoutput="${outputdir}/patch-command-output-$1.txt"

  echo "Patching and configuring kernel ..."
  (
    local srcdir="$PWD"
    cd "${outputdir}/linux-$1"                                      \
    && if [ "${multiple_patches}" = "false" ]; then
         patch -p1 -f -s <"${patchfile}" >"${patchoutput}"
       else
         rm -f "${patchoutput}"
         for p in "${outputdir}/${patchdir}"/*
         do
           echo "==== $p" >>"${patchoutput}"
           patch -p1 -f -s <"${p}" >>"${patchoutput}" 2>&1
         done
       fi \
    && if [ -e $srcdir/srpt/patches/kernel-${kver}-pre-cflags.patch ]; then 
         echo "$srcdir/srpt/patches/kernel-${kver}-pre-cflags.patch ..." \
           >>"${patchoutput}"
         patch -p1 -f -s <$srcdir/srpt/patches/kernel-${kver}-pre-cflags.patch \
	   >>"${patchoutput}";
       else
         echo "srpt/patches/kernel-${kver}-pre-cflags.patch not found."; \
       fi \
    && make -s allmodconfig &>"${outputdir}/make-config-output.txt" \
    && sed -i.tmp 's/^CONFIG_DEBUG_STRICT_USER_COPY_CHECKS=y$/CONFIG_DEBUG_STRICT_USER_COPY_CHECKS=n/' .config \
    && for c in CONFIG_BINARY_PRINTF CONFIG_BLK_DEV_IO_TRACE CONFIG_BRANCH_PROFILE_NONE CONFIG_CONTEXT_SWITCH_TRACER CONFIG_DYNAMIC_FTRACE CONFIG_EVENT_TRACE_TEST_SYSCALLS CONFIG_EVENT_TRACING CONFIG_FTRACE CONFIG_FTRACE_MCOUNT_RECORD CONFIG_FTRACE_NMI_ENTER CONFIG_FTRACE_SELFTEST CONFIG_FTRACE_STARTUP_TEST CONFIG_FTRACE_SYSCALLS CONFIG_FUNCTION_GRAPH_TRACER CONFIG_FUNCTION_PROFILER CONFIG_FUNCTION_TRACER CONFIG_GENERIC_TRACER CONFIG_HAVE_FTRACE_NMI_ENTER CONFIG_IRQSOFF_TRACER CONFIG_IWLWIFI_DEVICE_TRACING CONFIG_IWM_TRACING CONFIG_KVM_MMU_AUDIT CONFIG_MAC80211_DRIVER_API_TRACER CONFIG_MMIOTRACE CONFIG_NET_DROP_MONITOR CONFIG_NOP_TRACER CONFIG_SCHED_TRACER CONFIG_STACK_TRACER CONFIG_TRACEPOINTS CONFIG_TRACER_MAX_TRACE CONFIG_TRACING; do sed -i.tmp "s/^$c=y\$/$c=n/" .config; done \
    && make -s oldconfig &>/dev/null 
  )
}

# Patches and compiles a kernel tree. Assumes that there is a vanilla kernel
# tree present in directory "${outputdir}/linux-$1".
function compile_patched_kernel {
  local kver="$(kernel_version $1)"
  local plevel="$(patchlevel $1)"
  local outputfile="${outputdir}/kernel-$1-compilation-output.txt"

  echo "Compiling kernel $1 ..."
  (
    (
      cd "${outputdir}/linux-$1" \
      && LC_ALL=C make -s -k bzImage modules
    )
  ) &> "${outputfile}"
  echo "See also ${outputfile}."
  return 0
}

# Compile subdirectory $2 of the patched kernel tree linux-$1.
function compile_kernel {
  local kver="$(kernel_version $1)"
  local plevel="$(patchlevel $1)"
  local outputfile="${outputdir}/compilation-$1-output.txt"
  local subdir="$2"

  echo "Compiling the patched kernel ..."
  if (cd "${outputdir}/linux-$1" \
      && make -s prepare \
      && make -s scripts \
      && LC_ALL=C make -k M="${subdir}"
     ) &> "${outputfile}"
  then
    local errors=$(grep -c ' error:' "${outputfile}")
    local warnings=$(grep -c ' warning:' "${outputfile}")
    echo "${errors} errors / ${warnings} warnings."
  else
    echo FAILED
  fi
  cat "${outputfile}" | grep -E 'warning:|error:' | sort | uniq -c
  return 0
}

# Run the source code verification tool 'sparse' on the SCST code. Assumes that
# there is a patched kernel tree present in directory "${outputdir}/linux-$1".
# For more information about endianness annotations, see also
# http://lwn.net/Articles/205624/.
function run_sparse {
  local k="$1"
  local kver="$(kernel_version $1)"
  local plevel="$(patchlevel $1)"
  local outputfile="${outputdir}/sparse-$1-output.txt"
  local subdir="$2"
  shift
  shift

  echo "Running sparse on the patched kernel in ${subdir} $@ ..."
  ( 
    cd "${outputdir}/linux-$k" \
    && make -s prepare \
    && make -s scripts \
    && if grep -q '^CONFIG_PPC=y$' .config; then LC_ALL=C make -k M=arch/powerpc/lib; fi \
    && LC_ALL=C make -k C=2 CF=-D__CHECK_ENDIAN__ M="${subdir}" "$@"
  ) &> "${outputfile}"
  local errors=$(grep -c ' error:' "${outputfile}")
  local warnings=$(grep -c ' warning:' "${outputfile}")
  echo "${errors} errors / ${warnings} warnings."
  cat "${outputfile}" \
    | grep -E 'warning:|error:' \
    | sed -e 's/^[^ ]*:[^ ]*:[^ ]*: //' \
          -e "s/context imbalance in '[^']*':/context imbalance in <function>:/g" \
          -e "s/context problem in '[^']*': '[^']*'/context problem in <function>: <function>/g" \
          -e "s/function '[^']*'/function/g" \
          -e "s/symbol '[^']*'/symbol/g" \
    | sort \
    | uniq -c
  return 0
}

function run_checkstack {
  local kver="$(kernel_version $1)"
  local plevel="$(patchlevel $1)"
  local outputfile="${outputdir}/checkstack-$1-output.txt"

  echo "Running checkstack on the patched $1 kernel ..."
  (
    cd "${outputdir}/linux-$1" \
    && make -s prepare \
    && make -s scripts \
    && LC_ALL=C make -k checkstack
  ) &> "${outputfile}"
  echo "See also ${outputfile}."
  return 0
}

function run_namespacecheck {
  local kver="$(kernel_version $1)"
  local plevel="$(patchlevel $1)"
  local outputfile="${outputdir}/namespacecheck-$1-output.txt"

  echo "Running namespacecheck on the patched $1 kernel ..."
  (
    cd "${outputdir}/linux-$1" \
    && make -s prepare \
    && make -s scripts \
    && LC_ALL=C make -k namespacecheck
  ) &> "${outputfile}"
  echo "See also ${outputfile}."
  return 0
}

function run_headers_check {
  local kver="$(kernel_version $1)"
  local plevel="$(patchlevel $1)"
  local outputfile="${outputdir}/headers_check-$1-output.txt"

  echo "Running headers check on the patched $1 kernel ..."
  (
    cd "${outputdir}/linux-$1" \
    && make -s prepare \
    && make -s scripts \
    && LC_ALL=C make -k headers_check
  ) &> "${outputfile}"
  local errors=$(grep -c '^[^ ]' "${outputfile}")
  echo "${errors} errors."
  grep '^[^ ]' "${outputfile}" | sed 's/.*: //' | sort | uniq -c
  return 0
}

function run_make_htmldocs {
  local kver="$(kernel_version $1)"
  local plevel="$(patchlevel $1)"
  local outputfile="${outputdir}/htmldocs-$1-output.txt"

  echo "Generating HTML documentation for the patched $1 kernel ..."
  (
    cd "${outputdir}/linux-$1" \
    && make -s prepare \
    && make -s scripts \
    && LC_ALL=C make -k htmldocs
  ) &> "${outputfile}"
  echo "See also ${outputfile}."
  return 0
}


#########################
# Argument verification #
#########################

export LC_ALL=C

if [ ! -e scst -o ! -e iscsi-scst -o ! -e srpt ]; then
  echo "Please run this script from inside the SCST subversion source tree."
  exit 1
fi

# Where to store persistenly downloaded kernel tarballs and kernel patches.
kernel_sources="$HOME/software/downloads"
# URL for downloading kernel tarballs and kernel patches.
kernel_mirror="ftp://ftp.kernel.org/pub/linux/kernel"
kernel_longterm="http://www.kernel.org/pub/linux/kernel"
kernel_versions=""
# Directory in which the regression test output files will be stored. Must be
# an absolute path.
outputdir="${PWD}/regression-test-output-$(date +%Y-%m-%d_%Hh%Mm%Ss)"
# Driver configuration.
mpt_scst="false"
multiple_patches="false"
qla2x00t="false"
remove_temporary_files_at_end="false"
run_local_compilation="true"
scst_local="true"
quiet_download="false"

set -- $(/usr/bin/getopt "c:d:j:hklpq" "$@")
while [ "$1" != "${1#-}" ]
do
  case "$1" in
    '-c') kernel_sources="$2"; shift; shift;;
    '-d') outputdir="$2"; shift; shift;;
    '-h') usage; exit 1;;
    '-j') export MAKEFLAGS="-j$2"; shift; shift;;
    '-k') remove_temporary_files_at_end="true"; shift;;
    '-l') run_local_compilation="false"; shift;;
    '-p') multiple_patches="true"; shift;;
    '-q') quiet_download="true"; shift;;
    '--') shift;;
    *)    usage; exit 1;;
  esac
done

kernel_versions="$*"

# RHEL 4.x / CentOS 4.x has a kernel based on version 2.6.9.
# RHEL 5.x / CentOS 5.x has a kernel based on version 2.6.18.
# Ubuntu 8.04 (Hardy Heron) has a kernel based on version 2.6.24.
# Ubuntu 8.10 (Intrepid Ibex) has a kernel based on version 2.6.27.
# openSUSE 11.0 has a kernel based on version 2.6.25.
# openSUSE 11.1 has a kernel based on version 2.6.27.
#kernel_versions="2.6.23.17 2.6.24.7 2.6.25.20 2.6.26.8 2.6.27.21 2.6.28.9 2.6.29.1"

if [ "${kernel_versions}" = "" ]; then
  usage
  exit 1
fi


####################
# Regression tests #
####################

if [ "$(type -p sparse)" = "" ]; then
  echo "Error: sparse has not yet been installed."
  echo "See also http://www.kernel.org/pub/software/devel/sparse/."
fi

if ! mkdir -p "${outputdir}"; then
  if [ -e "${outputdir}" ]; then
    echo "Error: directory ${outputdir} already exists."
  else
    echo "Error: could not create directory ${outputdir}."
  fi
fi

test_scst_tree_patches || exit $?
if [ "${run_local_compilation}" = "true" ]; then
  compile_scst_unpatched || exit $?
  compile_scst_patched debug2release || exit $?
  compile_scst_patched debug2perf    || exit $?
  compile_scst_patched enable_proc  || exit $?
fi

first_iteration="true"
for kv in ${kernel_versions}
do
  echo "=========================="
  printf "= kernel %-15s =\n" "${kv}"
  echo "=========================="

  full_check="false"
  generate_kernel_patch_options=""
  ibmvio="false"
  run_checkpatch="true"
  run_sparse="true"
  global_multiple_patches="${multiple_patches}"
  while [ "${kv%-?}" != "${kv}" -o "${kv%-??}" != "${kv}" ]; do
    kv_without_opt="${kv%-?}"
    if [ "${kv_without_opt}" = "${kv}" ]; then
      kv_without_opt="${kv%-??}"
    fi
    kopt="${kv#${kv_without_opt}}"
    case "${kopt}" in
      '-f')  full_check="true";;
      '-i')  ibmvio="true";;
      '-nc') run_checkpatch="false";;
      '-ns') run_sparse="false";;
      '-u')  generate_kernel_patch_options="-u";;
      '-p')  multiple_patches="true";;
      *)     echo "Error: unknown option ${kopt}."; exit 1;;
    esac
    kv="${kv_without_opt}"
  done
  patchdir="patchdir-${kv}"
  k="${kv}"

  download_kernel $k || continue
  generate_kernel_patch $k "${generate_kernel_patch_options}" || continue
  ( cd "${outputdir}" && extract_kernel_tree $k ) || continue
  if [ "${run_checkpatch}" = "true" ]; then
    run_checkpatch $k
  fi
  patch_and_configure_kernel $k
  if [ "${run_sparse}" = "true" ]; then
    run_sparse $k drivers/scst
    mv ${outputdir}/sparse-$k-output.txt ${outputdir}/sparse-$k-scst-output.txt
    if [ "${ibmvio}" = "true" ]; then
      run_sparse $k drivers/scsi libsrp.ko scsi_sysfs.ko \
                                 scsi_transport_fc.ko scsi_transport_srp.ko
      mv ${outputdir}/sparse-$k-output.txt \
         ${outputdir}/sparse-$k-scsi-output.txt
      if [ $(uname -m) = "ppc32" -o $(uname -m) = "ppc64" ]; then
        run_sparse $k drivers/scsi/ibmvscsi ibmvstgt.ko
        mv ${outputdir}/sparse-$k-output.txt \
           ${outputdir}/sparse-$k-ibmvstgt-output.txt
      fi
    fi
  fi
  compile_kernel $k drivers/scst
  if [ "${full_check}" = "true" ]; then
    run_headers_check $k
    compile_patched_kernel $k
    run_checkstack $k
    run_namespacecheck $k
    run_make_htmldocs $k
  fi
  if [ "${remove_temporary_files_at_end}" = "true" ]; then
    rm -rf "${outputdir}"
    mkdir -p "${outputdir}"
  fi

  multiple_patches="${global_multiple_patches}"
done
