#!/bin/sh

if [ -z "$1" -o x$1 == x-h -o x$1 == x--help ]; then
    echo ""
    echo " build <directory>"
    echo ""
    echo " sources are expected in <directory>"
    echo " special value for arch: 'native' doesn't use r_arch at all."
    echo ""
    echo " Optional env. settings:"
    echo "   BASE       - abs. path to build system base"
    echo "   PKGTYPE    - install.packages() default type (defaults to mac.binary)"
    echo "   XTRAMAKEF  - make flags to append"
    echo "   FORCE_OS_VERSION - force cross-build for particular Darwin verison"
    echo "   skip_build - if set to something existing tar ball is used"
    echo ""
    exit 0;
fi

: ${BASE=/Volumes/Builds/R4}

if [ ! -e "$BASE" ]; then
    echo "*** WARNING: BASE ($BASE) does not exist, falling back to current directory"
    BASE="`pwd`"
fi

# set -x

: ${TAR=tar}

RBUILDS=$BASE

## for oscode, ARCH, TNAME
. $BASE/common

RD="$1"
if [ -z "$RD" ]; then
    echo "ERROR missing R directory to build" >&2
    exit 1
fi

tarch=$ARCH

## TARCH is used for R_ARCH mutli-arch builds unless "native"
TARCH=native
TNAMEWOA=`echo $TNAME|sed 's:-.*::'`

## big-sur has a separate package tree
if [ "$oscode" = big-sur ]; then
    PKGSUFFIX=".$TNAME"
fi

: ${PKGTYPE=mac.binary$PKGSUFFIX}

if [ -z "$JOBID" ]; then
    JOBID=build-$TNAME-$RD-$$-`date +%s`
    export JOBID
fi
LOCKJOB=`cat "$BASE/LOCK" 2>/dev/null`
if [ -n "$LOCKJOB" ]; then
    if [ "$LOCKJOB" != "$JOBID" ]; then
	echo "ERROR: build locked by $LOCKJOB (I am $JOBID)" >&2
	exit 1
    fi
    locked=no
else
    locked=yes
    echo $JOBID > "$BASE/LOCK"
fi

## arm build uses /opt/R
if [ -e /opt/R/$ARCH ]; then
PREFIX=/opt/R/$ARCH
else
PREFIX=/usr/local
fi
PATH=$PREFIX/bin:$PATH

if [ -z $(which gfortran 2>/dev/null) ]; then
   if [ -e /opt/gfortran/bin/gfortran ]; then
      PATH=/opt/gfortran/bin:$PATH
   else
      echo "** ERROR: cannot find GNU Fortran **" >&2
      exit 1
   fi
fi

## it is better to ship with Apple stubs than a particular version
#JH=`/usr/libexec/java_home`
#if [ -n "$JH" ]; then
#    PATH=$JH/bin:$PATH
#fi

for texpath in /opt/TinyTex/bin /opt/texinfo/bin /Library/TeX/texbin; do
    if [ -e "$texpath" ]; then
	PATH="$texpath:$PATH"
	break
    fi
done

RBASE="$BASE/$RD"

## NOTE: the precedence has changed, we prefer our libraries to Xquartz,
##       because it is no longer updated
PKG_CONFIG_PATH=/usr/lib/pkgconfig:$PREFIX/lib/pkgconfig:$PREFIX/share/pkgconfig:/opt/X11/lib/pkgconfig:/opt/X11/share/pkgconfig
export PKG_CONFIG_PATH

CONSH=$BASE/consh

host=`hostname -s`

ncpu=`/usr/sbin/sysctl hw.ncpu|sed -n 's/hw.ncpu: //p'`
if [ -n "${ncpu}" ]; then
    : ${MAKEF=-j$ncpu}
fi

if [ -n "${XTRAMAKEF}" ]; then
    MAKEF="${MAKEF} ${XTRAMAKEF}"
fi

export PATH
mkdir -p $BASE/$TNAME

cd $BASE
TS=`date +%s:%Y%m%d:%H%M%S`
if [ $TARCH = native ]; then
    RARCH=''
else
    RARCH="r_arch=$TARCH"
fi

echo "Building $RD for $TNAME (arch $TARCH, pkgType=\"${PKGTYPE}\", PREFIX=$PREFIX)"
clang --version

## check available disk space before proceeding - we require at least 1GB to make sure
DSKF=`df -g "$BASE/$TNAME" | awk '{print $4}' | tail -n 1`
if [ "$DSFK" = 0 ]; then
    echo "***FATAL ERROR: out of disk space!"
    echo "FATAL ERROR: out of disk space" > $BASE/$TNAME/$RD.FAILED
    if [ $locked = yes ]; then
	rm -f "$BASE/LOCK"
    fi
    exit 1
fi

## check flags for tar - we want --no-xattrs on tars that support it
if $TAR c --no-xattrs build > /dev/null; then
    TARFLAGS=--no-xattrs
fi

if [ -z "${skip_build}" ]; then
    build_ok=no
    now=`date +%s`    
    BUILDSUBDIR="builds/$RD-$now-$$"
    BUILDDIR="$BASE/$TNAME/$BUILDSUBDIR"
    if [ -e "$BUILDDIR" ]; then
	echo "ERROR: $BUILDDIR already exists!" >&2
	if [ $locked = yes ]; then
	    rm -f "$BASE/LOCK"
	fi
	exit 1
    fi
    mkdir -p $BUILDDIR
    mkdir -p "$BASE/$TNAME/$RD" 2>/dev/null ## for results
    
    echo "$TS:$RD:configure" >> $BUILDDIR/build.log
    mkdir -p $BUILDDIR/$RD
    cd $BUILDDIR/$RD
    use_build=no
    HOST=`$BASE/$RD/tools/config.guess`
    if [ -n "${FORCE_OS_VERSION}" ]; then
	HOST=`echo ${HOST}|sed "s/-darwin.*/-darwin${FORCE_OS_VERSION}/"`
	use_build=yes
    fi
    if [ $TARCH = ppc -a $ARCH = i386 ]; then
	HOST=`echo ${HOST}|sed s/^i386/powerpc/`
	use_build=yes
    fi
    if [ $TARCH = x86_64 -a $ARCH = i386 ]; then
	HOST=`echo ${HOST}|sed s/^i386/x86_64/`
	use_build=yes
    fi
    if [ ${use_build} = yes ]; then
	echo "  (forcing build name $HOST)"
	RARCH="--build=$HOST $RARCH"
    fi
    CONFF=`cat $BASE/conf.$TNAME 2>/dev/null`
    GCONFF=`cat $BASE/conf.$TNAMEWOA 2>/dev/null`

    ## for big-sur builds we append "-<arch>" to FW_VERSION
    if [ $oscode = big-sur ]; then
	grep '^PACKAGE_VERSION=' $BASE/$RD/configure | head -n1 > $BUILDDIR/R-version.sh
	echo 'echo $PACKAGE_VERSION' >> $BUILDDIR/R-version.sh
	PACKAGE_VERSION=`sh $BUILDDIR/R-version.sh`
	FW_VERSION=`echo "${PACKAGE_VERSION}" | sed 's/[\.][0-9]$//'`
	FWOVERRIDE="FW_VERSION=${FW_VERSION}-$ARCH"
    fi

    echo "Using configure $RARCH $FWOVERRIDE $GCONFF $CONFF"
    TS=`date +%s:%Y%m%d:%H%M%S`
    set > "$BUILDDIR/env"
    echo "$TS:$RD:confcall $RARCH $FWOVERRIDE $GCONFF $CONFF" >> $BUILDDIR/build.log
    ${CONSH} "$BASE/$RD/configure $RARCH $FWOVERRIDE $GCONFF $CONFF" $BUILDDIR/$RD.conf
    CRES=$?
    TS=`date +%s:%Y%m%d:%H%M%S`
    echo "$TS:$RD:configure:$CRES" >> $BUILDDIR/build.log
    if [ $CRES = 0 ]; then
	cd $BUILDDIR/$RD
	echo "#define PLATFORM_PKGTYPE \"${PKGTYPE}\"" >> src/include/config.h
	echo "make $MAKEF"
	echo "$TS:$RD:make" >> $BUILDDIR/build.log
	${CONSH} "make $MAKEF" $BUILDDIR/$RD.build
	MRES=$?
	TS=`date +%s:%Y%m%d:%H%M%S`
	echo "$TS:$RD:make:$MRES" >> $BUILDDIR/build.log
	
	if [ $MRES = 0 ]; then
	    cd $BUILDDIR
	    
            # create a build tar-ball, e.g. for check on another machine
	    echo "$BUILDDIR" > $RD/build-base
	    $TAR fcz $RD-$TNAME-bld.tar.gz $RD
	    cd $BUILDDIR/$RD
	    TS=`date +%s:%Y%m%d:%H%M%S`
	    
	    set > $BUILDDIR/build-check-env
	    echo "make check"
	    echo "$TS:$RD:check" >> $BUILDDIR/build.log
	    ${CONSH} "make check" $BUILDDIR/$RD.check
	    CR=$?
	    TS=`date +%s:%Y%m%d:%H%M%S`
	    echo "$TS:$RD:check:$CR" >> $BUILDDIR/build.log
	    
	    if [ $CR = 0 ]; then
		echo "SUCCESS"
		touch $BUILDDIR/$RD.SUCCESS
		echo $BUILDSUBDIR > $BUILDDIR/build-path
		## on success replace "last-success" with the current build
		ln -sfn $BUILDDIR $BASE/$TNAME/$RD/last-success
		TS=`date +%s:%Y%m%d:%H%M%S`
		echo "$TS:$RD:build-success:$BUILDSUBDIR:$BASE/$TNAME/$RD/last-success" >> $BASE/$TNAME/build.log
		ln -sfn last-success $BASE/$TNAME/$RD/current
		build_ok=yes
	    else
		echo "make check FAILED" > $BUILDDIR/$RD.FAILED
		## add tails of failed test output to the log file (if any)
		FT=`ls tests/*/*.fail tests/*.fail 2>/dev/null`
		if [ -n "$FT" ]; then
		    for ft in $FT; do
			echo '#@2@#====== FAILED TEST: ' "$ft" '@#2#@' >> $BUILDDIR/$RD.check
			tail -n 20 $ft >> $BUILDDIR/$RD.check
		    done
		fi
	    fi
	else
	    echo "make  FAILED" > $BUILDDIR/$RD.FAILED
	fi
    else
	echo "Configure FAILED"
	echo "configure FAILED" > $BUILDDIR/$RD.FAILED
    fi
    if [ "$build_ok" != yes ]; then
	## on failure keep the build dir and symlink it
	TS=`date +%s:%Y%m%d:%H%M%S`
	echo "$TS:$RD:build-failed:$BUILDSUBDIR" >> $BASE/$TNAME/build.log
	ln -sfn $BUILDDIR $BASE/$TNAME/$RD/last-failed
	ln -sfn last-failed $BASE/$TNAME/$RD/current
    fi
fi ## skip_build

TS=`date +%s:%Y%m%d:%H%M%S`


####==== install -> framework

if [ -e $BASE/$TNAME/$RD/current/$RD.SUCCESS ]; then
    LOGDIR=$BASE/$TNAME/$RD/current
    miss=yes
    failed=yes
    if [ -e "$BASE/$TNAME/$RD/current/$RD-$TNAME-bld.tar.gz" -a -e "$BASE/$TNAME/$RD/current/$RD.SUCCESS" ]; then
	echo "$TS:$RD:common:collect:0:$TNAME" >> $LOGDIR/build.log
	miss=no
    else
	echo "Missing build result for $RD-$TNAME $BASE/$TNAME/$RD/current/$RD-$TNAME-bld.tar.gz" >&2
	echo "Missing build result for $RD-$TNAME" >> $LOGDIR/$RD.ERROR
	echo "$TS:$RD:common:collect:1:$TNAME" >> $LOGDIR/build.log
    fi

    if [ $miss = no ]; then
	failed=no
	echo "NOTE: logs in $LOGDIR"
	echo "Remove previous framework ..."
	$BASE/rmfw ## this is the setuid version to make sure ...
	RFWH=/Library/Frameworks/R.framework/Resources
	echo "$RD:$TNAME"
	cd $BASE/$TNAME/$RD/current/$RD
	TS=`date +%s:%Y%m%d:%H%M%S`
	echo "$RD:$TNAME:install"
	echo "$TS:$RD:$tarch:install" >> $LOGDIR/build.log
	make install > $LOGDIR/$RD.$tarch.inst 2> $LOGDIR/$RD.$tarch.inst.err
	echo "$TS:$RD:$tarch:install:$?" >> $LOGDIR/build.log
	## additional parts
	echo "$RD:$TNAME:install-tests"
	make install-tests >>  $LOGDIR/$RD.$tarch.inst 2>&1
	echo "$RD:$TNAME:install-tests:$?"
	echo "$RD:$TNAME:install-pdf"
	make install-pdf >>  $LOGDIR/$RD.$tarch.inst 2>&1
	echo "$RD:$TNAME:install-pdf:$?"
	if [ $failed = yes ]; then
	    echo "make install failed for $tarch"
	    echo "make install failed for $tarch" >> $LOGDIR/$RD.FAILED
	fi
	TS=`date +%s:%Y%m%d:%H%M%S`
	echo "$TS:$RD:$tarch:install:$CR" >> $LOGDIR/build.log	
    fi
    if [ $failed = no ]; then
	echo "$RD:$TNAME:fixup"
        ## fixup permissions (first run to make sure it's writable)
	$BASE/fixup

	## determine full path to lib (via id entry in libR)
	LIBDIR=`otool -L /Library/Frameworks/R.framework/R | sed -n '/\/libR/s/.*\(\/Library.*\)\/libR.dylib.*/\1/p'`

	if [ -z "$LIBDIR" ]; then
	    echo "ERROR: cannot determine R.framework paths!"
	    if [ $locked = yes ]; then
		rm -f "$BASE/LOCK"
	    fi
	    exit 1
	fi

	## copy vecLib BLAS stub if present
	if [ -f "$BASE/libRblas.vecLib.dylib" ]; then
	    cp -p "$BASE/libRblas.vecLib.dylib" "$LIBDIR/"
	    install_name_tool -id "$LIBDIR/libRblas.dylib" "$LIBDIR/libRblas.vecLib.dylib"
	fi
	## symlink BLAS to .0 if present
	if [ -e "$LIBDIR/libRblas.dylib" ]; then
	    mv "$LIBDIR/libRblas.dylib" "$LIBDIR/libRblas.0.dylib"
	    ln -s libRblas.0.dylib "$LIBDIR/libRblas.dylib"
	fi
	## copy omp if present
	if [ -f "$BASE/libomp.dylib" ]; then
	    cp -p "$BASE/libomp.dylib" "$LIBDIR/"
	    install_name_tool -id "$LIBDIR/libomp.dylib" "$LIBDIR/libomp.dylib"
	fi

	## /usr/local libraries
	ULL=`otool -L /Library/Frameworks/R.framework/Resources/lib/*.dylib | grep -E '^\t(/usr/local|/opt)' | grep -v X11 | sed -e 's:^.::' -e 's: (.*::' | sort | uniq`
	## check in those as well
	ULL2=`otool -L $ULL | grep -E '^\t(/usr/local|/opt)' | grep -v X11 | sed -e 's:^.::' -e 's: (.*::' | sort | uniq`
	ULL="$ULL $ULL2"

	## pass 1 : copy external libs
	for lib in $ULL; do
	    echo " - copy $lib"
	    libn=`basename $lib`
	    cp -p $lib "$LIBDIR/"
	    install_name_tool -id "$LIBDIR/$libn" "$LIBDIR/$libn"
	done
	## pass 2 : adjust paths
	for lib in $ULL; do
	    echo " - fix linked to $lib"
	    libn=`basename $lib`
	    chmod +w "$LIBDIR/"* 2> /dev/null
	    for dst in `ls $LIBDIR/*.dylib`; do
		install_name_tool -change "${lib}" "$LIBDIR/$libn" $dst
	    done
	done

	## generate dSYMs
	for l in "$LIBDIR/"libR*.dylib; do
	    echo " - generating dsym for $l"
	    dsymutil "$l"
	done

	## copy qpdf (if present) and set R_QPDF accordingly
	if [ -e "$BASE/qpdf" ]; then
	    echo " - copy qpdf"
	    cp "$BASE/qpdf" /Library/Frameworks/R.framework/Resources/bin/qpdf
	    renv="/Library/Frameworks/R.framework/Resources/etc/Renviron"
	    if [ -e "$renv" ]; then
		echo '## CRAN R ships with qpdf inside so use it unless directed otherwise' >> "$renv"
		echo 'R_QPDF=${R_QPDF-'"'/Library/Frameworks/R.framework/Resources/bin/qpdf'"'}' >> "$renv"
	    fi
	fi
	
	# unpack fontconfig config files and include fc-cache
	if [ -e $BASE/fontconfig-add.tar.gz ]; then
	    echo " - extract fontconfig-add.tar.gz"
	    $TAR fxz $BASE/fontconfig-add.tar.gz -C "$RFWH"
	fi
	if [ -e $PREFIX/bin/fc-cache ]; then
	    echo " - copy fc-cache"
	    cp -p $PREFIX/bin/fc-cache $RFWH/bin/fc-cache
	fi
	# remove any existing cache
	rm -rf $RFWH/fontconfig/cache/*
	
	ARHOME=`dirname "$LIBDIR"`
	$BASE/fixpathR "$ARHOME"
	$BASE/fixup

	if [ -e $BASE/unlock-sign ]; then
	    . $BASE/unlock-sign
	fi

	## see if full siging works
	rm -f /tmp/sign-test
	touch /tmp/sign-test
	if codesign -s dev /tmp/sign-test; then
	    $BASE/sign-dev /Library/Frameworks/R.framework
	else
	    echo falling back to empty sig
	    $BASE/sign-dev /Library/Frameworks/R.framework -
	fi
	rm /tmp/sign-test

	$TAR fcz $BASE/$TNAME/$RD/current/$RD.tar.gz $TARFLAGS -C / /Library/Frameworks/R.framework
	#mkdir -p "$BASE/deploy/$oscode/$RD" 2>/dev/null
	#cp $BASE/$RD-$TNAME.tar.gz "$BASE/deploy/$oscode/$RD/"

	cd $BASE

	echo "SUCCESS"
	touch $BASE/$TNAME/$RD/current/$RD.FW.SUCCESS
    fi # failed=no
fi

if [ $locked = yes ]; then
    rm -f "$BASE/LOCK"
fi

## copy results to deployment repo
rm -rf "$BASE/deploy/$oscode/$RD/$tarch"
mkdir -p "$BASE/deploy/$oscode/$RD/$tarch" 2>/dev/null
for f in "$RD.SUCCESS" "$RD.FAILED" "$RD.ERROR" "$RD.FW.SUCCESS" "$RD.tar.gz" "$RD.check" "$RD.conf" "$RD.build" check.err conf.err build.err check conf build build.log; do
    if [ -e "$BASE/$TNAME/$RD/current/$f" ]; then cp -p "$BASE/$TNAME/$RD/current/$f" "$BASE/deploy/$oscode/$RD/$tarch/$f"; fi
done