mirror of https://github.com/KLayout/klayout.git
284 lines
8.2 KiB
Plaintext
284 lines
8.2 KiB
Plaintext
|
|
#!/bin/bash
|
|||
|
|
#----------------------------------------------------------------------
|
|||
|
|
# File: KLayoutNightlyBuild.Bash
|
|||
|
|
#
|
|||
|
|
# What: The Bash contents for "KLayoutNightlyBuild.app" script bundle
|
|||
|
|
# that invokes 'nightlyBuild.py' for automation.
|
|||
|
|
#
|
|||
|
|
# Last modified: 2025-11-05
|
|||
|
|
#----------------------------------------------------------------------
|
|||
|
|
dryrun="no"
|
|||
|
|
|
|||
|
|
#-------------------------------------------------------------------------
|
|||
|
|
# Tool logging
|
|||
|
|
#-------------------------------------------------------------------------
|
|||
|
|
set -Eeuo pipefail
|
|||
|
|
export LANG=en_US.UTF-8
|
|||
|
|
export LC_ALL=en_US.UTF-8
|
|||
|
|
|
|||
|
|
LOG="/tmp/KLayoutNightlyBuild.$(date +%Y%m%d-%H%M%S).log"
|
|||
|
|
{
|
|||
|
|
echo "=== START $(date) ==="
|
|||
|
|
echo "whoami: $(whoami)"
|
|||
|
|
echo "pwd: $(pwd)"
|
|||
|
|
echo "shell: $SHELL"
|
|||
|
|
echo "osvers: $(sw_vers -productVersion)"
|
|||
|
|
echo "ENV (partial):"
|
|||
|
|
env | grep -E '^(PATH|HOME|LANG|LC_|MyEmail)' | sort
|
|||
|
|
} >>"$LOG" 2>&1
|
|||
|
|
|
|||
|
|
BASH_XTRACEFD=9
|
|||
|
|
exec 9>>"$LOG"
|
|||
|
|
set -x
|
|||
|
|
|
|||
|
|
exec >>"$LOG" 2>&1
|
|||
|
|
|
|||
|
|
#-------------------------------------------------------------------------
|
|||
|
|
# Detect and set the OS name.
|
|||
|
|
#-------------------------------------------------------------------------
|
|||
|
|
osvers=$(sw_vers -productVersion)
|
|||
|
|
osname=$(sw_vers -productName)
|
|||
|
|
|
|||
|
|
case "$osvers" in
|
|||
|
|
12.*) osname="Monterey" ;;
|
|||
|
|
13.*) osname="Ventura" ;;
|
|||
|
|
14.*) osname="Sonoma" ;;
|
|||
|
|
15.*) osname="Sequoia" ;;
|
|||
|
|
26.*) osname="Tahoe" ;;
|
|||
|
|
*) osname="Unknown" ;;
|
|||
|
|
esac
|
|||
|
|
|
|||
|
|
#-------------------------------------------------------------------------
|
|||
|
|
# Set PATH
|
|||
|
|
# to use /opt/local/bin/addr2line -> gaddr2line in MacPorts.
|
|||
|
|
#-------------------------------------------------------------------------
|
|||
|
|
export PATH=/opt/local/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
|
|||
|
|
|
|||
|
|
#-------------------------------------------------------------------------
|
|||
|
|
# Decide PYBIN (priority: env → MacPorts → Homebrew → Anaconda → /usr/bin)
|
|||
|
|
# KLAYOUT_PYBIN or PYBIN
|
|||
|
|
#-------------------------------------------------------------------------
|
|||
|
|
PYBIN_CANDIDATES=()
|
|||
|
|
# launchctl setenv KLAYOUT_PYBIN ... will work)
|
|||
|
|
[[ -n "${KLAYOUT_PYBIN:-}" ]] && PYBIN_CANDIDATES+=("$KLAYOUT_PYBIN")
|
|||
|
|
[[ -n "${PYBIN:-}" ]] && PYBIN_CANDIDATES+=("$PYBIN")
|
|||
|
|
|
|||
|
|
# candidates
|
|||
|
|
PYBIN_CANDIDATES+=(
|
|||
|
|
/opt/local/bin/python3 # MacPorts
|
|||
|
|
/usr/local/bin/python3 # Homebrew (Intel)
|
|||
|
|
/opt/homebrew/bin/python3 # Homebrew (Apple Silicon)
|
|||
|
|
/Applications/anaconda3/bin/python3 # Anaconda
|
|||
|
|
/usr/bin/python3 # System
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
PYBIN=""
|
|||
|
|
for p in "${PYBIN_CANDIDATES[@]}"; do
|
|||
|
|
if [[ -n "$p" && -x "$p" ]]; then
|
|||
|
|
PYBIN="$p"
|
|||
|
|
break
|
|||
|
|
fi
|
|||
|
|
done
|
|||
|
|
|
|||
|
|
if [[ -z "$PYBIN" ]]; then
|
|||
|
|
echo "[FATAL] python3 not found. Tried: ${PYBIN_CANDIDATES[*]}"
|
|||
|
|
exit 127
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
echo "[INFO] Using PYBIN=$PYBIN"
|
|||
|
|
"$PYBIN" -V || true
|
|||
|
|
|
|||
|
|
#-------------------------------------------------------------------------
|
|||
|
|
# Set build parameters
|
|||
|
|
#-------------------------------------------------------------------------
|
|||
|
|
workdir=$HOME/GitWork/klayout
|
|||
|
|
passbox=$HOME/Dropbox/PrivatePassBox
|
|||
|
|
drylog=nightlyBuild-dryrun.log
|
|||
|
|
jobsCSV=nightlyBuild.csv
|
|||
|
|
|
|||
|
|
#-------------------------------------------------------------------------
|
|||
|
|
# Set mailer (postfix) parameters
|
|||
|
|
#
|
|||
|
|
# launchctl setenv MyEmail1 "my_real_email_address_1"
|
|||
|
|
# launchctl setenv MyEmail2 "my_real_email_address_2"
|
|||
|
|
#
|
|||
|
|
# launchctl getenv MyEmail1
|
|||
|
|
# launchctl getenv MyEmail2
|
|||
|
|
#-------------------------------------------------------------------------
|
|||
|
|
if [[ -z "${MyEmail1:-}" || -z "${MyEmail2:-}" ]]; then
|
|||
|
|
if [[ -f "$HOME/.klayout_env" ]]; then
|
|||
|
|
# shellcheck disable=SC1090
|
|||
|
|
source "$HOME/.klayout_env"
|
|||
|
|
fi
|
|||
|
|
fi
|
|||
|
|
# Re-check
|
|||
|
|
MyEmail1_val="${MyEmail1:-}"
|
|||
|
|
MyEmail2_val="${MyEmail2:-}"
|
|||
|
|
|
|||
|
|
if [[ -z "$MyEmail1_val" ]]; then
|
|||
|
|
echo "[FATAL] MyEmail1 is not set. Run: launchctl setenv MyEmail1 'you@example.com'"
|
|||
|
|
exit 64
|
|||
|
|
fi
|
|||
|
|
if [[ -z "$MyEmail2_val" ]]; then
|
|||
|
|
echo "[WARN] MyEmail2 is not set. Cc will be empty."
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
title="Status of KLayout <nightlyBuild.py>"
|
|||
|
|
addrFrom=$MyEmail1_val
|
|||
|
|
addrTo=$MyEmail1_val
|
|||
|
|
addrCc=$MyEmail2_val
|
|||
|
|
mailer="sendmail"
|
|||
|
|
sleeptime=5
|
|||
|
|
|
|||
|
|
####----------------------------------------------------------------------
|
|||
|
|
#### Initialize
|
|||
|
|
####----------------------------------------------------------------------
|
|||
|
|
function Initialize()
|
|||
|
|
{
|
|||
|
|
cd "$workdir"
|
|||
|
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] PATH=$PATH PYBIN=$PYBIN" >> "$workdir/nightlyBuild-env.log"
|
|||
|
|
|
|||
|
|
if [ -f "$jobsCSV" ]; then
|
|||
|
|
targetOp="--qttarget=$jobsCSV"
|
|||
|
|
else
|
|||
|
|
targetOp=""
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
if [ -L ./nightlyBuild.py ]; then
|
|||
|
|
echo "OK! nightlyBuild.py symbolic link exists ..."
|
|||
|
|
else
|
|||
|
|
ln -s ./macbuild/nightlyBuild.py .
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
if [ "$dryrun" == "yes" ]; then
|
|||
|
|
cmd=( "./nightlyBuild.py" $targetOp --build --pymod --dryrun )
|
|||
|
|
dryout="> $drylog"
|
|||
|
|
else
|
|||
|
|
cmd=( "./nightlyBuild.py" $targetOp --build --pymod --test )
|
|||
|
|
dryout=""
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
line1="### Initialized Nightly Build ###"
|
|||
|
|
line2=" OS: $osname"
|
|||
|
|
msg1=$(printf "%s\n%s\n%s\n" "$line1" "$line2" "${cmd[*]}")
|
|||
|
|
SendMail "$msg1"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
####----------------------------------------------------------------------
|
|||
|
|
#### Upload log files
|
|||
|
|
####----------------------------------------------------------------------
|
|||
|
|
function UploadLog()
|
|||
|
|
{
|
|||
|
|
shopt -s nullglob
|
|||
|
|
logfiles=(*.log)
|
|||
|
|
[ ${#logfiles[@]} -gt 0 ] && cp -p "${logfiles[@]}" "$passbox"
|
|||
|
|
shopt -u nullglob
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
####----------------------------------------------------------------------
|
|||
|
|
#### Build
|
|||
|
|
####----------------------------------------------------------------------
|
|||
|
|
function Build()
|
|||
|
|
{
|
|||
|
|
# Prepare build log file
|
|||
|
|
buildlogfile="$workdir/nightlyBuild-$(date +%Y%m%d-%H%M%S).log"
|
|||
|
|
timestamp_start=$(date "+%Y-%m-%d %H:%M:%S")
|
|||
|
|
echo "===== Nightly Build Started at $timestamp_start =====" > $buildlogfile
|
|||
|
|
|
|||
|
|
# Start caffeinate in background to prevent sleep
|
|||
|
|
# -d : Prevent display sleep
|
|||
|
|
# -i : Prevent idle sleep
|
|||
|
|
# -m : Prevent disk sleep
|
|||
|
|
# -s : Prevent system sleep
|
|||
|
|
# -u : Declare user activity (optional)
|
|||
|
|
#caffeinate -dimsu &
|
|||
|
|
caffeinate -imsu &
|
|||
|
|
caff_pid=$!
|
|||
|
|
|
|||
|
|
logmsg1="Started caffeinate (PID=$caff_pid) to prevent sleep during build"
|
|||
|
|
echo "$logmsg1" | tee -a $buildlogfile
|
|||
|
|
|
|||
|
|
# Prepare trap cleanup message
|
|||
|
|
cleanupmsg="Cleaning up caffeinate (PID=$caff_pid)"
|
|||
|
|
# Set trap
|
|||
|
|
trap "echo '$cleanupmsg' | tee -a $buildlogfile; kill $caff_pid 2>/dev/null || true; wait $caff_pid 2>/dev/null || true" INT TERM EXIT
|
|||
|
|
|
|||
|
|
# Run the build and test command
|
|||
|
|
build_test_cmd=( "$PYBIN" "${cmd[@]}" )
|
|||
|
|
echo "Executing: ${build_test_cmd[*]} $dryout" | tee -a "$buildlogfile"
|
|||
|
|
if [ "$dryrun" == "yes" ]; then
|
|||
|
|
"${build_test_cmd[@]}" >"$drylog" 2>&1
|
|||
|
|
else
|
|||
|
|
"${build_test_cmd[@]}"
|
|||
|
|
fi
|
|||
|
|
build_status=$?
|
|||
|
|
|
|||
|
|
# Stop caffeinate
|
|||
|
|
kill "$caff_pid" 2>/dev/null || true
|
|||
|
|
wait "$caff_pid" 2>/dev/null || true
|
|||
|
|
|
|||
|
|
logmsg2="Stopped caffeinate (PID=$caff_pid)"
|
|||
|
|
echo "$logmsg2" | tee -a $buildlogfile
|
|||
|
|
|
|||
|
|
# Clear trap
|
|||
|
|
trap - INT TERM EXIT
|
|||
|
|
|
|||
|
|
# Build result handling
|
|||
|
|
timestamp_end=$(date "+%Y-%m-%d %H:%M:%S")
|
|||
|
|
echo "===== Nightly Build Ended at $timestamp_end =====" >> $buildlogfile
|
|||
|
|
|
|||
|
|
if [ $build_status -ne 0 ]; then
|
|||
|
|
line1="!!! Failed Nightly Build !!!"
|
|||
|
|
line2=" OS: $osname"
|
|||
|
|
msg2=$(printf "%s\n%s\n%s\n" "$line1" "$line2" "${cmd[*]}")
|
|||
|
|
echo "$msg2" >> $buildlogfile
|
|||
|
|
UploadLog
|
|||
|
|
else
|
|||
|
|
line1="### Succeeded Nightly Build ###"
|
|||
|
|
line2=" OS: $osname"
|
|||
|
|
msg2=$(printf "%s\n%s\n%s\n" "$line1" "$line2" "${cmd[*]}")
|
|||
|
|
echo "$msg2" >> $buildlogfile
|
|||
|
|
if [ "$dryrun" == "yes" ]; then
|
|||
|
|
UploadLog
|
|||
|
|
fi
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
# Send mail with entire build log
|
|||
|
|
SendMail "$(cat $buildlogfile)"
|
|||
|
|
return $build_status
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
####----------------------------------------------------------------------
|
|||
|
|
#### Send mail
|
|||
|
|
####----------------------------------------------------------------------
|
|||
|
|
function SendMail() {
|
|||
|
|
$mailer -i -t <<EOF
|
|||
|
|
From: $addrFrom
|
|||
|
|
To: $addrTo
|
|||
|
|
Cc: $addrCc
|
|||
|
|
Subject: $title
|
|||
|
|
Content-Type: text/plain; charset=UTF-8
|
|||
|
|
|
|||
|
|
$1
|
|||
|
|
EOF
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
####----------------------------------------------------------------------
|
|||
|
|
#### Sleep
|
|||
|
|
####----------------------------------------------------------------------
|
|||
|
|
function Sleep()
|
|||
|
|
{
|
|||
|
|
sleep $1
|
|||
|
|
# 10 = 10s => 10 seconds
|
|||
|
|
# 5m => 5 minutes
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
####----------------------------------------------------------------------
|
|||
|
|
#### Main
|
|||
|
|
####----------------------------------------------------------------------
|
|||
|
|
Initialize
|
|||
|
|
Build
|
|||
|
|
exit $?
|
|||
|
|
|
|||
|
|
# EOF
|