new file mode 100644
@@ -0,0 +1,162 @@
+From: Khem Raj <khem.raj@oss.qualcomm.com>
+Date: Wed, 21 Oct 2026 18:00:00 -0700
+Subject: [PATCH] ruff: Use portable-atomic for AtomicU64 on targets without
+ 64-bit atomics
+
+std only provides AtomicU64 on targets with 64-bit atomics, so ruff
+fails to build for e.g. riscv32:
+
+ error[E0432]: unresolved import `std::sync::atomic::AtomicU64`
+ --> crates/ruff/src/cache.rs:7:25
+ |
+ 7 | use std::sync::atomic::{AtomicU64, Ordering};
+ | ^^^^^^^^^ no `AtomicU64` in `sync::atomic`
+
+FileCache::last_seen holds milliseconds since the Unix epoch, so it has
+to stay 64-bit. On targets without 64-bit atomics, use the AtomicU64
+from portable-atomic, which is already in the dependency tree, instead.
+rkyv's AtomicLoad wrapper only supports the std atomics, so add an
+equivalent wrapper that archives it as an ArchivedU64, which keeps the
+on-disk cache format the same on all targets. Targets with 64-bit
+atomics keep using std's AtomicU64 unchanged.
+
+Upstream-Status: Submitted [https://github.com/astral-sh/ruff/pull/29627]
+Signed-off-by: Khem Raj <khem.raj@oss.qualcomm.com>
+---
+ Cargo.toml | 1 +
+ Cargo.lock | 1 +
+ crates/ruff/Cargo.toml | 3 +++
+ crates/ruff/src/cache.rs | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
+ 4 files changed, 58 insertions(+), 4 deletions(-)
+
+--- a/Cargo.toml
++++ b/Cargo.toml
+@@ -170,6 +170,7 @@
+ phf = { version = "0.11.3", default-features = false }
+ phf_codegen = { version = "0.11.3", default-features = false }
+ pep440_rs = { version = "0.7.1", default-features = false }
++portable-atomic = { version = "1.13.1", default-features = false, features = ["fallback"] }
+ pretty_assertions = { version = "1.3.0", default-features = false, features = ["std"] }
+ proc-macro2 = { version = "1.0.79", default-features = false, features = ["proc-macro"] }
+ pyproject-toml = { version = "0.13.4", default-features = false }
+--- a/Cargo.lock
++++ b/Cargo.lock
+@@ -2523,6 +2523,7 @@
+ "mimalloc",
+ "notify",
+ "path-absolutize",
++ "portable-atomic",
+ "rayon",
+ "regex",
+ "rkyv",
+--- a/crates/ruff/Cargo.toml
++++ b/crates/ruff/Cargo.toml
+@@ -77,6 +77,9 @@
+ [target.'cfg(target_os = "windows")'.dependencies]
+ mimalloc = { workspace = true }
+
++[target.'cfg(not(target_has_atomic = "64"))'.dependencies]
++portable-atomic = { workspace = true }
++
+ [dev-dependencies]
+ # Enable test rules during development
+ ruff_linter = { workspace = true, features = ["clap", "test-rules"] }
+--- a/crates/ruff/src/cache.rs
++++ b/crates/ruff/src/cache.rs
+@@ -4,7 +4,9 @@
+ use std::io::{self, Write};
+ use std::path::{Path, PathBuf};
+ use std::sync::Mutex;
+-use std::sync::atomic::{AtomicU64, Ordering};
++#[cfg(target_has_atomic = "64")]
++use std::sync::atomic::AtomicU64;
++use std::sync::atomic::Ordering;
+ use std::time::{Duration, SystemTime};
+
+ use anyhow::{Context, Result};
+@@ -23,6 +25,10 @@
+ use ruff_workspace::Settings;
+ use ruff_workspace::resolver::Resolver;
+
++// `std` only provides `AtomicU64` on targets with 64-bit atomics.
++#[cfg(not(target_has_atomic = "64"))]
++use portable_atomic::AtomicU64;
++
+ /// [`Path`] that is relative to the package root in [`PackageCache`].
+ pub(crate) type RelativePath = Path;
+ /// [`PathBuf`] that is relative to the package root in [`PackageCache`].
+@@ -341,12 +347,56 @@
+ ///
+ /// Represented as the number of milliseconds since Unix epoch. This will
+ /// break in 1970 + ~584 years (~2554).
+- #[rkyv(with = rkyv::with::AtomicLoad<rkyv::with::Relaxed>)]
++ #[cfg_attr(
++ target_has_atomic = "64",
++ rkyv(with = rkyv::with::AtomicLoad<rkyv::with::Relaxed>)
++ )]
++ #[cfg_attr(not(target_has_atomic = "64"), rkyv(with = AtomicU64LoadRelaxed))]
+ last_seen: AtomicU64,
+
+ data: FileCacheData,
+ }
+
++/// Archives a [`portable_atomic::AtomicU64`] as its value, like
++/// [`rkyv::with::AtomicLoad`] with [`rkyv::with::Relaxed`] does for the `std`
++/// one, so the on-disk format is the same on all targets.
++#[cfg(not(target_has_atomic = "64"))]
++struct AtomicU64LoadRelaxed;
++
++#[cfg(not(target_has_atomic = "64"))]
++impl rkyv::with::ArchiveWith<AtomicU64> for AtomicU64LoadRelaxed {
++ type Archived = rkyv::primitive::ArchivedU64;
++ type Resolver = ();
++
++ fn resolve_with(field: &AtomicU64, (): Self::Resolver, out: rkyv::Place<Self::Archived>) {
++ out.write(rkyv::primitive::ArchivedU64::from_native(
++ field.load(Ordering::Relaxed),
++ ));
++ }
++}
++
++#[cfg(not(target_has_atomic = "64"))]
++impl<S: rkyv::rancor::Fallible + ?Sized> rkyv::with::SerializeWith<AtomicU64, S>
++ for AtomicU64LoadRelaxed
++{
++ fn serialize_with(_: &AtomicU64, _: &mut S) -> std::result::Result<Self::Resolver, S::Error> {
++ Ok(())
++ }
++}
++
++#[cfg(not(target_has_atomic = "64"))]
++impl<D: rkyv::rancor::Fallible + ?Sized>
++ rkyv::with::DeserializeWith<rkyv::primitive::ArchivedU64, AtomicU64, D>
++ for AtomicU64LoadRelaxed
++{
++ fn deserialize_with(
++ field: &rkyv::primitive::ArchivedU64,
++ _: &mut D,
++ ) -> std::result::Result<AtomicU64, D::Error> {
++ Ok(AtomicU64::new(field.to_native()))
++ }
++}
++
+ impl FileCache {
+ /// Return whether or not the file in the cache was linted and found to have no diagnostics.
+ pub(crate) fn linted(&self) -> bool {
+@@ -503,7 +553,6 @@
+ use std::io;
+ use std::io::Write;
+ use std::path::{Path, PathBuf};
+- use std::sync::atomic::AtomicU64;
+ use std::time::SystemTime;
+
+ use anyhow::Result;
+@@ -522,7 +571,7 @@
+ use ruff_workspace::Settings;
+
+ use crate::cache::{self, ChangeData, FileCache, FileCacheData, FileCacheKey};
+- use crate::cache::{Cache, RelativePathBuf};
++ use crate::cache::{AtomicU64, Cache, RelativePathBuf};
+ use crate::commands::format::{FormatCommandError, FormatMode, FormatResult, format_path};
+ use crate::diagnostics::{Diagnostics, lint_path};
+
@@ -6,6 +6,8 @@ LIC_FILES_CHKSUM = "file://LICENSE;md5=e679ca4f742cbfa29ab6a499529c2d39"
DEPENDS += "python3-maturin-native"
+SRC_URI += "file://0001-ruff-Use-portable-atomic-for-AtomicU64-on-targets-wi.patch"
+
SRC_URI[sha256sum] = "5bb796c5112e9fb9527f2ef2faa0a130d6312d19de92bcfaddd70dbb2d41020f"
inherit pypi python_setuptools3_rust cargo-update-recipe-crates
std only provides AtomicU64 on targets with 64-bit atomics, so ruff fails to build for riscv32: error[E0432]: unresolved import `std::sync::atomic::AtomicU64` --> crates/ruff/src/cache.rs:7:25 Add a patch that uses portable-atomic's AtomicU64 for the cache timestamp on such targets, archived in the same on-disk format. AI-Generated: Uses Claude Code Signed-off-by: Khem Raj <khem.raj@oss.qualcomm.com> --- ...e-atomic-for-AtomicU64-on-targets-wi.patch | 162 ++++++++++++++++++ .../python/python3-ruff_0.17.1.bb | 2 + 2 files changed, 164 insertions(+) create mode 100644 meta-python/recipes-devtools/python/python3-ruff/0001-ruff-Use-portable-atomic-for-AtomicU64-on-targets-wi.patch