diff --git a/.github/workflows/branchbuild.yml b/.github/workflows/branchbuild.yml deleted file mode 100644 index 765d2dc6..00000000 --- a/.github/workflows/branchbuild.yml +++ /dev/null @@ -1,223 +0,0 @@ -name: Test Build - -on: - push: - paths-ignore: - - 'docs/**' - pull_request: - -jobs: - no_cython_install: - name: "Test install with generated cython files" - runs-on: "ubuntu-latest" - - steps: - - uses: "actions/checkout@v6" - with: - submodules: 'true' - - - uses: "actions/setup-python@v6" - with: - python-version: "3.13" - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install Cython - - - name: Generate cython - run: | - chmod +x ./tools/generate_cython.sh - ./tools/generate_cython.sh - - - name: build - run: | - git apply ./tools/sdist.patch - pip install build; python -m build --sdist - # test whether tarball contains all files required for compiling - pip install dist/rapidfuzz-*.tar.gz -v - - - name: Install testing dependencies - run: | - pip install pytest hypothesis pandas - - - name: Test with pytest - run: | - pytest tests - - system_install: - name: "Test installation using system supplied libs" - runs-on: "ubuntu-latest" - - steps: - - uses: "actions/checkout@v6" - - - uses: "actions/setup-python@v6" - with: - python-version: "3.13" - - - name: Install rapidfuzz-cpp - run: | - git clone https://github.com/rapidfuzz/rapidfuzz-cpp.git - cd rapidfuzz-cpp - git checkout v3.3.3 - mkdir build && cd build - cmake .. -DCMAKE_BUILD_TYPE=Release - cmake --build . - sudo cmake --build . --target install - - - name: Install taskflow - run: | - git clone https://github.com/taskflow/taskflow.git - cd taskflow - git checkout v3.3.0 - mkdir build && cd build - cmake .. -DTF_BUILD_TESTS=0 -DTF_BUILD_EXAMPLES=0 - cmake --build . - sudo cmake --build . --target install - - - name: build - run: | - pip install . -v - - - name: Install testing dependencies - run: | - python -m pip install --upgrade pip - pip install pytest hypothesis pandas - - - name: Test with pytest - run: | - pytest tests - - system_install2: - name: "Test installation using newest version of Taskflow" - runs-on: "ubuntu-latest" - - steps: - - uses: "actions/checkout@v6" - - - uses: "actions/setup-python@v6" - with: - python-version: "3.13" - - - name: Install rapidfuzz-cpp - run: | - git clone https://github.com/rapidfuzz/rapidfuzz-cpp.git - cd rapidfuzz-cpp - git checkout v3.3.3 - mkdir build && cd build - cmake .. -DCMAKE_BUILD_TYPE=Release - cmake --build . - sudo cmake --build . --target install - - - name: Install taskflow - run: | - git clone https://github.com/taskflow/taskflow.git - cd taskflow - git checkout v3.6.0 - mkdir build && cd build - cmake .. -DTF_BUILD_TESTS=0 -DTF_BUILD_EXAMPLES=0 - cmake --build . - sudo cmake --build . --target install - - - name: build - run: | - pip install . -v - - - name: Install testing dependencies - run: | - python -m pip install --upgrade pip - pip install pytest hypothesis pandas - - - name: Test with pytest - run: | - pytest tests - - tests: - name: "Python ${{ matrix.python-version }}" - runs-on: ${{matrix.os}} - strategy: - fail-fast: false - matrix: - python-version: ["3.10", "3.11", "3.12", "3.13", "3.13t", "3.14", "3.14t"] - os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, windows-11-arm, macos-15-intel, macos-14] - exclude: - - python-version: "3.10" - os: windows-11-arm - - steps: - - uses: "actions/checkout@v6" - with: - submodules: 'true' - - uses: "actions/setup-python@v6" - with: - allow-prereleases: true - python-version: "${{ matrix.python-version }}" - - - name: build - run: | - pip install . -v - - - name: Install testing dependencies - run: | - python -m pip install --upgrade pip - pip install pytest hypothesis pandas mypy pyright==1.1.400 pyinstaller[hook_testing] - - - name: Test type stubs - run: | - python tools/test_scorer_typing.py - python -m mypy tools/test_process_typing.py --warn-unused-ignores - python -m pyright -p tools/pyrightconfig.json tools/test_process_typing.py - - - name: Test with pytest and backtrace in case of SegFault - if: runner.os == 'Linux' - run: | - sudo apt update && sudo apt install systemd-coredump - tools/seg_wrapper.sh pytest tests - - - name: Test with pytest - if: runner.os != 'Linux' - run: | - pytest tests - - - name: test pyinstaller packaging - run: | - python -m PyInstaller.utils.run_tests --include_only rapidfuzz. - - - name: test cx_freeze packaging - if: matrix.python-version != '3.13' && matrix.python-version != '3.13t' && matrix.python-version != '3.14' && matrix.python-version != '3.14t' - working-directory: tests/freezeTools - run: | - pip install cx_freeze - cxfreeze --script script.py --target-dir cxfreezeDist - ./cxfreezeDist/script - - # validate sse2 code on gcc - sde_tests: - name: "Test sse2 code on Linux (gcc)" - runs-on: ubuntu-latest - steps: - - uses: "actions/checkout@v6" - with: - submodules: 'true' - - - uses: "actions/checkout@v6" - with: - repository: rapidfuzz/intel-sde - path: sde - - - uses: "actions/setup-python@v6" - with: - python-version: "3.13" - - - name: build - run: | - pip install . -v - - - name: Install testing dependencies - run: | - python -m pip install --upgrade pip - pip install pytest hypothesis pandas mypy - - - name: Test on nehalem which does not support avx2 - run: ./sde/sde -nhm -- pytest tests diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml deleted file mode 100644 index fa5fff09..00000000 --- a/.github/workflows/coverage.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: Coverage of Test Build - -on: - push: - paths-ignore: - - 'docs/**' - -jobs: - tests: - name: "Generate coverage report for tests" - runs-on: ubuntu-latest - steps: - - uses: "actions/checkout@v6" - with: - submodules: 'true' - - uses: "actions/setup-python@v6" - with: - python-version: "3.11" - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install pytest hypothesis pandas coverage cython - - - name: Generate cython - run: | - chmod +x ./tools/generate_cython.sh - ./tools/generate_cython.sh --linetrace - - # for cython tests inplace installation is required - - name: build - run: CMAKE_ARGS=-DRAPIDFUZZ_ENABLE_COVERAGE=1 pip install -e . -v - - - name: Test with pytest and generate coverage info - run: | - PYTHONPATH=$(pwd)/src coverage run -m pytest tests - coverage xml - coverage report - - - uses: codecov/codecov-action@v6 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: ./coverage.xml - flags: unittests - fail_ci_if_error: true - verbose: true diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index 79689532..00000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Build docs - -on: - push: - branches: - - main - - workflow_dispatch: - -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages -permissions: - contents: read - pages: write - id-token: write - -# Allow one concurrent deployment -concurrency: - group: "pages" - cancel-in-progress: true - -jobs: - build: - runs-on: [ubuntu-latest] - steps: - - uses: actions/checkout@v6 - with: - submodules: 'true' - - uses: actions/setup-python@v6 - - - name: Install dependencies - run: | - python -m pip install -r docs/requirements.txt - python -m pip install . - - - name: Build Site - run: sphinx-build -b html docs build/html - - - name: Upload artifact - uses: actions/upload-pages-artifact@v5 - with: - path: "build/html" - - deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: build - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v5 diff --git a/.github/workflows/nanvix-ci.yml b/.github/workflows/nanvix-ci.yml new file mode 100644 index 00000000..0ce155a7 --- /dev/null +++ b/.github/workflows/nanvix-ci.yml @@ -0,0 +1,56 @@ +# Copyright(c) The Maintainers of Nanvix. +# Licensed under the MIT License. + +name: Nanvix CI + +on: + schedule: + - cron: "0 13 * * *" + push: + branches: ["nanvix/**"] + pull_request: + branches: ["nanvix/**"] + workflow_dispatch: + +permissions: + contents: write + actions: write + issues: write + pull-requests: write + +concurrency: + group: ${{ github.workflow }}-${{ github.ref_name || github.ref || 'default' }} + cancel-in-progress: true + +jobs: + ci: + if: github.event_name != 'schedule' && github.event_name != 'workflow_dispatch' + uses: nanvix/workflows/.github/workflows/nanvix-ci.yml@v2.0.0 + with: + zutil-version: "v0.8.2" + docker-image: "ghcr.io/nanvix/toolchain-python:latest" + platforms: '["microvm"]' + memory-sizes: '["256mb"]' + windows-matrix-exclude: '[]' + skip-full-test-modes: '[]' + caller-event-name: ${{ github.event_name }} + windows-test: false + secrets: + GH_TOKEN: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} + DISPATCH_TOKEN: ${{ secrets.DISPATCH_TOKEN }} + + ci-scheduled: + if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' + uses: nanvix/workflows/.github/workflows/nanvix-ci.yml@v2.0.0 + with: + zutil-version: "v0.8.2" + docker-image: "ghcr.io/nanvix/toolchain-python:latest" + platforms: '["microvm"]' + memory-sizes: '["256mb"]' + windows-matrix-exclude: '[]' + skip-full-test-modes: '[]' + caller-event-name: 'schedule' + windows-test: false + secrets: + GH_TOKEN: ${{ secrets.GH_TOKEN || secrets.GITHUB_TOKEN }} + DISPATCH_TOKEN: ${{ secrets.DISPATCH_TOKEN }} diff --git a/.github/workflows/releasebuild.yml b/.github/workflows/releasebuild.yml deleted file mode 100644 index c98be29d..00000000 --- a/.github/workflows/releasebuild.yml +++ /dev/null @@ -1,191 +0,0 @@ -name: Full Build - -on: - release: - types: - - published - - workflow_dispatch: - -jobs: - build_sdist: - name: Build source distribution - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - with: - submodules: 'true' - - uses: actions/setup-python@v6 - with: - python-version: "3.11" - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install pytest hypothesis pandas mypy Cython - - # The cythonized files allow installation from the sdist without cython - - name: Generate cython - run: | - chmod +x ./tools/generate_cython.sh - ./tools/generate_cython.sh - - - name: Build sdist - run: | - git apply ./tools/sdist.patch - pip install build - python -m build --sdist - # test whether tarball contains all files required for compiling - pip install dist/rapidfuzz-*.tar.gz -v - - #- name: Test type stubs - # # prevent import from rapidfuzz - # working-directory: .github - # run: | - # python -m mypy.stubtest rapidfuzz --ignore-missing-stub - - - name: Test with pytest and backtrace in case of SegFault - run: | - tools/seg_wrapper.sh pytest tests - - - uses: actions/upload-artifact@v7 - with: - name: artifact-sdist - path: dist/*.tar.gz - - build_wheels: - name: Build wheel for ${{ matrix.os }} - needs: [build_sdist] - runs-on: ${{ matrix.runs-on }} - strategy: - fail-fast: false - matrix: - include: - - os: linux-intel - runs-on: ubuntu-latest - - os: linux-arm - runs-on: ubuntu-24.04-arm - - os: linux-riscv64 - runs-on: ubuntu-24.04-riscv - archs: riscv64 - - os: linux-s390x - runs-on: ubuntu-24.04-s390x - archs: s390x - - os: linux-ppc64le - runs-on: ubuntu-24.04-ppc64le - archs: ppc64le - - os: windows-intel - runs-on: windows-latest - - os: windows-arm - runs-on: windows-11-arm - - os: macos-intel - # macos-15-intel is the last x86_64 runner - runs-on: macos-15-intel - - os: macos-arm - # macos-14+ (including latest) are ARM64 runners - runs-on: macos-latest - #- os: android-intel - # runs-on: ubuntu-latest - # platform: android - #- os: android-arm - # # GitHub Actions doesn't currently support the Android emulator on any ARM - # # runner. So we build on a non-ARM runner, which will skip the tests. - # runs-on: ubuntu-latest - # platform: android - # archs: arm64_v8a - #- os: ios - # runs-on: macos-latest - # platform: ios - # no official wheel support on pypi - #- os: pyodide - # runs-on: ubuntu-latest - # platform: pyodide - - steps: - - uses: actions/download-artifact@v8 - with: - name: artifact-sdist - path: dist - - - name: Copy wheel - shell: bash - run: cp dist/*.tar.gz rapidfuzz.tar.gz - - - uses: IBM/setup-python-pz@291287e36d7c893a50c857d8bc014d67edd58ada - if: matrix.archs == 'ppc64le' || matrix.archs == 's390x' - with: - python-version: '3.11' - - - name: Build wheels - if: matrix.archs != 'ppc64le' && matrix.archs != 's390x' - uses: pypa/cibuildwheel@v3.4.1 - env: - CIBW_PLATFORM: ${{ matrix.platform || 'auto' }} - CIBW_ARCHS: ${{ matrix.archs || 'auto' }} - with: - package-dir: rapidfuzz.tar.gz - output-dir: wheelhouse - - - name: Build Wheel (ppc64le and s390x) - if: matrix.archs == 'ppc64le' || matrix.archs =='s390x' - env: - CIBW_PLATFORM: linux - CIBW_ARCHS: ${{ matrix.archs }} - run: | - python -m pip install cibuildwheel==3.4.1 - python -m cibuildwheel --output-dir wheelhouse rapidfuzz.tar.gz - - - uses: actions/upload-artifact@v7 - with: - name: artifact-${{ matrix.os }}-${{ strategy.job-index }} - path: ./wheelhouse/*.whl - - build_wheels_pyodide: - name: Build wheels on ubuntu-latest/pyodide - needs: [build_sdist] - runs-on: ubuntu-latest - - steps: - - uses: actions/download-artifact@v8 - with: - name: artifact-sdist - path: dist - - - name: Copy wheel - run: cp dist/*.tar.gz rapidfuzz.tar.gz - - - name: Build wheels - if: matrix.archs != 'riscv64' - uses: pypa/cibuildwheel@v3.4.1 - env: - CIBW_PLATFORM: pyodide - CIBW_ARCHS: auto - with: - package-dir: rapidfuzz.tar.gz - output-dir: wheelhouse - - - name: Upload wheels - uses: actions/upload-artifact@v7 - with: - name: pyodide-wheel - path: ./wheelhouse/*.whl - - - deploy-wheels: - if: github.event_name == 'release' && github.event.action == 'published' - needs: [build_wheels] - name: deploy wheels to pypi - runs-on: ubuntu-latest - environment: pypi-release - permissions: - id-token: write - steps: - - uses: actions/download-artifact@v8 - with: - path: dist - pattern: artifact-* - merge-multiple: true - - - uses: pypa/gh-action-pypi-publish@v1.14.0 - with: - verbose: true diff --git a/.github/workflows/submodule.yml b/.github/workflows/submodule.yml deleted file mode 100644 index 28f6ee53..00000000 --- a/.github/workflows/submodule.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: Submodule Test - -on: - push: - paths-ignore: - - 'docs/**' - -jobs: - tests: - name: "test if submodules are up to date" - runs-on: ubuntu-latest - - steps: - - uses: "actions/checkout@v6" - with: - submodules: 'true' - # checkout never fetches tags for submodules so do it manually - - name: fetch submodule tags - run: | - git submodule foreach --recursive 'git fetch --tags' - - - uses: "actions/setup-python@v6" - with: - python-version: "3.11" - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install GitPython - - - name: test submodule version - run: | - python tools/test_submodules.py diff --git a/.nanvix/nanvix.toml b/.nanvix/nanvix.toml new file mode 100644 index 00000000..d69956fb --- /dev/null +++ b/.nanvix/nanvix.toml @@ -0,0 +1,10 @@ +[package] +name = "rapidfuzz" +version = "3.14.5" +nanvix-version = "0.12.552" + +[builds] +[builds.matrix] +platforms = ["microvm"] +modes = ["standalone"] +memory = ["256mb"] diff --git a/.nanvix/z.py b/.nanvix/z.py new file mode 100644 index 00000000..b08b4c11 --- /dev/null +++ b/.nanvix/z.py @@ -0,0 +1,86 @@ +# Copyright(c) The Maintainers of Nanvix. +# Licensed under the MIT License. + +"""Nanvix build script for rapidfuzz C++ extensions. + +Usage: + ./z setup # Download Nanvix sysroot and CPython headers + ./z build # Cross-compile rapidfuzz C++ extensions + ./z test # (no-op — tested via nanvix-python) + ./z release # Package librapidfuzz.a release tarball + ./z clean # Remove build artifacts + ./z distclean # Deep clean +""" + +import os +import shutil +import sys +from pathlib import Path + +from nanvix_zutil import ZScript, log + + +class RapidfuzzBuild(ZScript): + """Build script for nanvix/RapidFuzz.""" + + SYSROOT_REQUIRED_FILES: tuple[str, ...] = ( + "lib/libposix.a", + "lib/user.ld", + ) + + @property + def _nanvix_port_dir(self) -> Path: + return self.repo_root / "nanvix-port" + + @property + def _dist_dir(self) -> Path: + return self._nanvix_port_dir / "dist" + + def setup(self) -> bool: + ok = super().setup() + if not ok: + return False + log.info("setup complete") + return True + + def build(self) -> None: + log.info("cross-compiling rapidfuzz C++ extensions...") + script = self._nanvix_port_dir / "build-nanvix.sh" + if not script.is_file(): + log.error(f"build script not found: {script}") + sys.exit(1) + + self.run("bash", "nanvix-port/build-nanvix.sh") + + lib = self._dist_dir / "librapidfuzz.a" + if lib.is_file(): + log.info(f"build complete: {lib} ({lib.stat().st_size // 1024} KB)") + else: + log.error("build failed: librapidfuzz.a not found") + sys.exit(1) + + def test(self) -> None: + log.info("rapidfuzz extensions are tested via nanvix-python — skipping") + + def release(self) -> None: + import tarfile + + lib = self._dist_dir / "librapidfuzz.a" + if not lib.is_file(): + log.error("librapidfuzz.a not found — run ./z build first") + sys.exit(1) + + platform = os.environ.get("NANVIX_MACHINE", "microvm") + memory = os.environ.get("NANVIX_MEMORY_SIZE", "256mb") + tag = f"rapidfuzz-{platform}-standalone-{memory}" + tarball = self._dist_dir / f"{tag}.tar.gz" + + with tarfile.open(tarball, "w:gz") as tf: + tf.add(lib, arcname=f"{tag}/lib/librapidfuzz.a") + + log.info(f"release: {tarball} ({tarball.stat().st_size // 1024} KB)") + + def clean(self) -> None: + if self._dist_dir.is_dir(): + shutil.rmtree(self._dist_dir) + log.info("cleaned dist/") diff --git a/nanvix-port/build-nanvix.sh b/nanvix-port/build-nanvix.sh new file mode 100755 index 00000000..57b32716 --- /dev/null +++ b/nanvix-port/build-nanvix.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# Copyright(c) The Maintainers of Nanvix. +# Licensed under the MIT License. +# +# Cross-compile rapidfuzz C++ extensions for Nanvix (i686). +# Produces dist/librapidfuzz.a containing all extension modules. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +DIST_DIR="$SCRIPT_DIR/dist" + +TOOLCHAIN="${NANVIX_TOOLCHAIN:-/opt/nanvix}" +CXX="${TOOLCHAIN}/bin/i686-nanvix-g++" +AR="${TOOLCHAIN}/bin/i686-nanvix-ar" + +CPYTHON_HEADERS="$SCRIPT_DIR/cpython-headers" + +CXXFLAGS="-O2 -fPIC -std=c++17 -DNDEBUG -fpermissive" +CXXFLAGS="$CXXFLAGS -I${CPYTHON_HEADERS}" +CXXFLAGS="$CXXFLAGS -I${REPO_ROOT}/src/rapidfuzz" +CXXFLAGS="$CXXFLAGS -I${REPO_ROOT}/extern/rapidfuzz-cpp/rapidfuzz" + +mkdir -p "$DIST_DIR/obj" + +echo "[rapidfuzz] Cross-compiling C++ extensions for i686-nanvix..." + +# Compile each extension module +SOURCES=( + "$REPO_ROOT/src/rapidfuzz/utils.cpp" + "$REPO_ROOT/src/rapidfuzz/fuzz.cpp" + "$REPO_ROOT/src/rapidfuzz/fuzz_sse2.cpp" + "$REPO_ROOT/src/rapidfuzz/FeatureDetector/CpuInfo.cpp" + "$REPO_ROOT/src/rapidfuzz/distance/_initialize.cpp" + "$REPO_ROOT/src/rapidfuzz/distance/metrics.cpp" + "$REPO_ROOT/src/rapidfuzz/distance/metrics_sse2.cpp" +) + +for src in "${SOURCES[@]}"; do + if [ -f "$src" ]; then + obj="$DIST_DIR/obj/$(basename "${src%.cpp}.o")" + echo " CC $src" + $CXX $CXXFLAGS -c "$src" -o "$obj" + else + echo " SKIP $src (not found)" + fi +done + +echo "[rapidfuzz] Creating static archive..." +$AR rcs "$DIST_DIR/librapidfuzz.a" "$DIST_DIR"/obj/*.o + +echo "[rapidfuzz] Done: $DIST_DIR/librapidfuzz.a" +ls -la "$DIST_DIR/librapidfuzz.a" diff --git a/z b/z new file mode 100755 index 00000000..6ba00847 --- /dev/null +++ b/z @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +# Copyright(c) The Maintainers of Nanvix. +# Licensed under the MIT License. + +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +case "$(uname -s)" in + CYGWIN* | MINGW* | MSYS*) + exec powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$SCRIPT_DIR/z.ps1" "$@" + ;; + *) exec "$SCRIPT_DIR/z.sh" "$@" ;; +esac diff --git a/z.sh b/z.sh new file mode 100755 index 00000000..2c36ef50 --- /dev/null +++ b/z.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +# Copyright(c) The Maintainers of Nanvix. +# Licensed under the MIT License. + +set -euo pipefail + +PINNED_VERSION="0.8.2" +RAW_ZUTIL_VERSION="${NANVIX_ZUTIL_VERSION:-$PINNED_VERSION}" +ZUTIL_VERSION="${RAW_ZUTIL_VERSION#v}" +REPO_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd -P)" +VENV="$REPO_ROOT/.nanvix/venv" + +function _resolve_venv_paths() { + if [ -d "$VENV/Scripts" ]; then + VENV_BIN="$VENV/Scripts/nanvix-zutil.exe" + VENV_PYTHON="$VENV/Scripts/python.exe" + else + VENV_BIN="$VENV/bin/nanvix-zutil" + VENV_PYTHON="$VENV/bin/python" + fi +} +_resolve_venv_paths +ZUTIL_GLOBAL_VERSION="$(nanvix-zutil --version 2>/dev/null || true)" + +function bootstrap() { + echo "nanvix-zutil not found -- bootstrapping nanvix-zutil==${ZUTIL_VERSION}..." >&2 + if ! command -v python3 &>/dev/null; then + echo "Error: python3 not found." >&2 + exit 1 + fi + WHEEL_URL="https://github.com/nanvix/zutils/releases/download/v${ZUTIL_VERSION}/nanvix_zutil-${ZUTIL_VERSION}-py3-none-any.whl" + if [ -d "$VENV" ]; then python3 -m venv --clear "$VENV"; else python3 -m venv "$VENV"; fi + _resolve_venv_paths + "$VENV_PYTHON" -m pip install --quiet "nanvix-zutil[lint] @ ${WHEEL_URL}" +} + +BIN="" +if [ ! -d "$VENV" ] && [ -z "$ZUTIL_GLOBAL_VERSION" ]; then + bootstrap + BIN="$VENV_BIN" +elif [ -x "$VENV_BIN" ]; then + VENV_VERSION="$("$VENV_BIN" --version 2>/dev/null || true)" + if [ "$VENV_VERSION" != "nanvix-zutil ${ZUTIL_VERSION}" ]; then bootstrap; fi + BIN="$VENV_BIN" +elif [ -d "$VENV" ] && ! command -v nanvix-zutil &>/dev/null; then + bootstrap + BIN="$VENV_BIN" +else + BIN="nanvix-zutil" +fi + +ARGS=() +while [[ $# -gt 0 ]]; do + case "$1" in + --with-nanvix=*) + WITH_NANVIX="$(cd -- "${1#--with-nanvix=}" && pwd -P)" + export WITH_NANVIX + shift + ;; + --with-nanvix) + WITH_NANVIX="$(cd -- "$2" && pwd -P)" + export WITH_NANVIX + shift 2 + ;; + *) + ARGS+=("$1") + shift + ;; + esac +done + +if [[ "${ARGS[0]:-}" == "distclean" ]]; then + "$BIN" "${ARGS[@]}" + EC=$? + if [ -d "$VENV" ]; then rm -rf "$VENV" 2>/dev/null || true; fi + exit $EC +fi +exec "$BIN" "${ARGS[@]}"