Author: Ludovic Henry, Qualcomm
A working package ecosystem is part of a platform’s infrastructure. Developers should be able to ask for Python, CMake, OpenSSL, NumPy, or a compiler without first rebuilding a dependency tree by hand. For RISC-V, making that experience routine requires more than porting a package manager: it requires a target sysroot, cross-compiling and native toolchains, build images, dependency-ordered package migrations, target-aware tests, and enough real hardware to run continuous integration at scale.
RISE has been working with the conda-forge community and openKylin contributors to build that foundation for linux-riscv64. The result is an early but usable conda-forge platform: RISC-V Miniforge installers are available, Python and hundreds of package artifacts can be installed from conda-forge, the migration is progressing through the dependency graph, and selected feedstocks are already building and testing on physical machines provided through RISE RISC-V Runners.
This post explains how that bootstrap worked, what hundreds of feedstock changes taught us, and what we need next: broader package coverage, an RVA23 path, and substantially more fast native hardware.
Bootstrapping a package ecosystem
The migration effort begins in conda-forge issue #1744, opened in 2022. The work accelerated after openKylin presented its RISC-V case to the conda-forge core meeting in November 2025 and committed sustained engineering resources. The collaborators followed a playbook conda-forge had refined while enabling other architectures: build the lowest layers first, establish maintainership, and only then let migration automation expand through the package graph.
The first prerequisite was technical: build the lowest layer identified in the issue’s maintained bootstrap checklist. The work was organized as a dependency stack. Each layer enabled the next, ending with a standard build environment that is now available as linux-anvil-riscv64.
| Layer | Contents | Why it was needed |
|---|---|---|
| 1. Target platform base | Sysroot and kernel headers | Defines the RISC-V Linux headers, libraries, and glibc baseline against which packages link |
| 2. Toolchains and core libraries | Binutils, GCC cross compiler, Rust, and core libraries | Makes it possible to cross-compile the package base and build tools that run on RISC-V |
| 3. Package ecosystem base | Python, NumPy, OpenBLAS, SciPy, CMake, and their dependencies | Provides the package closure needed by standard conda-forge build and scientific-computing workflows |
| 4. Build environment | conda-forge-ci-setup and linux-anvil-riscv64 |
Delivers the standardized setup package and image used to build conda feedstocks for linux-riscv64 |
The sysroot provides the headers and libraries against which linux-riscv64 binaries are linked. conda-forge selected a Rocky Linux 10-derived, glibc 2.39 base because Rocky published RISC-V packages while AlmaLinux did not yet. The first binutils and GCC 15 compiler packages ran on x86-64 but emitted RISC-V code. Those cross-compilers made it possible to produce enough of the target stack to build cross-native compiler packages: binaries created with the cross-toolchain, but which run on RISC-V and compile for RISC-V.
Cross-compilation, emulation, and native execution
| Cross-compilation | Emulated target testing | Native compilation and testing | |
|---|---|---|---|
| Where the build runs | linux-64/x86-64 builder |
linux-64/x86-64 builder |
linux-riscv64 builder |
| What produces the binary | RISC-V cross-toolchain and sysroot | RISC-V cross-toolchain and sysroot | Native toolchain running on RISC-V |
| Where target code runs | It does not run as part of the build | QEMU on the x86-64 builder | The RISC-V runner itself |
| Main use | Bootstrap and scale across the package graph | Target execution when native hardware is unavailable | Native compilation and direct target validation |
| Trade-off | Efficient use of existing build capacity, but target execution is separate | Provides execution coverage, but slower and less representative than hardware | Direct and trustworthy target execution, but depends on native hardware capacity |
Cross-compilation gives conda-forge scalable access to its existing builder fleet. Native execution catches assumptions that compilation alone cannot find and is necessary for software that cannot practically be cross-built. A sustainable platform needs both rather than treating either as a universal answer.
By May 2026, the cross-toolchain and activation packages were in place. By July, RISC-V-hosted GNU compiler and binutils packages had landed. This completed the toolchain path from an x86-64 cross build to a compiler running on RISC-V itself. Python was a major dependency-graph unlock; Miniforge release 26.7.2-0 added a linux-riscv64 installer, and conda-forge published the Rocky Linux 10-based linux-anvil-riscv64 image. A RISC-V user could then install the package manager and Python from conda-forge, while maintainers had a standard environment in which to build additional packages.
Maintainer capacity was established alongside the technical work. The @conda-forge/help-riscv64 team gives migration PRs a group of architecture specialists to call on. A feedstock maintainer does not need to become a RISC-V toolchain expert simply because a new platform is being added.
How the automatic migration works
The bootstrap packages were intentionally handled first. Once the initial toolchain was available, conda-forge-bot #6316 enabled an automated, dependency-aware linux-riscv64 migration. conda-forge-pinning #8787 identified the initial package closure needed to build conda-forge-ci-setup and the linux-anvil-riscv64 image.
The bot generated the standard Support linux-riscv64 platform PRs for that closure. It adds the RISC-V build-platform mapping and rerenders the generated CI configuration:
build_platform:
linux_riscv64: linux_64
This mapping means that the generated job runs on x86-64 while resolving a linux-riscv64 host prefix and selecting the target compiler and sysroot. The bot follows the dependency graph, so it can start with low-level libraries and make dependents eligible when their parents have completed. That process produced the complete bootstrap stack shown above.
Automation did not remove the need for engineering, but it made the repetitive configuration change consistent and dependency ordered. Package-specific failures still need review and manual fixes. It can also encounter graph problems. For example, cyclic dependencies can make a package appear complete when it actually requires manual intervention, as discussed in issue #1744. Those cases explain why a small number of migration PRs are opened or repaired manually.
The live migration dashboard now tracks the remaining work beyond the bootstrap closure. Additional feedstocks can rely on linux-anvil-riscv64 and conda-forge-ci-setup; the next concern is using native RISC-V runners to compile and test them on the target architecture.
Python as a dependency-graph unlock
The migration graph records immediate_children, with an edge from a package to the packages it unblocks. Python is a useful example of why the bootstrap order matters. It depends on a base stack that includes ncurses, sqlite, libedit, openssl, zlib, and zstd; once Python is available, it unblocks package infrastructure and a broad set of Python extensions and applications.
The diagram is deliberately a selected subgraph, not Python’s complete dependency closure.
A real cycle in the migration graph
Dependency automation needs an exception path because package graphs can contain cycles. For example, there is a cyclic dependency between python, llvmdev, cctools-and-ld64, and ncurses: python depends on ncurses, ncurses depends on cctools, cctools depends on llvmdev, llvmdev depends on python.
This is why the bot cannot be treated as a replacement for maintainers. It can apply the standard platform configuration and order ordinary edges, but a cycle requires a deliberate bootstrap decision or repair before the remaining nodes can proceed.
Build tools are not target libraries
Cross-compilation requires recipes to distinguish three worlds cleanly: tools that run during the build, libraries linked into the target artifact, and dependencies needed when that artifact runs. Code generators must execute on the build machine; headers and libraries must describe the target. Python extension builds may need cross-python; Rust, Go, C, C++, and Fortran projects each bring their own target triples and build-system behavior.
Many migration PRs therefore moved dependencies between build, host, and run sections or added explicit cross-compilation guards. These are not RISC-V-only hacks, they repair portability assumptions and make the recipes better descriptions of how the software is actually built.
A green build is not necessarily a tested package
A RISC-V executable cannot run directly on an x86-64 host. Depending on the project, tests had to run under QEMU, wait for native CI, or be split so build-host tools ran immediately while target checks ran elsewhere. Numerical libraries also exposed architecture-specific tolerances and runtime limits.
The migration uncovered a particularly important gap: some v1 cross-compiled recipes using rattler-build did not execute target tests. The community tracked that in the conda-forge discussion and the upstream rattler-build issue, rather than confusing successful compilation with successful validation. Native runners are valuable here because they turn architecture support from “the file linked” into “the software actually ran.”
Architecture shortcuts become correctness bugs
A surprisingly common pattern was logic equivalent to “Linux and not Arm means x86-64.” Once linux-riscv64 exists, that can download an x86-64 archive and place it in a package carrying a RISC-V label. The Pandoc migration caught exactly this class of risk: upstream did not provide the required RISC-V binary, and conda-forge did not yet have the native Haskell compiler needed to build one. The correct result was not to publish a misleading green package.
Other projects needed corrected archive names, GNU target triples, compiler flags, architecture detection, source builds instead of upstream binary bundles, or temporary selectors where an upstream runtime was not available. Larger packages such as Python, OpenBLAS, NumPy, SciPy, LLVM, R, Node.js, MPI and systemd required deeper work and unlocked many descendants when fixed.
This is where collaboration matters. conda-forge brought migration machinery, feedstock knowledge, reviews, and publication infrastructure. openKylin contributors brought distro experience, dedicated engineers, bootstrap work, package fixes, and hardware. RISE contributed toolchain and feedstock engineering, native package work, and reusable CI infrastructure. Individual feedstock maintainers contributed the project-specific judgment needed to decide whether a test adjustment was legitimate or merely hid a bug.
Running conda-forge on real RISC-V hardware
The completed bootstrap stack is deliberately cross-compilable, because that was the fastest way to establish the standard RISC-V build environment. The next phase is different: use that environment and real hardware to build additional feedstocks natively and run their target tests directly. The conda-forge organization enabled the RISE RISC-V Runners GitHub App and RISE worked with the community to make native jobs usable from generated feedstock workflows.
The service provisions an ephemeral runner on a physical RISC-V node, registers it just in time with GitHub Actions, and gives the job Docker support. A GitHub workflow requests it with:
runs-on: ubuntu-24.04-riscv
In the past month alone, it recorded 269 conda-forge jobs across 39 feedstock repositories. It shows that the generated conda-forge workflows are reaching shared native RISC-V capacity and that work is happening in the upstream repositories where maintainers can inspect results and apply fixes.
The set included NumPy, SciPy, LLVM and Clang, systemd, uv, mypy, wcwidth, shellcheck, coreutils, and multiple Rust and Go packages. NumPy and SciPy exercised Python ABI and free-threading matrices; other jobs built long-running native Rust or systems packages. Every audited job used the ubuntu-24.04-riscv label and was scheduled onto the Scaleway EM-RV1 pool.
This removes a practical barrier for feedstock maintainers. They can use managed native RISC-V capacity to enable native compilation as well as native testing. A feedstock can build RISC-V code on a RISC-V machine instead of cross-compiling it from x86-64, then execute its target test suite on the same architecture. This is faster than emulating RISC-V binaries under QEMU and gives stronger evidence that the package works on the intended hardware. It also makes failures visible as upstream CI results attached to the repositories where fixes belong.
The hardware layer is also collaborative. openKylin has offered additional RISC-V capacity, and RISE has been integrating that hardware behind the same runner interface. This lets projects request a stable label while the service manages placement, lifecycle, and capacity underneath it.
The next baseline: RVA23
The initial conda-forge toolchain deliberately targets the broadly compatible rv64gc baseline. That decision removed an early blocker and matches existing systems, but it is not the end of the performance story.
The ratified RVA23 application profile gives binary software ecosystems a substantially richer common baseline, including mandatory vector support. For numerical computing, compression, cryptography, media, and machine learning, that creates opportunities which a scalar-oriented baseline cannot fully exploit.
RVA23 is an ISA profile, not by itself a Conda ABI or distribution policy. conda-forge will still need to decide how packages advertise compatibility, whether RVA23 is a new baseline or an optimized variant, how runtime dispatch is used, and whether older machines remain supported. It will also require builders and test machines that actually implement the profile. RISE’s next objective is to enable RVA23 hardware in enough volume to build, test, and continuously maintain RVA23 packages, not simply produce a one-time benchmark.
Work with us
The current platform is usable, but many feedstocks still haven’t been migrated. The next phase is to finish foundational dependencies, enable more feedstocks, improve native test coverage, establish an RVA23 packaging path, and scale the machines behind it.
This work also demonstrates a repeatable model for software organizations that need architecture enablement without creating a long-lived downstream port. The goal is to land the required recipes, tooling, tests, and CI support in the upstream projects that already maintain them.
This project illustrates what RISE can provide to software organizations and open-source communities:
- architecture and toolchain expertise from bootstrap through native execution;
- coordinated engineering across upstream projects rather than a private downstream fork;
- migration analysis and hands-on fixes across large dependency graphs;
- managed access to physical RISC-V CI hardware; and
- a neutral place for vendors and communities to pool investment in shared software needs.
We thank the conda-forge maintainers, the openKylin team, hardware partners, and every feedstock maintainer who reviewed or repaired a migration PR. This result belongs to that collaboration.
To contribute package fixes, follow the live migration dashboard and join the discussion in issue #1744. To contribute hardware, bring a software ecosystem to RISC-V, or explore a RISE collaboration, contact a RISE representative or email info@riseproject.dev.