From 802307555234d2c6b8e465b822de10dc76919c76 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Sun, 13 Sep 2026 03:55:24 +0200 Subject: [PATCH 1/9] mini-racer: add build-mini-racer.yml for riscv64 wheels Builds v8 (branch-heads/14.4) from source for riscv64, the shape upstream already uses for x64/arm64: a build-dll job runs v8_build.py to produce libmini_racer.so, then build-wheel packages it into a single py3-none-manylinux_riscv64 wheel (the .so is not linked against libpython, so one wheel serves every interpreter). v8's own riscv64 codegen backend (src/codegen/riscv) is mature and actively developed; the gap is entirely in tooling: PyMiniRacer's build script does not recognize riscv64 as a host arch, its wheel tagging mislabels non-aarch64 riscv builds as x86_64, and Chromium's prebuilt clang is only published for amd64/arm64/mac hosts, so the build must use v8's existing riscv64-linux-gnu- gcc_toolchain instead. All three are patched in patches/mini-racer/0.14.1/. --- .github/workflows/build-mini-racer.yml | 154 ++++++++++++++++++ ...ze-riscv64-as-a-native-v8_target_cpu.patch | 32 ++++ ...64-wheels-their-own-manylinux-platfo.patch | 39 +++++ ...riscv64-linux-gnu-gcc_toolchain-not-.patch | 42 +++++ 4 files changed, 267 insertions(+) create mode 100644 .github/workflows/build-mini-racer.yml create mode 100644 patches/mini-racer/0.14.1/0001-builder-recognize-riscv64-as-a-native-v8_target_cpu.patch create mode 100644 patches/mini-racer/0.14.1/0002-setup-give-riscv64-wheels-their-own-manylinux-platfo.patch create mode 100644 patches/mini-racer/0.14.1/0003-builder-use-the-riscv64-linux-gnu-gcc_toolchain-not-.patch diff --git a/.github/workflows/build-mini-racer.yml b/.github/workflows/build-mini-racer.yml new file mode 100644 index 000000000..c7204e879 --- /dev/null +++ b/.github/workflows/build-mini-racer.yml @@ -0,0 +1,154 @@ +# SPDX-FileCopyrightText: 2026 The RISE Project +# SPDX-License-Identifier: MIT +--- +# Based on the `build-dll`/`build-wheel` jobs of +# https://github.com/bpcreech/PyMiniRacer/blob/v0.14.1/.github/workflows/build.yml +name: Build mini-racer wheels (riscv64) + +on: + workflow_dispatch: + inputs: + version: + description: 'mini-racer version to build (git tag without leading v)' + required: true + default: '0.14.1' + pull_request: + paths: + - '.github/workflows/build-mini-racer.yml' + - 'patches/mini-racer/**' + +run-name: build-mini-racer - ${{ inputs.version || '0.14.1' }} + +concurrency: + group: ${{ github.workflow }}-${{ inputs.version || '0.14.1' }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read + +env: + MINI_RACER_VERSION: ${{ inputs.version || '0.14.1' }} + +jobs: + setup: + uses: $/.github/workflows/_setup.yml + + build_dll: + name: Build v8 dll for mini-racer ${{ inputs.version || '0.14.1' }}-riscv64 + needs: [setup] + runs-on: ubuntu-24.04-riscv + timeout-minutes: 1440 # 24h: a from-scratch monolithic v8 build + container: + image: quay.io/pypa/manylinux_2_39_riscv64 + + steps: + - name: Checkout mini-racer v${{ env.MINI_RACER_VERSION }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: bpcreech/PyMiniRacer + ref: v${{ env.MINI_RACER_VERSION }} + persist-credentials: false + + - name: Checkout python-wheels + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: python-wheels + persist-credentials: false + + - name: Patch mini-racer source + run: git apply python-wheels/patches/mini-racer/${{ env.MINI_RACER_VERSION }}/*.patch + + - name: Install build dependencies + run: dnf install -y git curl which + + - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 + with: + # the v8 build still doesn't work with a system Python > 3.11 (upstream + # build.yml pins the same version): + python-version: '3.11' + activate-environment: true + enable-cache: false + + - name: Provide a riscv64-linux-gnu- prefixed toolchain + run: | + mkdir -p "${RUNNER_TEMP}/riscv64-toolchain/bin" + for tool in gcc g++ ar as ld nm objcopy objdump ranlib readelf strip; do + real=$(command -v "${tool}" || true) + [ -n "${real}" ] && ln -sf "${real}" "${RUNNER_TEMP}/riscv64-toolchain/bin/riscv64-linux-gnu-${tool}" + done + echo "${RUNNER_TEMP}/riscv64-toolchain/bin" >> "${GITHUB_PATH}" + + - name: Build dll with v8 + run: | + uv sync --no-install-project + uv run --no-project builder/v8_build.py --out-path=_dll --target-cpu=riscv64 + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: mini-racer-${{ env.MINI_RACER_VERSION }}-dll-riscv64 + path: _dll + if-no-files-found: error + + build_wheel: + name: Build and test mini-racer ${{ inputs.version || '0.14.1' }} wheel for riscv64 + needs: [setup, build_dll] + runs-on: ubuntu-24.04-riscv + container: + image: quay.io/pypa/manylinux_2_39_riscv64 + + steps: + - name: Checkout mini-racer v${{ env.MINI_RACER_VERSION }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: bpcreech/PyMiniRacer + ref: v${{ env.MINI_RACER_VERSION }} + persist-credentials: false + + - name: Checkout python-wheels + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: python-wheels + persist-credentials: false + + - name: Patch mini-racer source + run: git apply python-wheels/patches/mini-racer/${{ env.MINI_RACER_VERSION }}/*.patch + + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: mini-racer-${{ env.MINI_RACER_VERSION }}-dll-riscv64 + path: src/py_mini_racer + + - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 + with: + python-version: '3.12' + activate-environment: true + enable-cache: false + + - name: Build wheel + run: uv build + + # The wheel is "py3-none-" (one v8 build serves every interpreter); + # test it against each one the image ships, since uv has no riscv64 pythons to download. + - name: Test wheel + run: | + for py in /opt/python/cp312-cp312 /opt/python/cp313-cp313 /opt/python/cp314-cp314 /opt/python/cp314t-cp314t; do + "${py}/bin/pip" install dist/*.whl pytest + "${py}/bin/python" -m pytest tests + "${py}/bin/pip" uninstall -y mini-racer + done + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: mini-racer-${{ env.MINI_RACER_VERSION }}-py3-manylinux_riscv64 + path: dist/* + if-no-files-found: error + + publish: + name: Publish mini-racer ${{ inputs.version || '0.14.1' }} + needs: [setup, build_wheel] + permissions: + contents: write + pull-requests: write + uses: $/.github/workflows/_publish-wheel.yml + with: + artifact-pattern: mini-racer-${{ inputs.version || '0.14.1' }}-py3-manylinux_riscv64 diff --git a/patches/mini-racer/0.14.1/0001-builder-recognize-riscv64-as-a-native-v8_target_cpu.patch b/patches/mini-racer/0.14.1/0001-builder-recognize-riscv64-as-a-native-v8_target_cpu.patch new file mode 100644 index 000000000..c97d7a6f5 --- /dev/null +++ b/patches/mini-racer/0.14.1/0001-builder-recognize-riscv64-as-a-native-v8_target_cpu.patch @@ -0,0 +1,32 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Sun, 13 Sep 2026 03:48:55 +0200 +Subject: [PATCH 1/2] builder: recognize riscv64 as a native v8_target_cpu + +get_local_v8_target_cpu() maps platform.machine() to a v8 target_cpu +string for every architecture PyMiniRacer's build.yml matrix builds +(x86/x64/arm/arm64/s390x/ppc64), but not riscv64: on a native riscv64 +host it raises UnknownArchError before the --target-cpu override in +run_build() is even consulted, since that override still calls this +function to decide whether to cross-install a sysroot. + +Upstream-Status: To upstream [not yet submitted to bpcreech/PyMiniRacer] + +Signed-off-by: Ludovic Henry +--- + builder/v8_build.py | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/builder/v8_build.py b/builder/v8_build.py +index 090bb8c..cbcbce0 100644 +--- a/builder/v8_build.py ++++ b/builder/v8_build.py +@@ -61,6 +61,8 @@ def get_local_v8_target_cpu() -> str: + return "s390x" + if m == "ppc64": + return "ppc64" ++ if m == "riscv64": ++ return "riscv64" + + raise UnknownArchError(m) + diff --git a/patches/mini-racer/0.14.1/0002-setup-give-riscv64-wheels-their-own-manylinux-platfo.patch b/patches/mini-racer/0.14.1/0002-setup-give-riscv64-wheels-their-own-manylinux-platfo.patch new file mode 100644 index 000000000..0be1e6422 --- /dev/null +++ b/patches/mini-racer/0.14.1/0002-setup-give-riscv64-wheels-their-own-manylinux-platfo.patch @@ -0,0 +1,39 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Sun, 13 Sep 2026 03:48:55 +0200 +Subject: [PATCH 2/2] setup: give riscv64 wheels their own manylinux platform + tag + +_get_platform_tag() only special-cases aarch64 among non-x86_64 +manylinux tags; every other architecture, riscv64 included, falls +through to the final "return manylinux_2_27_x86_64", mislabeling a +riscv64 build as x86_64. Unlike x86_64/aarch64, v8's sysroot fetcher +has no riscv64 sysroot to bundle (build/linux/sysroot_scripts), so a +riscv64 build cannot claim the same backward-compatible manylinux_2_27 +floor those get; it is only as portable as the glibc of the image it +was built in, so report that instead. + +Upstream-Status: To upstream [not yet submitted to bpcreech/PyMiniRacer] + +Signed-off-by: Ludovic Henry +--- + setup.py | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/setup.py b/setup.py +index 380195c..48df240 100644 +--- a/setup.py ++++ b/setup.py +@@ -30,6 +30,12 @@ def _get_platform_tag() -> str: + if tag.endswith("_aarch64"): + return "manylinux_2_27_aarch64" + ++ # Unlike x86_64/aarch64, v8's sysroot fetcher has no riscv64 sysroot to ++ # bundle, so the wheel is only as portable as the glibc it was built ++ # against; report that instead of silently mislabeling it x86_64: ++ if tag.endswith("_riscv64"): ++ return "manylinux_2_39_riscv64" ++ + return "manylinux_2_27_x86_64" + + return tag diff --git a/patches/mini-racer/0.14.1/0003-builder-use-the-riscv64-linux-gnu-gcc_toolchain-not-.patch b/patches/mini-racer/0.14.1/0003-builder-use-the-riscv64-linux-gnu-gcc_toolchain-not-.patch new file mode 100644 index 000000000..06d8e7382 --- /dev/null +++ b/patches/mini-racer/0.14.1/0003-builder-use-the-riscv64-linux-gnu-gcc_toolchain-not-.patch @@ -0,0 +1,42 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Sun, 13 Sep 2026 03:51:38 +0200 +Subject: [PATCH] builder: use the riscv64-linux-gnu- gcc_toolchain, not v8's + clang + +Chromium's prebuilt clang toolchain (third_party/llvm-build, fetched by +gclient's DEPS from the chromium-browser-clang GCS bucket) is only +published for amd64/arm64/mac hosts; the Linux object is +unconditioned on host_cpu (only on host_os == "linux"), so gclient +sync happily downloads an x86_64 clang binary on a riscv64 host that +can never execute it. v8's own build/toolchain/linux/BUILD.gn already +defines a gcc_toolchain("riscv64") for exactly this situation (see +https://github.com/riscv-collab/v8/wiki/Cross-compiled-Build); passing +is_clang=false for a riscv64 target routes gn at that toolchain instead +of the missing clang one. + +Upstream-Status: To upstream [not yet submitted to bpcreech/PyMiniRacer] + +Signed-off-by: Ludovic Henry +--- + builder/v8_build.py | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/builder/v8_build.py b/builder/v8_build.py +index cbcbce0..7a08487 100644 +--- a/builder/v8_build.py ++++ b/builder/v8_build.py +@@ -251,6 +251,13 @@ def run_build(build_dir: Path, args: Args) -> None: + # https://gitlab.alpinelinux.org/alpine/aports/-/issues/16210 + opts["v8_enable_partition_alloc"] = "false" + ++ if target_cpu == "riscv64": ++ # Chromium's prebuilt clang (third_party/llvm-build) is only published ++ # for amd64/arm64/mac hosts, so a riscv64 build must instead use the ++ # riscv64-linux-gnu- gcc_toolchain already wired into ++ # build/toolchain/linux/BUILD.gn: ++ opts["is_clang"] = "false" ++ + if is_linux() and target_cpu != get_local_v8_target_cpu(): + run( + executable, From 2ae7a8c5cf57802d0f36eea59e9e9147b10d873a Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Sun, 13 Sep 2026 04:20:35 +0200 Subject: [PATCH 2/9] mini-racer: serialize gclient sync on riscv64 CI hit gclient sync's 'gsutil' bootstrap lockfile.LockError: [Errno 11] Resource temporarily unavailable, from concurrent sync workers racing to fetch the shared gsutil binary. depot_tools already serializes sync (--jobs=1) for 32-bit arm boards for the same reason; do the same for riscv64. https://github.com/riseproject-dev/python-wheels/actions/runs/34731759983/job/103655726279 --- ...er-serialize-gclient-sync-on-riscv64.patch | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 patches/mini-racer/0.14.1/0004-builder-serialize-gclient-sync-on-riscv64.patch diff --git a/patches/mini-racer/0.14.1/0004-builder-serialize-gclient-sync-on-riscv64.patch b/patches/mini-racer/0.14.1/0004-builder-serialize-gclient-sync-on-riscv64.patch new file mode 100644 index 000000000..2a03fa40f --- /dev/null +++ b/patches/mini-racer/0.14.1/0004-builder-serialize-gclient-sync-on-riscv64.patch @@ -0,0 +1,54 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Sun, 13 Sep 2026 04:20:09 +0200 +Subject: [PATCH] builder: serialize gclient sync on riscv64 + +depot_tools' own gclient.py already forces --jobs=1 for 32-bit arm +boards ("Some arm boards have issues with parallel sync"); a riscv64 +CI runner hits the same class of failure during 'gclient sync': +concurrent sync workers race to bootstrap the shared gsutil binary +under a flock()-based lockfile, and lose it with + + lockfile.LockError: ... (err: [Errno 11] Resource temporarily unavailable) + +Serialize the sync on riscv64 the same way upstream already does for +arm. + +Upstream-Status: To upstream [not yet submitted to bpcreech/PyMiniRacer] + +Signed-off-by: Ludovic Henry +--- + builder/v8_build.py | 15 ++++++++++++--- + 1 file changed, 12 insertions(+), 3 deletions(-) + +diff --git a/builder/v8_build.py b/builder/v8_build.py +index 7a08487..4c8f3f4 100644 +--- a/builder/v8_build.py ++++ b/builder/v8_build.py +@@ -173,14 +173,23 @@ solutions = [ + """ + ) + +- run( ++ gclient_args = [ + executable, + str(get_depot_tools_path() / "gclient.py"), + "sync", + "--revision", + f"v8@{revision}", +- cwd=get_workspace_path(), +- ) ++ ] ++ ++ if machine().lower() == "riscv64": ++ # depot_tools' own gclient.py already serializes sync ("-j1") on 32-bit ++ # arm boards because parallel syncs are flaky there; riscv64 boards hit ++ # the same class of failure (a "gsutil" bootstrap lockfile.LockError: ++ # [Errno 11] Resource temporarily unavailable from concurrent workers ++ # racing to fetch it), so do the same here. ++ gclient_args += ["--jobs", "1"] ++ ++ run(*gclient_args, cwd=get_workspace_path()) + + link_name = get_v8_path() / "custom_deps" / "mini_racer" + link_name.unlink(missing_ok=True) From d05eead9a1ff6f23c3dd753a773cf6f0f83e1c2a Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Sun, 13 Sep 2026 04:55:33 +0200 Subject: [PATCH 3/9] mini-racer: skip reclient/siso CIPD packages on riscv64 CI's gclient sync got past the gsutil lockfile race and then failed resolving two CIPD packages neither published for linux-riscv64: infra/rbe/client (remote build execution) and build/siso (the siso build tool). Both are optional accelerators the plain gn+ninja build never needs; skip them via custom_deps. https://github.com/riseproject-dev/python-wheels/actions/runs/34732817721/job/103658674867 --- ...client-siso-CIPD-packages-on-riscv64.patch | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 patches/mini-racer/0.14.1/0005-builder-skip-reclient-siso-CIPD-packages-on-riscv64.patch diff --git a/patches/mini-racer/0.14.1/0005-builder-skip-reclient-siso-CIPD-packages-on-riscv64.patch b/patches/mini-racer/0.14.1/0005-builder-skip-reclient-siso-CIPD-packages-on-riscv64.patch new file mode 100644 index 000000000..2fb969d62 --- /dev/null +++ b/patches/mini-racer/0.14.1/0005-builder-skip-reclient-siso-CIPD-packages-on-riscv64.patch @@ -0,0 +1,64 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Sun, 13 Sep 2026 04:54:55 +0200 +Subject: [PATCH] builder: skip reclient/siso CIPD packages on riscv64 + +v8's DEPS fetches 'buildtools/reclient' (infra/rbe/client, the remote +build execution client) and 'third_party/siso/cipd' (the siso build +tool) on every Linux host; their conditions already exclude +s390x/ppc64/zos and arm64-on-Linux, but not riscv64, and neither +package is published for linux-riscv64: + + no such package: infra/rbe/client/linux-riscv64 + no such package: build/siso/linux-riscv64 + +Both are optional build accelerators that run_build()'s plain +gn gen + ninja.py invocation never asks for, so skip fetching them via +custom_deps instead of failing gclient sync outright. + +Upstream-Status: To upstream [not yet submitted to bpcreech/PyMiniRacer] + +Signed-off-by: Ludovic Henry +--- + builder/v8_build.py | 23 ++++++++++++++++++----- + 1 file changed, 18 insertions(+), 5 deletions(-) + +diff --git a/builder/v8_build.py b/builder/v8_build.py +index 4c8f3f4..9ca7d2e 100644 +--- a/builder/v8_build.py ++++ b/builder/v8_build.py +@@ -159,16 +159,29 @@ def ensure_v8_src(revision: str) -> None: + if not gclient_file.exists(): + get_workspace_path().mkdir(parents=True, exist_ok=True) + ++ custom_deps = "{}" ++ if machine().lower() == "riscv64": ++ # v8's DEPS fetches these two CIPD packages on every Linux host ++ # (their conditions exclude s390x/ppc64/zos and arm64-on-Linux, ++ # but not riscv64), yet neither is published for linux-riscv64; ++ # both are optional build accelerators (remote execution, the ++ # siso build tool) that plain gn+ninja never asks for, so skip ++ # fetching them instead of failing gclient sync outright: ++ custom_deps = """{ ++ "v8/buildtools/reclient": None, ++ "v8/third_party/siso/cipd": None, ++ }""" ++ + gclient_file.write_text( +- """\ ++ f"""\ + solutions = [ +- { "name" : "v8", ++ {{ "name" : "v8", + "url" : "https://chromium.googlesource.com/v8/v8.git", + "deps_file" : "DEPS", + "managed" : False, +- "custom_deps" : {}, +- "custom_vars": {}, +- }, ++ "custom_deps" : {custom_deps}, ++ "custom_vars": {{}}, ++ }}, + ] + """ + ) From 0cda6c89a23491477a940e7b04cc5e6a9653439d Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Sun, 13 Sep 2026 05:38:37 +0200 Subject: [PATCH 4/9] mini-racer: fix the reclient/siso skip -- custom_deps had no effect CI proved the previous approach wrong: gclient still tried (and failed) to resolve infra/rbe/client/linux-riscv64 and build/siso/linux-riscv64 with the custom_deps override in place. CipdDependency.run() never consults the Dependency.url a custom_deps override changes, only should_process, which is derived straight from the DEPS-declared condition -- so nulling out a custom_deps entry can never skip a CIPD package. A global host_cpu var override doesn't work either: gn and ninja share the exact same guard clause and do have riscv64 packages, so spoofing host_cpu would stop them being fetched too. Patch the two conditions in the checked-out v8/DEPS directly instead, after letting the first sync attempt run to completion (it always finishes cloning v8 before it gets to CIPD resolution). https://github.com/riseproject-dev/python-wheels/actions/runs/34734236315/job/103662627780 --- ...client-siso-CIPD-packages-on-riscv64.patch | 142 ++++++++++++------ 1 file changed, 97 insertions(+), 45 deletions(-) diff --git a/patches/mini-racer/0.14.1/0005-builder-skip-reclient-siso-CIPD-packages-on-riscv64.patch b/patches/mini-racer/0.14.1/0005-builder-skip-reclient-siso-CIPD-packages-on-riscv64.patch index 2fb969d62..41c4c7def 100644 --- a/patches/mini-racer/0.14.1/0005-builder-skip-reclient-siso-CIPD-packages-on-riscv64.patch +++ b/patches/mini-racer/0.14.1/0005-builder-skip-reclient-siso-CIPD-packages-on-riscv64.patch @@ -1,64 +1,116 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Ludovic Henry -Date: Sun, 13 Sep 2026 04:54:55 +0200 +Date: Sun, 13 Sep 2026 05:38:02 +0200 Subject: [PATCH] builder: skip reclient/siso CIPD packages on riscv64 -v8's DEPS fetches 'buildtools/reclient' (infra/rbe/client, the remote -build execution client) and 'third_party/siso/cipd' (the siso build -tool) on every Linux host; their conditions already exclude -s390x/ppc64/zos and arm64-on-Linux, but not riscv64, and neither -package is published for linux-riscv64: +v8's DEPS fetches two CIPD packages unconditioned on riscv64: +buildtools/reclient (infra/rbe/client, the remote build execution +client) and third_party/siso/cipd (the siso build tool). Their +conditions exclude s390x/ppc64/zos and arm64-on-Linux, but not +riscv64, and CI confirmed neither package is published for +linux-riscv64: no such package: infra/rbe/client/linux-riscv64 no such package: build/siso/linux-riscv64 -Both are optional build accelerators that run_build()'s plain -gn gen + ninja.py invocation never asks for, so skip fetching them via -custom_deps instead of failing gclient sync outright. +Both are optional build accelerators that plain gn gen + ninja never +asks for. gn and ninja share the exact same guard clause and *do* +have riscv64 packages (verified against +https://chrome-infra-packages.appspot.com/p/gn/gn/linux-riscv64 and +the ninja/cpython3 3pp packages), so this can't be fixed by +overriding gclient's host_cpu var globally -- that would also stop gn +and ninja from being fetched. custom_deps can't target the two +packages by name either: CipdDependency.run() never consults the +Dependency.url a custom_deps override would change, only +should_process (derived from the DEPS-declared condition). + +Instead, let the first sync attempt run to completion (it always +clones v8 in full before it gets to resolving CIPD packages, so this +isn't wasted work), patch the two conditions out of the now-checked- +out DEPS on failure, and retry: gclient re-resolves "v8@{revision}" +to the same commit we are already on, so its git step is a no-op +(unless --reset/--force, neither of which we pass) and the patch +survives into the retry's dependency processing. Upstream-Status: To upstream [not yet submitted to bpcreech/PyMiniRacer] Signed-off-by: Ludovic Henry --- - builder/v8_build.py | 23 ++++++++++++++++++----- - 1 file changed, 18 insertions(+), 5 deletions(-) + builder/v8_build.py | 55 +++++++++++++++++++++++++++++++++++++++------ + 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/builder/v8_build.py b/builder/v8_build.py -index 4c8f3f4..9ca7d2e 100644 +index 4c8f3f4..37a1f15 100644 --- a/builder/v8_build.py +++ b/builder/v8_build.py -@@ -159,16 +159,29 @@ def ensure_v8_src(revision: str) -> None: - if not gclient_file.exists(): - get_workspace_path().mkdir(parents=True, exist_ok=True) +@@ -10,7 +10,7 @@ from platform import machine + from re import match + from shlex import join as shlexjoin + from shutil import copyfile, rmtree +-from subprocess import check_call ++from subprocess import CalledProcessError, check_call + from sys import executable, platform + from typing import TYPE_CHECKING + +@@ -182,14 +182,55 @@ solutions = [ + ] + + if machine().lower() == "riscv64": +- # depot_tools' own gclient.py already serializes sync ("-j1") on 32-bit +- # arm boards because parallel syncs are flaky there; riscv64 boards hit +- # the same class of failure (a "gsutil" bootstrap lockfile.LockError: +- # [Errno 11] Resource temporarily unavailable from concurrent workers +- # racing to fetch it), so do the same here. ++ # depot_tools' own gclient.py already serializes sync ("-j1") on ++ # 32-bit arm boards because parallel syncs are flaky there; riscv64 ++ # boards hit the same class of failure (a "gsutil" bootstrap ++ # lockfile.LockError: [Errno 11] Resource temporarily unavailable ++ # from concurrent workers racing to fetch it), so do the same here. + gclient_args += ["--jobs", "1"] -+ custom_deps = "{}" -+ if machine().lower() == "riscv64": -+ # v8's DEPS fetches these two CIPD packages on every Linux host -+ # (their conditions exclude s390x/ppc64/zos and arm64-on-Linux, -+ # but not riscv64), yet neither is published for linux-riscv64; -+ # both are optional build accelerators (remote execution, the -+ # siso build tool) that plain gn+ninja never asks for, so skip -+ # fetching them instead of failing gclient sync outright: -+ custom_deps = """{ -+ "v8/buildtools/reclient": None, -+ "v8/third_party/siso/cipd": None, -+ }""" +- run(*gclient_args, cwd=get_workspace_path()) ++ try: ++ run(*gclient_args, cwd=get_workspace_path()) ++ except CalledProcessError: ++ # v8 is already fully cloned by this point (gclient always ++ # fetches the solution itself before resolving CIPD packages): ++ # two of its DEPS entries, buildtools/reclient ++ # (infra/rbe/client) and third_party/siso/cipd (the siso build ++ # tool), are unconditioned on riscv64 (their conditions exclude ++ # s390x/ppc64/zos, and arm64 on Linux, but not riscv64) even ++ # though neither package is published for linux-riscv64. Both ++ # are optional build accelerators that plain gn gen + ninja ++ # never asks for. gn and ninja share the exact same guard ++ # clause and *do* have riscv64 packages, so this can't be fixed ++ # by overriding gclient's host_cpu var (it would also disable ++ # gn/ninja); patch the two conditions out of the checkout ++ # instead. ++ deps_file = get_v8_path() / "DEPS" ++ if not deps_file.exists(): ++ raise + - gclient_file.write_text( -- """\ -+ f"""\ - solutions = [ -- { "name" : "v8", -+ {{ "name" : "v8", - "url" : "https://chromium.googlesource.com/v8/v8.git", - "deps_file" : "DEPS", - "managed" : False, -- "custom_deps" : {}, -- "custom_vars": {}, -- }, -+ "custom_deps" : {custom_deps}, -+ "custom_vars": {{}}, -+ }}, - ] - """ - ) ++ text = deps_file.read_text() ++ for condition in ( ++ '(host_os == "linux" or host_os == "mac" or ' ++ 'host_os == "win") and host_cpu != "s390x" and ' ++ 'host_os != "zos" and host_cpu != "ppc64" and ' ++ '(host_cpu != "arm64" or host_os == "mac")', ++ 'not build_with_chromium and host_cpu != "s390x" and ' ++ 'host_os != "zos" and host_cpu != "ppc64"', ++ ): ++ text = text.replace( ++ condition, f'{condition} and host_cpu != "riscv64"', 1 ++ ) ++ deps_file.write_text(text) ++ ++ # Retrying re-resolves "v8@{revision}" to the same commit we ++ # are already sitting on, so gclient's git step is a no-op (it ++ # only touches the working tree on mismatch, or with ++ # --reset/--force, neither of which we pass) and the DEPS ++ # patch survives into this second sync's dependency processing. ++ run(*gclient_args, cwd=get_workspace_path()) ++ else: ++ run(*gclient_args, cwd=get_workspace_path()) + + link_name = get_v8_path() / "custom_deps" / "mini_racer" + link_name.unlink(missing_ok=True) From 1762cbf20715c5c77d9b94e150b53b6b4590bae7 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Sun, 13 Sep 2026 06:39:25 +0200 Subject: [PATCH 5/9] mini-racer: retry the DEPS patch without --revision CI proved the retry itself wrong: gclient still re-resolved and re-checked-out the v8 solution as normal (branch-heads/14.4 is a branch name, not a pinned hash, so the current_revision fast path never applied), refusing to proceed over our uncommitted DEPS edit ("You have uncommitted changes"). Drop --revision from the retry so the .gclient's "managed: False" takes effect (only happens when there is no revision override), skipping v8's git step entirely rather than touching the working tree. https://github.com/riseproject-dev/python-wheels/actions/runs/34735996717/job/103669201128 --- ...etry-the-DEPS-patch-without-revision.patch | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 patches/mini-racer/0.14.1/0006-builder-retry-the-DEPS-patch-without-revision.patch diff --git a/patches/mini-racer/0.14.1/0006-builder-retry-the-DEPS-patch-without-revision.patch b/patches/mini-racer/0.14.1/0006-builder-retry-the-DEPS-patch-without-revision.patch new file mode 100644 index 000000000..88886d3ca --- /dev/null +++ b/patches/mini-racer/0.14.1/0006-builder-retry-the-DEPS-patch-without-revision.patch @@ -0,0 +1,137 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Sun, 13 Sep 2026 06:38:56 +0200 +Subject: [PATCH] builder: retry the DEPS patch without --revision + +CI proved the retry itself wrong: gclient still passed +--revision v8@branch-heads/14.4 on the second sync, so it re-resolved +and re-checked-out the v8 solution as normal (branch-heads/14.4 is a +branch name, not a pinned hash, so the current_revision fast path +never applied) -- which refused to proceed over our uncommitted DEPS +edit: + + You have uncommitted changes. + cd into v8, run git status to see changes, + and commit, stash, or reset. + +Drop --revision from the retry instead. The .gclient's +"managed: False" for the v8 solution only takes effect when there is +no revision override for it (gclient.py: 'if not revision_override and +not self.managed: revision_override = "unmanaged"'), which then skips +its git step entirely ("unmanaged solution; skipping v8") rather than +touching the working tree -- leaving the patch in place for the +retry's DEPS reprocessing. + +Upstream-Status: To upstream [not yet submitted to bpcreech/PyMiniRacer] + +Signed-off-by: Ludovic Henry +--- + builder/v8_build.py | 88 +++++++++++++++++++++++---------------------- + 1 file changed, 45 insertions(+), 43 deletions(-) + +diff --git a/builder/v8_build.py b/builder/v8_build.py +index 37a1f15..aeaf0a3 100644 +--- a/builder/v8_build.py ++++ b/builder/v8_build.py +@@ -177,8 +177,6 @@ solutions = [ + executable, + str(get_depot_tools_path() / "gclient.py"), + "sync", +- "--revision", +- f"v8@{revision}", + ] + + if machine().lower() == "riscv64": +@@ -189,47 +187,51 @@ solutions = [ + # from concurrent workers racing to fetch it), so do the same here. + gclient_args += ["--jobs", "1"] + +- try: +- run(*gclient_args, cwd=get_workspace_path()) +- except CalledProcessError: +- # v8 is already fully cloned by this point (gclient always +- # fetches the solution itself before resolving CIPD packages): +- # two of its DEPS entries, buildtools/reclient +- # (infra/rbe/client) and third_party/siso/cipd (the siso build +- # tool), are unconditioned on riscv64 (their conditions exclude +- # s390x/ppc64/zos, and arm64 on Linux, but not riscv64) even +- # though neither package is published for linux-riscv64. Both +- # are optional build accelerators that plain gn gen + ninja +- # never asks for. gn and ninja share the exact same guard +- # clause and *do* have riscv64 packages, so this can't be fixed +- # by overriding gclient's host_cpu var (it would also disable +- # gn/ninja); patch the two conditions out of the checkout +- # instead. +- deps_file = get_v8_path() / "DEPS" +- if not deps_file.exists(): +- raise +- +- text = deps_file.read_text() +- for condition in ( +- '(host_os == "linux" or host_os == "mac" or ' +- 'host_os == "win") and host_cpu != "s390x" and ' +- 'host_os != "zos" and host_cpu != "ppc64" and ' +- '(host_cpu != "arm64" or host_os == "mac")', +- 'not build_with_chromium and host_cpu != "s390x" and ' +- 'host_os != "zos" and host_cpu != "ppc64"', +- ): +- text = text.replace( +- condition, f'{condition} and host_cpu != "riscv64"', 1 +- ) +- deps_file.write_text(text) +- +- # Retrying re-resolves "v8@{revision}" to the same commit we +- # are already sitting on, so gclient's git step is a no-op (it +- # only touches the working tree on mismatch, or with +- # --reset/--force, neither of which we pass) and the DEPS +- # patch survives into this second sync's dependency processing. +- run(*gclient_args, cwd=get_workspace_path()) +- else: ++ try: ++ run(*gclient_args, "--revision", f"v8@{revision}", cwd=get_workspace_path()) ++ except CalledProcessError: ++ if machine().lower() != "riscv64": ++ raise ++ ++ # v8 is already fully cloned by this point (gclient always fetches ++ # the solution itself before resolving CIPD packages): two of its ++ # DEPS entries, buildtools/reclient (infra/rbe/client) and ++ # third_party/siso/cipd (the siso build tool), are unconditioned on ++ # riscv64 (their conditions exclude s390x/ppc64/zos, and arm64 on ++ # Linux, but not riscv64) even though neither package is published ++ # for linux-riscv64. Both are optional build accelerators that ++ # plain gn gen + ninja never asks for. gn and ninja share the exact ++ # same guard clause and *do* have riscv64 packages, so this can't ++ # be fixed by overriding gclient's host_cpu var (it would also ++ # disable gn/ninja); patch the two conditions out of the checkout ++ # instead. ++ deps_file = get_v8_path() / "DEPS" ++ if not deps_file.exists(): ++ raise ++ ++ text = deps_file.read_text() ++ for condition in ( ++ '(host_os == "linux" or host_os == "mac" or ' ++ 'host_os == "win") and host_cpu != "s390x" and ' ++ 'host_os != "zos" and host_cpu != "ppc64" and ' ++ '(host_cpu != "arm64" or host_os == "mac")', ++ 'not build_with_chromium and host_cpu != "s390x" and ' ++ 'host_os != "zos" and host_cpu != "ppc64"', ++ ): ++ text = text.replace( ++ condition, f'{condition} and host_cpu != "riscv64"', 1 ++ ) ++ deps_file.write_text(text) ++ ++ # Retry *without* --revision: "branch-heads/14.4" isn't a pinned ++ # hash, so passing it again makes gclient re-resolve and re-check- ++ # out the v8 solution as normal, which refuses to proceed over our ++ # uncommitted DEPS edit ("You have uncommitted changes"). Our ++ # .gclient marks the v8 solution "managed: False", which gclient ++ # only honors (skipping its git step entirely, "unmanaged ++ # solution; skipping v8") when there is *no* revision override for ++ # it -- leaving our patch in place for this sync's DEPS ++ # reprocessing. + run(*gclient_args, cwd=get_workspace_path()) + + link_name = get_v8_path() / "custom_deps" / "mini_racer" From 135feab509a4c26336e5930d6a0ba108b96ba6df Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Sun, 13 Sep 2026 08:10:22 +0200 Subject: [PATCH 6/9] mini-racer: use_sysroot=false, use_custom_libcxx=false, enable_rust=false on riscv64 CI got past the DEPS/CIPD fixes into an actual ninja build for the first time, surfacing three independent problems: the bundled debian_trixie_riscv64-sysroot is missing a multiarch header our riscv64-linux-gnu- toolprefix trick expects, v8's custom libc++ needs GCC 15+ (ours is older), and the prebuilt Rust toolchain segfaults on riscv64 (published for amd64/arm64/mac hosts only, same as clang). https://github.com/riseproject-dev/python-wheels/actions/runs/34738407307/job/103678169855 --- ...oot-false-use_custom_libcxx-false-en.patch | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 patches/mini-racer/0.14.1/0007-builder-use_sysroot-false-use_custom_libcxx-false-en.patch diff --git a/patches/mini-racer/0.14.1/0007-builder-use_sysroot-false-use_custom_libcxx-false-en.patch b/patches/mini-racer/0.14.1/0007-builder-use_sysroot-false-use_custom_libcxx-false-en.patch new file mode 100644 index 000000000..a2a554d54 --- /dev/null +++ b/patches/mini-racer/0.14.1/0007-builder-use_sysroot-false-use_custom_libcxx-false-en.patch @@ -0,0 +1,71 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Sun, 13 Sep 2026 08:09:44 +0200 +Subject: [PATCH] builder: use_sysroot=false, use_custom_libcxx=false, + enable_rust=false on riscv64 + +CI got past the DEPS/CIPD fixes and into an actual ninja build for the +first time, which surfaced three independent problems: + +* use_sysroot defaults to Chromium's bundled + build/linux/debian_trixie_riscv64-sysroot, meant for *cross*-compiling + from an amd64 host. On a native riscv64 build host it fails + ('bits/wordsize.h: No such file or directory' -- its usr/include + lacks the multiarch subdirectory our riscv64-linux-gnu- toolprefix + trick expects from a real cross toolchain's built-in search paths). + Use the host's own headers/libc, which are already the right ones. + +* v8's custom (LLVM) libc++ requires GCC 15+ to build with GCC + (third_party/libc++/src/include/__configuration/compiler.h); our + manylinux_2_39_riscv64 image's GCC is older. Use system libstdc++, + which is what a GCC build normally pairs with. + +* Something still invokes third_party/rust-toolchain/bin/rustc despite + enable_rust defaulting to build_with_chromium (false for our + standalone checkout), and it segfaults on riscv64 + ('rustc --print=cfg --target=riscv64gc-unknown-linux-gnu' died with + SIGSEGV) -- like Chromium's clang, the prebuilt Rust toolchain is + published for amd64/arm64/mac hosts only. Disable Rust explicitly. + +Upstream-Status: To upstream [not yet submitted to bpcreech/PyMiniRacer] + +Signed-off-by: Ludovic Henry +--- + builder/v8_build.py | 24 ++++++++++++++++++++++++ + 1 file changed, 24 insertions(+) + +diff --git a/builder/v8_build.py b/builder/v8_build.py +index aeaf0a3..c82341e 100644 +--- a/builder/v8_build.py ++++ b/builder/v8_build.py +@@ -310,6 +310,30 @@ def run_build(build_dir: Path, args: Args) -> None: + # build/toolchain/linux/BUILD.gn: + opts["is_clang"] = "false" + ++ # use_sysroot defaults to fetching and building against Chromium's ++ # bundled build/linux/debian_trixie_riscv64-sysroot, meant for ++ # *cross*-compiling from an amd64 host; on a native riscv64 build ++ # host it fails outright (its usr/include lacks the multiarch ++ # bits/wordsize.h our riscv64-linux-gnu- toolprefix trick expects, ++ # since it isn't a real cross toolchain with matching built-in ++ # search paths). Use the host's own headers/libc instead, which are ++ # already the right ones for a native build: ++ opts["use_sysroot"] = "false" ++ ++ # v8's custom (LLVM) libc++ requires GCC 15+ to build with GCC ++ # (//third_party/libc++/src/include/__configuration/compiler.h); ++ # our manylinux_2_39_riscv64 image's GCC is older. Use the system ++ # libstdc++ instead, which is what a GCC build normally pairs with: ++ opts["use_custom_libcxx"] = "false" ++ ++ # enable_rust defaults to build_with_chromium (false for our ++ # standalone v8 checkout), but the prebuilt third_party/rust-toolchain ++ # is, like Chromium's clang, published for amd64/arm64/mac hosts only; ++ # something still invokes it for our riscv64 build and segfaults ++ # trying to run it, so disable Rust explicitly rather than rely on a ++ # default this pinned build/rust.gni revision may compute differently: ++ opts["enable_rust"] = "false" ++ + if is_linux() and target_cpu != get_local_v8_target_cpu(): + run( + executable, From d8e7109daac16c0cc3ca13bd95e0fabf6df6ae7a Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Sun, 13 Sep 2026 09:05:00 +0200 Subject: [PATCH 7/9] mini-racer: also disable v8_enable_temporal_support on riscv64 enable_rust=false alone doesn't work: gn gen failed with 'assert(enable_rust)' in build/rust/gni_impl/rust_target.gni, reached via v8_maybe_temporal -> third_party/rust/temporal_capi. v8_enable_temporal_support's own default already excludes ppc64 and s390x by name ('some architectures don't have Rust toolchains in Chromium') -- riscv64 is simply missing from that list. Temporal is runtime-flag-gated even when built in, so disabling it drops no default-on behavior. https://github.com/riseproject-dev/python-wheels/actions/runs/34742097041/job/103685126595 --- ...able-v8_enable_temporal_support-on-r.patch | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 patches/mini-racer/0.14.1/0008-builder-also-disable-v8_enable_temporal_support-on-r.patch diff --git a/patches/mini-racer/0.14.1/0008-builder-also-disable-v8_enable_temporal_support-on-r.patch b/patches/mini-racer/0.14.1/0008-builder-also-disable-v8_enable_temporal_support-on-r.patch new file mode 100644 index 000000000..5997a03a5 --- /dev/null +++ b/patches/mini-racer/0.14.1/0008-builder-also-disable-v8_enable_temporal_support-on-r.patch @@ -0,0 +1,59 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Sun, 13 Sep 2026 09:04:26 +0200 +Subject: [PATCH] builder: also disable v8_enable_temporal_support on riscv64 + +enable_rust=false alone doesn't work: gn gen failed with + + ERROR at //build/rust/gni_impl/rust_target.gni:41:3: Assertion failed. + assert(enable_rust) + See //build/rust/rust_static_library.gni:222:3: whence it was called. + See //build/rust/cargo_crate.gni:252:5: whence it was called. + See //third_party/rust/temporal_capi/v0_1/BUILD.gn:11:1: whence it was called. + See //third_party/rust/temporal_capi/BUILD.gn:33:19: which caused the + file to be included. + +v8_enable_temporal_support (gni/v8.gni) unconditionally depends on +//third_party/rust/temporal_capi via the v8_maybe_temporal group in +BUILD.gn, and its own default expression already excludes ppc64 and +s390x by name, with the comment "some architectures don't have Rust +toolchains in Chromium" -- riscv64 is simply missing from that list. +Temporal is runtime-flag-gated even when built in (needs +--harmony-temporal per gni/v8.gni's own comment), so disabling it +drops no default-on behavior. + +Upstream-Status: To upstream [not yet submitted to bpcreech/PyMiniRacer] + +Signed-off-by: Ludovic Henry +--- + builder/v8_build.py | 17 +++++++++++------ + 1 file changed, 11 insertions(+), 6 deletions(-) + +diff --git a/builder/v8_build.py b/builder/v8_build.py +index c82341e..0cc9bb3 100644 +--- a/builder/v8_build.py ++++ b/builder/v8_build.py +@@ -326,12 +326,17 @@ def run_build(build_dir: Path, args: Args) -> None: + # libstdc++ instead, which is what a GCC build normally pairs with: + opts["use_custom_libcxx"] = "false" + +- # enable_rust defaults to build_with_chromium (false for our +- # standalone v8 checkout), but the prebuilt third_party/rust-toolchain +- # is, like Chromium's clang, published for amd64/arm64/mac hosts only; +- # something still invokes it for our riscv64 build and segfaults +- # trying to run it, so disable Rust explicitly rather than rely on a +- # default this pinned build/rust.gni revision may compute differently: ++ # v8_enable_temporal_support (gni/v8.gni) already excludes ppc64 and ++ # s390x by name, with the comment "some architectures don't have ++ # Rust toolchains in Chromium" -- riscv64 is simply missing from ++ # that list. It pulls in //third_party/rust/temporal_capi ++ # unconditionally when true (v8_maybe_temporal in BUILD.gn), and the ++ # prebuilt third_party/rust-toolchain (like Chromium's clang) isn't ++ # usable on riscv64 (it segfaulted: 'rustc --print=cfg ++ # --target=riscv64gc-unknown-linux-gnu' died with SIGSEGV). ++ # Temporal is runtime-flag-gated even when built in (needs ++ # --harmony-temporal), so this drops no default-on behavior: ++ opts["v8_enable_temporal_support"] = "false" + opts["enable_rust"] = "false" + + if is_linux() and target_cpu != get_local_v8_target_cpu(): From 1bc590299b18fad00fb1e9499d7f5a5bfd164a46 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Sun, 13 Sep 2026 10:41:01 +0200 Subject: [PATCH 8/9] mini-racer: patch a GCC/Clang source incompatibility in unicode.h; install patch(1) CI got past gn gen and almost an hour into the actual ninja build, then failed compiling with GCC (is_clang=false): ../../src/strings/unicode.h:218:13: error: explicit specialization in non-namespace scope 'class unibrow::Utf8' unibrow::Utf8::WriteLeadingAscii's two explicit specializations are declared a second time inside the class body; clang accepts this as an extension, GCC correctly rejects it (explicit specializations of a member template must be at namespace scope). The out-of-line definitions in unicode.cc are already namespace-scope and standard-conformant on their own, so the in-class declarations are both redundant and non-portable. Wire up the previously-unused apply_patch() to drop them for a riscv64 build, and install the patch(1) utility it shells out to. https://github.com/riseproject-dev/python-wheels/actions/runs/34744357842/job/103689419698 --- .github/workflows/build-mini-racer.yml | 2 +- ...GCC-Clang-source-incompatibility-in-.patch | 78 +++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 patches/mini-racer/0.14.1/0009-builder-patch-a-GCC-Clang-source-incompatibility-in-.patch diff --git a/.github/workflows/build-mini-racer.yml b/.github/workflows/build-mini-racer.yml index c7204e879..8c3f89a43 100644 --- a/.github/workflows/build-mini-racer.yml +++ b/.github/workflows/build-mini-racer.yml @@ -59,7 +59,7 @@ jobs: run: git apply python-wheels/patches/mini-racer/${{ env.MINI_RACER_VERSION }}/*.patch - name: Install build dependencies - run: dnf install -y git curl which + run: dnf install -y git curl which patch - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 with: diff --git a/patches/mini-racer/0.14.1/0009-builder-patch-a-GCC-Clang-source-incompatibility-in-.patch b/patches/mini-racer/0.14.1/0009-builder-patch-a-GCC-Clang-source-incompatibility-in-.patch new file mode 100644 index 000000000..6c2dc281e --- /dev/null +++ b/patches/mini-racer/0.14.1/0009-builder-patch-a-GCC-Clang-source-incompatibility-in-.patch @@ -0,0 +1,78 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Sun, 13 Sep 2026 10:39:42 +0200 +Subject: [PATCH] builder: patch a GCC/Clang source incompatibility in + unicode.h + +CI got past gn gen and almost an hour into the actual ninja build, +then failed compiling with GCC (is_clang=false, see above): + + ../../src/strings/unicode.h:218:13: error: explicit specialization + in non-namespace scope 'class unibrow::Utf8' + ../../src/strings/unicode.h:219:10: error: template-id + 'WriteLeadingAscii' in declaration of primary template + +unibrow::Utf8::WriteLeadingAscii's two explicit specializations are +declared a second time inside the class body in unicode.h; clang +accepts this as an extension, GCC correctly rejects it per the +standard (explicit specializations of a member template must be +declared at namespace scope). The out-of-line definitions in +unicode.cc are already namespace-scope ('namespace unibrow { template +<> size_t Utf8::WriteLeadingAscii(...) {...} }') and +standard-conformant on their own, so the in-class declarations are +both redundant and non-portable. Wire up the previously-unused +apply_patch() to drop them for a riscv64 (GCC) build. + +Upstream-Status: To upstream [not yet submitted to bpcreech/PyMiniRacer] + +Signed-off-by: Ludovic Henry +--- + ...iscv64-gcc-unicode-write-leading-ascii.patch | 17 +++++++++++++++++ + builder/v8_build.py | 10 ++++++++++ + 2 files changed, 27 insertions(+) + create mode 100644 builder/0001-riscv64-gcc-unicode-write-leading-ascii.patch + +diff --git a/builder/0001-riscv64-gcc-unicode-write-leading-ascii.patch b/builder/0001-riscv64-gcc-unicode-write-leading-ascii.patch +new file mode 100644 +index 0000000..6efd1ce +--- /dev/null ++++ b/builder/0001-riscv64-gcc-unicode-write-leading-ascii.patch +@@ -0,0 +1,17 @@ ++--- src/strings/unicode.h 2026-09-13 10:38:02 +++++ src/strings/unicode.h 2026-09-13 10:38:02 ++@@ -214,14 +214,6 @@ ++ ++ template ++ static size_t WriteLeadingAscii(const Char* src, char* dest, size_t size); ++- ++- template <> ++- size_t WriteLeadingAscii(const uint8_t* src, char* dest, ++- size_t size); ++- ++- template <> ++- size_t WriteLeadingAscii(const uint16_t* src, char* dest, ++- size_t size); ++ ++ // Encode the given characters as Utf8 into the provided output buffer. ++ struct EncodingResult { +diff --git a/builder/v8_build.py b/builder/v8_build.py +index 0cc9bb3..77cbb87 100644 +--- a/builder/v8_build.py ++++ b/builder/v8_build.py +@@ -393,6 +393,16 @@ def build_v8(args: Args) -> None: + if args.fetch_only: + return + ++ if machine().lower() == "riscv64": ++ # GCC (used instead of clang on riscv64, see is_clang=false above) ++ # rejects unibrow::Utf8::WriteLeadingAscii's explicit specializations ++ # declared inside the class body ("explicit specialization in ++ # non-namespace scope 'class unibrow::Utf8'"); clang accepts it as ++ # an extension. The out-of-line definitions in unicode.cc are ++ # already namespace-scope and standard-conformant on their own, so ++ # the in-class declarations are both redundant and non-portable: ++ apply_patch("0001-riscv64-gcc-unicode-write-leading-ascii.patch") ++ + build_dir = get_v8_path() / "out.gn" / "build" + + run_build(build_dir, args) From 2f77572c55779d2fefebe0fed56c1a40335cf2f1 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Sun, 13 Sep 2026 13:55:27 +0200 Subject: [PATCH 9/9] mini-racer: fold in a second GCC compat fix, keep ninja going on riscv64 CI got past the unicode.h fix and ~46% (1011/2176) into the ninja build before hitting another GCC-only warning-as-error in bigint/mul-karatsuba.cc ('1 << (shift - 2)' is int, compared against a uint32_t; clang doesn't flag it, GCC does under -Wsign-compare). Cast it explicitly. Renamed the accumulating patch to reflect that this is evidently a class of issue, and pass ninja -k 1000 on riscv64 so one build surfaces as many of these as it can find, instead of one discovery per multi-hour CI cycle. https://github.com/riseproject-dev/python-wheels/actions/runs/34748309593/job/103700194955 --- ...a-second-GCC-compat-fix-keep-ninja-g.patch | 144 ++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 patches/mini-racer/0.14.1/0010-builder-fold-in-a-second-GCC-compat-fix-keep-ninja-g.patch diff --git a/patches/mini-racer/0.14.1/0010-builder-fold-in-a-second-GCC-compat-fix-keep-ninja-g.patch b/patches/mini-racer/0.14.1/0010-builder-fold-in-a-second-GCC-compat-fix-keep-ninja-g.patch new file mode 100644 index 000000000..430324113 --- /dev/null +++ b/patches/mini-racer/0.14.1/0010-builder-fold-in-a-second-GCC-compat-fix-keep-ninja-g.patch @@ -0,0 +1,144 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Sun, 13 Sep 2026 13:54:30 +0200 +Subject: [PATCH] builder: fold in a second GCC compat fix, keep ninja going on + riscv64 + +CI got past the unicode.h fix and ~46% (1011/2176) into the ninja +build before hitting another GCC-only warning-as-error: + + ../../src/bigint/mul-karatsuba.cc:51:38: error: comparison of + integer expressions of different signedness: 'uint32_t' and 'int' + [-Werror=sign-compare] + if (shift >= 2 && (len & additive) < (1 << (shift - 2))) { + +'1 << (shift - 2)' is int, 'len & additive' is uint32_t; clang doesn't +flag this particular comparison, GCC does. Cast it explicitly, the +same way the line above already assigns '1 << shift' into a uint32_t. + +Rename the accumulating patch to 0001-riscv64-gcc-compat.patch (was +scoped to the unicode.h fix alone) since this is evidently a class of +issue, not a one-off, and pass ninja -k 1000 on riscv64 so one build +surfaces as many of these as it can find, instead of one discovery per +multi-hour CI cycle. + +Upstream-Status: To upstream [not yet submitted to bpcreech/PyMiniRacer] + +Signed-off-by: Ludovic Henry +--- + builder/0001-riscv64-gcc-compat.patch | 28 ++++++++++++++++++ + ...cv64-gcc-unicode-write-leading-ascii.patch | 17 ----------- + builder/v8_build.py | 29 ++++++++++++++----- + 3 files changed, 49 insertions(+), 25 deletions(-) + create mode 100644 builder/0001-riscv64-gcc-compat.patch + delete mode 100644 builder/0001-riscv64-gcc-unicode-write-leading-ascii.patch + +diff --git a/builder/0001-riscv64-gcc-compat.patch b/builder/0001-riscv64-gcc-compat.patch +new file mode 100644 +index 0000000..ee8eb3a +--- /dev/null ++++ b/builder/0001-riscv64-gcc-compat.patch +@@ -0,0 +1,28 @@ ++--- src/bigint/mul-karatsuba.cc 2026-09-13 13:52:35 +++++ src/bigint/mul-karatsuba.cc 2026-09-13 13:52:35 ++@@ -48,7 +48,7 @@ ++ // Round up, unless we're only just above the threshold. This smoothes ++ // the steps by which time goes up as input size increases. ++ uint32_t additive = ((1 << shift) - 1); ++- if (shift >= 2 && (len & additive) < (1 << (shift - 2))) { +++ if (shift >= 2 && (len & additive) < static_cast(1 << (shift - 2))) { ++ return len; ++ } ++ return ((len + additive) >> shift) << shift; ++--- src/strings/unicode.h 2026-09-13 13:52:35 +++++ src/strings/unicode.h 2026-09-13 13:52:35 ++@@ -215,14 +215,6 @@ ++ template ++ static size_t WriteLeadingAscii(const Char* src, char* dest, size_t size); ++ ++- template <> ++- size_t WriteLeadingAscii(const uint8_t* src, char* dest, ++- size_t size); ++- ++- template <> ++- size_t WriteLeadingAscii(const uint16_t* src, char* dest, ++- size_t size); ++- ++ // Encode the given characters as Utf8 into the provided output buffer. ++ struct EncodingResult { ++ size_t bytes_written; +diff --git a/builder/0001-riscv64-gcc-unicode-write-leading-ascii.patch b/builder/0001-riscv64-gcc-unicode-write-leading-ascii.patch +deleted file mode 100644 +index 6efd1ce..0000000 +--- a/builder/0001-riscv64-gcc-unicode-write-leading-ascii.patch ++++ /dev/null +@@ -1,17 +0,0 @@ +---- src/strings/unicode.h 2026-09-13 10:38:02 +-+++ src/strings/unicode.h 2026-09-13 10:38:02 +-@@ -214,14 +214,6 @@ +- +- template +- static size_t WriteLeadingAscii(const Char* src, char* dest, size_t size); +-- +-- template <> +-- size_t WriteLeadingAscii(const uint8_t* src, char* dest, +-- size_t size); +-- +-- template <> +-- size_t WriteLeadingAscii(const uint16_t* src, char* dest, +-- size_t size); +- +- // Encode the given characters as Utf8 into the provided output buffer. +- struct EncodingResult { +diff --git a/builder/v8_build.py b/builder/v8_build.py +index 77cbb87..a5457e7 100644 +--- a/builder/v8_build.py ++++ b/builder/v8_build.py +@@ -361,12 +361,23 @@ def run_build(build_dir: Path, args: Args) -> None: + ) + + # Finally, actually do the build: +- run( ++ ninja_args = [ + executable, + str(get_depot_tools_path() / "ninja.py"), + # "-vv", # too much spam for GitHub Actions + "-C", + str(build_dir), ++ ] ++ if target_cpu == "riscv64": ++ # This is the first time this codebase has been compiled with GCC ++ # for this target in anger, so there is a long tail of small ++ # GCC-vs-clang source incompatibilities to find (see apply_patch() ++ # calls above). Keep going past failures so one build surfaces as ++ # many of them as possible, instead of one per multi-hour CI cycle: ++ ninja_args += ["-k", "1000"] ++ ++ run( ++ *ninja_args, + str(Path("custom_deps") / "mini_racer"), + cwd=get_v8_path(), + ) +@@ -395,13 +406,15 @@ def build_v8(args: Args) -> None: + + if machine().lower() == "riscv64": + # GCC (used instead of clang on riscv64, see is_clang=false above) +- # rejects unibrow::Utf8::WriteLeadingAscii's explicit specializations +- # declared inside the class body ("explicit specialization in +- # non-namespace scope 'class unibrow::Utf8'"); clang accepts it as +- # an extension. The out-of-line definitions in unicode.cc are +- # already namespace-scope and standard-conformant on their own, so +- # the in-class declarations are both redundant and non-portable: +- apply_patch("0001-riscv64-gcc-unicode-write-leading-ascii.patch") ++ # is stricter than clang about a few things in v8's C++, e.g. an ++ # explicit specialization declared inside a class body ++ # (unibrow::Utf8::WriteLeadingAscii in unicode.h -- the out-of-line ++ # definitions in unicode.cc are already namespace-scope and ++ # standard-conformant on their own) and a signed/unsigned ++ # comparison under -Werror=sign-compare (bigint/mul-karatsuba.cc). ++ # Both are narrow, mechanical fixes; accumulate any more of this ++ # kind we hit in the same patch: ++ apply_patch("0001-riscv64-gcc-compat.patch") + + build_dir = get_v8_path() / "out.gn" / "build" +