python3-ruff: Fix build on targets without 64-bit atomics

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>
This commit is contained in:
Khem Raj committed 2026-10-22 09:58:54 -07:00
1 parent 650510a4a4
commit 5934eca052
2 files changed
+164

No files matched your search

@@ -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