#!/bin/sh

# Copyright (c) 2008-2011 Aleksey Cheusov <vle@gmx.net>
# All rights reserved.
#
# See LICENSE file

######################################################################

set -e

export PATH=/usr/pkg/bin:$PATH

usage (){
    cat <<'EOF'
distbb - DISTributed Bulk Builds for pkgsrc

usage: distbb -h
       distbb -V
       distbb [OPTIONS]
       distbb -r -B <BUILDID> [OPTIONS]
OPTIONS:
  -h                display this help message
  -V                distbb version
  -C <config>       config file, the default is
                    /usr/pkg/etc/distbb.conf
  -p <packages>     build the specified packages
                    (implies -x summary -x checksum -x upload_pkgs)
  -f <filename>     build the packages listed in
                    the specified filename ( - for stdin),
                    by default all packages from pkgsrc source tree
                    are built
                    (implies -x summary -x checksum -x upload_pkgs)
  -k                skip updating pkg_src_summary
  -K                skip updating pkg_summary
  -D                show difference with previous bulk build
  -B <build_id>     set BUILD_ID (for debugging purposes)
  -r                if your bulk build failed for some reason, you
                    can try to continue it from the point it stopped at
  -s                exit status is 0, if ALL packages succeded,
                    or 5 otherwise
  -l <label>        label for bulk build, unless -B option is applied
                    bulk build file will be stored
                    in $REPORTS_DIR/$BUILD_ID-<label> directory
  -x <stage>        exclude <stage> from BUILD_STAGES.
                    Multiple -x are allowed.
                    "-x upload" is equivalent to
                    "-x upload_logs -x upload_pkgs".
  -X <stage>        enable <stage> (antonym for -x)
  -i                build all installed packages and generate pkg_summary.txt
  -I <pkg_db>       the same as -i but installed packages
                    are obtained from <pkg_db>
  -m <mode>         mode of dependency graph generation
                    0 - use package from pkgsrc tree (the default)
                    1 - use latest available binary if it satisfies
                    DEPENDS and BUILD_DEPENDS
  -a                build packages specified by -f or -p and
                    add new summaries to pkg_summary(5)
EOF
}

partial_bb (){
    export exclude_stage_summary=1 # see stage_checks
    exclude_stage_checksum=1
    exclude_stage_upload_pkgs=1
}

full_bb (){
    unset exclude_stage_summary || true
    unset exclude_stage_checksum || true
    unset exclude_stage_upload_pkgs || true
}

is_stage_valid (){
    # $1 - stage name
    for s in $BUILD_STAGES; do
	if test "$1" = "$s"; then
	    return 0
	fi
    done
    echo "Bad stage $1" 1>&2
    exit 1
}

exclude (){
    excluded_stages="$excluded_stages $1"
    eval "exclude_stage_$1=1"
}

unexclude (){
    unexcluded_stages="$unexcluded_stages $1"
    unset "exclude_stage_$1" || true
}

export deps_graph_mode=0

while getopts hVf:p:C:DB:l:kKx:X:rsiI:m:a f; do
    case $f in
	h)   usage; exit 0;;
	V)   echo distbb-0.47.2; exit 0;;
	f)   partial_bb; installed=; pkgs=; pkgs_fn="$OPTARG";;
	p)   partial_bb; installed=; pkgs_fn=; pkgs="$OPTARG";;
	C)   DISTBB_CONF="$OPTARG";;
	D)   export DISTBB_DIFF=1;;
	B)   BUILD_ID="$OPTARG";;
	l)   label="$OPTARG";;
	k)   skip_updating_src_summary=1;;
	K)   skip_updating_summary=1;;
	x)   exclude "$OPTARG";;
	X)   unexclude "$OPTARG";;
	r)   retry=1;;
	s)   strict=1;;
	i)   full_bb; pkgs=; pkgs_fn=; K_opt=; installed=1;;
	I)   full_bb; pkgs=; pkgs_fn=; K_opt="-K $OPTARG"; installed=1;;
	m)   export deps_graph_mode="$OPTARG";;
	a)   full_bb; export add_new_summaries=1;;
	'?') printf '%s\n' "$USAGE"; exit 1;;
    esac
done
shift `expr $OPTIND - 1`

export pkgs_fn

if test $# -ne 0; then
    usage
    exit 1
fi

exclude_stage_upload_pkgs="${exclude_stage_upload_pkgs}${exclude_stage_upload}"
exclude_stage_upload_logs="${exclude_stage_upload_logs}${exclude_stage_upload}"

on_exit () { rm -f $pkgs_fn; }
. /usr/pkg/libexec/psu/sig_handler.sh

if test -n "$pkgs$installed"; then
    export pkgs_fn=`mktemp /tmp/distbb.XXXXXX`
    test -n "$pkgs_fn"

    if test -n "$pkgs"; then
	for p in $pkgs; do
	    printf "%s\n" $p >> "$pkgs_fn"
	done
    else
	pkg_bin_summary -f PKGPATH,ASSIGNMENTS -- -u $K_opt |
	pkg_assignments2pkgpath |
	sed -n 's/^PKGPATH=//p' > "$pkgs_fn"
    fi
fi

if test -n "$retry" && test -z "$BUILD_ID"; then
    echo '-r option needs -B' 1>&2
    exit 1
fi

######################################################################

. /usr/pkg/bin/pipestatus

# date start for placint it to html/txt report
build_start="$(date -u '+%Y-%m-%d %H:%M') UTC"
# date start in YYYYMMDD-HHMM format
if test -z "$BUILD_ID"; then
    BUILD_ID="$(echo $build_start | awk '{gsub(/[:-]/, ""); print $1 "." $2}')"
    if test -n "$label"; then
	BUILD_ID="$BUILD_ID-$label"
    fi
fi

echo "BUILD_ID: $BUILD_ID" 1>&2

: ${DISTBB_CONF:=/usr/pkg/etc/distbb.conf}
. "$DISTBB_CONF"
. /usr/pkg/libexec/distbb/common

# -x|-X sanity checks
for s in $excluded_stages $unexcluded_stages; do
    is_stage_valid "$s"
done

export DISTBB_CONF
######################################################################
test_var (){
    set +e # workaround for buggy Korn Shell ('set -e' + if + eval + false)
    if eval "test -z \"\$$1\""; then
	printf "Variable $1 is unset\n", "$1" 1>&2
	exit 1
    fi
    set -e
}

test_var BMAKE
test_var PKGSRCDIR
test_var PKGSRC_PREFIX
test_var RMDIRS_CMD
test_var EXTRACT_BOOTSTRAP_CMD
test_var PACKAGES
test_var PKG_ALL_SUMMARY
test_var PKG_UPDATE_ALL_SUMMARY_CMD
test_var PKG_SUMMARY
test_var PKG_SRC_SUMMARY
test_var PKG_UPDATE_SRC_SUMMARY_CMD
test_var PKG_CMP_SUMMARY_CMD
test_var REPORTS_DIR
test_var PKG_SUFX
test_var CLEAN_TARGET
test_var TARGETS
test_var DISTBB_SLAVE
test_var OFFENDERS_CNT
test_var MAX_FAILED_DEPS_CNT
test_var REPORT_CMD
test_var UPLOAD_PKGS_PROG
test_var UPLOAD_LOGS_PROG
test_var LIST_ALL_PKGS_CMD
test_var DISTBB_LOCAL_MK
test_var MSCOMMONDIR

if is_true "$CROSS_COMPILE"; then
    test_var TARGET_MAKECONF
    test_var TARGET_PACKAGES
fi

if test "$MAIL_PROG" != ':'; then
    test_var USER_MAIL
fi
if is_true "$MASTER_MODE"; then
    test_var SLAVES
    test_var PSS_SLAVES
fi
if test "$UPLOAD_LOGS_PROG" != ':'; then
    test_var UPLOAD_LOGS_DEST
fi
if test "$UPLOAD_PKGS_PROG" != ':'; then
    test_var UPLOAD_PKGS_DEST
fi

if grep BUILD_START "$DISTBB_CONF" > /dev/null; then
    echo 'Replace BUILD_START variable with BUILD_ID in your distbb.conf!' 1>&2
    exit 1
fi

if test "$PKG_ALL_SUMMARY" = "$PKG_SUMMARY"; then
    echo 'PKG_ALL_SUMMARY and PKG_SUMMARY must not be equal' 1>&2
    exit 1
fi

if test "$I_AM_READY" -ne 4 2>/dev/null; then
    echo 'Read NEWS file! Your distbb.conf needs to be updated ;-)' 1>&2
    exit 1
fi

######################################################################

# exporting variables for stage_init
export skip_updating_src_summary
export skip_updating_summary
export build_start

#
run_stage (){
    # $1 - stage
    stage="$1"
    if echo "$stage" | grep '^/' > /dev/null; then
	stage_fn="$stage"
	stage=$(basename $stage)
    else
	stage_fn="/usr/pkg/libexec/distbb/stage_$stage"
    fi

    printf '==== %s ====\n' "$stage"
    done_fn=$tmpdir/done_stage_${stage}.tmp
    if test -f "${done_fn}"; then
	echo "  skipped"
    else
	eval ${stage_fn} "$REPORT1_DIR"
	touch ${done_fn}
    fi
}

#
if test -z "$retry"; then
    rm -f "$tmpdir/done_stage_init.tmp"
fi
run_stage init

for s in $BUILD_STAGES; do
    eval x='$'exclude_stage_$s
    if test -n "$x"; then
	done_fn=$tmpdir/done_stage_${s}.tmp
	printf '' > "$done_fn"
    fi
done

for s in $BUILD_STAGES; do
    run_stage "$s"
done

######################################################################

if test -n "$strict"; then
    if test -s "$packages_failed_notavail_fn" ||
	test -s "$packages_failed_scan_fn" ||
	test -s "$packages_failed_fn"
    then
	exit 5
    else
	exit 0
    fi
fi
