From b29e64c919b4e68e4d742a077c0fb4111485e8df Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Fri, 20 Sep 2024 15:12:06 +0200 Subject: [PATCH 01/21] Refactor build logic --- pyproject.toml | 83 +++++++----- setup.py | 64 --------- tools/build_all_wheels.py | 124 ++++++++++++++++++ .../download_wgpu_native.py | 66 ++++------ tools/hatch_build.py | 78 +++++++++++ 5 files changed, 280 insertions(+), 135 deletions(-) delete mode 100644 setup.py create mode 100644 tools/build_all_wheels.py rename download-wgpu-native.py => tools/download_wgpu_native.py (78%) create mode 100644 tools/hatch_build.py diff --git a/pyproject.toml b/pyproject.toml index 81456a66..0a632496 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,31 +1,54 @@ +# ===== Project info + +[project] +dynamic = ["version"] +name = "wgpu" +description = "WebGPU for Python" +readme = "README.md" +license = { file = "LICENSE" } +authors = [{ name = "Almar Klein" }, { name = "Korijn van Golen" }] +keywords = ["webgpu", "wgpu", "vulkan", "metal", "DX12", "opengl"] +requires-python = ">= 3.8" +dependencies = ["cffi>=1.15.0", "rubicon-objc>=0.4.1; sys_platform == 'darwin'"] + +[project.optional-dependencies] +jupyter = ["jupyter_rfb>=0.4.2"] +glfw = ["glfw>=1.9"] +docs = ["sphinx>7.2", "sphinx_rtd_theme"] +imgui = ["imgui-bundle>=1.2.1"] + +[project.entry-points."pyinstaller40"] +hook-dirs = "wgpu.__pyinstaller:get_hook_dirs" +tests = "wgpu.__pyinstaller:get_test_dirs" + +[project.urls] +Homepage = "https://github.com/pygfx/wgpu-py" +Documentation = "https://wgpu-py.readthedocs.io" +Repository = "https://github.com/pygfx/wgpu-py" + +# ===== Building +# +# We use the hatch build backend, because its modern, with pretty good defaults, +# and configurable. Flit is really nice, but a bit too simple for what we need +# with our binary libs. We use a hatch build hook to install the correct +# wgpu-native lib right before the wheel is build. See the tools dir. + [build-system] -requires = ["setuptools>=42"] -build-backend = "setuptools.build_meta" - - -[tool.cibuildwheel] -# we only build on one python version since the wheels are not bound to it -build = "cp312-*" -# Print system info before build -before-all = "uname -a" -# Can't list requests under build-system.requires because that step happens _after_ the before-build command -before-build = "pip install requests && python download-wgpu-native.py" -# This is sufficient to trigger an install of the built wheel -test-command = "echo Wheel installed" - -[tool.cibuildwheel.windows] -# Only for local use, overridden in cd.yml -archs = ["amd64"] - -[tool.cibuildwheel.macos] -# Only for local use, overridden in cd.yml -archs = ["arm64"] - -[tool.cibuildwheel.linux] -# wgpu-native does not build for musllinux yet -skip = "*musllinux*" -# Use custom images, with minimal version that matches wgpu-native -manylinux-x86_64-image = "quay.io/pypa/manylinux_2_28_x86_64" -manylinux-aarch64-image = "quay.io/pypa/manylinux_2_28_aarch64" -manylinux-i686-image = "quay.io/pypa/manylinux_2_28_i686" -manylinux-ppc64le-image = "quay.io/pypa/manylinux_2_28_ppc64le" +requires = ["requests", "hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.version] +path = "wgpu/__init__.py" + +[tool.hatch.build.targets.sdist] +packages = ["wgpu"] +exclude = ["*.so", "*.dll", "*.dylib"] + +[tool.hatch.build.targets.wheel] +packages = ["wgpu"] +artifacts = ["*.so", "*.dll", "*.dylib"] + +[tool.hatch.build.targets.wheel.hooks.custom] +path = "tools/hatch_build.py" + +# ===== Tooling diff --git a/setup.py b/setup.py deleted file mode 100644 index aa9bc24c..00000000 --- a/setup.py +++ /dev/null @@ -1,64 +0,0 @@ -import re -import platform - -from setuptools import find_packages, setup -from setuptools.command.bdist_wheel import get_platform -from setuptools.command.bdist_wheel import bdist_wheel as _bdist_wheel - -NAME = "wgpu" -SUMMARY = "WebGPU for Python" - -with open(f"{NAME}/__init__.py") as fh: - VERSION = re.search(r"__version__ = \"(.*?)\"", fh.read()).group(1) - - -class bdist_wheel(_bdist_wheel): # noqa: N801 - def finalize_options(self): - self.plat_name = get_platform(None) # force a platform tag - _bdist_wheel.finalize_options(self) - - -resources_globs = ["*.h", "*.idl"] -if platform.system() == "Linux": - resources_globs.append("*-release.so") -elif platform.system() == "Darwin": - resources_globs.append("*-release.dylib") -elif platform.system() == "Windows": - resources_globs.append("*-release.dll") -else: - pass # don't include binaries; user will have to arrange for the lib - -runtime_deps = ["cffi>=1.15.0", "rubicon-objc>=0.4.1; sys_platform == 'darwin'"] -extra_deps = { - "jupyter": ["jupyter_rfb>=0.4.2"], - "glfw": ["glfw>=1.9"], - "docs": ["sphinx>7.2", "sphinx_rtd_theme"], - "imgui": ["imgui-bundle>=1.2.1"], -} - -setup( - name=NAME, - version=VERSION, - packages=find_packages( - exclude=["codegen", "codegen.*", "tests", "tests.*", "examples", "examples.*"] - ), - package_data={f"{NAME}.resources": resources_globs}, - python_requires=">=3.8.0", - install_requires=runtime_deps, - extras_require=extra_deps, - license="BSD 2-Clause", - description=SUMMARY, - long_description=open("README.md").read(), - long_description_content_type="text/markdown", - author="Almar Klein", - author_email="almar.klein@gmail.com", - url="https://github.com/pygfx/wgpu-py", - cmdclass={"bdist_wheel": bdist_wheel}, - data_files=[("", ["LICENSE"])], - entry_points={ - "pyinstaller40": [ - "hook-dirs = wgpu.__pyinstaller:get_hook_dirs", - "tests = wgpu.__pyinstaller:get_test_dirs", - ], - }, -) diff --git a/tools/build_all_wheels.py b/tools/build_all_wheels.py new file mode 100644 index 00000000..ce1d5371 --- /dev/null +++ b/tools/build_all_wheels.py @@ -0,0 +1,124 @@ +""" +Script to build all wheels. + +Can be run from any Unix machine. +Relies on the build hook in `hatch_build.py` consuming WGPU_BUILD_PLATFORM_INFO. +""" + +import os +import sys +import shutil +import hashlib +import zipfile +from subprocess import run + +from download_wgpu_native import main as download_lib # noqa + + +# Define platform tags. These are pairs of wgpu-native-tag and wheel-tag. The +# former is used to download the appropriate binary lib, the latter is used to +# give the resulting wheel the correct name. Note that the build system does +# not check the name, so a typo here results in a broken wheel. +# +# Linux: The wgpu-native wheels are build on manylinux. The manylinux version +# listed below must match that. +# +# MacOS: On wgpu-native the MACOSX_DEPLOYMENT_TARGET is set to 10.13, I presume +# this affects the macos version in the name below. +# +# TODO: To help automate the naming, wgpu-native could include a file with +# build-info in the archive. This'd include the os and architecture themselves, +# the version of manylinux, and macos deployment target. + +PLATFORM_TAGS = [ + ("windows_x86_64", "win_amd64"), + ("windows_aarch64", "win_arm64"), + ("windows_i686", "win32"), + ("macos_x86_64", "macosx_10_9_x86_64"), + ("macos_aarch64", "macosx_11_0_arm64"), + ("linux_x86_64", "manylinux_2_28_x86_64"), + ("linux_aarch64", "manylinux_2_28_aarch64"), +] + + +# --- Prepare + + +if not sys.platform.startswith(("darwin", "win")): + print("WARNING: Building releases only really works on Unix") + +# Make sure we're in the project root, no matter where this is alled from. +root_dir = os.path.abspath(os.path.join(__file__, "..", "..")) +os.chdir(root_dir) +print(os.getcwd()) + +# Remove the dist directory for a fresh start +if os.path.isdir("dist"): + shutil.rmtree("dist") + + +# --- Build + + +# Build all wheels +for platform_info in PLATFORM_TAGS: + os.environ["WGPU_BUILD_PLATFORM_INFO"] = " ".join(platform_info) + run([sys.executable, "-m", "build", "-n", "-w"]) + +# Build sdist +run([sys.executable, "-m", "build", "-n", "-s"]) + +# Restore wgpu-native +download_lib() + + +# --- Checks produced files + + +all_tags = set(platform_info[1] for platform_info in PLATFORM_TAGS) +assert len(all_tags) == len(PLATFORM_TAGS), "Wheel tags in PLATFORM_TAGS are not unique" + +found_files = os.listdir("dist") +found_wheels = [fname for fname in found_files if fname.endswith(".whl")] +found_tags = {fname.split("none-")[1].split(".")[0] for fname in found_wheels} +assert found_tags == all_tags, f"Found tags does not match expected tags: {found_tags}\n{all_tags}" + +found_others = list(set(found_files) - set(found_wheels)) +assert len(found_others) == 1 and found_others[0].endswith(".tar.gz"), f"Found unexpected files: {found_others}" + +for archive_name in found_wheels: + assert "-any-" not in archive_name, f"There should not be an 'any' wheel: {archive_name}" + + +# --- Report and check content of archives + + +print("Dist archives:") + +# Simple check for sdist archive +for archive_name in found_others: + size = os.stat("dist/" + archive_name).st_size + print(f"{archive_name} ({size/1e6:0.2f} MB)") + assert size < 1e6, f"Did not expected {archive_name} to be this large" + +# Collect content of each wheel +hash_to_file = {} +for archive_name in found_wheels: + size = os.stat("dist/" + archive_name).st_size + print(f"{archive_name} ({size/1e6:0.2f} MB)") + z = zipfile.ZipFile("dist/" + archive_name) + flat_map = {os.path.basename(fi.filename): fi.filename for fi in z.filelist} + lib_hashes = [] + for fname in flat_map: + if fname.endswith((".so", ".dll", ".dylib")): + bb = z.read(flat_map[fname]) + hash = hashlib.sha256(bb).hexdigest() + lib_hashes.append(hash) + print(f" - {fname} ({len(bb)/1e6:0.2f} MB)\n {hash}") + assert len(lib_hashes) == 1, f"Expected 1 lib per wheel, got {len(lib_hashes)} in {archive_name}" + hash = lib_hashes[0] + assert hash not in hash_to_file, f"Same lib found in {hash_to_file[hash]} and archive_name" + hash_to_file[hash] = archive_name + +# Meta check +assert set(hash_to_file.values()) == set(found_wheels) diff --git a/download-wgpu-native.py b/tools/download_wgpu_native.py similarity index 78% rename from download-wgpu-native.py rename to tools/download_wgpu_native.py index 9f357334..dfc17c00 100644 --- a/download-wgpu-native.py +++ b/tools/download_wgpu_native.py @@ -9,17 +9,21 @@ import requests +DEFAULT_UPSTREAM = "gfx-rs/wgpu-native" + +ROOT_DIR = os.path.abspath(os.path.join(__file__, "..", "..")) + # The directory containing non-python resources that are included in packaging -RESOURCE_DIR = os.path.join("wgpu", "resources") +RESOURCE_DIR = os.path.join(ROOT_DIR, "wgpu", "resources") # The version installed through this script is tracked in the backend module -VERSION_FILE = os.path.join("wgpu", "backends", "wgpu_native", "__init__.py") +VERSION_FILE = os.path.join(ROOT_DIR, "wgpu", "backends", "wgpu_native", "__init__.py") # Whether to ensure we export \n instead of \r\n -FORCE_SIMPLE_NEWLINES = False +NEWLINE_FLAVOUR = b"\n" if sys.platform.startswith("win"): sample = open(os.path.join(RESOURCE_DIR, "codegen_report.md"), "rb").read() - if sample.count(b"\r\n") == 0: - FORCE_SIMPLE_NEWLINES = True + if sample.count(b"\r\n") > 0.5 * sample.count(b"\n"): + NEWLINE_FLAVOUR = b"\r\n" def get_current_version(): @@ -57,8 +61,9 @@ def extract_file(zip_filename, member, path): flat_map = {os.path.basename(fi.filename): fi.filename for fi in z.filelist} bb = z.read(flat_map[member]) # Make newlines consistent with Git rules etc. - if member.endswith(".h") and FORCE_SIMPLE_NEWLINES: + if member.endswith(".h"): bb = bb.replace(b"\r\n", b"\n") + bb = bb.replace(b"\n", NEWLINE_FLAVOUR) # Write to disk os.makedirs(path, exist_ok=True) with open(os.path.join(path, member), "wb") as f: @@ -66,9 +71,7 @@ def extract_file(zip_filename, member, path): def get_os_string(): - if os.environ.get("CIBUILDWHEEL") == "1" and os.getenv("CIBW_PLATFORM"): - return os.getenv("CIBW_PLATFORM") - elif sys.platform.startswith("win"): + if sys.platform.startswith("win"): return "windows" elif sys.platform.startswith("darwin"): return "macos" @@ -96,39 +99,19 @@ def get_arch(): detected_arch = "x86_64" else: detected_arch = "i686" - - if os.environ.get("CIBUILDWHEEL") == "1": - # When running in cibuildwheel, we derive the intended arch from - # an env var (the same one that cibuildwheel uses) that we set in cd.yml. - cibw_arch = os.getenv("CIBW_ARCHS") # must be singular - if not cibw_arch: - # Linux builds run on Docker, so env is not visible - cibw_arch = detected_arch - elif "," in cibw_arch: - raise RuntimeError("CIBW_ARCHS must have a single arch") - arch_map = { - "windows": { - "AMD64": "x86_64", - "ARM64": "aarch64", - "x86": "i686", - }, - "macos": { - "arm64": "aarch64", - "x86_64": "x86_64", - }, - "linux": { - "x86_64": "x86_64", - "aarch64": "aarch64", - "i868": "i686", - }, - } - maps_for_os = arch_map[get_os_string()] - return maps_for_os[cibw_arch] - return detected_arch -def main(version, os_string, arch, upstream): +def main(version=None, os_string=None, arch=None, upstream=None): + if version is None: + version = get_current_version() + if os_string is None: + os_string = get_os_string() + if arch is None: + arch = get_arch() + if upstream is None: + upstream = DEFAULT_UPSTREAM + for build in ["release"]: # ["release", "debug"] if os_string == "windows": arch += "-msvc" # -gnu is also available. @@ -136,7 +119,7 @@ def main(version, os_string, arch, upstream): url = f"https://github.com/{upstream}/releases/download/v{version}/{filename}" tmp = tempfile.gettempdir() zip_filename = os.path.join(tmp, filename) - print(f"Downloading {url} to {zip_filename}") + print(f"Downloading {url}") download_file(url, zip_filename) headerfile1 = "webgpu.h" headerfile2 = "wgpu.h" @@ -161,6 +144,7 @@ def main(version, os_string, arch, upstream): os.path.join(RESOURCE_DIR, binaryfile), os.path.join(RESOURCE_DIR, binaryfile_name), ) + current_version = get_current_version() if version != current_version: print(f"Version changed, updating {VERSION_FILE}") @@ -196,7 +180,7 @@ def main(version, os_string, arch, upstream): default=arch_string, choices=("x86_64", "i686", "aarch64"), ) - upstream = "gfx-rs/wgpu-native" + upstream = DEFAULT_UPSTREAM parser.add_argument( "--upstream", help=f"Upstream repository to download release from (default: {upstream})", diff --git a/tools/hatch_build.py b/tools/hatch_build.py new file mode 100644 index 00000000..15fd5224 --- /dev/null +++ b/tools/hatch_build.py @@ -0,0 +1,78 @@ +""" +Hook for building wheels with the hatchling build backend. + +* Set wheel to being platform-specific (not pure Python). +* Download the wgpu-native library before creating the wheel. +* Support cross-platform wheel building with a custom env var. +""" + +# Note on an alternative approach: +# +# In pyproject.toml set: +# +# build-backend = "local_build_backend" +# backend-path = ["tools"] +# +# In local_build_backend.py define functions like build_wheel and build_sdist +# that simply call the same function from hatchling or flit_core. But first +# download the lib. +# +# I found this approach pretty elegant (it works with any build-backend!) so I +# wanted to write it up. The downside for our use-case, however, is that the +# wheels must be renamed after building, and that the wheels are still marked as +# pure Python. + +import os +import sys +from subprocess import getoutput + +from hatchling.builders.hooks.plugin.interface import BuildHookInterface + +sys.path.insert(0, os.path.abspath(os.path.join(__file__, ".."))) + +from download_wgpu_native import main as download_lib # noqa + + +class CustomBuildHook(BuildHookInterface): + def initialize(self, version, build_data): + # See https://hatch.pypa.io/latest/plugins/builder/wheel/#build-data + + if self.target_name == "wheel": + + # Prepare + check_git_status() + remove_all_libs() + + # State that the wheel is not cross-platform + build_data["pure_python"] = False + + # Download and set tag + platform_info = os.getenv("WGPU_BUILD_PLATFORM_INFO") + if platform_info: + # A cross-platform build + wgpu_native_tag, wheel_tag = platform_info.split() + opsys, arch = wgpu_native_tag.split("_", 1) + build_data["tag"] = "py3-none-" + wheel_tag + download_lib(None, opsys, arch) + else: + # A build for this platform, e.g. ``pip install -e .`` + build_data["infer_tag"] = True + download_lib() + + # Make sure that the download did not bump the wgpu-native version + check_git_status() + + +def check_git_status(): + git_status = getoutput("git status --porcelain") + # print("Git status:\n" + git_status) + for line in git_status.splitlines(): + assert not line.strip().startswith("M wgpu/"), "Git has open changes!" + + +def remove_all_libs(): + dir = "wgpu/resources/" + for fname in os.listdir(dir): + if fname.endswith((".so", ".dll", ".dylib")): + os.remove(dir + fname) + print(f"Removed {fname} from resource dir") From acee4db87926b9992d779e061c67e4860d7f16a9 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Fri, 20 Sep 2024 15:49:18 +0200 Subject: [PATCH 02/21] update (simplify) cd.yml --- .github/workflows/cd.yml | 130 +++++++++++++-------------------------- 1 file changed, 42 insertions(+), 88 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index dac18a16..01e5c2cd 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -24,118 +24,72 @@ on: jobs: - release-builds: - name: Build wheel for ${{ matrix.platform }} ${{ matrix.arch }} - timeout-minutes: 10 + build-wheels: + name: Build all wheels + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + - name: Install dev dependencies + run: | + python -m pip install --upgrade pip build + - name: Build wheels (and sdist) + run: python tools/build_all_wheels.py + - name: Twine check + run: | + twine check dist/* + - name: Upload distributions + uses: actions/upload-artifact@v4 + with: + path: dist + name: dist + + test-wheels: + name: Test wheel for ${{ matrix.name }} + needs: [build-wheels] runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: - - platform: windows - arch: AMD64 - os: windows-latest - testable: true - - platform: windows - arch: ARM64 - os: windows-latest - - platform: windows - arch: x86 + - name: windows amd64 os: windows-latest - - platform: macos - arch: arm64 + - name: macos arm64 os: macos-latest - testable: true - - platform: macos - arch: x86_64 + - name: macos x86_64 os: macos-13 # last Intel MacOS - cibw_version: '==2.16' # delocation does not work for later versions - - platform: linux - arch: x86_64 - os: ubuntu-latest - testable: true - - platform: linux - arch: aarch64 + - name: linux amd64 os: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set up QEMU - if: matrix.platform == 'linux' && matrix.arch == 'aarch64' - uses: docker/setup-qemu-action@v3 - with: - platforms: arm64 - - name: Install dev dependencies - run: | - python -m pip install --upgrade pip wheel setuptools twine cibuildwheel${{ matrix.cibw_version}} - - name: Build wheels - run: python -m cibuildwheel --output-dir dist - env: - CIBW_PLATFORM: ${{ matrix.platform }} - CIBW_ARCHS: ${{ matrix.arch }} - - name: Twine check - run: | - twine check dist/* - - name: Test wheel - if: matrix.testable - shell: bash - run: | - rm -rf ./wgpu - filename=$(ls dist/*.whl) - pip install $filename - pushd $HOME - python -c 'import wgpu.backends.wgpu_native; print(wgpu.backends.wgpu_native._ffi.lib_path)' - popd - pip uninstall -y wgpu - git reset --hard HEAD - - name: Upload distributions - uses: actions/upload-artifact@v4 - with: - path: dist - name: ${{ matrix.platform }}-${{ matrix.arch }}-build - - - sdist-build: - name: Build sdist - timeout-minutes: 5 - runs-on: ubuntu-latest - strategy: - fail-fast: false - steps: - - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.12' - - name: Install dev dependencies - run: | - python -m pip install --upgrade pip - pip install -U -r dev-requirements.txt - - name: Create source distribution - run: | - python setup.py sdist - - name: Test sdist + - name: Download assets + uses: actions/download-artifact@v4 + with: + path: dist + - name: Install and test wheel shell: bash run: | rm -rf ./wgpu - filename=$(ls dist/*.tar.gz) - pip install $filename - # don't run tests, we just want to know if the sdist can be installed + pip install --find-links dist wgpu + pip uninstall wgpu + pip install --force-reinstall --no-deps --no-index --find-links dist wgpu + pushd $HOME + python -c 'import wgpu.backends.wgpu_native; print(wgpu.backends.wgpu_native._ffi.lib_path)' + popd pip uninstall -y wgpu git reset --hard HEAD - - name: Twine check - run: | - twine check dist/* - - name: Upload distributions - uses: actions/upload-artifact@v4 - with: - path: dist - name: sdist-build - publish: name: Publish to Github and Pypi runs-on: ubuntu-latest - needs: [release-builds, sdist-build] + needs: [builld-wheels, test-wheels] if: success() && startsWith(github.ref, 'refs/tags/v') steps: - uses: actions/checkout@v4 From e322b638d5cf0740b475433cb11601474a497bd7 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Fri, 20 Sep 2024 15:55:39 +0200 Subject: [PATCH 03/21] fix typo --- .github/workflows/cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 01e5c2cd..883286fb 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -89,7 +89,7 @@ jobs: publish: name: Publish to Github and Pypi runs-on: ubuntu-latest - needs: [builld-wheels, test-wheels] + needs: [build-wheels, test-wheels] if: success() && startsWith(github.ref, 'refs/tags/v') steps: - uses: actions/checkout@v4 From 03de529bda4422dc9a3462b07b36cf3ecc16cad5 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Fri, 20 Sep 2024 15:56:21 +0200 Subject: [PATCH 04/21] Fix ci (I think) --- .github/workflows/ci.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3393c4b7..659f647b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,7 +69,6 @@ jobs: run: | python -m pip install --upgrade pip pip install requests - python download-wgpu-native.py pip uninstall -q -y requests pip install -e . - name: Test imports @@ -122,7 +121,6 @@ jobs: run: | python -m pip install --upgrade pip pip install -U -r dev-requirements.txt - python download-wgpu-native.py pip install -e . - name: Test examples env: @@ -146,7 +144,6 @@ jobs: run: | python -m pip install --upgrade pip pip install -U requests numpy pytest - python download-wgpu-native.py pip install -e . pip install psutil glfw pyinstaller>=4.9 - name: Test PyInstaller @@ -195,7 +192,6 @@ jobs: run: | python -m pip install --upgrade pip pip install -U -r dev-requirements.txt - python download-wgpu-native.py pip install -e . - name: Unit tests run: | From ce9e2b316f6a56c21def2cd8b431bd1a494f29d0 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Fri, 20 Sep 2024 15:58:35 +0200 Subject: [PATCH 05/21] deps --- .github/workflows/cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 883286fb..ed4a7ddc 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -35,7 +35,7 @@ jobs: python-version: '3.12' - name: Install dev dependencies run: | - python -m pip install --upgrade pip build + python -m pip install --upgrade pip build, requests - name: Build wheels (and sdist) run: python tools/build_all_wheels.py - name: Twine check From 0a570c53c27a84c8667dbe5006d78d11f254ebd1 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Fri, 20 Sep 2024 15:59:41 +0200 Subject: [PATCH 06/21] typo --- .github/workflows/cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index ed4a7ddc..8adecd42 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -35,7 +35,7 @@ jobs: python-version: '3.12' - name: Install dev dependencies run: | - python -m pip install --upgrade pip build, requests + python -m pip install --upgrade pip build requests - name: Build wheels (and sdist) run: python tools/build_all_wheels.py - name: Twine check From 878b3aaabd5cb7626c4311adf76d999e74d09d84 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Fri, 20 Sep 2024 16:06:21 +0200 Subject: [PATCH 07/21] not sure why but need preinstalled here --- .github/workflows/cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 8adecd42..ec1fab2f 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -35,7 +35,7 @@ jobs: python-version: '3.12' - name: Install dev dependencies run: | - python -m pip install --upgrade pip build requests + python -m pip install --upgrade pip build hatchling requests - name: Build wheels (and sdist) run: python tools/build_all_wheels.py - name: Twine check From 5d5f7d32c9c94f5320730d077ecb817367731cbf Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Fri, 20 Sep 2024 16:07:38 +0200 Subject: [PATCH 08/21] black --- tests/test_set_override.py | 2 +- tools/build_all_wheels.py | 20 +++++++++++++++----- wgpu/gui/offscreen.py | 2 +- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/tests/test_set_override.py b/tests/test_set_override.py index 10ae6799..1f3b8251 100644 --- a/tests/test_set_override.py +++ b/tests/test_set_override.py @@ -132,7 +132,7 @@ def run_test( compute: bool = False, vertex_constants=None, fragment_constants=None, - compute_constants=None + compute_constants=None, ): assert render + compute == 1 device = self.device diff --git a/tools/build_all_wheels.py b/tools/build_all_wheels.py index ce1d5371..f8bc2cf3 100644 --- a/tools/build_all_wheels.py +++ b/tools/build_all_wheels.py @@ -81,13 +81,19 @@ found_files = os.listdir("dist") found_wheels = [fname for fname in found_files if fname.endswith(".whl")] found_tags = {fname.split("none-")[1].split(".")[0] for fname in found_wheels} -assert found_tags == all_tags, f"Found tags does not match expected tags: {found_tags}\n{all_tags}" +assert ( + found_tags == all_tags +), f"Found tags does not match expected tags: {found_tags}\n{all_tags}" found_others = list(set(found_files) - set(found_wheels)) -assert len(found_others) == 1 and found_others[0].endswith(".tar.gz"), f"Found unexpected files: {found_others}" +assert len(found_others) == 1 and found_others[0].endswith( + ".tar.gz" +), f"Found unexpected files: {found_others}" for archive_name in found_wheels: - assert "-any-" not in archive_name, f"There should not be an 'any' wheel: {archive_name}" + assert ( + "-any-" not in archive_name + ), f"There should not be an 'any' wheel: {archive_name}" # --- Report and check content of archives @@ -115,9 +121,13 @@ hash = hashlib.sha256(bb).hexdigest() lib_hashes.append(hash) print(f" - {fname} ({len(bb)/1e6:0.2f} MB)\n {hash}") - assert len(lib_hashes) == 1, f"Expected 1 lib per wheel, got {len(lib_hashes)} in {archive_name}" + assert ( + len(lib_hashes) == 1 + ), f"Expected 1 lib per wheel, got {len(lib_hashes)} in {archive_name}" hash = lib_hashes[0] - assert hash not in hash_to_file, f"Same lib found in {hash_to_file[hash]} and archive_name" + assert ( + hash not in hash_to_file + ), f"Same lib found in {hash_to_file[hash]} and archive_name" hash_to_file[hash] = archive_name # Meta check diff --git a/wgpu/gui/offscreen.py b/wgpu/gui/offscreen.py index 95b6e373..bbaa7be0 100644 --- a/wgpu/gui/offscreen.py +++ b/wgpu/gui/offscreen.py @@ -27,7 +27,7 @@ def configure( usage=flags.TextureUsage.RENDER_ATTACHMENT | flags.TextureUsage.COPY_SRC, view_formats=[], color_space="srgb", - alpha_mode="opaque" + alpha_mode="opaque", ): if format is None: format = self.get_preferred_format(device.adapter) From e1ef37c6c18c275ea064aded30e261470a463487 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Fri, 20 Sep 2024 16:09:09 +0200 Subject: [PATCH 09/21] twine too --- .github/workflows/cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index ec1fab2f..91e6d822 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -35,7 +35,7 @@ jobs: python-version: '3.12' - name: Install dev dependencies run: | - python -m pip install --upgrade pip build hatchling requests + python -m pip install --upgrade pip build hatchling requests twine - name: Build wheels (and sdist) run: python tools/build_all_wheels.py - name: Twine check From d44cede21ba0f86517a63f6f45ec3cf1d10fc273 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Fri, 20 Sep 2024 16:12:13 +0200 Subject: [PATCH 10/21] no input --- .github/workflows/cd.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 91e6d822..066a91ca 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -77,9 +77,9 @@ jobs: shell: bash run: | rm -rf ./wgpu - pip install --find-links dist wgpu - pip uninstall wgpu - pip install --force-reinstall --no-deps --no-index --find-links dist wgpu + pip install --no-input --find-links dist wgpu + pip uninstall --no-input wgpu + pip install --no-input --force-reinstall --no-deps --no-index --find-links dist wgpu pushd $HOME python -c 'import wgpu.backends.wgpu_native; print(wgpu.backends.wgpu_native._ffi.lib_path)' popd From edf264c144ffa88f42c2d76bf39021973210adbd Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Fri, 20 Sep 2024 16:14:19 +0200 Subject: [PATCH 11/21] try --- .github/workflows/cd.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 066a91ca..4a4ec1c8 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -77,9 +77,8 @@ jobs: shell: bash run: | rm -rf ./wgpu - pip install --no-input --find-links dist wgpu - pip uninstall --no-input wgpu - pip install --no-input --force-reinstall --no-deps --no-index --find-links dist wgpu + pip install --find-links dist wgpu + pip install --force-reinstall --no-deps --no-index --find-links dist wgpu pushd $HOME python -c 'import wgpu.backends.wgpu_native; print(wgpu.backends.wgpu_native._ffi.lib_path)' popd From 8c3db4ccde16d8a21b6c4b67bd577844b0cc5dc5 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Sat, 21 Sep 2024 23:32:41 +0200 Subject: [PATCH 12/21] remove dev reqs --- .github/workflows/cd.yml | 33 ++++++++++++++++++++----------- .github/workflows/ci.yml | 16 ++++++--------- .github/workflows/screenshots.yml | 6 ++---- README.md | 11 ++++------- dev-requirements.txt | 18 ----------------- pyproject.toml | 22 ++++++++++++++++----- 6 files changed, 50 insertions(+), 56 deletions(-) delete mode 100644 dev-requirements.txt diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 4a4ec1c8..775db3c8 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -1,15 +1,8 @@ # Github Actions script to produce binary wheels. # -# Note that a lot of the cibuildwheel config is in pyproject.toml. -# -# We perform one build for each wheel that we generate, i.e. one per architecture. -# In download_wgpu_native.py, we detect CIBW_PLATFORM and CIBW_ARCHS to determine -# the required binary from wgpu-native. -# -# If https://github.com/pypa/cibuildwheel/issues/944 gets implemented, we can build more wheels per build. -# -# Also includes the sdist build that does not include a binary. - +# * One build to create all wheels (cross-platform). +# * One build (with matrix) test the wheels on a selection of platforms. +# * One build to publish the wheels on GitHub and Pypi. name: CD @@ -35,7 +28,8 @@ jobs: python-version: '3.12' - name: Install dev dependencies run: | - python -m pip install --upgrade pip build hatchling requests twine + python -m pip install --upgrade pip + pip install -U -e .[build] - name: Build wheels (and sdist) run: python tools/build_all_wheels.py - name: Twine check @@ -45,7 +39,7 @@ jobs: uses: actions/upload-artifact@v4 with: path: dist - name: dist + name: all_wheels test-wheels: name: Test wheel for ${{ matrix.name }} @@ -73,10 +67,15 @@ jobs: uses: actions/download-artifact@v4 with: path: dist + - name: Flatten dist dir + run: | + find dist -mindepth 2 -type f -exec mv -f '{}' dist/ ';' + rm -rf dist/*/ - name: Install and test wheel shell: bash run: | rm -rf ./wgpu + # Install 'normally' to install deps, then force the install from dist-folder and nothing else pip install --find-links dist wgpu pip install --force-reinstall --no-deps --no-index --find-links dist wgpu pushd $HOME @@ -84,6 +83,16 @@ jobs: popd pip uninstall -y wgpu git reset --hard HEAD + - name: Install from sdist + shell: bash + run: | + rm -rf ./wgpu + rm -rf ./dist/*.whl + # Install 'normally' to install deps, then force the install from dist-folder and nothing else + pip install --find-links dist wgpu + pip install --force-reinstall --no-deps --no-index --find-links dist wgpu + pip uninstall -y wgpu + git reset --hard HEAD publish: name: Publish to Github and Pypi diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 659f647b..012900a2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: - name: Install dev dependencies run: | python -m pip install --upgrade pip - pip install -U black flake8 flake8-black pep8-naming + pip install -U -e .[lint] - name: Flake8 run: | flake8 . @@ -48,7 +48,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -U pytest numpy black cffi + pip install -U -e .[codegen] - name: Test codegen run: | pytest -v codegen @@ -95,7 +95,7 @@ jobs: - name: Install dev dependencies run: | python -m pip install --upgrade pip - pip install -U -r dev-requirements.txt + pip install -U -e .[docs] - name: Build docs run: | cd docs @@ -120,8 +120,7 @@ jobs: - name: Install dev dependencies run: | python -m pip install --upgrade pip - pip install -U -r dev-requirements.txt - pip install -e . + pip install -U -e .[examples] - name: Test examples env: EXPECT_LAVAPIPE: true @@ -143,9 +142,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -U requests numpy pytest - pip install -e . - pip install psutil glfw pyinstaller>=4.9 + pip install -U -e .[pyinstaller] - name: Test PyInstaller run: | pyinstaller --version @@ -191,8 +188,7 @@ jobs: - name: Install dev dependencies run: | python -m pip install --upgrade pip - pip install -U -r dev-requirements.txt - pip install -e . + pip install -U -e .[tests] - name: Unit tests run: | pytest -v tests diff --git a/.github/workflows/screenshots.yml b/.github/workflows/screenshots.yml index bf32d297..048c7198 100644 --- a/.github/workflows/screenshots.yml +++ b/.github/workflows/screenshots.yml @@ -8,7 +8,7 @@ on: jobs: screenshots: - name: Regenerate + name: Regenerate screenshot timeout-minutes: 10 runs-on: 'ubuntu-latest' steps: @@ -26,9 +26,7 @@ jobs: - name: Install dev dependencies run: | python -m pip install --upgrade pip - pip install -U -r dev-requirements.txt - python download-wgpu-native.py - pip install -e . + pip install -U -e .[screenshots] - name: Regenerate screenshots run: | pytest -v --regenerate-screenshots -k test_examples_screenshots examples diff --git a/README.md b/README.md index c807850d..927d61b3 100644 --- a/README.md +++ b/README.md @@ -115,18 +115,15 @@ This code is distributed under the 2-clause BSD license. ## Developers * Clone the repo. -* Install devtools using `pip install -r dev-requirements.txt` (you can replace +* Install devtools using `pip install -e .[dev]` (you can replace `pip` with `pipenv` to install to a virtualenv). -* Install wgpu-py in editable mode by running `pip install -e .`, this will also - install runtime dependencies as needed. -* Run `python download-wgpu-native.py` to download the upstream wgpu-native - binaries. +* Using `pip install -e .` will also download the upstream wgpu-native + binaries. You can use `python tools/download_wgpu_native.py` when needed. * Or alternatively point the `WGPU_LIB_PATH` environment variable to a custom - build. + build of `wgpu-native`. * Use `black .` to apply autoformatting. * Use `flake8 .` to check for flake errors. * Use `pytest .` to run the tests. -* Use `pip wheel --no-deps .` to build a wheel. ### Updating to a later version of WebGPU or wgpu-native diff --git a/dev-requirements.txt b/dev-requirements.txt deleted file mode 100644 index 417f50d6..00000000 --- a/dev-requirements.txt +++ /dev/null @@ -1,18 +0,0 @@ -# For unit tests, linting, etc. -requests -numpy -pytest -black -flake8 -flake8-black -pep8-naming -sphinx -imageio -pyinstaller -psutil - -# Building wheels -setuptools -twine -auditwheel; sys_platform == 'linux' -cibuildwheel diff --git a/pyproject.toml b/pyproject.toml index 0a632496..4f0010a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,10 +12,19 @@ requires-python = ">= 3.8" dependencies = ["cffi>=1.15.0", "rubicon-objc>=0.4.1; sys_platform == 'darwin'"] [project.optional-dependencies] +# For users jupyter = ["jupyter_rfb>=0.4.2"] glfw = ["glfw>=1.9"] -docs = ["sphinx>7.2", "sphinx_rtd_theme"] imgui = ["imgui-bundle>=1.2.1"] +# For devs / ci +build = ["build", "hatchling", "requests", "twine"] +codegen = ["pytest", "numpy", "black", "cffi"] +lint = ["black", "flake8", "flake8-black", "pep8-naming"] +docs = ["sphinx>7.2", "sphinx_rtd_theme"] +tests = ["numpy", "pytest", "psutil"] +examples = ["wgpu[tests]", "imageio"] +pyinstaller = ["wgpu[tests]", "glfw", "pyinstaller>=4.9"] +dev = ["wgpu[build,codegen,lint,docs,tests,examples]"] [project.entry-points."pyinstaller40"] hook-dirs = "wgpu.__pyinstaller:get_hook_dirs" @@ -28,10 +37,11 @@ Repository = "https://github.com/pygfx/wgpu-py" # ===== Building # -# We use the hatch build backend, because its modern, with pretty good defaults, -# and configurable. Flit is really nice, but a bit too simple for what we need -# with our binary libs. We use a hatch build hook to install the correct -# wgpu-native lib right before the wheel is build. See the tools dir. +# There are a variety of build backends, e.g.: +# * setuptools: need this if you have a setup.py +# * flit: really nice for pure Python libs. +# * hatchling: modern, with pretty good defaults, and configurable. Just right for us! +# * mesonpy: numpy, scikit-image, et al. use this because they compile stuff. [build-system] requires = ["requests", "hatchling"] @@ -48,6 +58,8 @@ exclude = ["*.so", "*.dll", "*.dylib"] packages = ["wgpu"] artifacts = ["*.so", "*.dll", "*.dylib"] +# We use a hatch build hook to install the correct wgpu-native lib right before +# the wheel is build, and to allow cross-platform builds. See the tools dir. [tool.hatch.build.targets.wheel.hooks.custom] path = "tools/hatch_build.py" From 6a0c5a2215fd50f80709446f134f9cba596da26c Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Sat, 21 Sep 2024 23:52:16 +0200 Subject: [PATCH 13/21] Fix ci/cd --- .github/workflows/cd.yml | 3 +-- .github/workflows/ci.yml | 4 ++-- .github/workflows/screenshots.yml | 2 +- pyproject.toml | 7 +++---- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 775db3c8..0a4c3203 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -88,8 +88,7 @@ jobs: run: | rm -rf ./wgpu rm -rf ./dist/*.whl - # Install 'normally' to install deps, then force the install from dist-folder and nothing else - pip install --find-links dist wgpu + pip install requests pip install --force-reinstall --no-deps --no-index --find-links dist wgpu pip uninstall -y wgpu git reset --hard HEAD diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 012900a2..39ef1462 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -120,7 +120,7 @@ jobs: - name: Install dev dependencies run: | python -m pip install --upgrade pip - pip install -U -e .[examples] + pip install -U -e .[tests,examples] - name: Test examples env: EXPECT_LAVAPIPE: true @@ -142,7 +142,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -U -e .[pyinstaller] + pip install -U -e .[tests] glfw pyinstaller - name: Test PyInstaller run: | pyinstaller --version diff --git a/.github/workflows/screenshots.yml b/.github/workflows/screenshots.yml index 048c7198..3a8815d4 100644 --- a/.github/workflows/screenshots.yml +++ b/.github/workflows/screenshots.yml @@ -26,7 +26,7 @@ jobs: - name: Install dev dependencies run: | python -m pip install --upgrade pip - pip install -U -e .[screenshots] + pip install -U -e .[tests,examples] - name: Regenerate screenshots run: | pytest -v --regenerate-screenshots -k test_examples_screenshots examples diff --git a/pyproject.toml b/pyproject.toml index 4f0010a9..16477b1d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,12 +18,11 @@ glfw = ["glfw>=1.9"] imgui = ["imgui-bundle>=1.2.1"] # For devs / ci build = ["build", "hatchling", "requests", "twine"] -codegen = ["pytest", "numpy", "black", "cffi"] +codegen = ["pytest", "numpy", "black"] lint = ["black", "flake8", "flake8-black", "pep8-naming"] docs = ["sphinx>7.2", "sphinx_rtd_theme"] -tests = ["numpy", "pytest", "psutil"] -examples = ["wgpu[tests]", "imageio"] -pyinstaller = ["wgpu[tests]", "glfw", "pyinstaller>=4.9"] +tests = ["numpy", "pytest", "psutil", "imageio"] +examples = [] dev = ["wgpu[build,codegen,lint,docs,tests,examples]"] [project.entry-points."pyinstaller40"] From 81f905c9cacd9d8461c3f168d7140377350d42d0 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Sat, 21 Sep 2024 23:56:18 +0200 Subject: [PATCH 14/21] do korijns earlier trick --- .github/workflows/cd.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 0a4c3203..ee056acf 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -88,8 +88,8 @@ jobs: run: | rm -rf ./wgpu rm -rf ./dist/*.whl - pip install requests - pip install --force-reinstall --no-deps --no-index --find-links dist wgpu + filename=$(ls dist/*.tar.gz) + pip install $filename pip uninstall -y wgpu git reset --hard HEAD From 6220bdaf608fbf5c2c81b37aebd7c363e2bada71 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Mon, 23 Sep 2024 12:16:14 +0200 Subject: [PATCH 15/21] tweak in error msg --- wgpu/backends/wgpu_native/_ffi.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/wgpu/backends/wgpu_native/_ffi.py b/wgpu/backends/wgpu_native/_ffi.py index 641dd5ad..e563f6be 100644 --- a/wgpu/backends/wgpu_native/_ffi.py +++ b/wgpu/backends/wgpu_native/_ffi.py @@ -84,10 +84,14 @@ def get_wgpu_lib_path(): # Note that this can be a false positive, e.g. ARM linux. embedded_path = get_resource_filename(lib_filename) if not os.path.isfile(embedded_path): # no-cover - download_hint = _maybe_get_hint_on_download_script() - pip_hint = _maybe_get_pip_hint() + env_hint = "You can set the WGPU_LIB_PATH env var to the location of the wgpu-native library." + download_hint = _maybe_get_hint_on_download_script().strip() + pip_hint = _maybe_get_pip_hint().strip() + hints = [pip_hint, download_hint, env_hint] + hints = "\n".join([hint for hint in hints if hint]) + hints = "\n" + hints if hints else "" raise RuntimeError( - f"Could not find WGPU library in {embedded_path}. {download_hint} {pip_hint}" + f"Could not find WGPU library in {embedded_path}.{hints}" ) else: return embedded_path @@ -95,13 +99,15 @@ def get_wgpu_lib_path(): def _maybe_get_hint_on_download_script(): root_dir = os.path.join(get_resource_filename(""), "..", "..") - filename = os.path.abspath(os.path.join(root_dir, "download-wgpu-native.py")) + filename = os.path.abspath( + os.path.join(root_dir, "tools", "download_wgpu_native.py") + ) uses_repo = os.path.isfile(filename) uses_custom_lib = os.getenv("WGPU_LIB_PATH", "").strip() if uses_repo and not uses_custom_lib: - return "You may need to run download-wgpu-native.py (in the root of the repo)." + return "You may need to run tools/download_wgpu_native.py." return "" From fb840b65b9b039e1dfe03cb3bafa2a2b1aa3dab4 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Mon, 23 Sep 2024 12:32:45 +0200 Subject: [PATCH 16/21] Fix sdist --- pyproject.toml | 1 + tools/hatch_build.py | 25 +++++++++++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 16477b1d..292bd8e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,6 +52,7 @@ path = "wgpu/__init__.py" [tool.hatch.build.targets.sdist] packages = ["wgpu"] exclude = ["*.so", "*.dll", "*.dylib"] +force-include = { "tools" = "tools" } [tool.hatch.build.targets.wheel] packages = ["wgpu"] diff --git a/tools/hatch_build.py b/tools/hatch_build.py index 15fd5224..a1759c0f 100644 --- a/tools/hatch_build.py +++ b/tools/hatch_build.py @@ -4,6 +4,7 @@ * Set wheel to being platform-specific (not pure Python). * Download the wgpu-native library before creating the wheel. * Support cross-platform wheel building with a custom env var. +* Note that for sdist we go into pure-Python mode. """ # Note on an alternative approach: @@ -24,11 +25,12 @@ import os import sys -from subprocess import getoutput +from subprocess import run, PIPE, STDOUT from hatchling.builders.hooks.plugin.interface import BuildHookInterface -sys.path.insert(0, os.path.abspath(os.path.join(__file__, ".."))) +root_dir = os.path.abspath(os.path.join(__file__, "..", "..")) +sys.path.insert(0, os.path.join(root_dir, "tools")) from download_wgpu_native import main as download_lib # noqa @@ -37,7 +39,11 @@ class CustomBuildHook(BuildHookInterface): def initialize(self, version, build_data): # See https://hatch.pypa.io/latest/plugins/builder/wheel/#build-data - if self.target_name == "wheel": + # We only do our thing when this is a wheel build from the repo. + # If this is an sdist build, or a wheel build from an sdist, + # we go pure-Python mode, and expect the user to set WGPU_LIB_PATH. + + if self.target_name == "wheel" and is_git_repo(): # Prepare check_git_status() @@ -63,16 +69,23 @@ def initialize(self, version, build_data): check_git_status() +def is_git_repo(): + return os.path.isdir(os.path.join(root_dir, ".git")) + + def check_git_status(): - git_status = getoutput("git status --porcelain") + p = run( + "git status --porcelain", shell=True, cwd=root_dir, stdout=PIPE, stderr=STDOUT + ) + git_status = p.stdout.decode(errors="ignore") # print("Git status:\n" + git_status) for line in git_status.splitlines(): assert not line.strip().startswith("M wgpu/"), "Git has open changes!" def remove_all_libs(): - dir = "wgpu/resources/" + dir = os.path.join(root_dir, "wgpu", "resources") for fname in os.listdir(dir): if fname.endswith((".so", ".dll", ".dylib")): - os.remove(dir + fname) + os.remove(os.path.join(dir, fname)) print(f"Removed {fname} from resource dir") From e3b009668fc4abb8d4356875783600f00d5602d1 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Mon, 23 Sep 2024 12:32:57 +0200 Subject: [PATCH 17/21] Update references to download script --- .github/ISSUE_TEMPLATE/update-wgpu.md | 2 +- codegen/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/update-wgpu.md b/.github/ISSUE_TEMPLATE/update-wgpu.md index e6fb7314..fa7344e2 100644 --- a/.github/ISSUE_TEMPLATE/update-wgpu.md +++ b/.github/ISSUE_TEMPLATE/update-wgpu.md @@ -47,7 +47,7 @@ For context, see the [codegen readme](https://github.com/pygfx/wgpu-py/blob/main *The lines below can be copied in the PR's top post.* -* [ ] Run `python download-wgpu-native.py --version xx` to download the latest webgpu.h and DLL. +* [ ] Run `python tools/download_wgpu_native.py --version xx` to download the latest webgpu.h and DLL. * [ ] Run `python codegen` to apply the automatic patches to the code. * [ ] It may be necessary to tweak the `hparser.py` to adjust to new formatting. * [ ] Diff the report for new differences to take into account. diff --git a/codegen/README.md b/codegen/README.md index fdeebb6d..d997f57f 100644 --- a/codegen/README.md +++ b/codegen/README.md @@ -136,7 +136,7 @@ The majority of work in the wgpu-native backend is the conversion of Python dic ### The update process -* Download the latest `webgpu.h` and DLL using `python download-wgpu-native.py --version xx` +* Download the latest `webgpu.h` and DLL using `python tools/download_wgpu_native.py --version xx` * Run `python codegen` to apply the automatic patches to the code. * It may be necessary to tweak the `hparser.py` to adjust to new formatting. * Diff the report for new differences to take into account. From bdb22ab96565fdc5fb1127d2f8ae4ebe1f84a3b0 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Mon, 23 Sep 2024 12:41:20 +0200 Subject: [PATCH 18/21] black --- wgpu/backends/wgpu_native/_ffi.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/wgpu/backends/wgpu_native/_ffi.py b/wgpu/backends/wgpu_native/_ffi.py index e563f6be..b174aa32 100644 --- a/wgpu/backends/wgpu_native/_ffi.py +++ b/wgpu/backends/wgpu_native/_ffi.py @@ -90,9 +90,7 @@ def get_wgpu_lib_path(): hints = [pip_hint, download_hint, env_hint] hints = "\n".join([hint for hint in hints if hint]) hints = "\n" + hints if hints else "" - raise RuntimeError( - f"Could not find WGPU library in {embedded_path}.{hints}" - ) + raise RuntimeError(f"Could not find WGPU library in {embedded_path}.{hints}") else: return embedded_path From 67f10443b5ff2b8a17d8e49179f186c863ca9836 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Mon, 23 Sep 2024 12:43:41 +0200 Subject: [PATCH 19/21] fix cd --- .github/workflows/cd.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index ee056acf..1bb39c63 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -68,6 +68,7 @@ jobs: with: path: dist - name: Flatten dist dir + shell: bash run: | find dist -mindepth 2 -type f -exec mv -f '{}' dist/ ';' rm -rf dist/*/ @@ -88,6 +89,7 @@ jobs: run: | rm -rf ./wgpu rm -rf ./dist/*.whl + # Install sdist, but no test, because there is no wgpu-native lib now. filename=$(ls dist/*.tar.gz) pip install $filename pip uninstall -y wgpu From a173d125eaf3838d8c868ceb758f5e7ecedff381 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Mon, 23 Sep 2024 12:53:03 +0200 Subject: [PATCH 20/21] Move coverage config to pyproject.toml --- pyproject.toml | 8 ++++++++ setup.cfg | 12 ------------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 292bd8e4..e770d266 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,3 +64,11 @@ artifacts = ["*.so", "*.dll", "*.dylib"] path = "tools/hatch_build.py" # ===== Tooling + +[tool.coverage.report] +exclude_also = [ + # Have to re-enable the standard pragma, plus a less-ugly flavor + "pragma: no cover", + "no-cover", + "raise NotImplementedError", +] diff --git a/setup.cfg b/setup.cfg index 4cbd3ac4..67c6424a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -22,15 +22,3 @@ per-file-ignores = tests/test_wgpu_native_compute_tex.py : F821,F722 examples/*.py: F821,F722 examples/triangle_qt*.py: E402 - - -[coverage:report] - -exclude_lines = - # Remember that these are reg exp - - # Have to re-enable the standard pragma, plus a less-ugly flavor - pragma: no cover - no-cover - - raise NotImplementedError From 34a650e0b73580c9a93d157689156e9d9bbdce27 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Mon, 23 Sep 2024 14:45:56 +0200 Subject: [PATCH 21/21] apply review suggestions --- .github/workflows/ci.yml | 2 -- README.md | 9 ++++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 39ef1462..ecd09af8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,8 +68,6 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install requests - pip uninstall -q -y requests pip install -e . - name: Test imports env: diff --git a/README.md b/README.md index 927d61b3..be33400e 100644 --- a/README.md +++ b/README.md @@ -115,12 +115,11 @@ This code is distributed under the 2-clause BSD license. ## Developers * Clone the repo. -* Install devtools using `pip install -e .[dev]` (you can replace - `pip` with `pipenv` to install to a virtualenv). +* Install devtools using `pip install -e .[dev]`. * Using `pip install -e .` will also download the upstream wgpu-native - binaries. You can use `python tools/download_wgpu_native.py` when needed. - * Or alternatively point the `WGPU_LIB_PATH` environment variable to a custom - build of `wgpu-native`. + binaries. + * You can use `python tools/download_wgpu_native.py` when needed. + * Or point the `WGPU_LIB_PATH` environment variable to a custom build of `wgpu-native`. * Use `black .` to apply autoformatting. * Use `flake8 .` to check for flake errors. * Use `pytest .` to run the tests.