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
484 changes: 484 additions & 0 deletions .github/workflows/build-runai-model-streamer-s3.yml

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions docs/packages/runai-model-streamer-s3.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package-name: runai-model-streamer-s3
source-code: https://github.com/run-ai/runai-model-streamer
license: Apache-2.0 AND curl AND Zlib
versions:
- version: 0.16.1
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
From 820707245ffc7f9f363fc5de3f5375daacb0a8b9 Mon Sep 17 00:00:00 2001
From: Ludovic Henry <git@ludovic.dev>
Date: Thu, 24 Sep 2026 03:32:40 +0000
Subject: [PATCH] toolchain: add riscv64 as a supported architecture

configure_toolchain() and its per-arch .bazelrc block are already fully
generic over the arch string (native.platform constraint is
@platforms//cpu:<arch>, gcc tools are resolved as
/usr/bin/<arch>-linux-gnu-<tool>) -- riscv64 was simply never added to
either list.

No -march flag is added for riscv64 (unlike aarch64's, which exists only
for a google-cloud-cpp ARM dependency libstreamer.so does not build
against): a native riscv64 build uses the compiler's own default target.

Upstream-Status: To upstream [this port does not open issues/PRs against third-party repos; riscv64 toolchain support is exactly the kind of change upstream would want once a downstream build proves it works]
---
cpp/.bazelrc | 3 +++
cpp/toolchain/configure.bzl | 2 +-
2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/cpp/.bazelrc b/cpp/.bazelrc
index 830e568..4a6ccd2 100644
--- a/cpp/.bazelrc
+++ b/cpp/.bazelrc
@@ -19,5 +19,8 @@ build:aarch64 --copt="-march=armv8-a+crc+crypto"
build:x86_64 --platform_suffix=x86_64
build:x86_64 --platforms=@toolchain//:x86_64

+build:riscv64 --platform_suffix=riscv64
+build:riscv64 --platforms=@toolchain//:riscv64
+
# Azurite testing support (enables AZURE_STORAGE_ACCOUNT_KEY for local testing)
build:azurite --define azurite_testing=true
diff --git a/cpp/toolchain/configure.bzl b/cpp/toolchain/configure.bzl
index 6b89f54..9a04125 100644
--- a/cpp/toolchain/configure.bzl
+++ b/cpp/toolchain/configure.bzl
@@ -4,7 +4,7 @@ load("//toolchain:rules.bzl", "get_target_triplet", "runai_crosstool_tools")

# This should map to the devcontainer Dockerfile
# We install gcc-x86-64-linux-gnu and gcc-aarch64-linux-gnu toolchains
-ARCHITECTURES = ["x86_64", "aarch64"]
+ARCHITECTURES = ["x86_64", "aarch64", "riscv64"]
OS = "linux-gnu"

def _get_gcc_version(repository_ctx, arch):
--
2.43.0

Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
From 75c45793987328b8011252f0f24950e94456b336 Mon Sep 17 00:00:00 2001
From: Ludovic Henry <git@ludovic.dev>
Date: Thu, 24 Sep 2026 08:01:06 +0000
Subject: [PATCH] toolchain: use gcc's own -dumpmachine triplet for builtin
include dirs

configure_toolchain()'s cxx_builtin_include_directories are built from the
same os+arch naming-convention triplet (<arch>-linux-gnu) used to resolve
tool paths (/usr/bin/<arch>-linux-gnu-<tool>). That equivalence holds for a
genuine Debian cross-toolchain package (a real x86_64-linux-gnu-gcc reports
exactly that as its own -dumpmachine), but not when the tool path is a
same-arch *native* compiler reachable only via a symlink under the expected
name -- its own compiled-in builtin search path (/usr/lib/gcc/<real
triplet>/<ver>/include, etc.) still uses its real vendor triplet regardless
of what name it is invoked under.

This is exactly riscv64's manylinux_riscv64 image: gcc is Red Hat's native
riscv64-redhat-linux build, symlinked to /usr/bin/riscv64-linux-gnu-gcc so
runai_crosstool_tools() finds it. Bazel's own absolute-path sandboxing then
rejects gcc's builtin headers (e.g. stddef.h) because
/usr/lib/gcc/riscv64-redhat-linux/<ver>/include is not in the declared
riscv64-linux-gnu-based list.

Thread the compiler's actual -dumpmachine output down as gcc_machine and use
it (falling back to the naming-convention triplet when unset, so x86_64 and
aarch64 are unaffected) for cxx_builtin_include_directories specifically,
while tool_paths keeps using the naming-convention triplet the workflow's
symlinks provide.

Upstream-Status: To upstream [this port does not open issues/PRs against third-party repos; making cxx_builtin_include_directories match the compiler's own reported triplet is a general robustness fix upstream would likely want]
---
cpp/toolchain/configure.bzl | 7 ++++++-
cpp/toolchain/template/toolchain.bzl | 23 ++++++++++++++++++++---
2 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/cpp/toolchain/configure.bzl b/cpp/toolchain/configure.bzl
index 9a04125..510ef90 100644
--- a/cpp/toolchain/configure.bzl
+++ b/cpp/toolchain/configure.bzl
@@ -11,6 +11,10 @@ def _get_gcc_version(repository_ctx, arch):
gcc_tool = runai_crosstool_tools(get_target_triplet(OS, arch))["gcc"]
return repository_ctx.execute([gcc_tool, "-dumpversion"]).stdout.strip()

+def _get_gcc_machine(repository_ctx, arch):
+ gcc_tool = runai_crosstool_tools(get_target_triplet(OS, arch))["gcc"]
+ return repository_ctx.execute([gcc_tool, "-dumpmachine"]).stdout.strip()
+
def _get_host_arch(repository_ctx):
return repository_ctx.execute(["/usr/bin/uname", "-m"]).stdout.strip()

@@ -18,9 +22,10 @@ def _cc_autoconf_toolchain_impl(repository_ctx):
define_statements = []
for arch in ARCHITECTURES:
gcc_version = _get_gcc_version(repository_ctx, arch)
+ gcc_machine = _get_gcc_machine(repository_ctx, arch)
host_arch = _get_host_arch(repository_ctx)
toolchain_name = arch
- define_statements.append('define_toolchain(name = "%s", os = "%s", host_arch = "%s", arch = "%s", gcc_version = "%s")' % (toolchain_name, OS, host_arch, arch, gcc_version))
+ define_statements.append('define_toolchain(name = "%s", os = "%s", host_arch = "%s", arch = "%s", gcc_version = "%s", gcc_machine = "%s")' % (toolchain_name, OS, host_arch, arch, gcc_version, gcc_machine))

repository_ctx.template(
"BUILD",
diff --git a/cpp/toolchain/template/toolchain.bzl b/cpp/toolchain/template/toolchain.bzl
index 6a4c6cf..36daffc 100644
--- a/cpp/toolchain/template/toolchain.bzl
+++ b/cpp/toolchain/template/toolchain.bzl
@@ -73,6 +73,15 @@ def _impl(ctx):
target_triplet = get_target_triplet(ctx.attr.os, ctx.attr.arch)
tool_paths = [tool_path(name = k, path = v) for k, v in runai_crosstool_tools(target_triplet).items()]

+ # gcc_machine is the triplet gcc itself reports (`gcc -dumpmachine`), which is
+ # what its own builtin include search path is actually built from. It is
+ # normally identical to target_triplet (a real x86_64-linux-gnu/aarch64-linux-gnu
+ # cross-gcc reports exactly that), but differs when the "gcc" behind
+ # target_triplet's tool path is a same-arch native compiler under a different
+ # vendor triplet (e.g. Red Hat's riscv64-redhat-linux) made reachable only by a
+ # symlink under the expected name.
+ include_triplet = ctx.attr.gcc_machine if ctx.attr.gcc_machine else target_triplet
+
# Documented at
# https://docs.bazel.build/versions/main/skylark/lib/cc_common.html#create_cc_toolchain_config_info.
#
@@ -89,7 +98,7 @@ def _impl(ctx):
abi_version = "unknown",
abi_libc_version = "unknown",
tool_paths = tool_paths,
- cxx_builtin_include_directories = _get_include_directories(target_triplet, ctx.attr.gcc_version, ctx.attr.use_cross),
+ cxx_builtin_include_directories = _get_include_directories(include_triplet, ctx.attr.gcc_version, ctx.attr.use_cross),
features = features
)

@@ -111,6 +120,11 @@ _toolchain_config = rule(
mandatory = True,
doc = "GCC major version to use (eg: 9)",
),
+ "gcc_machine": attr.string(
+ mandatory = False,
+ default = "",
+ doc = "The triplet `gcc -dumpmachine` reports, if it differs from os/arch's own naming convention",
+ ),
"use_cross": attr.bool(
mandatory = False,
default = False,
@@ -121,7 +135,7 @@ _toolchain_config = rule(
)


-def define_toolchain(name, os, host_arch, arch, gcc_version):
+def define_toolchain(name, os, host_arch, arch, gcc_version, gcc_machine = ""):
""""define_toolchain creates rules for a cc_toolchain.

This expects toolchain tools to exist at a canonical path.
@@ -134,6 +148,9 @@ def define_toolchain(name, os, host_arch, arch, gcc_version):
host_arch: The host architecture for this toolchain
arch: The target architecture for this toochain
gcc_version: The GCC version of this toolchain
+ gcc_machine: The triplet `gcc -dumpmachine` reports for this toolchain's
+ compiler, if it differs from the os/arch naming convention used for
+ the tool paths themselves
"""

native.platform(
@@ -145,7 +162,7 @@ def define_toolchain(name, os, host_arch, arch, gcc_version):
)

toolchain_config_name = "%s_toolchain_config" % name
- _toolchain_config(name = toolchain_config_name, os = os, arch = arch, gcc_version = gcc_version, use_cross = host_arch != arch)
+ _toolchain_config(name = toolchain_config_name, os = os, arch = arch, gcc_version = gcc_version, gcc_machine = gcc_machine, use_cross = host_arch != arch)

empty_target_name = "%s_empty" % name
empty_target_label = ":%s" % empty_target_name
--
2.43.0

Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ludovic Henry <git@ludovic.dev>
Date: Sat, 26 Sep 2026 06:00:00 +0000
Subject: [PATCH] third_party: add riscv64 to the AWS SDK library select

The @aws alias selects a per-arch aws_library() (headers and static
libraries under /opt/<arch>-aws, /opt/<arch>-ssl, ...) by target CPU, but
only has x86_64 and aarch64 branches, so any other --config fails with
"configurable attribute doesn't match this configuration". aws_library()
itself is already generic over the arch string; add the matching
config_setting and branch for riscv64.

Upstream-Status: To upstream [this port does not open issues/PRs against third-party repos; riscv64 support in the S3 backend is exactly the kind of change upstream would want once a downstream build proves it works]
---
cpp/third_party/aws.BUILD | 9 +++++++++
1 file changed, 9 insertions(+)

diff --git a/cpp/third_party/aws.BUILD b/cpp/third_party/aws.BUILD
index 7cab4ea..c81e4ac 100644
--- a/cpp/third_party/aws.BUILD
+++ b/cpp/third_party/aws.BUILD
@@ -25,13 +25,22 @@ config_setting(
],
)

+config_setting(
+ name = "target_riscv64",
+ constraint_values = [
+ "@platforms//cpu:riscv64",
+ ],
+)
+
aws_library(name = "aws_aarch", arch = "aarch64")
aws_library(name = "aws_x86_64", arch = "x86_64")
+aws_library(name = "aws_riscv64", arch = "riscv64")

alias(
name = "aws",
actual = select({
":target_x86_64": ":aws_x86_64",
":target_aarch64": ":aws_aarch",
+ ":target_riscv64": ":aws_riscv64",
}),
)
--
2.43.0

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ludovic Henry <git@ludovic.dev>
Date: Sat, 26 Sep 2026 06:00:00 +0000
Subject: [PATCH] s3: ship the licences of the statically linked dependencies

libstreamers3.so statically links the AWS SDK for C++ (and its CRT
libraries, s2n-tls included), OpenSSL, libcurl and zlib, but the explicit
license_files=("LICENSE",) only ships the project's own licence. curl's
licence and the Apache-2.0 NOTICE files of the AWS libraries require
their notices to travel with the binary. Also match LICENSE.* next to
setup.py, where the build stages each dependency's licence text.

Upstream-Status: To upstream [this port does not open issues/PRs against third-party repos; the upstream wheels have the same gap]
---
py/runai_model_streamer_s3/setup.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/py/runai_model_streamer_s3/setup.py b/py/runai_model_streamer_s3/setup.py
index ae0e820..00c7b9a 100644
--- a/py/runai_model_streamer_s3/setup.py
+++ b/py/runai_model_streamer_s3/setup.py
@@ -22,7 +22,7 @@ assert_lib_exists()
setup(
name="runai-model-streamer-s3",
version=VERSION,
- license_files=("LICENSE",),
+ license_files=("LICENSE", "LICENSE.*"),
packages=find_packages(),
install_requires=["boto3"],
data_files=[("/runai_model_streamer/libstreamer/lib/", [LIB])],
--
2.43.0

Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Ludovic Henry <git@ludovic.dev>
Date: Sat, 26 Sep 2026 06:00:00 +0000
Subject: [PATCH] s3: fix the stale _filter_allow/_filter_ignore unit tests

Since #80 the S3 package's test_filter_allow and
test_filter_allow_full_path call _filter_ignore() with an allow_pattern
keyword, and test_filter_ignore passes ignore_pattern, but both helpers
in files/files.py take (paths, patterns). All three raise TypeError
against a stock 0.16.1 install (PyPI's x86_64 wheel included); upstream
CI never notices because py/Makefile's test target only runs the
runai_model_streamer package's tests. Only the stale-test hunk of the
upstream commit is taken, aligning with the GCS/Azure copies.

Upstream-Status: Backport [https://github.com/run-ai/runai-model-streamer/commit/c6a6543f195158ee3175b66504044dac1801de27]
---
.../runai_model_streamer_s3/files/tests/test_files.py | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/py/runai_model_streamer_s3/runai_model_streamer_s3/files/tests/test_files.py b/py/runai_model_streamer_s3/runai_model_streamer_s3/files/tests/test_files.py
index eb8ba87..0722c09 100644
--- a/py/runai_model_streamer_s3/runai_model_streamer_s3/files/tests/test_files.py
+++ b/py/runai_model_streamer_s3/runai_model_streamer_s3/files/tests/test_files.py
@@ -9,23 +9,23 @@ import runai_model_streamer_s3.files.files as files

class TestFiles(unittest.TestCase):
def test_filter_allow(self):
- res = files._filter_ignore(
+ res = files._filter_allow(
["test_file1.txt1", "test_file2.txt2", "test_file3.txt3"],
- allow_pattern=["*.txt2"]
+ patterns=["*.txt2"]
)
self.assertEqual(res, ["test_file2.txt2"])

def test_filter_allow_full_path(self):
- res = files._filter_ignore(
+ res = files._filter_allow(
["test_file1.txt1", "dir/test_file2.txt2", "test_file3.txt3"],
- allow_pattern=["*.txt2"]
+ patterns=["*.txt2"]
)
self.assertEqual(res, ["dir/test_file2.txt2"])

def test_filter_ignore(self):
res = files._filter_ignore(
["test_file1.txt1", "test_file2.txt2", "test_file3.txt3"],
- ignore_pattern=["*.txt2"]
+ patterns=["*.txt2"]
)
self.assertEqual(res, ["test_file1.txt1", "test_file3.txt3"])

Loading