diff --git a/.github/workflows/build-fasttext-wheel.yml b/.github/workflows/build-fasttext-wheel.yml new file mode 100644 index 000000000..e83ff2faf --- /dev/null +++ b/.github/workflows/build-fasttext-wheel.yml @@ -0,0 +1,101 @@ +# SPDX-FileCopyrightText: 2026 The RISE Project +# SPDX-License-Identifier: MIT +--- +# This workflow is based on: https://github.com/messense/fasttext-wheel/blob/master/.github/workflows/Linux.yml +name: Build fasttext-wheel wheels (riscv64) + +on: + workflow_dispatch: + inputs: + version: + description: 'fasttext-wheel version to build (facebookresearch/fastText git tag, e.g. v0.9.2)' + required: true + default: 'v0.9.2' + pull_request: + paths: + - '.github/workflows/build-fasttext-wheel.yml' + - 'patches/fasttext-wheel/**' + +concurrency: + group: ${{ github.workflow }}-${{ inputs.version || 'v0.9.2' }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read # to fetch code (actions/checkout) + +env: + # `inputs.version` is empty on pull_request events; default to v0.9.2 there. + FASTTEXT_WHEEL_VERSION: ${{ inputs.version || 'v0.9.2' }} + MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64 + +jobs: + setup: + uses: $/.github/workflows/_setup.yml + + build_wheels: + needs: [setup] + name: Build fasttext-wheel ${{ inputs.version || 'v0.9.2' }} ${{ matrix.python }}-manylinux_riscv64 + runs-on: ubuntu-24.04-riscv + strategy: + fail-fast: false + matrix: + # Per-interpreter (not abi3): the pybind11 extension links the version-specific ABI. + python: + - "cp312" + - "cp313" + - "cp314" + - "cp314t" + + steps: + # facebookresearch/fastText is the actual source; fasttext-wheel (messense) + # is only a build/publish wrapper around it that renames the distribution. + - name: Checkout fastText ${{ env.FASTTEXT_WHEEL_VERSION }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: facebookresearch/fastText + ref: ${{ env.FASTTEXT_WHEEL_VERSION }} + persist-credentials: false + + - name: Checkout python-wheels + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: python-wheels + persist-credentials: false + + - name: Patch fastText source + run: git apply python-wheels/patches/fasttext-wheel/${{ env.FASTTEXT_WHEEL_VERSION }}/00*.patch + + - name: Build wheels + uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0 + with: + output-dir: wheelhouse/ + only: ${{ matrix.python }}-manylinux_riscv64 + env: + CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }} + # No pyproject.toml, so `python -m build`'s isolated venv has no pip; + # setup.py's get_pybind_include self-installs pybind11 via `pip install` + # there and fails with "No module named pip". Pre-install its + # install_requires (pybind11, setuptools, numpy) and skip isolation. + CIBW_BEFORE_BUILD: pip install pybind11 setuptools numpy + CIBW_BUILD_FRONTEND: "pip; args: --no-build-isolation" + # runtests.py (upstream's `python_test.sh` entry point) lives outside the + # installed package, so run the same gen_unit_tests() suite it calls + # directly against the installed fasttext.tests subpackage instead. + CIBW_TEST_COMMAND: >- + python -c "import unittest, sys; from fasttext.tests.test_script import gen_unit_tests; r = unittest.TextTestRunner(verbosity=2).run(unittest.TestLoader().loadTestsFromTestCase(gen_unit_tests())); sys.exit(0 if r.wasSuccessful() else 1)" + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: fasttext-wheel-${{ inputs.version || 'v0.9.2' }}-${{ matrix.python }}-manylinux_riscv64 + path: wheelhouse/*.whl + if-no-files-found: error + + publish: + name: Publish fasttext-wheel ${{ inputs.version || 'v0.9.2' }} + needs: [setup, build_wheels] + permissions: + contents: write + pull-requests: write + uses: $/.github/workflows/_publish-wheel.yml + with: + artifact-pattern: fasttext-wheel-${{ inputs.version || 'v0.9.2' }}-*-manylinux_riscv64 diff --git a/patches/fasttext-wheel/v0.9.2/0001-setup-rename-distribution-to-fasttext-wheel.patch b/patches/fasttext-wheel/v0.9.2/0001-setup-rename-distribution-to-fasttext-wheel.patch new file mode 100644 index 000000000..07fc5da6b --- /dev/null +++ b/patches/fasttext-wheel/v0.9.2/0001-setup-rename-distribution-to-fasttext-wheel.patch @@ -0,0 +1,33 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Tue, 8 Sep 2026 00:00:00 +0000 +Subject: [PATCH] setup: rename distribution to fasttext-wheel + +Upstream-Status: Inappropriate [downstream redistribution naming, irrelevant to facebookresearch/fastText] + +fasttext-wheel is messense's community-maintained PyPI distribution built from +this same source tree, published under a different project name because +`fasttext` on PyPI is a separate, unrelated distribution. messense/fasttext-wheel's +own manylinux.sh build script does this exact rename with a `sed` before +building (`sed -i "s/name='fasttext',/name='fasttext-wheel',/" fastText/setup.py`). +Reapply it here so the built wheel carries the `fasttext-wheel` project name +while the importable `fasttext` package/module path is unchanged. + +Signed-off-by: Ludovic Henry +--- + setup.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/setup.py b/setup.py +index a1b2c3d..e4f5a6b 100644 +--- a/setup.py ++++ b/setup.py +@@ -166,7 +166,7 @@ def _get_readme(): + + + setup( +- name='fasttext', ++ name='fasttext-wheel', + version=__version__, + author='Onur Celebi', + author_email='celebio@fb.com', diff --git a/patches/fasttext-wheel/v0.9.2/0002-FastText-fix-numpy-2.x-copy-False-compatibility.patch b/patches/fasttext-wheel/v0.9.2/0002-FastText-fix-numpy-2.x-copy-False-compatibility.patch new file mode 100644 index 000000000..c971dbb00 --- /dev/null +++ b/patches/fasttext-wheel/v0.9.2/0002-FastText-fix-numpy-2.x-copy-False-compatibility.patch @@ -0,0 +1,54 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Tue, 8 Sep 2026 00:00:00 +0000 +Subject: [PATCH] FastText: fix numpy 2.x `copy=False` compatibility + +Upstream-Status: To upstream [facebookresearch/fastText archived (read-only) 2024-03-19, blocking new PRs] + +`np.array(obj, copy=False)` raised on numpy 1.x only when a copy could not be +avoided but silently allowed one otherwise; numpy 2.0 made `copy=False` a hard +requirement and raises `ValueError: Unable to avoid copy while creating an +array as requested` whenever a copy is actually needed, which +`_FastText.predict`, `_Meter.scores` and `_Meter.precision_recall_curve` hit on +every call. This reproduces off riscv64 (confirmed on macOS/arm64): all four +`test_supervised_predict_*` unit tests fail identically on any platform once +numpy >= 2.0 is installed, which our registry serves as the only riscv64 +build. Switching to `np.asarray`, numpy's own documented migration fix, +preserves numpy 1.x behaviour and fixes numpy 2.x. + +Signed-off-by: Ludovic Henry +--- + python/fasttext_module/fasttext/FastText.py | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/python/fasttext_module/fasttext/FastText.py b/python/fasttext_module/fasttext/FastText.py +index 1234567..89abcde 100644 +--- a/python/fasttext_module/fasttext/FastText.py ++++ b/python/fasttext_module/fasttext/FastText.py +@@ -43,7 +43,7 @@ class _Meter(object): + else: + y_scores, y_true = ([], ()) + +- return np.array(y_scores, copy=False), np.array(y_true, copy=False) ++ return np.asarray(y_scores), np.asarray(y_true) + + def precision_recall_curve(self, label=None): + """Return precision/recall curve""" +@@ -58,7 +58,7 @@ class _Meter(object): + else: + precision, recall = ([], ()) + +- return np.array(precision, copy=False), np.array(recall, copy=False) ++ return np.asarray(precision), np.asarray(recall) + + def precision_at_recall(self, recall, label=None): + """Return precision for a given recall""" +@@ -229,7 +229,7 @@ class _FastText(object): + else: + probs, labels = ([], ()) + +- return labels, np.array(probs, copy=False) ++ return labels, np.asarray(probs) + + def get_input_matrix(self): + """ diff --git a/patches/fasttext-wheel/v0.9.2/0003-args-fix-missing-cstdint-include-for-uint64_t.patch b/patches/fasttext-wheel/v0.9.2/0003-args-fix-missing-cstdint-include-for-uint64_t.patch new file mode 100644 index 000000000..32fda8208 --- /dev/null +++ b/patches/fasttext-wheel/v0.9.2/0003-args-fix-missing-cstdint-include-for-uint64_t.patch @@ -0,0 +1,45 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Tue, 8 Sep 2026 00:00:00 +0000 +Subject: [PATCH] args: fix missing include for uint64_t + +Upstream-Status: Backport [https://github.com/facebookresearch/fastText/commit/6c2204ba66776b700095ff73e3e599a908ffd9c3] + +args.cc uses `uint64_t` in `getAutotuneModelSize()` without including +``, relying on it being pulled in transitively by another header. +This happens to hold on the x86_64/aarch64 manylinux images' GCC/libstdc++, +but fails on manylinux_riscv64's (newer, stricter) toolchain with: + + src/args.cc:468:3: error: 'uint64_t' was not declared in this scope + src/args.cc:17:1: note: 'uint64_t' is defined in header ''; + this is probably fixable by adding '#include ' + +Upstream hit the same failure under GCC 13 (whose header-dependency +changes stopped transitively exposing , see +https://gcc.gnu.org/gcc-13/porting_to.html#header-dep-changes) and fixed it +in commit 6c2204ba66776b700095ff73e3e599a908ffd9c3 ("fix compile error with +gcc13 #1281 (#1340)", 2023-11-27) by adding the same include, before the +repo was archived (2024-03-19). Backport that one-line fix onto v0.9.2, +whose args.cc still includes `` rather than the `` that +commit's context lines show (an unrelated later modernization), so the +insertion point here is next to the `` include instead. + +Signed-off-by: Ludovic Henry +--- + src/args.cc | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/args.cc b/src/args.cc +index 7ac8c2f..b47bd24 100644 +--- a/src/args.cc ++++ b/src/args.cc +@@ -9,6 +9,7 @@ + #include "args.h" + + #include ++#include + + #include + #include +-- +2.50.1 (Apple Git-155)