#!/bin/sh

# usage: $0 DIRNAME PHASE ED PD FD BD RD TD PKGD [PLISTCHECK]
#   PHASE is 1 (checksum) or 2 (package)

cleanup() {
  status=$1

  if [ -e ${dir}/.keep ]; then
    cd ${dir}
    objdir=$(make -V WRKDIR)
    tar cfjC /tmp/work.tbz ${objdir}/.. work
  fi

  if [ ${status} -gt 0 ]; then
      cat /tmp/make.log${status}
  fi

  echo 1 > /tmp/status
  echo "================================================================"
  echo -n "build of ${dir} ended at "
  date

  exit 0
}

add_pkg() {
  pkgs=$*

  echo add_pkg $pkgs
  cd /tmp/depends
  export PKG_PATH=/tmp/depends
  if [ ! -z "${pkgs}" ]; then
    arch=$(uname -m)
    echo "adding dependencies"
    for i in $pkgs; do
      echo "pkg_add $i"
      base=$(basename $i .tgz)
      base=$(basename $base .tbz)
      base=$(basename $base .txz)
      if [ -n "${use_pkgng}" ]; then
        if pkg-static info -q -e ${base}; then
          echo "skipping $base, already added"
        else
          pkgname=${base%-*}
          if [ "${pkgname}" = "pkg" ]; then
            tar xf $i -C . -s ",/.*/,,g" "*/pkg-static"
            if ! ./pkg-static add $i; then
              echo "error in dependency $i, exiting"
              echo "1" > /tmp/status
              exit 1
            fi
          else
            if ! pkg-static add $i; then
              echo "error in dependency $i, exiting"
              echo "1" > /tmp/status
              exit 1
            fi
          fi
        fi
      else
        if pkg_info -q -e ${base}; then
          echo "skipping ${base}, already added"
        else
          if ! pkg_add $i; then
            echo "error in dependency $i, exiting"
            echo "1" > /tmp/status
            exit 1
          fi
        fi
      fi
    done
  fi
}

del_pkg() {
  pkgs=$*

  cd /tmp/depends
  export PKG_PATH=/tmp/depends
  if [ ! -z "${pkgs}" ]; then
    recursion=1
    dellist=""
    while [ $recursion -eq 1 ]; do
      unset delpkg nextpkg
      recursion=0
      for i in $pkgs; do
        base=$(basename $i .tgz)
        base=$(basename $base .tbz)
        base=$(basename $base .txz)
        if [ -n "${use_pkgng}" ]; then
         if [ "$(pkg-static query %?r $base)" = "1" ]; then
            recursion=1
            nextpkg="${base} ${nextpkg}"
          elif pkg-static info -q -e ${base}; then
            delpkg="${base} ${delpkg}"
          fi
        else
          if [ -s /var/db/pkg/${base}/+REQUIRED_BY ]; then
            recursion=1
            nextpkg="${base} ${nextpkg}"
          elif [ -d /var/db/pkg/${base}/ ]; then
            delpkg="${base} ${delpkg}"
          fi
        fi
      done
      pkgs="${nextpkg}"
      if [ "$dellist" != "" -a "$dellist" = "$delpkg" ]; then
        echo "deleted list =\""$dellist"\", packages to delete ="\"$delpkg\" #"
        echo "The following packages were left behind (perhaps your dependency list is incomplete):"
        if [ -n "${use_pkgng}" ]; then
          pkg-static info -qa
        else
          ls /var/db/pkg
        fi
        echo "error in pkg_delete, exiting"
        cleanup 0
      else
        for j in ${delpkg}; do
          echo "Deleting ${j}"
          if [ -n "${use_pkgng}" ]; then
            if ! pkg-static delete -y -f ${j}; then
              echo "--> error in pkg delete, exiting"
            fi
          else
            if ! (pkg_delete -f $j); then
              echo "--> error in pkg_delete, exiting"
              cleanup 0
            fi
          fi
        done
        dellist=$delpkg
      fi
    done
  fi
  find /var/db/pkg -type f -empty -delete
}

dir=$1
phase=$2

ED=$3
PD=$4
FD=$5
BD=$6
RD=$7
TD=$8
PKGD=$9
PLISTCHECK=${10}

use_pkgng=$(make -f /a/ports/Mk/bsd.port.mk -VWITH_PKGNG)

L=$(echo ${LOCALBASE} | sed 's,^/,,')

if [ $phase = 1 ]; then

  cd $dir || exit 1
  echo "build started at $(date)"
  echo "port directory: ${dir}"
  echo "building for:  $(uname -rm)"
  echo "maintained by: $(make maintainer)"
  echo "Makefile ident: $(ident ${dir}/Makefile | grep 'FreeBSD:' | sed 's/^[ \t]*//')"
  echo "prefixes: LOCALBASE=${L} PREFIX=$(make -V PREFIX)"
  echo "Begin Configuration:"
  echo "---Begin Environment---"
  printenv
  echo "---End Environment---"
  echo ""
  echo "---Begin OPTIONS List---"
  make showconfig
  echo "---End OPTIONS List---"
  echo ""
  optionsfile=$(make -V OPTIONSFILE)
  if [ -f "${optionsfile}" ]; then
      echo "---Begin OPTIONS configuration---"
      cat ${optionsfile}
      echo "---End OPTIONS configuration---"
      echo ""
  fi
  echo "End Configuration."

  echo "PKG_DEPENDS=${PKGD}"
  echo "FETCH_DEPENDS=${FD}"
  echo "PATCH_DEPENDS=${PD}"
  echo "EXTRACT_DEPENDS=${ED}"
  echo "BUILD_DEPENDS=${BD}"
  echo "RUN_DEPENDS=${RD}"
  echo "TEST_DEPENDS=${TD}"

  #Allow ports to notice they're being run in Tinderbox
  export PACKAGE_BUILDING=1

  # Stash a copy of /etc/master.passwd and /etc/group to detect whether someone modifies it
  cp /etc/master.passwd /etc/master.passwd-save
  cp /etc/group /etc/group-save

  # Files we do not care about changing between pre-build and post-cleanup
  cat > /tmp/mtree.preexclude <<EOF
./root/*
./var/*
./tmp/*
./etc/make.conf.bak
./etc/make.conf
./etc/periodic.conf
./etc/rc.conf
./work/*
./compat/linux/proc
./proc
./usr/share/man/cat*/*
./${L}/etc/apache
./${L}/news
./${L}/share/xml
./${L}/etc/gconf
./${L}/etc/mateconf
./var/db/fontconfig
EOF

  add_pkg ${PKGD}

  # Record a "pristine" mtree.
  mtree -X /tmp/mtree.preexclude -xcn -k uid,gid,mode -p / > /tmp/mtree.pristine

  add_pkg $FD

  cd $dir || exit 1
  pkgname=$(make package-name)
  echo "================================================================"
  echo "====================<phase 1: make checksum>===================="

  if /pnohang $TIMEOUT /tmp/make.log1 ${pkgname} make checksum; then
    cat /tmp/make.log1
    echo "0" > /tmp/status
  else
    cleanup 1
  fi

else

  cd $dir || exit 1
  pkgname=$(make package-name)

  echo "================================================================"
  echo "====================<phase 2: make extract>===================="

  add_pkg ${ED}
  cd $dir
  /pnohang $TIMEOUT /tmp/make.log2 ${pkgname} make extract || cleanup 2
  cat /tmp/make.log2
  del_pkg ${ED}

  # Fetch depends still need to be here for 'make extract' since that target
  # always reruns 'make fetch' due to the lack of fetch cookie (and no place
  # to put it since WRKDIR isn't created by 'make fetch')
  del_pkg $FD

  echo "================================================================"
  echo "====================<phase 3: make patch>===================="
  add_pkg ${PD}
  cd $dir
  /pnohang $TIMEOUT /tmp/make.log3 ${pkgname} make patch || cleanup 3
  cat /tmp/make.log3
  del_pkg ${PD}

  echo "================================================================"
  echo "====================<phase 4: make build>===================="

  add_pkg ${BD}

  # Files we do not care about changing between pre-build and post-cleanup
  cat > /tmp/mtree.buildexclude <<EOF
./var/log/*
./tmp/*
./work/*
./compat/linux/proc
./proc
./root/*
./var/mail/*
./var/tmp/*
./usr/share/man/cat*/*
./${L}/etc/apache
./${L}/news
./${L}/share/xml
./${L}/etc/gconf
./${L}/etc/mateconf
./var/db/fontconfig
EOF
  # Record a "pristine" mtree.
  mtree -X /tmp/mtree.buildexclude -xcn -k uid,gid,mode -p / > /tmp/mtree.prebuild

  xvfb=0
  if which -s Xvfb; then
    xvfb=1
    pid=$(echo $$ % 32768 | bc)
    X11BASE=$(which Xvfb | sed -e 's./bin/Xvfb..')
    Xvfb :${pid} -fp ${X11BASE}/lib/X11/fonts/misc &
    DISPLAY=:${pid}
    export DISPLAY
  fi

  cd $dir
  portdir=$(echo ${dir} | sed -e 's|^/usr/ports/||' -e 'y/\//_/')

  if [ -f .sleepme ]; then
      set > /tmp/.set_${portdir}
      touch /tmp/.sleep_${portdir}
      while [ -f /tmp/.sleep_${portdir} ]; do
	  echo "Sleeping for 60 seconds."
	  sleep 60
      done
      rm -f /tmp/.set_${portdir}
  fi

  /pnohang $TIMEOUT /tmp/make.log4 ${pkgname} make build || cleanup 4
  cat /tmp/make.log4

  echo "================================================================"
  echo "====================<phase 5: make test>===================="

  if [ -z "${TB_SKIP_TESTS}" -o ${TB_SKIP_TESTS} -eq 0 ]; then

      if [ -n "${use_pkgng}" ]; then
	  pkg-static info -qa | sort > /tmp/pkgs_pre_test
      else
	  pkg_info | awk '{print $1}' | sort > /tmp/pkgs_pre_test
      fi
      add_pkg ${TD}
      if [ -n "${use_pkgng}" ]; then
	  pkg-static info -qa | sort > /tmp/pkgs_post_test
      else
	  pkg_info | awk '{print $1}' | sort > /tmp/pkgs_post_test      
      fi
      cd $dir
      /pnohang $TIMEOUT /tmp/make.log5 ${pkgname} make -k regression-test
      cat /tmp/make.log5

      RTD=`comm -3 /tmp/pkgs_pre_test /tmp/pkgs_post_test | tr -d '\t'`
      del_pkg ${RTD}
      rm /tmp/pkgs_pre_test /tmp/pkgs_post_test
  else
      echo "=== Regression tests skipped. ==="
  fi

  # Guess what mtree we're using.
  mtree -b -f /tmp/mtree.prebuild >/dev/null 2>&1
  rc=$?
  leading=''
  trailing=' '
  if [ ${rc} -ne 1 ]; then
      # NetBSD mtree
      leading='extra: '
      trailing=''
  fi

  mtree -X /tmp/mtree.buildexclude -x -f /tmp/mtree.prebuild -p / | egrep -v "^${leading}(${L}/var|${L}/lib/X11/xserver/SecurityPolicy|${L}/share/nls/POSIX|${L}/share/nls/en_US.US-ASCII|etc/services|compat${trailing}|${L}${trailing}|etc/manpath.config|etc/.*.bak|${L}/info/dir|${L}/lib/X11/fonts/.*/fonts\.|${L}/man/..(${trailing}|/man.${trailing})|${L}/lib/X11/fonts/TrueType|${L}/etc/gconf/gconf.xml.defaults/%gconf-tree.*.xml|${L}/etc/mateconf/mateconf.xml.defaults/%mateconf-tree.*.xml|usr/X11R6${trailing})" > /tmp/list.preinstall

  if [ -s /tmp/list.preinstall ]; then
      echo "================================================================"
      echo "Fatal error: filesystem was touched prior to 'make install' phase"
      cat /tmp/list.preinstall
      echo "================================================================"
      cleanup 0
  fi

  echo "================================================================"
  echo "====================<phase 6: make install>===================="

  add_pkg ${RD}

  cat > /tmp/mtree.exclude <<EOF
./root/*
./var/*
./tmp/*
./etc/make.conf.bak
./etc/make.conf
./etc/periodic.conf
./etc/rc.conf
./work/*
./compat/linux/proc
./proc
EOF
  mtree -X /tmp/mtree.exclude -xcn -k uid,gid,mode -p / > /tmp/mtree

  cd $dir
  if /pnohang $TIMEOUT /tmp/make.log6 ${pkgname} make install; then
    cat /tmp/make.log6
    echo "0" > /tmp/status
  else
    cleanup 6
  fi

  echo "================================================================"
  echo "====================<phase 7: make package>===================="
  cd $dir
  if /pnohang $TIMEOUT /tmp/make.log7 ${pkgname} make package; then
      echo "0" > /tmp/status
  else
      echo "1" > /tmp/status
  fi

  cat /tmp/make.log7
  prefix=$(make -V PREFIX)
  du -skx / | awk '{print $1}' > /tmp/size
  cd $dir || exit 1
  pkgname=$(make package-name)
  del_pkg ${pkgname}


  mtree -X /tmp/mtree.exclude -x -f /tmp/mtree -p / | egrep -v "^${leading}(${L}/var|${L}/lib/X11/xserver/SecurityPolicy|${L}/share/nls/POSIX|${L}/share/nls/en_US.US-ASCII|etc/services|compat${trailing}|${L}${trailing}|etc/manpath.config|etc/.*.bak|${L}/info/dir|${L}/lib/X11/fonts/.*/fonts\.|${L}/man/..(${trailing}|/man.${trailing})|${L}/lib/X11/fonts/TrueType|${L}/etc/gconf/gconf.xml.defaults/%gconf-tree.*.xml|${L}/etc/mateconf/mateconf.xml.defaults/%mateconf-tree.*.xml|var/db/fontconfig/*|usr/X11R6${trailing})" > /tmp/list3

  dirty=0
  if [ -s /tmp/list3 ]; then
    cd /
    grep -E 'extra: | extra$' /tmp/list3 | sed -E -e 's|extra:?||' | awk '{print $1}' | xargs -J % find % -ls > /tmp/list4
    grep -E 'missing: | missing$' /tmp/list3 > /tmp/list5
    grep -vE '(extra:|missing:) | (extra|missing)$' /tmp/list3 > /tmp/list6
    if [ -n "${PLISTCHECK}" ]; then
      if grep -vqE "(${L})/etc/" /tmp/list4; then
        echo "1" > /tmp/status
        dirty=1
      fi
      if [ -s /tmp/list5 -o -s /tmp/list6 ]; then
        echo "1" > /tmp/status
        dirty=1
      fi
    fi
      echo "================================================================"
  fi


  echo
  echo "=== Checking filesystem state"

  if [ -s /tmp/list4 ]; then
    echo "list of extra files and directories in / (not present before this port was installed but present after it was deinstalled)"
    cat /tmp/list4
    touch /tmp/leftovers
  fi
  if [ -s /tmp/list5 ]; then
    echo "list of files present before this port was installed but missing after it was deinstalled)"
    cat /tmp/list5
    touch /tmp/leftovers
  fi
  if [ -s /tmp/list6 ]; then
    echo "list of filesystem changes from before and after port installation and deinstallation"
    cat /tmp/list6
    touch /tmp/leftovers
  fi
  if [ "${dirty}" = 1 ]; then
    cleanup 0
  fi

  # BUILD_DEPENDS need to be present at install-time, e.g. gmake
  # Concatenate and remove duplicates
  BRD=$(echo $BD $RD | tr ' ' '\n' | sort -u | tr '\n' ' ')
  del_pkg ${BRD}
# XXX: Check for leftover packages
  if [ -z "${use_pkgng}" ]; then
    cd /var/db/pkg
    if [ $(echo $(echo * | wc -c)) != 2 ]; then
      echo "leftover packages:" *
      del_pkg *
      echo "1" > /tmp/status
      cleanup 0
    fi
  fi

  # Compare the state of the filesystem now to clean system (should again be clean)
  mtree -X /tmp/mtree.preexclude -x -f /tmp/mtree.pristine -p / | egrep -v "^${leading}(${L}/var|${L}/lib/X11/xserver/SecurityPolicy|${L}/share/nls/POSIX|${L}/share/nls/en_US.US-ASCII|etc/services|compat${trailing}|${L}${trailing}|etc/manpath.config|etc/.*.bak|${L}/info/dir|${L}/lib/X11/fonts/.*/fonts\.|${L}/man/..(${trailing}|/man.${trailing})|${L}/lib/X11/fonts/TrueType|${L}/etc/gconf/gconf.xml.defaults/%gconf-tree.*.xml|${L}/etc/mateconf/mateconf.xml.defaults/%mateconf-tree.*.xml|var/db/fontconfig/*|usr/X11R6${trailing})" > /tmp/list3
  echo
  echo "=== Checking filesystem state after all packages deleted"

  if [ -s /tmp/list3 ]; then
      cd /
      grep -E 'extra: | extra$' /tmp/list3 | sed -E -e 's|extra:?||' | awk '{print $1}' | xargs -J % find % -ls > /tmp/list4
      grep -E 'missing: | missing$' /tmp/list3 > /tmp/list5
      grep -vE '(extra:|missing:) | (extra|missing)$' /tmp/list3 > /tmp/list6
      if [ -n "${PLISTCHECK}" ]; then
	  if grep -vqE "(${L})/etc/" /tmp/list4; then
	      #echo "1" > /tmp/status
	  fi
	  if [ -s /tmp/list5 ]; then
	      #echo "1" > /tmp/status
	  fi
      fi
      echo "================================================================"

      if [ -s /tmp/list4 ]; then
	  echo "list of extra files and directories in / (not present on clean system but present after everything was deinstalled)"
	  cat /tmp/list4
	  touch /tmp/leftovers
      fi
      if [ -s /tmp/list5 ]; then
	  echo "list of files present on clean system but missing after everything was deinstalled)"
	  cat /tmp/list5
	  touch /tmp/leftovers
      fi
      if [ -s /tmp/list6 ]; then
	  echo "list of filesystem changes from before and after all port installation/deinstallation"
	  cat /tmp/list6
	  touch /tmp/leftovers
      fi
  fi

  cmp /etc/group /etc/group-save || (echo "=== /etc/group was modified:"; diff -du /etc/group-save /etc/group)
  cmp /etc/master.passwd /etc/master.passwd-save || (echo "=== /etc/master.passwd was modified:"; diff -du /etc/master.passwd-save /etc/master.passwd)

  if [ ${xvfb} = 1 ]; then
    kill $(jobid %1)
  fi

  if [ -e ${dir}/.keep ]; then
    cd ${dir}
    objdir=$(make -V WRKDIR)
    tar cfjC /tmp/work.tbz ${objdir}/.. work
  fi

  echo "================================================================"
  echo -n "build of ${dir} ended at "
  date

fi

exit 0
