mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 19:01:54 +00:00
drain_one() prints each target's log the moment that target finishes, so with four suites running concurrently the output of the red one lands somewhere between three green ones. The rc table at the end names which target failed but not why, and finding the assertion meant grepping several thousand lines of interleaved output. The failed targets' logs are now replayed after the summary, each between markers carrying the target name, followed by a FAILED TARGETS line. A green run prints nothing extra.
155 lines
3.8 KiB
Bash
Executable File
155 lines
3.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Generic parallel-make wrapper. Takes one or more make targets as
|
|
# args, runs each as its own background `make` invocation (capped at
|
|
# INFINITO_WORKER_CPU concurrent workers), and at the end prints a
|
|
# per-target wall-clock breakdown plus the overall elapsed time.
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
|
cd "${REPO_ROOT}"
|
|
|
|
if [[ $# -eq 0 ]]; then
|
|
echo "scripts/make/parallel.sh: at least one make target required" >&2 # nocheck: self-path-reference
|
|
exit 2
|
|
fi
|
|
|
|
# shellcheck source=scripts/meta/env/load.sh
|
|
source scripts/meta/env/load.sh
|
|
|
|
: "${INFINITO_WORKER_CPU:?INFINITO_WORKER_CPU must be set (provided by default.env via the env loader)}"
|
|
|
|
n_targets=$#
|
|
if ((n_targets < INFINITO_WORKER_CPU)); then
|
|
jobs="${n_targets}"
|
|
else
|
|
jobs="${INFINITO_WORKER_CPU}"
|
|
fi
|
|
|
|
format_duration() {
|
|
local seconds="$1"
|
|
printf '%dm %02ds' "$((seconds / 60))" "$((seconds % 60))"
|
|
}
|
|
|
|
echo
|
|
echo "🚀 parallel make dispatch"
|
|
echo " 📋 targets: $*"
|
|
echo " ⚙️ jobs: ${jobs} (cpu cap: ${INFINITO_WORKER_CPU})"
|
|
echo
|
|
|
|
tmpdir="$(mktemp -d)"
|
|
trap 'rm -rf "${tmpdir}"' EXIT
|
|
|
|
declare -A pid_to_target=()
|
|
declare -A target_start=()
|
|
declare -A target_dur=()
|
|
declare -A target_rc=()
|
|
declare -A target_log=()
|
|
|
|
start_ts="$(date +%s)"
|
|
|
|
active=0
|
|
to_launch=("$@")
|
|
launch_idx=0
|
|
|
|
drain_one() {
|
|
local pid=""
|
|
local rc=0
|
|
wait -n -p pid || rc=$?
|
|
local tgt="${pid_to_target[${pid}]}"
|
|
target_rc["${tgt}"]="${rc}"
|
|
target_dur["${tgt}"]=$(($(date +%s) - target_start["${tgt}"]))
|
|
if ((rc == 0)); then
|
|
echo "✅ ${tgt} ($(format_duration "${target_dur[${tgt}]}"))"
|
|
else
|
|
echo "❌ ${tgt} ($(format_duration "${target_dur[${tgt}]}")) exit=${rc}"
|
|
fi
|
|
cat "${target_log[${tgt}]}"
|
|
active=$((active - 1))
|
|
}
|
|
|
|
for tgt in "${to_launch[@]}"; do
|
|
while ((active >= jobs)); do
|
|
drain_one
|
|
done
|
|
log="${tmpdir}/${launch_idx}.log"
|
|
target_log["${tgt}"]="${log}"
|
|
target_start["${tgt}"]="$(date +%s)"
|
|
(make -- "${tgt}") >"${log}" 2>&1 &
|
|
pid_to_target[$!]="${tgt}"
|
|
active=$((active + 1))
|
|
launch_idx=$((launch_idx + 1))
|
|
done
|
|
|
|
while ((active > 0)); do
|
|
drain_one
|
|
done
|
|
|
|
total=$(($(date +%s) - start_ts))
|
|
|
|
echo
|
|
echo "📊 per-target wall-clock:"
|
|
overall_rc=0
|
|
for tgt in "$@"; do
|
|
rc="${target_rc[${tgt}]}"
|
|
if ((rc == 0)); then
|
|
status="✅"
|
|
else
|
|
status="❌"
|
|
overall_rc="${rc}"
|
|
fi
|
|
printf ' %s %-30s %s\n' "${status}" "${tgt}" "$(format_duration "${target_dur[${tgt}]}")"
|
|
done
|
|
|
|
echo
|
|
if ((overall_rc == 0)); then
|
|
echo "✨ total $(format_duration "${total}") 🎉"
|
|
else
|
|
echo "💥 total $(format_duration "${total}") (exit=${overall_rc})"
|
|
fi
|
|
echo
|
|
|
|
failed_targets=()
|
|
for tgt in "$@"; do
|
|
((target_rc["${tgt}"] == 0)) || failed_targets+=("${tgt}")
|
|
done
|
|
|
|
if ((${#failed_targets[@]} > 0)); then
|
|
for tgt in "${failed_targets[@]}"; do
|
|
echo "──────── output of failed target: ${tgt} ────────"
|
|
cat "${target_log[${tgt}]}"
|
|
echo "──────── end of ${tgt} ────────"
|
|
echo
|
|
done
|
|
echo "FAILED TARGETS: ${failed_targets[*]}"
|
|
echo
|
|
fi
|
|
|
|
if [[ -n "${GITHUB_STEP_SUMMARY:-}" ]]; then
|
|
{
|
|
echo "## 🚀 Parallel make targets"
|
|
echo
|
|
echo "| Target | Status | Duration |"
|
|
echo "|---|:---:|---:|"
|
|
for tgt in "$@"; do
|
|
if ((target_rc[${tgt}] == 0)); then
|
|
status="✅"
|
|
else
|
|
status="❌ exit=${target_rc[${tgt}]}"
|
|
fi
|
|
# shellcheck disable=SC2016 # backticks are markdown, not expansion
|
|
printf '| `%s` | %s | %s |\n' \
|
|
"${tgt}" "${status}" "$(format_duration "${target_dur[${tgt}]}")"
|
|
done
|
|
if ((overall_rc == 0)); then
|
|
printf '| **total** | ✨ | %s |\n' "$(format_duration "${total}")"
|
|
else
|
|
printf '| **total** | 💥 exit=%s | %s |\n' \
|
|
"${overall_rc}" "$(format_duration "${total}")"
|
|
fi
|
|
echo
|
|
} >>"${GITHUB_STEP_SUMMARY}"
|
|
fi
|
|
|
|
exit "${overall_rc}"
|