* rust: cleanup Error, add &error_fatal bindings

* rust: do not add qemuutil to Rust crates
 * rust: migration: allow nested offset_of
 * rust: add back to Ubuntu 22.04 jobs
 * checkpatch: remove bogus patch prefix warning
 -----BEGIN PGP SIGNATURE-----
 
 iQFIBAABCgAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmkKKfgUHHBib256aW5p
 QHJlZGhhdC5jb20ACgkQv/vSX3jHroPIjAf/YrgwlyfL7Uocrga95I4+bVTluEI9
 Fi7Bf5jpKuS4AyeZvyp56S3pTPKdsOb1QUEj95b99DvwkQnDp6JlV4fgOWTZdyCv
 S0okaNNRG+kGVwrd+Ie4lvTt/ljNyVPPE3EiVAjrJ6Uy/0wKWwd/2hNuJgfpOgJH
 DlUkVB+tlzRcZVvgq35jNxiGZPZYmQnv2lwuDspyIg4Tt8dcJt0DbrwDeiN2oPKC
 4wxfd21ui9RVyKKjHzFL7p0i/Ap8WgwKhZMqe+Ab5Zz93cE0FU1Jl3SSS/fEPJSu
 cAy5csQZWfpODzwduwsWYnUYBzw5FPTSZ31aWJqJCdBw8OBBgflOQ7Qhaw==
 =vdV1
 -----END PGP SIGNATURE-----

Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging

* rust: cleanup Error, add &error_fatal bindings
* rust: do not add qemuutil to Rust crates
* rust: migration: allow nested offset_of
* rust: add back to Ubuntu 22.04 jobs
* checkpatch: remove bogus patch prefix warning

# -----BEGIN PGP SIGNATURE-----
#
# iQFIBAABCgAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmkKKfgUHHBib256aW5p
# QHJlZGhhdC5jb20ACgkQv/vSX3jHroPIjAf/YrgwlyfL7Uocrga95I4+bVTluEI9
# Fi7Bf5jpKuS4AyeZvyp56S3pTPKdsOb1QUEj95b99DvwkQnDp6JlV4fgOWTZdyCv
# S0okaNNRG+kGVwrd+Ie4lvTt/ljNyVPPE3EiVAjrJ6Uy/0wKWwd/2hNuJgfpOgJH
# DlUkVB+tlzRcZVvgq35jNxiGZPZYmQnv2lwuDspyIg4Tt8dcJt0DbrwDeiN2oPKC
# 4wxfd21ui9RVyKKjHzFL7p0i/Ap8WgwKhZMqe+Ab5Zz93cE0FU1Jl3SSS/fEPJSu
# cAy5csQZWfpODzwduwsWYnUYBzw5FPTSZ31aWJqJCdBw8OBBgflOQ7Qhaw==
# =vdV1
# -----END PGP SIGNATURE-----
# gpg: Signature made Tue 04 Nov 2025 05:29:44 PM CET
# gpg:                using RSA key F13338574B662389866C7682BFFBD25F78C7AE83
# gpg:                issuer "pbonzini@redhat.com"
# gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [unknown]
# gpg:                 aka "Paolo Bonzini <pbonzini@redhat.com>" [unknown]
# gpg: WARNING: The key's User ID is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4  E2F7 7E15 100C CD36 69B1
#      Subkey fingerprint: F133 3857 4B66 2389 866C  7682 BFFB D25F 78C7 AE83

* tag 'for-upstream' of https://gitlab.com/bonzini/qemu:
  rust: add back to Ubuntu 22.04 jobs
  rust: migration: allow nested offset_of
  rust: do not add qemuutil to Rust crates
  rust: pull error_fatal out of SysbusDeviceMethods::sysbus_realize
  rust/util: replace Error::err_or_unit/err_or_else with Error::with_errp
  rust/util: use anyhow's native chaining capabilities
  rust/util: add ensure macro
  scripts/checkpatch.pl: remove bogus patch prefix warning

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2025-11-05 16:07:18 +01:00
commit 917ac07f9a
17 changed files with 207 additions and 166 deletions

View file

@ -39,7 +39,7 @@ build-system-ubuntu:
- job: amd64-ubuntu2204-container
variables:
IMAGE: ubuntu2204
CONFIGURE_ARGS: --enable-docs
CONFIGURE_ARGS: --enable-docs --enable-rust
TARGETS: alpha-softmmu microblazeel-softmmu mips64el-softmmu
MAKE_CHECK_ARGS: check-build

View file

@ -117,14 +117,14 @@ Rust build dependencies
a newer version using ``cargo install bindgen-cli``.
QEMU requires Rust 1.83.0. This is available on all supported platforms
with two exception: Ubuntu LTS releases 22.04 and 24.04, and the
``mips64el`` architecture on Debian bookworm. For all other
except for the ``mips64el`` architecture on Debian bookworm. For all other
architectures, Debian bookworm provides a new-enough Rust compiler
in the ``rustc-web`` package.
It is expected that in the future Ubuntu will provide updated packages
like the existing ``rustc-1.82`` package. The path to ``rustc`` and
``rustdoc`` will have to be provided manually to the configure script.
For Ubuntu 22.04 ("Jammy") and 24.04 ("Noble") updated versions of
Rust are available through packages such as ``rustc-1.83`` package;
the path to ``rustc`` and ``rustdoc`` has to be provided manually to
the configure script.
Some distros prefer to avoid vendored crate sources, and instead use
local sources from e.g. ``/usr/share/cargo/registry``. QEMU includes a

View file

@ -39,4 +39,4 @@ _chardev_rs = static_library(
dependencies: [glib_sys_rs, common_rs, qemu_macros],
)
chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [chardev, qemuutil])
chardev_rs = declare_dependency(link_with: [_chardev_rs], dependencies: [chardev])

View file

@ -17,7 +17,7 @@ use migration::{
};
use qom::{prelude::*, ObjectImpl, Owned, ParentField, ParentInit};
use system::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder};
use util::{log::Log, log_mask_ln};
use util::{log::Log, log_mask_ln, ResultExt};
use crate::registers::{self, Interrupt, RegisterOffset};
@ -697,7 +697,7 @@ pub unsafe extern "C" fn pl011_create(
let chr = unsafe { Owned::<Chardev>::from(&*chr) };
dev.prop_set_chr("chardev", &chr);
}
dev.sysbus_realize();
dev.sysbus_realize().unwrap_fatal();
dev.mmio_map(0, addr);
dev.connect_irq(0, &irq);

View file

@ -4,12 +4,13 @@
//! Bindings to access `sysbus` functionality from Rust.
use std::{ffi::CStr, ptr::addr_of_mut};
use std::ffi::CStr;
pub use bindings::SysBusDeviceClass;
use common::Opaque;
use qom::{prelude::*, Owned};
use system::MemoryRegion;
use util::{Error, Result};
use crate::{
bindings,
@ -107,14 +108,12 @@ where
}
}
fn sysbus_realize(&self) {
// TODO: return an Error
fn sysbus_realize(&self) -> Result<()> {
assert!(bql::is_locked());
unsafe {
bindings::sysbus_realize(
self.upcast().as_mut_ptr(),
addr_of_mut!(util::bindings::error_fatal),
);
Error::with_errp(|errp| {
bindings::sysbus_realize(self.upcast().as_mut_ptr(), errp);
})
}
}
}

View file

@ -25,7 +25,10 @@ use system::{
bindings::{address_space_memory, address_space_stl_le, hwaddr},
MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED,
};
use util::timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND};
use util::{
ensure,
timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND},
};
use crate::fw_cfg::HPETFwConfig;
@ -728,14 +731,14 @@ impl HPETState {
}
fn realize(&self) -> util::Result<()> {
if self.num_timers < HPET_MIN_TIMERS || self.num_timers > HPET_MAX_TIMERS {
Err(format!(
"hpet.num_timers must be between {HPET_MIN_TIMERS} and {HPET_MAX_TIMERS}"
))?;
}
if self.int_route_cap == 0 {
Err("hpet.hpet-intcap property not initialized")?;
}
ensure!(
(HPET_MIN_TIMERS..=HPET_MAX_TIMERS).contains(&self.num_timers),
"hpet.num_timers must be between {HPET_MIN_TIMERS} and {HPET_MAX_TIMERS}"
);
ensure!(
self.int_route_cap != 0,
"hpet.hpet-intcap property not initialized"
);
self.hpet_id.set(HPETFwConfig::assign_hpet_id()?);

View file

@ -5,6 +5,7 @@
use std::ptr::addr_of_mut;
use common::Zeroable;
use util::{self, ensure};
/// Each `HPETState` represents a Event Timer Block. The v1 spec supports
/// up to 8 blocks. QEMU only uses 1 block (in PC machine).
@ -36,7 +37,7 @@ pub static mut hpet_fw_cfg: HPETFwConfig = HPETFwConfig {
};
impl HPETFwConfig {
pub(crate) fn assign_hpet_id() -> Result<usize, &'static str> {
pub(crate) fn assign_hpet_id() -> util::Result<usize> {
assert!(bql::is_locked());
// SAFETY: all accesses go through these methods, which guarantee
// that the accesses are protected by the BQL.
@ -47,9 +48,7 @@ impl HPETFwConfig {
fw_cfg.count = 0;
}
if fw_cfg.count == 8 {
Err("Only 8 instances of HPET are allowed")?;
}
ensure!(fw_cfg.count != 8, "Only 8 instances of HPET are allowed");
let id: usize = fw_cfg.count.into();
fw_cfg.count += 1;

View file

@ -141,24 +141,24 @@ pub const fn vmstate_varray_flag<T: VMState>(_: PhantomData<T>) -> VMStateFlags
/// [`Owned`]: ../../qom/qom/struct.Owned.html
#[macro_export]
macro_rules! vmstate_of {
($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])? $(, $test_fn:expr)? $(,)?) => {
($struct_name:ty, $($field_name:ident).+ $([0 .. $($num:ident).+ $(* $factor:expr)?])? $(, $test_fn:expr)? $(,)?) => {
$crate::bindings::VMStateField {
name: ::core::concat!(::core::stringify!($field_name), "\0")
name: ::core::concat!(::core::stringify!($($field_name).+), "\0")
.as_bytes()
.as_ptr().cast::<::std::os::raw::c_char>(),
offset: ::std::mem::offset_of!($struct_name, $field_name),
$(num_offset: ::std::mem::offset_of!($struct_name, $num),)?
offset: ::std::mem::offset_of!($struct_name, $($field_name).+),
$(num_offset: ::std::mem::offset_of!($struct_name, $($num).+),)?
$(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)?
// The calls to `call_func_with_field!` are the magic that
// computes most of the VMStateField from the type of the field.
..$crate::call_func_with_field!(
$crate::vmstate::vmstate_base,
$struct_name,
$field_name
$($field_name).+
)$(.with_varray_flag($crate::call_func_with_field!(
$crate::vmstate::vmstate_varray_flag,
$struct_name,
$num))
$($num).+))
$(.with_varray_multiply($factor))?)?
}
};

View file

@ -43,7 +43,7 @@ _util_rs = static_library(
dependencies: [anyhow_rs, libc_rs, foreign_rs, glib_sys_rs, common_rs, qom, qemuutil],
)
util_rs = declare_dependency(link_with: [_util_rs], dependencies: [qemuutil, qom])
util_rs = declare_dependency(link_with: [_util_rs])
rust.test('rust-util-tests', _util_rs,
dependencies: [qemuutil, qom],

View file

@ -14,8 +14,7 @@
//! [`ptr_or_propagate`](crate::Error::ptr_or_propagate) can be used to build
//! a C return value while also propagating an error condition
//!
//! * [`err_or_else`](crate::Error::err_or_else) and
//! [`err_or_unit`](crate::Error::err_or_unit) can be used to build a `Result`
//! * [`with_errp`](crate::Error::with_errp) can be used to build a `Result`
//!
//! This module is most commonly used at the boundary between C and Rust code;
//! other code will usually access it through the
@ -38,7 +37,9 @@ use std::{
borrow::Cow,
ffi::{c_char, c_int, c_void, CStr},
fmt::{self, Display},
panic, ptr,
ops::Deref,
panic,
ptr::{self, addr_of_mut},
};
use foreign::{prelude::*, OwnedPointer};
@ -49,94 +50,85 @@ pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub struct Error {
msg: Option<Cow<'static, str>>,
/// Appends the print string of the error to the msg if not None
cause: Option<anyhow::Error>,
cause: anyhow::Error,
file: &'static str,
line: u32,
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.cause.as_ref().map(AsRef::as_ref)
}
impl Deref for Error {
type Target = anyhow::Error;
#[allow(deprecated)]
fn description(&self) -> &str {
self.msg
.as_deref()
.or_else(|| self.cause.as_deref().map(std::error::Error::description))
.expect("no message nor cause?")
fn deref(&self) -> &Self::Target {
&self.cause
}
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut prefix = "";
if let Some(ref msg) = self.msg {
write!(f, "{msg}")?;
prefix = ": ";
}
if let Some(ref cause) = self.cause {
write!(f, "{prefix}{cause}")?;
} else if prefix.is_empty() {
panic!("no message nor cause?");
}
Ok(())
Display::fmt(&format_args!("{:#}", self.cause), f)
}
}
impl From<String> for Error {
impl<E> From<E> for Error
where
anyhow::Error: From<E>,
{
#[track_caller]
fn from(msg: String) -> Self {
let location = panic::Location::caller();
Error {
msg: Some(Cow::Owned(msg)),
cause: None,
file: location.file(),
line: location.line(),
}
}
}
impl From<&'static str> for Error {
#[track_caller]
fn from(msg: &'static str) -> Self {
let location = panic::Location::caller();
Error {
msg: Some(Cow::Borrowed(msg)),
cause: None,
file: location.file(),
line: location.line(),
}
}
}
impl From<anyhow::Error> for Error {
#[track_caller]
fn from(error: anyhow::Error) -> Self {
let location = panic::Location::caller();
Error {
msg: None,
cause: Some(error),
file: location.file(),
line: location.line(),
}
fn from(src: E) -> Self {
Self::new(anyhow::Error::from(src))
}
}
impl Error {
/// Create a new error from an [`anyhow::Error`].
///
/// This wraps the error with QEMU's location tracking information.
/// Most code should use the `?` operator instead of calling this directly.
#[track_caller]
pub fn new(cause: anyhow::Error) -> Self {
let location = panic::Location::caller();
Self {
cause,
file: location.file(),
line: location.line(),
}
}
/// Create a new error from a string message.
///
/// This is a convenience wrapper around [`Error::new`] for simple string
/// errors. Most code should use the [`ensure!`](crate::ensure) macro
/// instead of calling this directly.
#[track_caller]
pub fn msg(src: impl Into<Cow<'static, str>>) -> Self {
Self::new(anyhow::Error::msg(src.into()))
}
#[track_caller]
#[doc(hidden)]
#[inline(always)]
pub fn format(args: fmt::Arguments) -> Self {
// anyhow::Error::msg will allocate anyway, might as well let fmt::format doit.
let msg = fmt::format(args);
Self::new(anyhow::Error::msg(msg))
}
/// Create a new error, prepending `msg` to the
/// description of `cause`
#[track_caller]
pub fn with_error(msg: impl Into<Cow<'static, str>>, cause: impl Into<anyhow::Error>) -> Self {
let location = panic::Location::caller();
Error {
msg: Some(msg.into()),
cause: Some(cause.into()),
file: location.file(),
line: location.line(),
fn do_with_error(
msg: Cow<'static, str>,
cause: anyhow::Error,
location: &'static panic::Location<'static>,
) -> Error {
Error {
cause: cause.context(msg),
file: location.file(),
line: location.line(),
}
}
do_with_error(msg.into(), cause.into(), panic::Location::caller())
}
/// Consume a result, returning `false` if it is an error and
@ -221,39 +213,53 @@ impl Error {
}
}
/// Convert a C `Error*` into a Rust `Result`, using
/// `Ok(())` if `c_error` is NULL. Free the `Error*`.
/// Pass a C `Error*` to the closure, and convert the result
/// (either the return value of the closure, or the error)
/// into a Rust `Result`.
///
/// # Safety
///
/// `c_error` must be `NULL` or valid; typically it was initialized
/// with `ptr::null_mut()` and passed by reference to a C function.
pub unsafe fn err_or_unit(c_error: *mut bindings::Error) -> Result<()> {
// SAFETY: caller guarantees c_error is valid
unsafe { Self::err_or_else(c_error, || ()) }
}
/// One exit from `f`, `c_error` must be unchanged or point to a
/// valid C [`struct Error`](bindings::Error).
pub unsafe fn with_errp<T, F: FnOnce(&mut *mut bindings::Error) -> T>(f: F) -> Result<T> {
let mut c_error: *mut bindings::Error = ptr::null_mut();
/// Convert a C `Error*` into a Rust `Result`, calling `f()` to
/// obtain an `Ok` value if `c_error` is NULL. Free the `Error*`.
///
/// # Safety
///
/// `c_error` must be `NULL` or point to a valid C [`struct
/// Error`](bindings::Error); typically it was initialized with
/// `ptr::null_mut()` and passed by reference to a C function.
pub unsafe fn err_or_else<T, F: FnOnce() -> T>(
c_error: *mut bindings::Error,
f: F,
) -> Result<T> {
// SAFETY: caller guarantees c_error is valid
let err = unsafe { Option::<Self>::from_foreign(c_error) };
match err {
None => Ok(f()),
Some(err) => Err(err),
// SAFETY: guaranteed by the postcondition of `f`
match (f(&mut c_error), unsafe { c_error.into_native() }) {
(result, None) => Ok(result),
(_, Some(err)) => Err(err),
}
}
}
/// Extension trait for `std::result::Result`, providing extra
/// methods when the error type can be converted into a QEMU
/// Error.
pub trait ResultExt {
/// The success type `T` in `Result<T, E>`.
type OkType;
/// Report a fatal error and exit QEMU, or return the success value.
/// Note that, unlike [`unwrap()`](std::result::Result::unwrap), this
/// is not an abort and can be used for user errors.
fn unwrap_fatal(self) -> Self::OkType;
}
impl<T, E> ResultExt for std::result::Result<T, E>
where
Error: From<E>,
{
type OkType = T;
fn unwrap_fatal(self) -> T {
// SAFETY: errp is valid
self.map_err(|err| unsafe {
Error::from(err).propagate(addr_of_mut!(bindings::error_fatal))
})
.unwrap()
}
}
impl FreeForeign for Error {
type Foreign = bindings::Error;
@ -302,8 +308,7 @@ impl FromForeign for Error {
};
Error {
msg: FromForeign::cloned_from_foreign(error.msg),
cause: None,
cause: anyhow::Error::msg(String::cloned_from_foreign(error.msg)),
file: file.unwrap(),
line: error.line as u32,
}
@ -311,6 +316,53 @@ impl FromForeign for Error {
}
}
/// Ensure that a condition is true, returning an error if it is false.
///
/// This macro is similar to [`anyhow::ensure`] but returns a QEMU [`Result`].
/// If the condition evaluates to `false`, the macro returns early with an error
/// constructed from the provided message.
///
/// # Examples
///
/// ```
/// # use util::{ensure, Result};
/// # fn check_positive(x: i32) -> Result<()> {
/// ensure!(x > 0, "value must be positive");
/// # Ok(())
/// # }
/// ```
///
/// ```
/// # use util::{ensure, Result};
/// # const MIN: i32 = 123;
/// # const MAX: i32 = 456;
/// # fn check_range(x: i32) -> Result<()> {
/// ensure!(
/// x >= MIN && x <= MAX,
/// "{} not between {} and {}",
/// x,
/// MIN,
/// MAX
/// );
/// # Ok(())
/// # }
/// ```
#[macro_export]
macro_rules! ensure {
($cond:expr, $fmt:literal, $($arg:tt)*) => {
if !$cond {
let e = $crate::Error::format(format_args!($fmt, $($arg)*));
return $crate::Result::Err(e);
}
};
($cond:expr, $err:expr $(,)?) => {
if !$cond {
let e = $crate::Error::msg($err);
return $crate::Result::Err(e);
}
};
}
#[cfg(test)]
mod tests {
use std::ffi::CStr;
@ -345,19 +397,10 @@ mod tests {
unsafe { CStr::from_ptr(bindings::error_get_pretty(local_err)) }
}
#[test]
#[allow(deprecated)]
fn test_description() {
use std::error::Error;
assert_eq!(super::Error::from("msg").description(), "msg");
assert_eq!(super::Error::from("msg".to_owned()).description(), "msg");
}
#[test]
fn test_display() {
assert_eq!(&*format!("{}", Error::from("msg")), "msg");
assert_eq!(&*format!("{}", Error::from("msg".to_owned())), "msg");
assert_eq!(&*format!("{}", Error::msg("msg")), "msg");
assert_eq!(&*format!("{}", Error::msg("msg".to_owned())), "msg");
assert_eq!(&*format!("{}", Error::from(anyhow!("msg"))), "msg");
assert_eq!(
@ -374,7 +417,7 @@ mod tests {
assert!(Error::bool_or_propagate(Ok(()), &mut local_err));
assert_eq!(local_err, ptr::null_mut());
let my_err = Error::from("msg");
let my_err = Error::msg("msg");
assert!(!Error::bool_or_propagate(Err(my_err), &mut local_err));
assert_ne!(local_err, ptr::null_mut());
assert_eq!(error_get_pretty(local_err), c"msg");
@ -391,7 +434,7 @@ mod tests {
assert_eq!(String::from_foreign(ret), "abc");
assert_eq!(local_err, ptr::null_mut());
let my_err = Error::from("msg");
let my_err = Error::msg("msg");
assert_eq!(
Error::ptr_or_propagate(Err::<String, _>(my_err), &mut local_err),
ptr::null_mut()
@ -403,13 +446,16 @@ mod tests {
}
#[test]
fn test_err_or_unit() {
fn test_with_errp() {
unsafe {
let result = Error::err_or_unit(ptr::null_mut());
assert_match!(result, Ok(()));
let result = Error::with_errp(|_errp| true);
assert_match!(result, Ok(true));
let err = error_for_test(c"msg");
let err = Error::err_or_unit(err.into_inner()).unwrap_err();
let err = Error::with_errp(|errp| {
*errp = error_for_test(c"msg").into_inner();
false
})
.unwrap_err();
assert_eq!(&*format!("{err}"), "msg");
}
}

View file

@ -6,4 +6,4 @@ pub mod log;
pub mod module;
pub mod timer;
pub use error::{Error, Result};
pub use error::{Error, Result, ResultExt};

View file

@ -1741,13 +1741,7 @@ sub process {
}
} elsif ($line =~ /^\+\+\+\s+(\S+)/) {
$realfile = $1;
$realfile =~ s@^([^/]*)/@@ if (!$file);
$p1_prefix = $1;
if (!$file && $tree && $p1_prefix ne '' &&
-e "$root/$p1_prefix") {
WARN("patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n");
}
$realfile =~ s@^[^/]*/@@ if (!$file);
if (defined $fileinfo && !$fileinfo->{isgit}) {
$fileinfo->{lineend} = $oldhere;

View file

@ -119,7 +119,7 @@ packages:
- python3-wheel
- python3-yaml
- rpm2cpio
- rustc-1.77
- rustc-1.83
- sed
- socat
- sparse

View file

@ -117,7 +117,7 @@ packages:
- python3-wheel
- python3-yaml
- rpm2cpio
- rustc-1.77
- rustc-1.83
- sed
- socat
- sparse

View file

@ -124,7 +124,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \
python3-wheel \
python3-yaml \
rpm2cpio \
rustc-1.77 \
rustc-1.83 \
sed \
socat \
sparse \
@ -155,8 +155,8 @@ ENV LANG "en_US.UTF-8"
ENV MAKE "/usr/bin/make"
ENV NINJA "/usr/bin/ninja"
ENV PYTHON "/usr/bin/python3"
ENV RUSTC=/usr/bin/rustc-1.77
ENV RUSTDOC=/usr/bin/rustdoc-1.77
ENV RUSTC=/usr/bin/rustc-1.83
ENV RUSTDOC=/usr/bin/rustdoc-1.83
ENV CARGO_HOME=/usr/local/cargo
ENV PATH=$CARGO_HOME/bin:$PATH
RUN DEBIAN_FRONTEND=noninteractive eatmydata \

View file

@ -70,8 +70,8 @@ mappings:
rust:
Debian12: rustc-web
Ubuntu2204: rustc-1.77
Ubuntu2404: rustc-1.77
Ubuntu2204: rustc-1.83
Ubuntu2404: rustc-1.83
pypi_mappings:
# Request more recent version

View file

@ -152,8 +152,8 @@ fedora_rustup_nightly_extras = [
]
ubuntu2204_rust_extras = [
"ENV RUSTC=/usr/bin/rustc-1.77\n",
"ENV RUSTDOC=/usr/bin/rustdoc-1.77\n",
"ENV RUSTC=/usr/bin/rustc-1.83\n",
"ENV RUSTDOC=/usr/bin/rustdoc-1.83\n",
"ENV CARGO_HOME=/usr/local/cargo\n",
'ENV PATH=$CARGO_HOME/bin:$PATH\n',
"RUN DEBIAN_FRONTEND=noninteractive eatmydata \\\n",