Cider Isn't Darwin Emulation, Really
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156#!/usr/bin/env bash# Run the WHOLE darling-testsuite batch and print a per-case verdict and a rate.## scripts/run-dts-batch.sh build everything, run everything# scripts/run-dts-batch.sh --no-build reuse whatever is already built## ONE CONTAINER FOR ALL CASES, which matters twice over. It is far faster than a container per# case, and it is the only way to read a case's REAL exit code: `cider shell` does not propagate a# guest that dies on a signal, so from outside, an aborted case and a passing one both look like 0# (see scripts/run-dts-case.sh). Inside a single guest shell, $? after each case is the true code.## DARLING_TESTSUITE_RESOURCE_PATH MUST BE THE PARENT of the testsuite directory, not the testsuite# directory itself: the cases ask for paths that already begin with "testsuite/", so pointing at# .../darling-testsuite/testsuite silently doubles the component and every resource-using case# fails with ENOENT. Getting that wrong made four cases look like product defects.## EXIT CODES: 0 pass, 134 an assertion fired, 139 a segfault, 1 the case could not load or is one# of the WILL_FAIL cases where a non-zero exit IS the pass.set -u
REPO=$(cd "$(dirname "$0")/.." && pwd)cd "$REPO"OUT=${OUT:-/tmp/dts-batch}mkdir -p "$OUT"
# Every case target in the generated BUCK, minus the per-case object libraries.grep '^ name = "dts_' vendor/src/BUCK | sed 's/.*name = "//; s/",//' | grep -v '_obj$' | sort > "$OUT/targets.txt"
if [ "${1:-}" != "--no-build" ]; then sed 's|^|//vendor/src:|' "$OUT/targets.txt" > "$OUT/labels.txt" # --keep-going: several cases do not compile yet and that must not stop the rest. xargs -a "$OUT/labels.txt" buck2 build --keep-going > "$OUT/build.log" 2>&1 || truefi
ART=$(ls -td "$REPO"/buck-out/v2/art/root/*/vendor/src 2>/dev/null | head -1)[ -n "$ART" ] || { echo "no buck-out artifacts; build first" >&2; exit 2; }
: > "$OUT/built.txt"; : > "$OUT/nobuild.txt"while read -r t; do if [ -x "$ART/__${t}__/$t" ]; then echo "$t" >> "$OUT/built.txt"; else echo "$t" >> "$OUT/nobuild.txt"; fidone < "$OUT/targets.txt"
RES=$(realpath vendor/src/darling-testsuite)# EACH CASE IS COPIED INTO THE PREFIX AND RUN FROM THERE, not run in place from buck-out. A binary# executed through /Volumes/SystemRoot gets the HOST spelling of its own path, and CFBundleCreate# needs an existing directory, so CFBundleGetMainBundle answers NULL and anything that asks for the# main bundle fails. That is what made the Security case trap (task #205). Upstream installs its# cases before running them, so this is also closer to how the suite is meant to run.# A FEW CASES CARE WHAT THEY ARE CALLED. The posix_spawn pair builds# "$(getcwd)/<DARLING_IDENTIFIER>.<case>" and compares it against argv[0], and the parent spawns the# child by that RELATIVE name, so both have to be present and invoked under the name CMake would# have used. Our target names put an underscore where CMake puts a dot.python3 scripts/gen-testsuite-buck.py --cmake-names 2>/dev/null > "$OUT/cmake-names.txt" || : > "$OUT/cmake-names.txt"runname() { awk -v t="$1" '$1 == t {print $2; found=1} END {if (!found) print t}' "$OUT/cmake-names.txt"}
# STAGE EVERYTHING FIRST, then run, so a case that spawns a sibling does not depend on the order# the two happen to appear in.{ echo '#!/bin/sh' echo "export DARLING_TESTSUITE_RESOURCE_PATH=/Volumes/SystemRoot$RES" echo 'mkdir -p /tmp/dts' echo 'cd /tmp/dts' # THE PHYSICAL PATH, not the literal /tmp and not the shell's logical pwd. In the prefix /tmp is # a symlink to private/tmp exactly as on macOS, so getcwd(3) answers /private/tmp/dts while the # pwd BUILTIN answers /tmp/dts. The posix_spawn child compares argv[0] against getcwd plus its # own name, and fails on that difference alone. Measured: via the logical path it aborts, via # the physical path it exits 0. echo 'D=$(pwd -P); cd "$D"' while read -r t; do src="/Volumes/SystemRoot$(realpath "$ART")/__${t}__/$t" echo "cp \"$src\" \"\$D/$(runname "$t")\" && chmod +x \"\$D/$(runname "$t")\"" done < "$OUT/built.txt" while read -r t; do echo "\"\$D/$(runname "$t")\" >/dev/null 2>&1; echo \"CASE $t EXIT \$?\"" done < "$OUT/built.txt"} > "$OUT/batch.sh"chmod +x "$OUT/batch.sh"
CIDER=$(ls -t "$REPO"/buck-out/v2/art/root/*/src/linux/launcher/__cider__/cider 2>/dev/null | head -1)CIDERD=$(ls -t "$REPO"/buck-out/v2/art/root/*/src/linux/server/__ciderd__/ciderd 2>/dev/null | head -1)MLDR=$(ls -t "$REPO"/buck-out/v2/art/root/*/src/darwin/loader/__mldr__/mldr 2>/dev/null | head -1)RT=$(ls -td "$REPO"/buck-out/v2/art/root/*/buck/prefix/__cider_prefix__/cider_prefix__prefix 2>/dev/null | head -1)ELF_LIBS=$(grep '^elf_lib_dirs' "$REPO/.buckconfig.local" 2>/dev/null | sed 's/^elf_lib_dirs *= *//')
# THE BACKEND FOLLOWS THE ENVIRONMENT, so an empty WAYLAND_DISPLAY silently flips every case to# the X11 backend, which has no server here and dies in its failing-init path: 10 of 67 crashed# with signal 11 from a shell that simply lacked the variable, and the tree was innocent. Discover# the socket the way app-drive.sh does rather than trust the caller's environment.if [ -z "${WAYLAND_DISPLAY:-}" ]; then WAYLAND_DISPLAY=$(ls "${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"/wayland-[0-9]* 2>/dev/null \ | grep -v '\.lock$' | grep -vE 'renderD' | sort | head -1 | xargs -r basename) [ -n "$WAYLAND_DISPLAY" ] && export WAYLAND_DISPLAY \ && echo "batch: WAYLAND_DISPLAY was empty, discovered $WAYLAND_DISPLAY"fi
pkill -9 -x 'mldr|cider|ciderd|shellspawn' 2>/dev/nullsleep 1mkdir -p /tmp/cider-dts-1000# cider refuses a prefix that already exists. chmod first: a previous run's clonefile cases leave# behind copies that inherited a read-only mode from their source, and plain rm -rf cannot remove# those from a read-only directory.chmod -R u+w /tmp/cider-dts-1000/prefix 2>/dev/nullrm -rf /tmp/cider-dts-1000/prefix
env LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 \ CIDERPREFIX=/tmp/cider-dts-1000/prefix CIDER_NO_LAUNCHD=1 \ LD_LIBRARY_PATH="$ELF_LIBS${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" \ DYLD_INSERT_LIBRARIES=/usr/lib/swift/libswiftCompat.dylib \ CIDER_COMPAT_LIBRARY=/usr/lib/swift/libswiftCompat.dylib \ DSERVER_PATH="$(realpath "$CIDERD")" DSERVER_MLDR_PATH="$(realpath "$MLDR")" \ DSERVER_LIBEXEC_PATH="$(realpath "$RT")/libexec/cider" \ timeout "${LIMIT:-1800}" "$CIDER" shell /bin/sh "/Volumes/SystemRoot$OUT/batch.sh" \ > "$OUT/run.log" 2>&1pkill -9 -x 'mldr|cider|ciderd|shellspawn' 2>/dev/null
# CTest marks these WILL_FAIL: a non-zero exit is the pass.WILLFAIL=$(python3 scripts/gen-testsuite-buck.py --willfail 2>/dev/null | tr '\n' ' ')# Cases upstream has not written. They exit(1) with every line commented out, so they fail on real# macOS too; counting them as failures here reads as a defect in this port and is not one.PLACEHOLDER=$(python3 scripts/gen-testsuite-buck.py --placeholders 2>/dev/null | tr '\n' ' ')
pass=0; fail=0; unwritten=0: > "$OUT/failed.txt"; : > "$OUT/placeholder.txt"while read -r _ t _ rc; do short=${t#dts_} skip=0 for p in $PLACEHOLDER; do case "$short" in *"$p") skip=1 ;; esac; done if [ "$skip" = 1 ]; then unwritten=$((unwritten + 1)); echo "$rc $short" >> "$OUT/placeholder.txt"; continue fi expect_fail=0 for w in $WILLFAIL; do case "$short" in *"$w") expect_fail=1 ;; esac; done if { [ "$rc" = 0 ] && [ "$expect_fail" = 0 ]; } || { [ "$rc" != 0 ] && [ "$expect_fail" = 1 ]; }; then pass=$((pass + 1)) else fail=$((fail + 1)); echo "$rc $short" >> "$OUT/failed.txt" fidone < <(grep '^CASE ' "$OUT/run.log")
wired=$(wc -l < "$OUT/targets.txt"); built=$(wc -l < "$OUT/built.txt")ran=$(grep -c '^CASE ' "$OUT/run.log")echo "wired $wired, built $built, ran $ran, passed $pass, failed $fail, unwritten upstream $unwritten"echoecho "failures (exit code, case):"sort -n "$OUT/failed.txt"if [ "$unwritten" -gt 0 ]; then echo echo "not counted, upstream never wrote a body:" sort -n "$OUT/placeholder.txt"fiechoecho "did not build: $(wc -l < "$OUT/nobuild.txt"), see $OUT/nobuild.txt and $OUT/build.log"[ "$fail" -eq 0 ]