Build libfastled.a using ninja. Skips the build entirely if the source fingerprint hasn't changed. Returns (success, was_rebuilt) — was_rebuilt is True if sources changed.
(build_dir: Path, verbose: bool = False)
| 747 | |
| 748 | |
| 749 | def build_library(build_dir: Path, verbose: bool = False) -> tuple[bool, bool]: |
| 750 | """ |
| 751 | Build libfastled.a using ninja. |
| 752 | |
| 753 | Skips the build entirely if the source fingerprint hasn't changed. |
| 754 | Returns (success, was_rebuilt) — was_rebuilt is True if sources changed. |
| 755 | """ |
| 756 | # Fast path: skip meson/ninja if source tree hasn't changed |
| 757 | if _library_is_fresh(build_dir): |
| 758 | if verbose: |
| 759 | print("[WASM] Library up-to-date (fingerprint match), skipping meson") |
| 760 | else: |
| 761 | print("[WASM] Library up-to-date") |
| 762 | return True, False |
| 763 | |
| 764 | # Delete stale thin archive before rebuild. When sources change, the |
| 765 | # existing thin archive may reference .o files from renamed/deleted |
| 766 | # sources. The 'r' flag in emar reads existing members first and fails |
| 767 | # on missing paths. Deleting forces a fresh archive. |
| 768 | library_archive = build_dir / "ci" / "meson" / "wasm" / "libfastled.a" |
| 769 | if library_archive.exists(): |
| 770 | try: |
| 771 | library_archive.unlink() |
| 772 | print("[WASM] Deleted stale archive (source fingerprint changed)") |
| 773 | except OSError as e: |
| 774 | print(f"[WASM] Warning: Could not delete stale archive: {e}") |
| 775 | |
| 776 | cmd = [get_meson_executable(), "compile", "-C", str(build_dir), "fastled"] |
| 777 | if verbose: |
| 778 | cmd.append("-v") |
| 779 | |
| 780 | print("[WASM] Building libfastled.a...") |
| 781 | result = subprocess.run(cmd, cwd=PROJECT_ROOT, capture_output=True, text=True) |
| 782 | if result.returncode != 0: |
| 783 | # Print captured output so the user sees what happened |
| 784 | if result.stdout: |
| 785 | print(result.stdout, end="") |
| 786 | if result.stderr: |
| 787 | print(result.stderr, end="", file=sys.stderr) |
| 788 | |
| 789 | # Self-healing: detect stale build state (missing/renamed/deleted files) |
| 790 | # and auto-reconfigure meson before retrying. |
| 791 | combined_output = (result.stdout or "") + (result.stderr or "") |
| 792 | if _is_stale_build_error(combined_output): |
| 793 | print("[WASM] Stale build state detected, auto-recovering...") |
| 794 | if _recover_stale_wasm_build(build_dir): |
| 795 | print("[WASM] Retrying build after recovery...") |
| 796 | retry = subprocess.run(cmd, cwd=PROJECT_ROOT) |
| 797 | if retry.returncode == 0: |
| 798 | _save_library_fingerprint(build_dir) |
| 799 | print( |
| 800 | "[WASM] Library build successful (after stale build recovery)" |
| 801 | ) |
| 802 | return True, True |
| 803 | |
| 804 | # Self-healing: if a thin archive exists after failure, it may have |
| 805 | # stale member references. Delete and retry once. |
| 806 | if library_archive.exists(): |
no test coverage detected