From b31e7531d4533b4a79a284deea25d046416faae1 Mon Sep 17 00:00:00 2001 From: rhoadesScholar Date: Tue, 4 Aug 2026 02:34:32 +0000 Subject: [PATCH] feat(build): fail with actionable instructions when a C compiler or cargo is missing Building daisy from source compiles the Rust core through maturin. When a C linker or the Rust toolchain is absent, maturin's own error is cryptic and does not say what to install -- and since `pip install daisy` from an sdist takes this path, it is the first thing a new user sees. build_wrapper.py becomes the PEP 517 backend (backend-path = ["."]) and wraps maturin's build_wheel / build_editable with an up-front check for cc/gcc/clang and cargo, printing per-platform install instructions instead. build_sdist forwards UNWRAPPED -- packaging sources needs no toolchain, so gating it would be a regression. Everything else is re-exported from maturin, which defines no __all__, so get_requires_for_build_wheel, get_requires_for_build_sdist and prepare_metadata_for_build_wheel forward intact. Rebuilt against current v2.0 and reduced to just this. Dropped from the earlier version: the [project.optional-dependencies] dev/examples/docs extras (v2.0 kept [dependency-groups] and added a lint group, so those would now duplicate it -- and their "Your new dev extra" comments were authoring scaffolding), the matching docs.yml/README --group -> --extra changes (unnecessary for the same reason, and the cause of the docs-CI failure on the old branch), and an unrelated .vscode .gitignore line. Co-Authored-By: Claude Opus 5 --- build_wrapper.py | 95 ++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 3 +- 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 build_wrapper.py diff --git a/build_wrapper.py b/build_wrapper.py new file mode 100644 index 00000000..44547c16 --- /dev/null +++ b/build_wrapper.py @@ -0,0 +1,95 @@ +import shutil +import sys +from maturin import * # Forward everything from maturin + + +def check_build_environment(): + # ANSI Escape Codes + RED_BG = "\033[41m\033[1;37m" # Bold white text on red background + BOLD_RED = "\033[1;31m" # Bold red text + BOLD_GREEN = "\033[1;32m" # Bold green text + BOLD = "\033[1m" # Bold text + RESET = "\033[0m" # Reset formatting + + has_compiler = shutil.which("cc") or shutil.which("gcc") or shutil.which("clang") + has_cargo = shutil.which("cargo") + + # 1. Check for C Compiler / Linker + if not has_compiler: + print( + f"\n{RED_BG} BUILD ERROR: MISSING C COMPILER {RESET}\n", file=sys.stderr + ) + print( + f"{BOLD_RED}A system C compiler / linker ('cc', 'gcc', or 'clang') was not found.{RESET}", + file=sys.stderr, + ) + print( + f"{BOLD}This project compiles native Rust extensions and requires a system toolchain.{RESET}\n", + file=sys.stderr, + ) + + print( + "Please install a compiler using your system package manager:", + file=sys.stderr, + ) + print( + f" {BOLD_GREEN}Ubuntu/Debian{RESET}: sudo apt update && sudo apt install build-essential", + file=sys.stderr, + ) + print( + f' {BOLD_GREEN}Fedora/RHEL{RESET}: sudo dnf groupinstall "Development Tools"', + file=sys.stderr, + ) + print( + f" {BOLD_GREEN}Arch Linux{RESET}: sudo pacman -S base-devel", + file=sys.stderr, + ) + print( + f" {BOLD_GREEN}macOS{RESET}: xcode-select --install\n", + file=sys.stderr, + ) + sys.exit(1) + + # 2. Check for Cargo / Rust + if not has_cargo: + print(f"\n{RED_BG} BUILD ERROR: MISSING RUST {RESET}\n", file=sys.stderr) + print( + f"{BOLD_RED}The Rust compiler and package manager ('cargo') was not found.{RESET}", + file=sys.stderr, + ) + print( + f"{BOLD}This project includes Rust code and requires the Rust toolchain to build.{RESET}\n", + file=sys.stderr, + ) + + print("Please install Rust using the recommended installer:", file=sys.stderr) + print( + f" {BOLD_GREEN}Linux & macOS{RESET}: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh", + file=sys.stderr, + ) + print( + f" {BOLD_GREEN}Windows{RESET}: Download and run rustup-init.exe from https://rustup.rs\n", + file=sys.stderr, + ) + sys.exit(1) + + +# Intercept and wrap maturin's PEP 517 entry hooks +def build_wheel(*args, **kwargs): + check_build_environment() + import maturin + + return maturin.build_wheel(*args, **kwargs) + + +def build_editable(*args, **kwargs): + check_build_environment() + import maturin + + return maturin.build_editable(*args, **kwargs) + + +def build_sdist(*args, **kwargs): + import maturin + + return maturin.build_sdist(*args, **kwargs) diff --git a/pyproject.toml b/pyproject.toml index 1f6796ee..3b583649 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,7 +43,8 @@ Changelog = "https://github.com/funkelab/daisy/blob/main/CHANGELOG.md" [build-system] requires = ["maturin>=1.5,<2.0"] -build-backend = "maturin" +build-backend = "build_wrapper" +backend-path = ["."] [tool.maturin] manifest-path = "daisy-py/Cargo.toml"