#!/bin/sh
# Run a build job inside the ARM OABI chroot under QEMU.
#
# The netBook Pro is ARM OABI; nothing on a modern host produces OABI
# binaries, so the chroot (Debian etch, OABI) is booted as a real ARM
# machine and gcc runs natively inside it. QEMU's TCG is slow -- one boot
# per batch, so put as many packages in a job as will fit.
#
#   scripts/qbuild.sh <job.sh> [timeout-seconds]
#
# The job path may be relative or absolute.
#
# The job runs with its output on the serial console, which is captured to
# <job.sh>.log. It must not redirect stdout itself, or the completion
# marker is never seen and the harness waits for the full timeout.
set -e
W=$(cd "$(dirname "$0")/.." && pwd)
JOB=${1:?usage: qbuild.sh <job.sh> [timeout]}
# Resolve the job to an absolute path. The harness cds into $W/qemu before
# redirecting the console to "$JOB.log", so a relative job path put the log
# somewhere that does not exist: the redirect failed, the marker was never
# written, and the run "finished" in five seconds having built nothing --
# while still exiting 0 and printing an artefact list from the previous run.
JOB=$(cd "$(dirname "$JOB")" && pwd)/$(basename "$JOB")
TMO=${2:-5400}
# Firefox needs far more room than the other jobs: 290 MB of source and about
# a gigabyte of objects on top of the 276 MB chroot. Sized per job rather than
# always building a 4 GB image, because mke2fs -d has to write the whole thing
# before every boot.
BLOCKS=${QB_BLOCKS:-1572864}
MKE=/opt/homebrew/opt/e2fsprogs/sbin/mke2fs
DBG=/opt/homebrew/opt/e2fsprogs/sbin/debugfs
BR=$W/build/buildroot

cp "$JOB" "$BR/build/job.sh"
cat > "$BR/build.sh" <<'EOF'
#!/bin/sh
/bin/busybox mount -t proc proc /proc 2>/dev/null
/bin/busybox mount -t sysfs sysfs /sys 2>/dev/null
export PATH=/usr/bin:/bin:/usr/sbin:/sbin
export PKG_CONFIG_PATH=/usr/lib/pkgconfig
export HOME=/root LC_ALL=C
/bin/sh /build/job.sh 2>&1
echo "JOB_EXIT=$?"
sync
/bin/busybox sleep 3
sync
/bin/busybox sleep 2
echo BUILD_SCRIPT_DONE
EOF
chmod +x "$BR/build.sh" "$BR/build/job.sh"
rm -f "$BR/guitest.sh"          # the initramfs prefers it if present

echo "== packing the chroot into build.ext2"
rm -f "$W/qemu/build.ext2"
$MKE -q -t ext2 -b 1024 -d "$BR" -F -L BUILD \
     -O ^resize_inode,^dir_index "$W/qemu/build.ext2" "$BLOCKS"

echo "== booting (timeout ${TMO}s)"
cd "$W/qemu"
qemu-system-arm -M versatilepb -m 256 \
  -kernel vmlinuz-versatile -initrd initramfs.gz \
  -drive file=build.ext2,format=raw,if=none,id=hd0 \
  -device lsi53c895a,id=scsi0 -device scsi-hd,drive=hd0,bus=scsi0.0 \
  -append 'console=ttyAMA0 rw' -nographic -no-reboot \
  > "$JOB.log" 2>&1 &
QP=$!
i=0
while [ $i -lt "$TMO" ]; do
  kill -0 $QP 2>/dev/null || break
  grep -q BUILD_SCRIPT_DONE "$JOB.log" 2>/dev/null && break
  sleep 5; i=$((i+5))
done
# The disk is emulated SCSI with write-back caching, and the guest clock under
# TCG can run well ahead of the host -- a job whose kernel log reaches 38s can
# finish in 10s of wall time. Killing qemu straight after the marker then loses
# whatever writeback had not landed, and /build/out is simply absent from the
# image afterwards. Give it time to drain.
sleep 15
kill $QP 2>/dev/null || true
wait $QP 2>/dev/null || true
grep -q BUILD_SCRIPT_DONE "$JOB.log" && echo "== finished in ~${i}s" \
  || echo "== TIMED OUT after ${i}s"

# Pull /build/out back onto the host straight away. Every run re-creates
# build.ext2 from scratch, so anything left only inside the image is lost
# the next time a job is started.
OUT=$W/build/out
rm -rf "$OUT"
$DBG -R "rdump /build/out $W/build" build.ext2 >/dev/null 2>&1
# keep a running copy outside build/out, which the next run deletes
if [ -d "$OUT/bin" ]; then
  mkdir -p "$W/built/psion-apps"
  cp -p "$OUT"/bin/* "$W/built/psion-apps/" 2>/dev/null
fi
if [ -d "$OUT" ]; then
  echo "== artefacts in $OUT"
  find "$OUT" -type f ! -name '*.log' -exec ls -l {} \; 2>/dev/null | awk '{print "   "$NF"  "$5}'
fi
echo "== log: $JOB.log"
