#!/bin/sh

TOPDIR=$PWD

# CVSROOT is inherited from the environment
KERNELDIR=
LUSTRE=
PUBLISH=0
RELEASE=0
DO_SRC=1
TAG=
TARGET=
TARGET_ARCHS=
CONFIGURE_FLAGS=
EXTERNAL_PATCHES=
EXTRA_VERSION=

# from target file
KERNEL=
SERIES=
CONFIG=
VERSION=

RHBUILD=0
SUSEBUILD=0
LINUX26=0
SUSEBUILD=0

BASE_ARCHS=
BIGMEM_ARCHS=
BOOT_ARCHS=
JENSEN_ARCHS=
SMP_ARCHS=
BIGSMP_ARCHS=
UP_ARCHS=

DATE=$(date)

USE_DATESTAMP=1
RPMBUILD=

cleanup()
{
    true
}

error()
{
    [ "$1" ] && echo -e "\n${0##*/}: $1"
}

fatal()
{
    cleanup
    error "$2"
    exit $1
}

publishing()
{
    (( $PUBLISH )) || return 0
}

is_release()
{
    (( $RELEASE )) || return 0
}

list_targets()
{
    echo -n "Available targets:"
    for target in $TOPDIR/lustre/lustre/kernel_patches/targets/*.target ; do
	target_file=${target##*/}
	echo -n " ${target_file%%.target}"
    done
    echo
}

usage()
{
    cat <<EOF
Usage: ${0##*/} [OPTION]... [-- <lustre configure options>]

  -d CVSROOT
    Specifies the CVS Root to use when pulling files from CVS.  The
    environment variable \$CVSROOT is used if this option is not
    present.

  --external-patches=EXTERNAL_PATCHES
    Directory similar to lustre/lustre/kernel_patches/ that lbuild should
    look for seres and config files in before looking in the lustre
    tree.

  --extraversion=EXTRAVERSION
    Text to use for the rpm release and kernel extraversion.

  --kerneldir=KERNELDIR
    Directory containing Linux source tarballs referenced by target
    files.

  --lustre=LUSTRE
    Path to an existing lustre source tarball to use instead of
    pulling from CVS.

  --nosrc
    Do not build a .src.rpm, a full kernel patch, or a patched kernel
    tarball.

  --publish
    Publish the packages, patches, and tarballs on the ftp server.

  --release
    Specifies that the files generated do not include timestamps, and
    that this is an official release.

  --tag=TAG
    A CVS branch/tag name to build from when pulling from CVS.

  --target=TARGET
    The name of the target to build.  The available targets are listed
    below.

  --target-archs=TARGET_ARCHS
    A (space delimited) list of architectures to build.  By default,
    all of the archs supported by the TARGET will be built, in
    addition to a .src.rpm.  This option can limit those, for machines
    that can only build certain archs or if you only want a certain
    arch built (for testing, or a one-off kernel).

    Also note that by using a non-"base" arch (eg, i386) only kernels
    will be built - there will be no lustre-lite-utils package.

  --disable-datestamp
    Prevents the datestamp flag (-D) from being passed to cvs for 
    checkouts. This is a workaround for a problem encountered when 
    using lbuild with tinderbox.

EOF

#   list_targets

    fatal "$1" "$2"
}

check_options()
{
    if [ "$LUSTRE" ] ; then
	[ -r "$LUSTRE" ] || \
	    usage 1 "Could not find Lustre source tarball '$LUSTRE'."
    else
	[ "$CVSROOT" ] || \
	    usage 1 "Either specify a CVS Root with -d, or a Lustre source tarball with --lustre."
	[ "$TAG" ] || \
	    usage 1 "A branch/tag name must be specified with --tag when not building from a tarball."
    fi

    [ "$KERNELDIR" ] || \
	usage 1 "A kernel directory must be specified with --kerneldir."

    [ -d "$KERNELDIR" ] || \
	usage 1 "$KERNELDIR is not a directory."

    if ! (( $RELEASE )) ; then
	[ "$TAG" ] || \
	    usage 1 "When building a snapshot, a tag name must be used."
    fi

    TIMESTAMP=$(date -d "$DATE" "+%Y%m%d%H%M")

    [ "$TARGET" ] || usage 1 "A target must be specified with --target."
#    TARGET_FILE="$TOPDIR/lustre/kernel_patches/targets/$TARGET.target"
#    [ -r "$TARGET_FILE" ] || \
#	usage 1 "Target '$TARGET' was not found."

    RPMBUILD=$(which rpmbuild 2>/dev/null | head -1)
    if [ ! "$RPMBUILD" -o "$RPMBUILD" == "" ]; then
	RPMBUILD=$(which rpm 2>/dev/null | head -1)
	if [ ! "$RPMBUILD" -o "$RPMBUILD" == "" ]; then
	    usage 1 "Could not find binary for making rpms (tried rpmbuild and rpm)."
	fi
    fi
}

uniqify()
{
    echo $(echo "$*" | xargs -n 1 | sort -u)
}

load_target()
{
    EXTRA_VERSION_save="$EXTRA_VERSION"
    for patchesdir in "$EXTERNAL_PATCHES" "$TOPDIR/lustre/lustre/kernel_patches" ; do
	TARGET_FILE="$patchesdir/targets/$TARGET.target"
	[ -r "$TARGET_FILE" ] && break
    done
    [ -r "$TARGET_FILE" ] || \
	fatal 1 "Target $TARGET was not found."

    echo "Loading target config file $TARGET.target..."	

    . "$TARGET_FILE"

    [ "$KERNEL"  ] || fatal 1 "Target $TARGET did not specify a kernel."
#    [ "$SERIES"  ] || fatal 1 "Target $TARGET did not specify a kernel patch series."
#    [ "$CONFIG"  ] || fatal 1 "Target $TARGET did not specify a kernel config."
    [ "$VERSION" ] || fatal 1 "Target $TARGET did not specify a kernel version."

    if [ "$KERNELDIR" ] ; then
	KERNEL_FILE="$KERNELDIR/$KERNEL"
	[ -r "$KERNELDIR/$KERNEL" ] || \
	    fatal 1 "Target $TARGET's kernel file $KERNEL not found in kernel directory $KERNELDIR."
    fi

    if [ "$SERIES" ] ; then
        for series in $SERIES ; do
	    for patchesdir in "$EXTERNAL_PATCHES" "$TOPDIR/lustre/lustre/kernel_patches" ; do
	        [ -r "$patchesdir/series/$series" ] && continue 2
	    done
	    fatal 1 "Target $TARGET's series $SERIES could not be found.\nSearched:\n\t$EXTERNAL_PATCHES/series\n\t$TOPDIR/lustre/lustre/kernel_patches/series."
	done
    fi

    CONFIG_FILE="$TOPDIR/lustre/lustre/kernel_patches/kernel_configs/$CONFIG"
    [ -r "$CONFIG_FILE" ] || \
	fatal 1 "Target $TARGET's config file $CONFIG missing from $TOPDIR/lustre/lustre/kernel_patches/kernel_configs/."

    if [ "$EXTRA_VERSION_save" ] ; then
	EXTRA_VERSION="$EXTRA_VERSION_save"
    elif ! (( $RELEASE )) ; then
	EXTRA_VERSION="${EXTRA_VERSION}-${TAG}.${TIMESTAMP}"
    fi
    # EXTRA_VERSION=${EXTRA_VERSION//-/_}

    ALL_ARCHS="$BASE_ARCHS $BIGMEM_ARCHS $BOOT_ARCHS $JENSEN_ARCHS $SMP_ARCHS $BIGSMP_ARCHS $UP_ARCHS"

    BUILD_ARCHS=
    for arch in $(uniqify "$ALL_ARCHS") ; do
	if [ -z "$TARGET_ARCHS" ] || echo "$TARGET_ARCHS" | grep "$arch" >/dev/null 2>/dev/null ; then
	    BUILD_ARCHS="$BUILD_ARCHS $arch"
	fi
    done
    [ "$BUILD_ARCHS" ] || usage 1 "No available target archs to build."
    echo "Building for: $BUILD_ARCHS"
}

tarflags()
{
    case "$1" in
	'')
	    fatal 1 "tarflags(): File name argument missing."
	    ;;
	*.tar.gz)
	    echo 'zxf'
	    ;;
	*.tar.bz2)
	    echo 'jxf'
	    ;;
	*)
	    fatal 1 "tarflags(): Unrecognized tar extension in file: $1"
	    ;;
    esac
}

untar()
{
    echo "Untarring ${1##*/}..."
    tar $(tarflags "$1") "$1"
}

unpack_lustre()
{
    DIRNAME="lustre-$TAG-$TIMESTAMP"
    if [ "$LUSTRE" ] ; then
	untar "$LUSTRE"
	[ -d lustre ] || ln -sf lustre* lustre
    else
	if [ "$USE_DATESTAMP" ]; then
	    DATESTAMP="-D '$DATE'"
	else
	    DATESTAMP=""
	fi	    

	cvs -d "$CVSROOT" -qz3 co $DATESTAMP -d "$DIRNAME" lustre || \
	    fatal 1 "There was an error checking out toplevel Lustre from CVS."
	pushd "$DIRNAME" > /dev/null
	./lustrecvs "$TAG" || \
	    fatal 1 "There was an error checking out Lustre/Portals/Build from CVS."
	echo "Creating lustre tarball..."
	sh autogen.sh || fatal 1 "There was an error running autogen.sh."
	./configure --disable-{modules,utils,liblustre,tests,doc} || \
	    fatal 1 "There was an error running ./configure to create makefiles."
	make dist || fatal 1 "There was an error running 'make dist'."
	popd > /dev/null
	fname=`basename $DIRNAME/lustre-*.tar.gz`
	cp $DIRNAME/$fname . || fatal 1 "There was an error copying lustre tarball."
	LUSTRE="$PWD/$fname"
	ln -sf "$DIRNAME" lustre
    fi
}

unpack_linux()
{
    untar "$KERNEL_FILE"
    [ -d linux ] || ln -sf linux* linux
}

patch_linux()
{
    [ "$SERIES" ] || return 0
    FULL_PATCH="$PWD/lustre-kernel-${TARGET}-${EXTRA_VERSION}.patch"
    [ -f "$FULL_PATCH" ] && rm -f "$FULL_PATCH"
    pushd linux >/dev/null
    for series in $SERIES ; do
	echo -n "Applying series $series:"
	for patchesdir in "$EXTERNAL_PATCHES" "$TOPDIR/lustre/lustre/kernel_patches" ; do
	    [ -r "$patchesdir/series/$series" ] || continue
	    SERIES_FILE="$patchesdir/series/$series"
	    for patch in $(<"$SERIES_FILE") ; do
		echo -n " $patch"
		PATCH_FILE="$patchesdir/patches/$patch"
		[ -r "$PATCH_FILE" ] || \
		    fatal 1 "Patch $patch does not exist in Lustre tree."
		cat "$PATCH_FILE" >> "$FULL_PATCH" || \
		    fatal 1 "Error adding patch $patch to full patch."
		patch -s -p1 < "$PATCH_FILE" || fatal 1 "Error applying patch $patch."
	    done
	    break
	done
	echo
    done
    popd >/dev/null
    echo "Full patch has been saved in ${FULL_PATCH##*/}."
    echo "Replacing .config files..."
    [ -d linux/configs ] || mkdir linux/configs || \
        fatal 1 "Error creating configs directory."
    rm -f linux/configs/*
    copysuccess=0
    for patchesdir in "$EXTERNAL_PATCHES" "lustre/lustre/kernel_patches" ; do
	cp -v $patchesdir/kernel_configs/kernel-${VERSION}-${TARGET}*.config linux/configs/ >/dev/null && copysuccess=1
    done
    [ "$copysuccess" = "1" ] || \
	fatal 1 "Error copying in kernel configs."
}

pack_linux()
{
    TARBALL="$(readlink linux)-$EXTRA_VERSION.tar.gz"
    echo "Creating patched linux tarball $TARBALL..."
    tar zcf "$TARBALL" "$(readlink linux)" \
	--exclude "CVS" --exclude ".cvsignore" || \
	--exclude "*.orig" --exclude "*~" --exclude "*.rej" || \
	fatal 1 "Error creating patched Linux tarball."
}

clean_linux()
{
    [ -d linux ] || return 0
    echo "Cleaning linux..."
    [ -L linux ] && rm -rf $(readlink linux)
    rm -rf linux
}

prep_build()
{
    # make .spec file
    if [ -f /etc/init.d/functions -a -f /etc/sysconfig/network ] ; then
        ENABLE_INIT_SCRIPTS=""
    else
        ENABLE_INIT_SCRIPTS="# "
    fi
    sed \
	-e "s^@BASE_ARCHS@^$BASE_ARCHS^g" \
	-e "s^@BIGMEM_ARCHS@^$BIGMEM_ARCHS^g" \
	-e "s^@BIGSMP_ARCHS@^$BIGSMP_ARCHS^g" \
	-e "s^@BOOT_ARCHS@^$BOOT_ARCHS^g" \
	-e "s^@CONFIGURE_FLAGS@^$CONFIGURE_FLAGS^g" \
	-e "s^@ENABLE_INIT_SCRIPTS@^$ENABLE_INIT_SCRIPTS^g" \
	-e "s^@JENSEN_ARCHS@^$BOOT_ARCHS^g" \
	-e "s^@KERNEL_EXTRA_VERSION@^$EXTRA_VERSION^g" \
	-e "s^@KERNEL_RELEASE@^${EXTRA_VERSION//-/_}^g" \
	-e "s^@KERNEL_SOURCE@^$KERNEL^g" \
	-e "s^@KERNEL_VERSION@^$VERSION^g" \
	-e "s^@LINUX26@^$LINUX26^g" \
	-e "s^@LUSTRE_SOURCE@^${LUSTRE##*/}^g" \
	-e "s^@LUSTRE_TARGET@^$TARGET^g" \
	-e "s^@RHBUILD@^$RHBUILD^g" \
	-e "s^@SMP_ARCHS@^$SMP_ARCHS^g" \
	-e "s^@SUSEBUILD@^$SUSEBUILD^g" \
	-e "s^@SUSEBUILD@^$SUSEBUILD^g" \
	-e "s^@UP_ARCHS@^$UP_ARCHS^g" \
	< $TOPDIR/lustre/build/lustre-kernel-2.4.spec.in \
	> lustre-kernel-2.4.spec
    [ -d SRPMS ] || mkdir SRPMS
    [ -d RPMS ] || mkdir RPMS
    [ -d BUILD ] || mkdir BUILD
    [ -d SOURCES ] || mkdir SOURCES
    for script in linux-{rhconfig.h,merge-config.awk,merge-modules.awk} \
	suse-{functions.sh,post.sh,postun.sh,trigger-script.sh.in} ; do
	cp $TOPDIR/lustre/build/$script SOURCES
    done
    cp "$LUSTRE" "$KERNEL_FILE" SOURCES
    if [ "$EXTERNAL_PATCHES" -a -d "$EXTERNAL_PATCHES" ] ; then
	tar zcf SOURCES/external-patches.tar.gz -C "$EXTERNAL_PATCHES" series targets patches kernel_configs
    else
	touch SOURCES/external-patches.tar.gz
    fi
}

clean_lustre()
{
    [ -d lustre ] || return 0
    echo "Cleaning lustre..."
    [ -L lustre ] && rm -rf $(readlink lustre)
    rm -rf lustre
}

build()
{
    echo "Building rpms for: $BUILD_ARCHS..."
    targets=
    for arch in $BUILD_ARCHS ; do
	targets="--target $arch $targets"
    done

    $RPMBUILD $targets -bb lustre-kernel-2.4.spec \
	--define "_topdir $TOPDIR" || \
	fatal 1 "Error building rpms for $arch."

    if (( $DO_SRC )) ; then
	$RPMBUILD -bs lustre-kernel-2.4.spec \
	    --define "_topdir $TOPDIR" || \
	    fatal 1 "Error building .src.rpm."
    fi
}

publish()
{
    publishing || return 0
}

[ -r ~/.lbuildrc ] && . ~/.lbuildrc

options=$(getopt -o d:D:h -l external-patches:,extraversion:,kerneldir:,lustre:,nosrc,publish,release,tag:,target:,target-archs:,disable-datestamp -- "$@")

eval set -- "$options"
    
while [ "$1" ] ; do
    case "$1" in
	'')
	    usage 1
	    ;;
	-d)
	    CVSROOT=$2
	    shift 2
	    ;;
	-D)
	    DATE=$2
	    shift 2
	    ;;
	--external-patches)
	    EXTERNAL_PATCHES=$2
	    shift 2
	    ;;
	--extraversion)
	    EXTRA_VERSION=$2
	    shift 2
	    ;;
	--help | -h)
	    usage 0
	    ;;
	--kerneldir)
	    KERNELDIR=$2
	    shift 2
	    ;;
	--lustre)
	    LUSTRE=$2
	    shift 2
	    ;;
	--nosrc)
	    DO_SRC=0
	    shift 1
	    ;;
	--publish)
	    PUBLISH=1
	    shift
	    ;;
	--release)
	    RELEASE=1
	    shift
	    ;;
	--tag)
	    TAG=$2
	    shift 2
	    ;;
	--target)
	    TARGET=$2
	    shift 2
	    ;;
	--target-archs)
	    TARGET_ARCHS=$2
	    shift 2
	    ;;
	--disable-datestamp)
	    USE_DATESTAMP=
	    shift
	    ;;
	--)
	    shift
	    CONFIGURE_FLAGS=$@
	    break
	    ;; 
	*)
	    usage 1 "Unrecognized option: $1"
	    ;;
    esac
done

check_options

unpack_lustre
load_target

if (( $DO_SRC )) ; then
    unpack_linux
    patch_linux
    pack_linux
    clean_linux
fi

# prep_build needs the .spec.in from the lustre source
prep_build
clean_lustre

build
publish