* 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:
commit
917ac07f9a
17 changed files with 207 additions and 166 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()?);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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))?)?
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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],
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,4 +6,4 @@ pub mod log;
|
|||
pub mod module;
|
||||
pub mod timer;
|
||||
|
||||
pub use error::{Error, Result};
|
||||
pub use error::{Error, Result, ResultExt};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ packages:
|
|||
- python3-wheel
|
||||
- python3-yaml
|
||||
- rpm2cpio
|
||||
- rustc-1.77
|
||||
- rustc-1.83
|
||||
- sed
|
||||
- socat
|
||||
- sparse
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ packages:
|
|||
- python3-wheel
|
||||
- python3-yaml
|
||||
- rpm2cpio
|
||||
- rustc-1.77
|
||||
- rustc-1.83
|
||||
- sed
|
||||
- socat
|
||||
- sparse
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue