#!/bin/sh

# usage: $0 DIRNAME PHASE
#   PHASE is 1 (checksum) or 2 (package)

cleanup() {
  status=$1

  # Don't keep distfiles if 'make checksum' failed
  keep_distfiles=$(make -V ALWAYS_KEEP_DISTFILES)
  if [ ${status} -eq 1 -o -z "${keep_distfiles}" ]; then
    cd ${dir}
    distdir=$(make -V DISTDIR)
    if [ ! -z "${distdir}" ]; then
      rm -rf ${distdir}/*
    fi
  fi


  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
  touch /.dirty
  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)
      if pkg_info -q -e $base; then
        echo "skipping $base, already added"
      else
        if ! pkg_add $i; then
          echo "error in dependency $i, exiting"
          cleanup 0
        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)
        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
      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):"
        ls /var/db/pkg
        echo "error in pkg_delete, exiting"
        cleanup 0
      else 
        for j in ${delpkg}; do
          echo "Deleting ${j}"
          if ! (pkg_delete -f $j); then
            echo "--> error in pkg_delete, exiting"
            cleanup 0
          fi
        done
        dellist=$delpkg
      fi
    done
  fi
}

dir=$1
phase=$2

ED=$3
PD=$4
FD=$5
BD=$6
RD=$7

#export PATH=/ccache/libexec/ccache/:$PATH
#export CCACHE_PATH=/usr/bin:/usr/local/bin
export MALLOC_OPTIONS=AJ

L=`echo ${LOCALBASE} | sed 's,^/,,'`
Z=`ident ${dir}/Makefile | grep 'FreeBSD:' | sed 's/^[ \t]*//'`

cd $dir || exit 1
restr=$(make -V RESTRICTED)

# Keep restricted distfiles in a subdirectory for extra protection
# against leakage
if [ ! -z "$restr" ]; then
  echo "DISTDIR=${DISTDIR}"
  export DISTDIR=${DISTDIR}/RESTRICTED
  echo "DISTDIR=${DISTDIR}"
  mkdir -p ${DISTDIR}
fi

if [ $phase = 1 ]; then

  # note: if you change this header, also change processonelog and processlogs2
  cd $dir || exit 1
  echo "building for: $(uname -mr)"
  echo "maintained by: $(make maintainer)"
  echo "port directory: ${dir}"
  echo "Makefile ident: ${Z}"
  echo "build started at $(date)"

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

  echo "prefixes: LOCALBASE=${L}"

  #Allow ports to notice they're being run on bento
  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
./work/*
./compat/linux/proc
./usr/share/man/cat*/*
./usr/local/etc/apache
./usr/local/etc/apache2
./usr/local/news
./usr/local/share/xml
./usr/local/etc/gconf
./var/db/fontconfig
EOF
  # 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
./root/*
./var/mail/*
./var/tmp/*
./usr/share/man/cat*/*
./usr/local/etc/apache
./usr/local/etc/apache2
./usr/local/news
./usr/local/share/xml
./usr/local/etc/gconf
./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=${JAIL_ADDR}:${pid}
    export DISPLAY
  fi

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

  echo "================================================================"
  echo "====================<phase 5: make test>===================="
  cd $dir
  /pnohang $TIMEOUT /tmp/make.log5 ${pkgname} make -k regression-test
  cat /tmp/make.log5
  mtree -X /tmp/mtree.buildexclude -x -f /tmp/mtree.prebuild -p / | egrep -v "^(${L}/var|${L}/lib/X11/xserver/SecurityPolicy|${L}/share/nls/POSIX|${L}/share/nls/en_US.US-ASCII|etc/services|compat |usr/X11R6 |etc/manpath.config|etc/.*.bak|${L}/info/dir|${L}/lib/X11/fonts/.*/fonts\.|usr/local/man/..( |/man. )|${L}/lib/X11/fonts/TrueType|${L}/etc/gconf/gconf.xml.defaults/%gconf-tree.*.xml|var/db/fontconfig/* )" > /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
./work/*
./compat/linux/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
    cat /tmp/make.log7
    echo "0" > /tmp/status
    prefix=$(make -V PREFIX)
    del_pkg ${pkgname}
  else
    cleanup 7
  fi

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

  # Compare the state of the filesystem now to before the 'make install' phase
  dirty=0
  if [ -s /tmp/list3 ]; then
    cd /
    grep ' extra$' /tmp/list3 | awk '{print $1}' | xargs -J % find % -ls > /tmp/list4
    grep ' missing$' /tmp/list3 > /tmp/list5
    grep -vE ' (extra|missing)$' /tmp/list3 > /tmp/list6
    if [ "x${NOPLISTCHECK}" = "x" ]; then
      if grep -vq "$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
  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
  fi
  if [ -s /tmp/list6 ]; then
    echo "list of filesystem changes from before and after port installation and deinstallation"
    cat /tmp/list6
  fi
  if [ "${dirty}" = 1 ]; then
    cleanup 0
  fi

  # BUILD_DEPENDS and RUN_DEPENDS are both present at install-time (e.g. gmake)
  # Concatenate and remove duplicates
  BRD=$(echo $BD $RD | tr ' ' '\n' | sort -u | tr '\n' ' ')
  del_pkg ${BRD}
  cd /var/db/pkg
  if [ $(echo $(echo * | wc -c)) != 2 ]; then
    echo "leftover packages:" *
    del_pkg *
    echo "1" > /tmp/status
    cleanup 0
  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 "^(${L}/var|${L}/lib/X11/xserver/SecurityPolicy|${L}/share/nls/POSIX|${L}/share/nls/en_US.US-ASCII|etc/services|compat |usr/X11R6 |etc/manpath.config|etc/.*.bak|${L}/info/dir|${L}/lib/X11/fonts/.*/fonts\.|usr/local/man/..( |/man. )|${L}/lib/X11/fonts/TrueType )" > /tmp/list3

  echo
  echo "=== Checking filesystem state after all packages deleted"

  if [ -s /tmp/list3 ]; then
    cd /
    grep ' extra$' /tmp/list3 | awk '{print $1}' | xargs -J % find % -ls > /tmp/list4
    grep ' missing$' /tmp/list3 > /tmp/list5
    grep -vE ' (extra|missing)$' /tmp/list3 > /tmp/list6
    if [ "x${NOPLISTCHECK}" = "x" ]; then
      if grep -vq "$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 /.dirty
    fi
    if [ -s /tmp/list5 ]; then
      echo "list of files present on clean system but missing after everything was deinstalled)"
      cat /tmp/list5
      touch /.dirty
    fi
    if [ -s /tmp/list6 ]; then
      echo "list of filesystem changes from before and after all port installation/deinstallation"
      cat /tmp/list6
      touch /.dirty
    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

  # XXX Don't keep distfiles if checksum mismatches
  cd ${dir}
  keep_distfiles=$(make -V ALWAYS_KEEP_DISTFILES)
  distdir=$(make -V DISTDIR)
  if [ -z "${keep_distfiles}" -a ! -z "${distdir}" ]; then
    rm -rf ${distdir}/*
  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
