|
33 | 33 | # pytorch/executorch, builds the smoke consumer for ESP32-S3 with it, fails |
34 | 34 | # if the registry serves files other than <dir>'s archive, and runs the |
35 | 35 | # build on the emulator. |
| 36 | +# test_espressif_idf_nightly.sh wheel <dir> <dev version> <commit> |
| 37 | +# Checks that the ExecuTorch nightly wheels of the version's date exist for |
| 38 | +# every platform and Python and were built from <commit>, so a project can |
| 39 | +# install the executorch its kernel bundles need. Refuses today's nightly, |
| 40 | +# which a later one could replace. Logs go under <dir>. |
| 41 | +# test_espressif_idf_nightly.sh rehearse <dir> |
| 42 | +# Checks that wheel refuses a version that is not a dev version and today's |
| 43 | +# nightly, then runs it on the latest finished nightly of origin/nightly's |
| 44 | +# last 60 days, the nightly index's window, that has every wheel. Nightly |
| 45 | +# wheels can fail to build for days, so finding none only warns. |
| 46 | +# test_espressif_idf_nightly.sh torch <dir> |
| 47 | +# Needs only <dir> from prepare: checks that the torch the component's |
| 48 | +# VERSION names resolves from its index for every platform and Python. |
36 | 49 |
|
37 | 50 | set -euo pipefail |
38 | 51 |
|
39 | 52 | et_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd) |
40 | | -usage="usage: $0 prepare <dir> [stamp options] | build <dir> <shard> <shards> | fetch <dir> <URL> <sha256> | registry <dir> <URL>" |
| 53 | +usage="usage: $0 prepare <dir> [stamp options] | build <dir> <shard> <shards> | fetch <dir> <URL> <sha256> | registry <dir> <URL> | wheel <dir> <dev version> <commit> | rehearse <dir> | torch <dir>" |
41 | 54 | mode=${1:?${usage}} |
42 | 55 | dir=${2:?${usage}} |
43 | 56 | mkdir -p "${dir}" |
44 | 57 | dir=$(cd "${dir}" && pwd) |
45 | 58 | cd "${et_root}" |
46 | 59 | targets=(esp32 esp32s3 esp32c3 esp32c5 esp32c6 esp32p4) |
| 60 | +# The platforms and CPython versions ExecuTorch's wheels are built for, as |
| 61 | +# .github/workflows/build-wheels-{linux,aarch64-linux,macos,windows}.yml set them. |
| 62 | +wheel_platforms=(manylinux_2_28_x86_64 manylinux_2_28_aarch64 macosx_14_0_arm64 win_amd64) |
| 63 | +wheel_pythons=(311 312 313 314 315) |
| 64 | +nightly_index=https://download.pytorch.org/whl/nightly/cpu |
47 | 65 |
|
48 | 66 | # Runs a command with its output in a log, and shows the log's tail if it fails. |
49 | 67 | logged() { |
@@ -395,6 +413,116 @@ settings_applied() { |
395 | 413 | [[ "${applied}" == true ]] |
396 | 414 | } |
397 | 415 |
|
| 416 | +# Prints each wheel a dev version lacks: version, the nightly index's listing. |
| 417 | +missing_wheels() { |
| 418 | + local listed platform python |
| 419 | + listed=$(grep -F "executorch-${1/-dev./.dev}" <<< "$2" || true) |
| 420 | + for platform in "${wheel_platforms[@]}"; do |
| 421 | + for python in "${wheel_pythons[@]}"; do |
| 422 | + grep -qF -- "-cp${python}-cp${python}-${platform}.whl" <<< "${listed}" \ |
| 423 | + || echo "cp${python}-${platform}" |
| 424 | + done |
| 425 | + done |
| 426 | +} |
| 427 | + |
| 428 | +# Checks a dev version's nightly wheels: version, commit. |
| 429 | +check_wheels() { |
| 430 | + local version=$1 commit=$2 listing missing wheels platform python |
| 431 | + if [[ "${version}" != *-dev.* ]]; then |
| 432 | + echo "FAIL: ${version} is not a dev version" >&2 |
| 433 | + exit 1 |
| 434 | + fi |
| 435 | + # A second nightly the same day would replace the wheels of this one. |
| 436 | + if [[ "${version##*-dev.}" == "$(date -u +%Y%m%d)" ]]; then |
| 437 | + echo "FAIL: ${version} is today's nightly, which a later nightly today could replace;" \ |
| 438 | + "publish it after 00:00 UTC, before the next nightly." >&2 |
| 439 | + exit 1 |
| 440 | + fi |
| 441 | + # The version's date is its nightly commit's UTC date, and the wheels' is |
| 442 | + # the day they were built; when the two differ, no wheels are found. |
| 443 | + listing=$(curl -sSf --retry 3 "${nightly_index}/executorch/") |
| 444 | + missing=$(missing_wheels "${version}" "${listing}") |
| 445 | + if [[ -n "${missing}" ]]; then |
| 446 | + echo "FAIL: the nightly index has no ${version} wheel for ${missing//$'\n'/ }" >&2 |
| 447 | + exit 1 |
| 448 | + fi |
| 449 | + wheels=$(mktemp -d) |
| 450 | + # shellcheck disable=SC2064 # wheels is local, so expand it now. |
| 451 | + trap "rm -rf '${wheels}'" EXIT |
| 452 | + mkdir -p "${dir}/logs" |
| 453 | + for platform in "${wheel_platforms[@]}"; do |
| 454 | + for python in "${wheel_pythons[@]}"; do |
| 455 | + logged "${dir}/logs/nightly-wheel-cp${python}-${platform}.log" python -m pip download \ |
| 456 | + --no-deps --only-binary=:all: --platform "${platform}" \ |
| 457 | + --python-version "3.${python#3}" --implementation cp --index-url "${nightly_index}" \ |
| 458 | + --dest "${wheels}" "executorch==${version/-dev./.dev}" |
| 459 | + done |
| 460 | + done |
| 461 | + python - "${commit}" "${wheels}"/executorch-*.whl <<'PY' |
| 462 | +import re |
| 463 | +import sys |
| 464 | +import zipfile |
| 465 | +
|
| 466 | +commit, wheels = sys.argv[1], sys.argv[2:] |
| 467 | +wrong = [] |
| 468 | +for wheel in wheels: |
| 469 | + version_py = zipfile.ZipFile(wheel).read("executorch/version.py").decode() |
| 470 | + built = re.search(r"^git_version: .* = '(\w+)'", version_py, re.MULTILINE) |
| 471 | + if not built or built.group(1) != commit: |
| 472 | + wrong.append(f"{wheel} ({built.group(1) if built else 'no commit'})") |
| 473 | +if wrong: |
| 474 | + sys.exit(f"FAIL: not built from the component's {commit}: {', '.join(wrong)}") |
| 475 | +print(f"OK: all {len(wheels)} wheels were built from {commit}") |
| 476 | +PY |
| 477 | +} |
| 478 | + |
| 479 | +wheel() { |
| 480 | + check_wheels "${3:?${usage}}" "${4:?${usage}}" |
| 481 | +} |
| 482 | + |
| 483 | +rehearse() { |
| 484 | + local today output listing commit day version |
| 485 | + today=$(date -u +%Y%m%d) |
| 486 | + output=$( (check_wheels 1.6.0 0) 2>&1 || true) |
| 487 | + if [[ "${output}" != *"is not a dev version"* ]]; then |
| 488 | + echo "FAIL: the wheel check did not refuse 1.6.0: ${output}" >&2 |
| 489 | + exit 1 |
| 490 | + fi |
| 491 | + output=$( (check_wheels "1.6.0-dev.${today}" 0) 2>&1 || true) |
| 492 | + if [[ "${output}" != *"is today's nightly"* ]]; then |
| 493 | + echo "FAIL: the wheel check did not refuse today's nightly: ${output}" >&2 |
| 494 | + exit 1 |
| 495 | + fi |
| 496 | + echo "OK: the wheel check refuses a version that is not a dev version and today's nightly" |
| 497 | + listing=$(curl -sSf --retry 3 "${nightly_index}/executorch/") |
| 498 | + for commit in $(git rev-list --first-parent --since=60.days origin/nightly); do |
| 499 | + day=$(TZ=UTC git log -1 --format=%cd --date=format-local:%Y%m%d "${commit}") |
| 500 | + version="$(git show "${commit}:version.txt")-dev.${day}" |
| 501 | + if [[ "${day}" != "${today}" && -z "$(missing_wheels "${version}" "${listing}")" ]]; then |
| 502 | + check_wheels "${version}" "${commit}" |
| 503 | + return |
| 504 | + fi |
| 505 | + done |
| 506 | + echo "::warning::no nightly of the last 60 days has every wheel; the dev wheel check went unrehearsed." |
| 507 | +} |
| 508 | + |
| 509 | +torch() { |
| 510 | + local version_file="${dir}/component/executorch/VERSION" requirement index target platform python |
| 511 | + requirement=$(sed -n 's/^torch_requirement: *//p' "${version_file}") |
| 512 | + index=$(sed -n 's/^torch_index: *//p' "${version_file}") |
| 513 | + target=$(mktemp -d) |
| 514 | + mkdir -p "${dir}/logs" |
| 515 | + for platform in "${wheel_platforms[@]}"; do |
| 516 | + for python in "${wheel_pythons[@]}"; do |
| 517 | + logged "${dir}/logs/torch-cp${python}-${platform}.log" python -m pip install --dry-run \ |
| 518 | + --no-deps --only-binary=:all: --platform "${platform}" \ |
| 519 | + --python-version "3.${python#3}" --implementation cp --target "${target}" \ |
| 520 | + --index-url "${index}" "${requirement}" |
| 521 | + done |
| 522 | + done |
| 523 | + echo "OK: ${requirement} resolves from ${index} for CPython ${wheel_pythons[*]} on ${wheel_platforms[*]}" |
| 524 | +} |
| 525 | + |
398 | 526 | # Runs a build on its emulator: build dir, target, log, then idf.py arguments. |
399 | 527 | run_firmware() { |
400 | 528 | local build=$1 target=$2 log=$3 |
@@ -429,5 +557,8 @@ case "${mode}" in |
429 | 557 | build) build "$@" ;; |
430 | 558 | fetch) fetch "$@" ;; |
431 | 559 | registry) registry "$@" ;; |
| 560 | + wheel) wheel "$@" ;; |
| 561 | + rehearse) rehearse ;; |
| 562 | + torch) torch ;; |
432 | 563 | *) echo "unknown mode ${mode}; ${usage}" >&2; exit 1 ;; |
433 | 564 | esac |
0 commit comments