Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions .github/workflows/build-pyicu-binary.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# SPDX-FileCopyrightText: 2026 The RISE Project
# SPDX-License-Identifier: MIT
---
# pyicu-binary is PyICU's own gitlab.pyicu.org/main/pyicu tree (setup.py:
# name="PyICU-binary"), which carries no wheel-build CI of its own.
name: Build pyicu-binary wheels (riscv64)

on:
workflow_dispatch:
inputs:
version:
description: 'pyicu-binary version to build (git tag without leading v, e.g. 2.7.4)'
required: true
default: '2.7.4'
pull_request:
paths:
- '.github/workflows/build-pyicu-binary.yml'
- 'patches/pyicu-binary/**'

concurrency:
group: ${{ github.workflow }}-${{ inputs.version || '2.7.4' }}-${{ 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 2.7.4 there.
PYICU_BINARY_VERSION: ${{ inputs.version || '2.7.4' }}
MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64

jobs:
setup:
uses: $/.github/workflows/_setup.yml

build_wheels:
needs: [setup]
name: Build pyicu-binary ${{ inputs.version || '2.7.4' }} ${{ matrix.python }}-manylinux_riscv64
runs-on: ubuntu-24.04-riscv
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
python: ["cp312", "cp313", "cp314", "cp314t"]

steps:
# gitlab.pyicu.org isn't github.com, so actions/checkout can't reach it.
- name: Checkout pyicu v${{ env.PYICU_BINARY_VERSION }}
run: |
git clone --depth 1 --branch "v${{ env.PYICU_BINARY_VERSION }}" \
https://gitlab.pyicu.org/main/pyicu.git pyicu

- name: Checkout python-wheels
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: python-wheels
persist-credentials: false

- name: Apply riscv64 patches
working-directory: pyicu
run: git apply -v ../python-wheels/patches/pyicu-binary/${{ env.PYICU_BINARY_VERSION }}/*.patch

- name: Build wheels
uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0
with:
package-dir: pyicu
output-dir: wheelhouse/
only: ${{ matrix.python }}-manylinux_riscv64
env:
CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }}
# setup.py links ICU dynamically via icu-config/pkg-config; auditwheel
# then vendors libicudata/libicuuc/libicui18n into the wheel (the
# "-binary" in the name), which is what makes it self-contained. Its
# Unicode License requires the notice to travel with the binary.
CIBW_BEFORE_ALL_LINUX: >-
dnf -y install libicu-devel &&
cp /usr/share/licenses/libicu/LICENSE {package}/LICENSE.icu
CIBW_TEST_REQUIRES: pytest six
CIBW_TEST_SOURCES: pyicu/test
# 2.7.4's own tests hardcode CLDR-formatted strings and a tzdata
# transition pinned to ICU <=69 (test_DateTimeParserGenerator's
# own ICU_VERSION check tops out at "68.0"); manylinux_2_39
# links 74.2, whose newer CLDR/tzdata legitimately renders these
# four differently (e.g. narrow-no-break-space in "5:30 PM",
# Fiji's DST rule having no further transitions). Not a riscv64
# or code defect - deselect rather than chase upstream's CLDR pin.
CIBW_TEST_COMMAND: >-
python -c "import icu; print('linked ICU', icu.ICU_VERSION)" &&
python -m pytest pyicu/test
--deselect pyicu/test/test_DateTimeParserGenerator.py::TestDateTimePatternGenerator::testAddPattern
--deselect pyicu/test/test_DateTimeParserGenerator.py::TestDateTimePatternGenerator::testGetBestPattern
--deselect pyicu/test/test_DateTimeParserGenerator.py::TestDateTimePatternGenerator::testReplaceFieldType
--deselect pyicu/test/test_TimeZone.py::TestTimeZone::testTransition

- name: Check the wheel bundles ICU and its licence
run: |
python3 - wheelhouse/*.whl <<'EOF'
import sys, zipfile
for whl in sys.argv[1:]:
names = zipfile.ZipFile(whl).namelist()
assert any(n.split("/")[-1].startswith("_icu") and n.endswith(".so") for n in names), (whl, names)
assert any(".libs/" in n and "icu" in n.lower() for n in names), (whl, names)
assert any(n.endswith(".dist-info/licenses/LICENSE.icu") for n in names), (whl, names)
print(whl, "OK")
EOF

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: pyicu-binary-${{ env.PYICU_BINARY_VERSION }}-${{ matrix.python }}-manylinux_riscv64
path: wheelhouse/*.whl
if-no-files-found: error

publish:
name: Publish pyicu-binary ${{ inputs.version || '2.7.4' }}
needs: [setup, build_wheels]
permissions:
contents: write
pull-requests: write
uses: $/.github/workflows/_publish-wheel.yml
with:
artifact-pattern: pyicu-binary-${{ inputs.version || '2.7.4' }}-*-manylinux_riscv64
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ludovic Henry <git@ludovic.dev>
Date: Tue, 8 Sep 2026 00:00:00 +0000
Subject: [PATCH] common: gate the deprecated PyUnicode_WCHAR_KIND case out on
Python 3.12+

Upstream-Status: Backport [https://gitlab.pyicu.org/main/pyicu/-/commit/8850cfca5aeaf040170edca6796002feff0eb82c]

First released in v2.11 (an earlier attempt at the same fix is
e7a54d1, tightened to this final form the next day by 8850cfc).

CPython 3.12 removed the legacy PyUnicode_WCHAR_KIND enumerator along
with the rest of the pre-3.3 "legacy Unicode" C API
(Include/cpython/unicodeobject.h), so building this file against a
3.12+ header fails with:

common.cpp: In function '...PyObject_AsUnicodeString(...)':
common.cpp:358:16: error: 'PyUnicode_WCHAR_KIND' was not declared
in this scope; did you mean 'PyUnicode_4BYTE_KIND'?

Every other PyUnicode_KIND case already predates the switch this
enumerator lives in, so the fix is the same one upstream shipped two
releases after 2.7.4: only compile the WCHAR_KIND arm on interpreters
old enough to still declare it.

Signed-off-by: Ludovic Henry <git@ludovic.dev>
---
common.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/common.cpp b/common.cpp
index 601f1c3..98f86ff 100644
--- a/common.cpp
+++ b/common.cpp
@@ -355,6 +355,7 @@ EXPORT UnicodeString &PyObject_AsUnicodeString(PyObject *object,
PyUnicode_READY(object);

switch (PyUnicode_KIND(object)) {
+#if PY_VERSION_HEX < 0x030c0000
case PyUnicode_WCHAR_KIND: { // this code path should be deprecated
if (SIZEOF_WCHAR_T == sizeof(UChar))
{
@@ -381,7 +382,7 @@ EXPORT UnicodeString &PyObject_AsUnicodeString(PyObject *object,
}
break;
}
-
+#endif
case PyUnicode_1BYTE_KIND: {
Py_ssize_t len = PyUnicode_GET_LENGTH(object);
Py_UCS1 *pchars = PyUnicode_1BYTE_DATA(object);
--
2.43.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ludovic Henry <git@ludovic.dev>
Date: Tue, 8 Sep 2026 00:00:00 +0000
Subject: [PATCH] setup: rename the project to PyICU-binary

Upstream-Status: Inappropriate [the gitlab.pyicu.org/main/pyicu git tree itself always names the project "PyICU" (checked at tag v2.7.4); "PyICU-binary" is the distribution name whoever runs the actual PyPI release renames it to before building, done outside this git tree, so it is not something to submit upstream]

The PyPI project this repo is porting, and the one pypi.riseproject.dev
is meant to mirror, is "PyICU-binary" (see PyPI's own PyICU-binary
sdist, whose setup.py carries this exact name= override) - the plain
git checkout the workflow builds from stays "PyICU" throughout its
history, including at the v2.7.4 tag. Without this the built wheel's
own METADATA reports Name: pyicu, and _publish-wheel.yml derives the
published package name straight from that field, so the workflow would
register and publish the wrong package.

Signed-off-by: Ludovic Henry <git@ludovic.dev>
---
setup.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/setup.py b/setup.py
index 3d85f87..ff78c9f 100644
--- a/setup.py
+++ b/setup.py
@@ -246,7 +246,7 @@ if sys.version_info < (2, 4):
return iterable


-setup(name="PyICU",
+setup(name="PyICU-binary",
description='Python extension wrapping the ICU C++ API',
long_description=open('README.md').read(),
long_description_content_type="text/markdown",
--
2.43.0