finished docker manylinux compliation for pypi

This commit is contained in:
ykcheungab
2026-08-03 17:07:54 +08:00
parent 3117ddd5b9
commit d7c5845646
31 changed files with 709 additions and 52 deletions
+123 -22
View File
@@ -1,36 +1,80 @@
"""Build a Nuitka-compiled orisys wheel and publish it to SDK/{version}/.
Pipeline:
1. Compile src/orisys → .pyd
1. Compile src/orisys → extension module (.pyd / .so)
2. Stage wheel tree (compiled module + bundled files from pyproject.toml)
3. Run bdist_wheel and move output to SDK/{version}/
4. Smoke-test imports in a temporary venv
3. Run bdist_wheel; on Linux, auditwheel-repair to a manylinux tag
4. Smoke-test imports in a temporary venv (required for --target pypi-linux)
Run from repo root: python build/build_nuitka.py
Targets:
internal – local/SDK delivery (default). Linux still repairs with auditwheel.
pypi-linux – PyPI binary wheel; requires Linux + CPython 3.10/3.11/3.12 + compliant tag.
Run from repo root:
python build/build_nuitka.py
python build/build_nuitka.py --target pypi-linux
"""
from __future__ import annotations
import argparse
import os
import shutil
import subprocess
import sys
from pathlib import Path
from nuitka_compile import compile_orisys, copy_compiled_orisys, install_nuitka
from paths import NUITKA_COMPILE_DIR, NUITKA_WHEEL_DIR, REPO_ROOT, repo_path
from paths import NUITKA_COMPILE_DIR, NUITKA_WHEEL_DIR, REPO_ROOT, repaired_wheel_dir, repo_path
from pyproject_utils import bundle_items, extras_require, get_version, install_requires
from verify import verify_built_wheel
from wheel_build import publish_wheel_to_sdk, run_bdist_wheel, write_wheel_setup
from wheel_build import (
is_compliant_manylinux_wheel,
publish_wheel_to_sdk,
repair_linux_wheel,
run_bdist_wheel,
write_wheel_setup,
)
from wheel_layout import copy_bundle_items, copy_orisys_blend, package_data_from_bundle
VALID_TARGETS = ("internal", "pypi-linux")
# ABI tags that manylinux images ship under /opt/python/cpXYY-cpXYY.
PYPI_LINUX_PYTHON_VERSIONS = ((3, 10), (3, 11), (3, 12))
def _reset_dir(path) -> None:
def _reset_dir(path: Path) -> None:
"""Remove and recreate a build output directory."""
if path.exists():
shutil.rmtree(path)
path.mkdir(parents=True)
def build() -> None:
def _validate_pypi_linux_environment() -> None:
if sys.platform != "linux":
raise SystemExit(
"[ERROR] --target pypi-linux must run inside a Linux manylinux container "
"(for example quay.io/pypa/manylinux_2_28_x86_64 via Docker Desktop)."
)
if sys.version_info[:2] not in PYPI_LINUX_PYTHON_VERSIONS:
supported = ", ".join(f"{major}.{minor}" for major, minor in PYPI_LINUX_PYTHON_VERSIONS)
raise SystemExit(
f"[ERROR] --target pypi-linux requires CPython {supported}, got "
f"{sys.version_info.major}.{sys.version_info.minor}. "
"Use /opt/python/cp310-cp310 (or cp311/cp312) in the manylinux_2_28 image."
)
def build(*, target: str = "internal", verify: bool | None = None) -> Path:
if target not in VALID_TARGETS:
raise SystemExit(f"[ERROR] Unknown target {target!r}; choose from {VALID_TARGETS}")
if target == "pypi-linux":
_validate_pypi_linux_environment()
# pypi-linux always verifies; internal opts in with --verify.
if verify is None:
verify = target == "pypi-linux"
# --- 1. Compile ---
install_nuitka()
@@ -38,7 +82,7 @@ def build() -> None:
_reset_dir(NUITKA_COMPILE_DIR)
_reset_dir(NUITKA_WHEEL_DIR)
print("\n=== Compiling with Nuitka ===")
print(f"\n=== Compiling with Nuitka (target={target}, version={version}) ===")
env = os.environ.copy()
src_dir = repo_path("src")
env["PYTHONPATH"] = src_dir.as_posix() + os.pathsep + env.get("PYTHONPATH", "")
@@ -71,23 +115,80 @@ def build() -> None:
extras_require=extras_require(REPO_ROOT),
)
# --- 3. Build wheel and publish to SDK/ ---
# --- 3. Build wheel, repair on Linux, publish to SDK/ ---
wheel_path = run_bdist_wheel(NUITKA_WHEEL_DIR)
if wheel_path is None:
print("\n[ERROR] Wheel not found.")
return
raise SystemExit("\n[ERROR] Wheel not found after bdist_wheel.")
final_wheel = publish_wheel_to_sdk(wheel_path, version)
print(f"\n[SUCCESS] Wheel created and moved to: {final_wheel}")
repair_dir = repaired_wheel_dir()
final_candidate = repair_linux_wheel(wheel_path, repair_dir)
# # --- 4. Verify (isolated venv; does not touch the active environment) ---
# print("\n=== Verifying finger mesh/viewer imports ===")
# try:
# verify_built_wheel(final_wheel)
# except subprocess.CalledProcessError as exc:
# print(f"\n[WARNING] Wheel verify failed: {exc}")
# print("The wheel was built successfully; verification runs in an isolated temp venv.")
if target == "pypi-linux" and not is_compliant_manylinux_wheel(final_candidate):
raise SystemExit(
f"[ERROR] Wheel is not PyPI-compliant: {final_candidate.name}. "
"Expected a manylinux_2_17_x86_64 (or newer) platform tag."
)
# Keep a durable copy under dist/repaired for upload tooling.
dist_repaired = REPO_ROOT / "dist" / "repaired"
dist_repaired.mkdir(parents=True, exist_ok=True)
dist_copy = dist_repaired / final_candidate.name
if dist_copy.resolve() != final_candidate.resolve():
shutil.copy2(final_candidate, dist_copy)
final_wheel = publish_wheel_to_sdk(final_candidate, version)
print(f"\n[SUCCESS] Wheel created and copied to: {final_wheel}")
print(f"[SUCCESS] Upload candidate also at: {dist_copy}")
# --- 4. Verify (isolated venv; does not touch the active environment) ---
if verify:
print("\n=== Verifying wheel imports in isolated venv ===")
try:
verify_built_wheel(final_wheel)
print("[SUCCESS] Wheel smoke test passed.")
except subprocess.CalledProcessError as exc:
if target == "pypi-linux":
raise SystemExit(
f"[ERROR] Wheel verify failed (required for pypi-linux): {exc}"
) from exc
print(f"\n[WARNING] Wheel verify failed: {exc}")
print("The wheel was built successfully; verification runs in an isolated temp venv.")
else:
print("\n[INFO] Skipping wheel verify (pass --verify to enable).")
return final_wheel
def main(argv: list[str] | None = None) -> None:
parser = argparse.ArgumentParser(description="Build a Nuitka-compiled orisys wheel.")
parser.add_argument(
"--target",
choices=VALID_TARGETS,
default="internal",
help="internal: SDK delivery; pypi-linux: manylinux PyPI binary wheel",
)
parser.add_argument(
"--verify",
action="store_true",
help="Run isolated venv smoke test (always on for --target pypi-linux)",
)
parser.add_argument(
"--skip-verify",
action="store_true",
help="Skip smoke test when using --target internal",
)
args = parser.parse_args(argv)
verify: bool | None
if args.target == "pypi-linux":
verify = True
elif args.verify:
verify = True
elif args.skip_verify:
verify = False
else:
verify = None
build(target=args.target, verify=verify)
if __name__ == "__main__":
build()
main()