1#!/bin/bash 2 3## Copyright (C) 2018 Robert Krawitz 4## 5## This program is free software; you can redistribute it and/or modify 6## it under the terms of the GNU General Public License as published by 7## the Free Software Foundation; either version 2, or (at your option) 8## any later version. 9## 10## This program is distributed in the hope that it will be useful, 11## but WITHOUT ANY WARRANTY; without even the implied warranty of 12## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13## GNU General Public License for more details. 14## 15## You should have received a copy of the GNU General Public License 16## along with this program. If not, see <https://www.gnu.org/licenses/>. 17## 18## Build release tarball 19 20# Note that the shebang line is explicit here, not indirected through 21# autoconf. This allows the script to be run in a non-initialized workspace. 22# We also require this script to be run in the root of a workspace so that 23# it can find the script directory to get the version without having to be 24# autotool-processed. 25 26function xtput() { 27 if [[ -z ${STP_TEST_RECURSIVE:-} && -n ${TERM:-} && ${TERM:-} != dumb ]] ; then 28 tput "$@" 29 fi 30} 31 32set -u 33 34# shellcheck disable=SC2155 35{ 36declare -r tbold="$(xtput bold)" 37declare -r tred="$(xtput setaf 1)$tbold" 38declare -r tyellow="$(xtput setaf 3)$tbold" 39declare -r tgreen="$(xtput setaf 2)$tbold" 40declare -r tpurple="$(xtput setaf 5)$tbold" 41declare -r tblue=$(xtput setaf 4) 42declare -r tcyan=$(xtput setaf 6) 43declare -r treset=$(xtput sgr0) 44} 45declare -i exitstatus=1 46declare -i stepstatus=-1 47declare currentmodule= 48declare -a failedmodules=() 49declare top_log 50function fatal() { 51 echo "${tred}FATAL: $*${treset}" 52 exit 1 53} 54 55# shellcheck disable=SC2155 56declare GIT=$(type -p git) 57[[ -n $GIT ]] || fatal "Can't find git" 58 59"$GIT" rev-parse --show-toplevel >/dev/null 2>&1 || fatal "Current directory is not a git workspace" 60declare rootdir 61rootdir="$("$GIT" rev-parse --show-toplevel 2>/dev/null)" 62[[ -n $rootdir ]] || fatal "Can't find workspace root" 63cd "$rootdir" || fatal "Can't cd to workspace root ($rootdir)" 64[[ -s ChangeLog.pre-5.2.11 ]] || fatal "$rootdir does not appear to be a Gutenprint tree" 65 66# shellcheck disable=SC2155 67declare MAKE=$(type -p make) 68[[ -n $MAKE ]] || fatal "Can't find make" 69 70# shellcheck disable=SC2155 71declare -r ROOT=$(pwd) 72declare tmpfile=/dev/null 73export STP_PARALLEL=${STP_PARALLEL:-$("$ROOT/scripts/count-cpus")} 74declare counter=1 75declare git_dirty= 76declare build_type=release 77[[ -n ${STP_BUILD_SNAPSHOT:-} ]] && build_type=snapshot 78 79# This can't simply be a constant because scripts/gversion might 80# not exist (or may be incorrect) prior to autogen being run. 81function pkg_version() { 82 "$ROOT/scripts/gversion" pkg 83} 84 85function pkg_tag() { 86 # shellcheck disable=SC2155 87 local version=$(pkg_version) 88 echo "gutenprint-${version//./_}" 89} 90 91# Clean up any trailing whitespace. 92 93function preflight() { 94 # shellcheck disable=SC2155 95 local trailing_ws="$("$GIT" grep -Il '[ ]$')" 96 if [[ -n $trailing_ws ]] ; then 97 console_log "*** ERROR: The following files have trailing whitespace:" 98 console_log "$trailing_ws" 99 return 2 100 fi 101 return 0 102} 103 104# Git pre-checks (not version-specific) 105 106function check_git() { 107 "$GIT" fetch 108 local gstatus=0 109 110 # Check for uncommitted files. 111 if [[ -n $("$GIT" status -uno --porcelain) ]] ; then 112 console_log "*** ERROR: Uncommitted changes in repository:" 113 "$GIT" status -uno --porcelain | console_log 114 gstatus=2 115 fi 116 117 # Ensure that the workspace is up to date (git status -uno 118 # --porcelain -b |grep -v ahead is empty -- it's OK to be ahead, 119 # but not behind) and that we don't need to rebase (no merges. 120 # Also check that we haven't diverged. 121 122 # shellcheck disable=SC2155 123 local ahead=$("$GIT" rev-list '@{u}..@') 124 # shellcheck disable=SC2155 125 local behind=$("$GIT" rev-list '@..@{u}') 126 if [[ -n $ahead && -n $behind ]] ; then 127 # Oops! Both ahead *and* behind remote. Really bad news! 128 console_log "*** ERROR: HEAD and remote have diverged!" 129 console_log "*** Please merge and rebase all changes!" 130 return 2 131 elif [[ -n $behind ]] ; then 132 # We're behind. Not good. 133 console_log "*** ERROR: Behind remote by $(wc -w <<< "$behind") commits." 134 return 2 135 elif [[ -n $ahead ]] ; then 136 # We're ahead. That's OK as long as there are no merge commits. 137 local merges=0 138 for h in $ahead ; do 139 (( $("$GIT" rev-parse "$h^@" |wc -w) > 1 )) && merges=$((merges + 1)) 140 done 141 console_log "*** Warning: Ahead of remote." 142 if (( merges > 0 )) ; then 143 (( merges != 1 )) && pl=s 144 console_log "*** ERROR: $merges merge${pl:-} between HEAD and remote" 145 return 2 146 fi 147 fi 148 return $gstatus 149} 150 151# Run autogen.sh to ensure that we're using default build settings 152# Everything else depends on this. 153 154function run_target() { 155 make "$1" && return 0 156 local lstatus=$? 157 console_log "*** ERROR: ${3:+$3 }make $1 failed" 158 (( lstatus >= 127 )) && return $lstatus 159 return "${2:-2}" 160} 161 162function run_maintainer_clean() { 163 if [[ -f Makefile ]] ; then 164 run_target maintainer-clean 165 else 166 return 0 167 fi 168} 169 170function run_autogen() { 171 # shellcheck disable=SC2086 172 ./autogen.sh ${STP_CONFIG_ARGS:-} && return 0 173 console_log "*** FATAL: autogen failed!" 174 return 1 175} 176 177function colorize() { 178 sed \ 179 -e "s/\*\*\* \(FATAL\|ERROR\):\(.*\)/${tred}*** \1:\2${treset}/" \ 180 -e "s/\*\*\* Warning:\(.*\)/${tyellow}*** Warning:\1${treset}/" 181} 182 183function run_clean() { 184 run_target clean 185} 186 187function run_build() { 188 run_target "${STP_PARALLEL:+-j$STP_PARALLEL}" 1 189} 190 191# Same as above, without make clean if we know we're in a clean 192# environment (e. g. CI) 193function run_build_fresh() { 194 # shellcheck disable=SC2086 195 ./autogen.sh ${STP_CONFIG_ARGS:-} && make "${STP_PARALLEL:+-j$STP_PARALLEL}" && return 0 196 console_log "*** FATAL preliminary build failed!" 197 return 1 198} 199 200# Git check tag. This can't be run until after the build, because we 201# don't have the version available until autogen. 202 203function check_git_tag() { 204 # Make sure that the tag that we're going to want to apply isn't 205 # already present. 206 [[ $build_type != release ]] && return 0 207 if [[ -n $("$GIT" show-ref "refs/tags/$(pkg_tag)") ]] ; then 208 console_log "*** ERROR: Tag named $(pkg_tag) is already present" 209 return 2 210 fi 211} 212 213function _cleanup_test_repo() { 214 if [[ -d $TESTREPO ]] ; then 215 rm -rf -- "$TESTREPO" 216 fi 217} 218 219# Check that we can build a clone of this workspace 220function _check_git_builds() { 221 # shellcheck disable=SC2155 222 export TESTREPO=$(mktemp -d "/tmp/stpbuild.XXXXXXXX") 223 trap _cleanup_test_repo EXIT SIGHUP SIGINT SIGQUIT SIGTERM 224 # shellcheck disable=SC2155 225 local rev=$("$GIT" rev-parse @) 226 cwd=$(pwd -P) 227 [[ -n $cwd ]] || { 228 console_log "*** ERROR: Can't find directory!" 229 return 2 230 } 231 cd "$TESTREPO" || { 232 console_log "*** ERROR: Can't cd to test repo directory $cwd!" 233 (( $? >= 127 )) && return 127 234 return 2 235 } 236 "$GIT" clone "$cwd" . || { 237 console_log "*** ERROR: Unable to clone repo" 238 (( $? >= 127 )) && return 127 239 return 2 240 } 241 "$GIT" checkout "$rev" || { 242 console_log "*** ERROR: Unable to check out rev $rev" 243 (( $? >= 127 )) && return 127 244 return 2 245 } 246 STP_TEST_RECURSIVE=$((${STP_TEST_RECURSIVE?-0}+1)) STP_LOG_NO_SUBDIR=1 STP_LOG_DIR=$STP_TEST_LOG_PREFIX scripts/build-release preflight run_autogen run_build run_distcheck_minimal || { 247 console_log "*** ERROR: Repo build failed!" 248 (( $? >= 127 )) && return 127 249 return 2 250 } 251} 252 253function check_git_builds() { 254 (_check_git_builds) 255} 256 257# Run make valgrind-minimal. 258# 259# This does a *very* limited set of valgrind checks, running 260# testpattern and rastertogutenprint on 9 (currently) selected 261# printers. It takes about 30 seconds on my laptop. Smoketest and 262# all. 263 264function run_valgrind_minimal() { 265 run_target check-valgrind-minimal 266} 267 268function run_valgrind_fast() { 269 run_target check-valgrind-fast 270} 271 272function run_check_minimal() { 273 run_target check-minimal 274} 275 276function run_check_fast() { 277 run_target check-fast 278} 279 280# Run make distcheck-fast. 281# 282# This actually builds the tarball, unpacks the tarball, builds it 283# out of tree, runs a short set of tests against it, does a local 284# make install, followed by make uninstall, and makes sure no 285# debris is left around. This runs configure with all default 286# arguments, so it is testing dynamically linked executables. 287# 288# The particular tests it runs are: 289# 290# - Conformance tests all non-translated non-simplified PPD files 291# and distinct global ones. 292# 293# - Runs test-rastertogutenprint on distinct printers, with fast 294# options (minimum paper size, lowest resolution, very fast 295# dithering). 296# 297# - Runs run-testpattern-2: 298# 299# + Distinct printers, fast options 300# 301# + Selected printers, with cross product of input mode (and bit 302# depth), color correction, ink type, and use gloss. 303# 304# It also has the property of maybe updating the .po files. These 305# will later need to be committed and included in the tag. So we 306# have to do our check for uncommitted bits prior to this. 307# 308# It has not escaped me that this could be part of a CI testing 309# process. I don't know if Sourceforge has the necessary gittage 310# (as GitHub does) to allow a merge bot to run something like this 311# and only merge to the main repository if this suite passes. 312# 313# The reason for the distcheck-fast is so that if something stupid 314# goes wrong it gets caught quickly. It takes about 270 seconmds 315# on my laptop. It would be Kind Of Annoying to spend hours 316# testing only to find out that something's not handling destdir 317# correctly or make clean isn't removing something. 318# 319# Note that this can't be combined with valgrind, since this builds 320# dynamic executables which can't conveniently be valground since 321# they're actually shell scripts. 322# 323# There's now an even faster check, distcheck-minimal, that only 324# tests a handful of printers. It takes about 50 seconds to run. 325# But that's really most useful for testing the distcheck 326# apparatus. 327# 328# So far we're at just over 5 minutes on a Skylake Xeon E3-1505Mv5, 329# which isn't too bad for a prerelease smoke test. The rest of this 330# takes a lot longer. 331 332function run_distcheck_fast() { 333 run_target distcheck-fast 334} 335 336# Run make check-valgrind 337# 338# This is slow. It tests only unique printers, and a lot of extra 339# combinations with a few printers, all using fast options. It 340# uses both CUPS and run-testpattern-2 testing. However, it's 341# essentially embarrassingly parallel. 342# 343# I'd like not to go too long without running it, as it's easy for 344# things to make their way in. For CI purposes, if we ever go 345# there, like to find a happy medium. 346 347function run_valgrind() { 348 run_target check-valgrind 349} 350 351# Run make check-full 352# 353# This one I'm not sure of; do we need this or is this well enough 354# covered by the combination of distcheck-fast and check-valgrind? 355# It does take a while, but I haven't benchmarked it lately. 356# 357# - Conformance test all PPD files 358# 359# - Run test-rastertogutenprint on all printers, with default options 360# 361# - Runs run-testpattern-2: 362# 363# + Distinct printers, default options 364# 365# + All printers, fast options 366# 367# + Distinct printers, fast options, with cross product of input 368# mode (and bit depth), color correction, ink type, and use 369# gloss. 370# 371# IIRC this takes 60-90 minutes on my laptop, but again, it 372# parallelizes very well. 373 374function run_full() { 375 run_target check-full 376} 377 378# Run make checksums-release to generate a new regression file. 379# 380# The problem here is what do we require for the release build. Do 381# we require a clean regression run (other than added 382# printers/modes)? There are legitimate reasons for changing, and 383# having to rerun the procedure because the release engineer forgot a 384# command line option is a bit harsh. Something better might be to 385# simply record changes unless there's an outright failure here, and 386# let those be reviewed. 387# 388# For CI purposes, the default might be to require no changes, with 389# human intervention if there are. 390# 391# This takes about 30 minutes on my laptop. This is extremely 392# scalable. Give us a really big machine instance to run it on, this 393# will run really fast. 394 395function run_checksums() { 396 make checksums 397 local lstatus=$? 398 if (( lstatus == 0 )) ; then 399 # shellcheck disable=SC2155 400 local csum_file="src/testpattern/Checksums/sums.$(pkg_version).zpaq" 401 if [[ ! -f $csum_file ]] ; then 402 console_log "*** ERROR: Can't find new checksums file $csum_file" 403 (( lstatus > 127 )) && return $lstatus 404 return 2 405 fi 406 cp -p "$csum_file" "$ARTIFACTDIR" 407 return 0 408 fi 409 console_log "*** ERROR: make checksums failed" 410 (( lstatus > 127 )) && return $lstatus 411 return 2 412} 413 414# Prep the release 415 416function git_prep_release() { 417 # .po files might have changed; nothing else should have! 418 # Add any of those changed files. 419 if [[ $build_type == release ]] ; then 420 "$GIT" add -u || return 1 421 # Add the checksums file. 422 # TBD whether to do this for snapshots. The file's not very big, 423 # but it's completely incompressible! 424 "$GIT" add -f "src/testpattern/Checksums/sums.$(pkg_version).zpaq" || return 1 425 # Commit this change 426 "$GIT" commit -m"Gutenprint $(pkg_version) release" || return 1 427 else 428 # Don't update the .po files for every snapshot. 429 echo "Cleaning up .po files" 430 "$GIT" checkout -- po 431 fi 432 # Shouldn't have anything left after this. 433 if "$GIT" status -uno --porcelain |grep -q -E -v 'po/.*\.po' ; then 434 console_log "*** ERROR: Unexpected untracked files:" 435 "$GIT" status -uno --porcelain |grep -E -v 'po/.*\.po' | console_log 436 return 1 437 fi 438 # Apply the tag. Ideally we should sign the tag too. 439 # But don't tag snapshot builds. 440 [[ $build_type != release ]] && return 0 441 "$GIT" tag -a "$(pkg_tag)" -m "Gutenprint $(pkg_version) release" || return 1 442} 443 444# make distcheck-minimal 445# 446# We have to rebuild the tarball in any event here, so that we pick up 447# the tag (to get a correct change log) and updated .po files. 448# A minimal distcheck only takes about a minute; we might as well 449# do a final sanity check. 450 451function run_distcheck_minimal() { 452 run_target distcheck-minimal 1 Final 453} 454 455function run_check_minimal() { 456 run_target check-minimal 457} 458 459# Save away build 460function save_build_artifacts() { 461 # shellcheck disable=SC2155 462 local tarball="$(pkg_version).tar.xz" 463 if [[ -s $tarball ]] ; then 464 cp -p "$tarball" "$ARTIFACTDIR" 465 else 466 echo "Cannot find $tarball" 467 return 1 468 fi 469} 470 471# Final release prep 472 473function finis() { 474 local extra_verbiage= 475 [[ $build_type == release ]] && { 476 STP_DATA_PATH=src/xml test/gen-printer-list > "printer-list.$(pkg_version)" || return 1 477 extra_verbiage=$(cat <<EOF 478 479 * Update the web site 480 481 * Merge the updated printer list in ${tcyan}printer-list.$(pkg_version)${tpurple} 482 into p_Supported_Printers.php and upload that to the web site 483EOF 484) 485} 486 console_log <<EOF 487${tpurple} 488================================================================ 489Remainder to be done manually: 490 491 * git push 492 493 * Upload the tarball (${tcyan}gutenprint-$(pkg_version).tar.xz${tpurple}) 494$extra_verbiage 495================================================================ 496$treset 497EOF 498 return 0 499} 500 501################################################################ 502# 503# Runtime 504# 505################################################################ 506 507# Note that if we change the format of this timestamp we have to 508# change console_log if the width changes. 509# 510# Unfortunately the shell built-in printf can't specify UTC. 511function stamp() { 512 printf '%(%Y-%m-%dT%H:%M:%S%z)T' -1 513} 514 515function date_sec() { 516 printf '%(%s)T' -1 517} 518 519function report_progress() { 520 local outst="RUNNING[$1]: " 521 shift 522 # shellcheck disable=SC2046 523 echo $([[ -z ${BUILD_VERBOSE:-} ]] && echo '' -n) ">>> $(stamp) $outst $*" 524} 525 526# This allows us to log to multiple outputs, including stdout and 527# (where available) file descriptors. Ideally we'd be able to build a 528# pipeline and eval it, but it's not clear that that's possible. 529function log1() { 530 if [[ $# -eq 0 || ($# == 1 && $1 == -) ]] ; then 531 exec cat 532 else 533 local dest="$1" 534 shift 535 # stdout needs to come last, because we just want to send data 536 # to stdout rather than teeing off or explicitly going to a file. 537 if [[ $dest == - && $# -gt 0 ]] ; then 538 # Protect against someone inadvertently specifying '-' twice! 539 # shellcheck disable=SC2046 540 log1 "$@" $([[ $1 == - ]] || echo -) 541 elif [[ $dest == -* ]] ; then 542 dest=${dest:1} 543 destdir=${dest%/*} 544 [[ -n $destdir && ! -d $destdir ]] && mkdir -p "$destdir" 545 if (( $# == 0 )) ; then 546 exec cat > "$dest" 547 elif [[ $* == - ]] ; then 548 exec tee "$dest" 549 else 550 exec tee "$dest" | log1 "$@" 551 fi 552 else 553 destdir=${dest%/*} 554 [[ -n $destdir && ! -d $destdir ]] && mkdir -p "$destdir" 555 if (( $# == 0 )) ; then 556 exec cat >> "$dest" 557 elif [[ $* == - ]] ; then 558 exec tee -a "$dest" 559 else 560 exec tee -a "$dest" | log1 "$@" 561 fi 562 fi 563 fi 564} 565 566function log() { 567 (log1 "$@" ${BUILD_VERBOSE:+-}) 568} 569 570function log_top1() { 571 log "$top_log" - 572} 573 574function red() { 575 sed -e "s/^/${tred}/" -e "s/$/${treset}/" 576} 577 578function green() { 579 sed -e "s/^/${tgreen}/" -e "s/$/${treset}/" 580} 581 582function log_top() { 583 if [[ -n "$*" ]] ; then 584 log_top1 <<< "$*" 585 else 586 log_top1 587 fi 588} 589 590# Log the output to the console as well as the master log file and the 591# per-operation log file. 592# 593# fd#4 (/dev/fd/4 -- let's hope we're building the package on 594# a system that supports /dev/fd, but linux does) 595# in the operation gets tied to stderr 596# 597# Note that fd#3 is used by lower levels 598# 599# Then we timestamp the data and send it to the top-level log (which 600# is not normally timestamped). 601# 602# Finally, we remove the existing timestamp (which relies upon the timestamp 603# format, ugh) and send it to stdout where it gets picked up and timestamped 604# again. 605function console_log1() { 606 if [[ -n ${STP_TEST_RECURSIVE:-} ]] ; then 607 tee >(exec cat 1>&4) >(timestamp | log_top | cut -c26-) 608 else 609 timestamp | log_top - | cut -c26- 610 fi 611} 612 613function console_log_immediate1() { 614 case "${STP_TEST_RECURSIVE:-0}" in 615 2) 616 tee >(exec cat 1>&6) >(exec cat 1>&5) >(exec cat 1>&4) >(timestamp | log_top | cut -c26-) 617 ;; 618 1) 619 tee >(exec cat 1>&5) >(exec cat 1>&4) >(timestamp | log_top | cut -c26-) 620 ;; 621 *) 622 tee >(exec cat 1>&2) >(timestamp | log_top | cut -c26-) 623 ;; 624 esac 625} 626 627function console_log() { 628 if [[ -n "$*" ]] ; then 629 console_log1 <<< "$*" 630 else 631 console_log1 632 fi 633} 634 635function console_log_immediate() { 636 if [[ -n "$*" ]] ; then 637 console_log_immediate1 <<< "$*" 638 else 639 console_log_immediate1 640 fi 641} 642 643function time_delta() { 644 local -i i=$((${2:-0} - ${1:-0})) 645 printf "%d:%02d:%02d" $((i / 3600)) $(((i % 3600) / 60)) $((i % 60)) 646} 647 648function report_step_status() { 649 (( stepstatus == -1 )) && return 650 [[ -z ${BUILD_VERBOSE:-} ]] && { 651 local ststatus=OK 652 local stcolor="${tgreen}" 653 if (( stepstatus)) ; then 654 ststatus=FAILED 655 stcolor="${tred}" 656 if (( stepstatus == 126 )) ; then 657 ststatus="NOT FOUND" 658 stcolor="${tpurple}" 659 elif (( stepstatus >= 127 )) ; then 660 ststatus=INTERRUPTED 661 fi 662 fi 663 printf "$stcolor%$((longest_op - ${#op} ))s $ststatus$treset\n" 664 } 665 [[ -n $currentmodule && $stepstatus -ne 0 ]] && failedmodules+=("$currentmodule") 666 currentmodule= 667 if [[ -f $tmpfile ]] ; then 668 [[ -s $tmpfile ]] && colorize < "$tmpfile" 669 rm -f -- "$tmpfile" 670 fi 671 stepstatus=-1 672} 673 674# shellcheck disable=SC2155 675function finish() { 676 local status=$exitstatus 677 local etime=$(date_sec) 678 local estamp=$(stamp) 679 report_step_status 680 if [[ $status != 0 || -n ${failedmodules[*]:-} ]] ; then 681 log_top "The following modules failed:" 682 log_top "$(printf " %s\n" "${failedmodules[@]:-}")" 683 log_top "*** Gutenprint $build_type build FAILED at $estamp ($(time_delta "$stime" "$etime"))" |red 684 else 685 log_top "*** Gutenprint $build_type build completed at $estamp ($(time_delta "$stime" "$etime"))" |green 686 fi 687 log "$top_log" <<< "================================================================" 688 if [[ -n ${TRAVIS_MODE:-} ]] ; then 689 # We really don't want the termination message from the deadman 690 exec 3>&2 2>/dev/null 691 kill %travis_deadman 692 wait %travis_deadman 693 exec 2>&3 3>&- 694 fi 695 trap - EXIT 696 exit "$status" 697} 698 699# Travis times out if there's no output for 10 minutes, but some things 700# go silent for quite a while 701function travis_deadman() { 702 while : ; do sleep 60; echo -e "\n${tblue}Mark $(uptime)${treset}" | log -; done 703} 704 705function timestamp() { 706 while read -r ; do echo "$(stamp) $REPLY"; done 707} 708 709# Run one operation. 710# shellcheck disable=SC2155 711function runit() { 712 local cmdname=$1 713 local cmd="$*" 714 local fcounter=$(printf "%02d" "$counter") 715 local local_logdir="$logdir/$fcounter.${cmd// /_/}" 716 mkdir -p "$local_logdir" 717 local logfile="$local_logdir/Master" 718 [[ -n ${DONTRUN_OP:-} ]] && logfile=/dev/null 719 local sstime=$(date_sec) 720 local ssstamp=$(stamp) 721 local status=0 722 local msg=completed 723 log "-$logfile" "$top_log" <<< "----------------------------------------------------------------" 724 if [[ -z ${DONTRUN_OP:-} ]] ; then 725 echo "$cmdname started at $ssstamp" | log "$logfile" "$top_log" 726 echo "Command: $cmd" | log "$logfile" "$top_log" 727 echo "Log file: ${logfile#${logdir}/}" | log "$top_log" 728 else 729 echo "$cmdname SKIPPED" | log "$top_log" 730 fi 731 report_progress "$fcounter" "$cmdname" 732 733 if [[ -z ${DONTRUN_OP:-} ]] ; then 734 # 735 stepstatus=127 736 currentmodule="$cmdname" 737 # Run the command, capturing console output as well as logged output. 738 if [[ -z $(type -t "$cmdname") ]] ; then 739 msg="NOT FOUND" 740 # 126 is also used for "permission denied", which amounts 741 # to the same thing -- it's something the user should not 742 # have tried to run. 743 stepstatus=126 744 else 745 if [[ -n ${STP_TEST_RECURSIVE:-} ]] ; then 746 STP_TEST_RECURSIVE=2 STP_TEST_LOG_PREFIX="$local_logdir/" $cmd </dev/null 4>"$tmpfile" 6>&5 5>&2 3>&1 2>&1 | timestamp | log "$logfile" 747 else 748 STP_TEST_RECURSIVE=1 STP_TEST_LOG_PREFIX="$local_logdir/" $cmd </dev/null 4>"$tmpfile" 5>&2 3>&1 2>&1 | timestamp | log "$logfile" 749 fi 750 stepstatus=${PIPESTATUS[0]} 751 (( stepstatus > 0 )) && msg=FAILED 752 fi 753 local -a emptyfiles=() 754 for f in "$local_logdir"/* ; do 755 [[ -f $f && ! -s $f ]] && emptyfiles+=("$f") 756 done 757 [[ -n "${emptyfiles[*]:-}" ]] && rm -f -- "${emptyfiles[@]}" 758 else 759 msg='(SKIPPED)' 760 fi 761 local setime=$(date_sec) 762 local sestamp=$(stamp) 763 if [[ -z ${DONTRUN_OP:-} ]] ; then 764 echo "$cmd $msg at $sestamp ($(time_delta "$sstime" "$setime"))" | log "$logfile" "$top_log" 765 echo "----------------------------------------------------------------" | log "$logfile" "$top_log" 766 fi 767 counter=$((counter+1)) 768} 769 770declare -a OPERATIONS=(preflight 771 check_git 772 check_git_tag 773 run_maintainer_clean 774 run_autogen 775 run_clean 776 run_build 777 check_git_builds 778 run_valgrind_minimal 779 run_distcheck_fast 780 run_valgrind 781 run_full 782 run_checksums 783 run_distcheck_minimal 784 git_prep_release 785 save_build_artifacts 786 finis) 787 788function get_longest_op() { 789 local longest_op=0 790 local op 791 for op in "${OPERATIONS[@]}" ; do 792 (( ${#op} > longest_op )) && longest_op=${#op} 793 done 794 echo $((longest_op + 2)) 795} 796 797# shellcheck disable=SC2206 798[[ -n ${STP_BUILD_OPERATIONS:-} ]] && OPERATIONS=($STP_BUILD_OPERATIONS) 799[[ -n "$*" ]] && OPERATIONS=("$@") 800 801trap finish EXIT SIGHUP SIGINT SIGQUIT SIGTERM 802 803# shellcheck disable=SC2155 804{ 805declare sstamp=$(stamp) 806declare stime=$(date_sec) 807} 808declare toplogdir=${STP_LOG_DIR:-"$ROOT/BuildLogs"} 809declare logdir="$toplogdir/Log.${sstamp// /_}" 810[[ -n ${STP_LOG_NO_SUBDIR:-} ]] && logdir=$toplogdir 811top_log="$logdir/00.Master" 812mkdir -p "$logdir" 813if [[ -z ${STP_LOG_NO_SUBDIR:-} ]] ; then 814 if [[ -L $toplogdir/Current ]] ; then 815 rm -f -- "$toplogdir/Previous" 816 mv "$toplogdir/Current" "$toplogdir/Previous" 817 fi 818 ln -s "${logdir##*/}" "$toplogdir/Current" 819fi 820export ARTIFACTDIR="$logdir/Artifacts" 821mkdir -p "$ARTIFACTDIR" 822 823[[ -n $("$GIT" status --porcelain -uno) ]] && git_dirty=' (dirty)' 824log "-$top_log" <<< "================================================================" 825 826log_top <<EOF 827${tbold}*** Gutenprint $build_type build started at $sstamp on $(uname -n)${treset} 828 829Directory: $ROOT 830Logs: ${logdir#${ROOT}/} 831Kernel: $(uname -o) $(uname -rv) 832Revision: $("$GIT" rev-parse @)$git_dirty 833Parallel: $STP_PARALLEL 834EOF 835 836[[ -n ${STP_TEST_ROTOR_CIRCUMFERENCE:-} && -n ${STP_TEST_ROTOR:-} ]] && 837 log_top "Rotor: $STP_TEST_ROTOR / $STP_TEST_ROTOR_CIRCUMFERENCE" 838 839# Or we could just do "env" here, but having them sorted makes it 840# easier to read. Variables containing '%%' are functions 841log_top <<EOF 842 843Environment: 844$(while read -r var ; do 845 eval "echo ' '$var=\${$var@Q}" 846done <<< "$(compgen -e)") 847 848CPU information: 849$(lscpu -e) 850$(lscpu) 851 852Memory information: 853$(free) 854 855Running operations: 856$(printf " %s\n" "${OPERATIONS[@]}") 857EOF 858 859if [[ -n ${TRAVIS_MODE:-} ]] ; then 860 export BUILD_VERBOSE=1 861 travis_deadman& 862fi 863 864declare -A SKIP_OPS 865[[ -n ${STP_BUILD_SKIP:-} ]] && 866 for o in ${STP_BUILD_SKIP//,/ } ; do SKIP_OPS[$o]=1; done 867declare -i runstatus=0 868# shellcheck disable=SC2155 869declare -i longest_op=$(get_longest_op) 870declare -i lstatus=0 871for op in "${OPERATIONS[@]}" ; do 872 [[ -z ${BUILD_VERBOSE:-} ]] && tmpfile=$(mktemp "/tmp/stpconsole.XXXXXXXX") 873 DONTRUN_OP="${DONTRUN:-}${SKIP_OPS[$op]:-}" runit "$op" 874 lstatus=$stepstatus 875 report_step_status 876 if (( lstatus > 0 )) ; then 877 runstatus=1 878 (( lstatus != 2 )) && break 879 fi 880done 881exitstatus="$runstatus" 882