rust: allow using build-root bindings.rs from cargo

Right now, using cargo with QEMU requires copying by hand the bindings.rs to the
source tree.  Instead, we can use an include file to escape the cage of cargo's
mandated source directory structure.

By running cargo within meson's "devenv" and adding a MESON_BUILD_ROOT
environment variable, it is easy for build.rs to find the file.  However, the
file must be symlinked into cargo's output directory for rust-analyzer to find
it.

Suggested-by: Junjie Mao <junjie.mao@hotmail.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Paolo Bonzini 2024-11-12 11:52:23 +01:00
parent 8c28667503
commit cb7ada5409
8 changed files with 63 additions and 35 deletions

View file

@ -3,6 +3,8 @@ project('qemu', ['c'], meson_version: '>=1.5.0',
'b_staticpic=false', 'stdsplit=false', 'optimization=2', 'b_pie=true'], 'b_staticpic=false', 'stdsplit=false', 'optimization=2', 'b_pie=true'],
version: files('VERSION')) version: files('VERSION'))
meson.add_devenv({ 'MESON_BUILD_ROOT' : meson.project_build_root() })
add_test_setup('quick', exclude_suites: ['slow', 'thorough'], is_default: true) add_test_setup('quick', exclude_suites: ['slow', 'thorough'], is_default: true)
add_test_setup('slow', exclude_suites: ['thorough'], env: ['G_TEST_SLOW=1', 'SPEED=slow']) add_test_setup('slow', exclude_suites: ['thorough'], env: ['G_TEST_SLOW=1', 'SPEED=slow'])
add_test_setup('thorough', env: ['G_TEST_SLOW=1', 'SPEED=thorough']) add_test_setup('thorough', env: ['G_TEST_SLOW=1', 'SPEED=thorough'])
@ -4090,7 +4092,7 @@ if have_rust
bindings_rs = rust.bindgen( bindings_rs = rust.bindgen(
input: 'rust/wrapper.h', input: 'rust/wrapper.h',
dependencies: common_ss.all_dependencies(), dependencies: common_ss.all_dependencies(),
output: 'bindings.rs', output: 'bindings.inc.rs',
include_directories: include_directories('.', 'include'), include_directories: include_directories('.', 'include'),
bindgen_version: ['>=0.60.0'], bindgen_version: ['>=0.60.0'],
args: bindgen_args, args: bindgen_args,

View file

@ -1,2 +0,0 @@
# Ignore generated bindings file overrides.
src/bindings.rs.inc

View file

@ -1,2 +1,2 @@
# Ignore generated bindings file overrides. # Ignore generated bindings file overrides.
src/bindings.rs /src/bindings.inc.rs

View file

@ -5,7 +5,7 @@ This library exports helper Rust types, Rust macros and C FFI bindings for inter
The C bindings can be generated with `bindgen`, using this build target: The C bindings can be generated with `bindgen`, using this build target:
```console ```console
$ ninja bindings.rs $ ninja bindings.inc.rs
``` ```
## Generate Rust documentation ## Generate Rust documentation
@ -13,5 +13,5 @@ $ ninja bindings.rs
To generate docs for this crate, including private items: To generate docs for this crate, including private items:
```sh ```sh
cargo doc --no-deps --document-private-items pyvenv/bin/meson devenv -w ../rust cargo doc --no-deps --document-private-items
``` ```

View file

@ -2,22 +2,42 @@
// Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org> // Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
use std::path::Path; #[cfg(unix)]
use std::os::unix::fs::symlink as symlink_file;
#[cfg(windows)]
use std::os::windows::fs::symlink_file;
use std::{env, fs::remove_file, io::Result, path::Path};
use version_check as rustc; use version_check as rustc;
fn main() { fn main() -> Result<()> {
if !Path::new("src/bindings.rs").exists() { // Placing bindings.inc.rs in the source directory is supported
panic!( // but not documented or encouraged.
"No generated C bindings found! Either build them manually with bindgen or with meson \ let path = env::var("MESON_BUILD_ROOT")
(`ninja bindings.rs`) and copy them to src/bindings.rs, or build through meson." .unwrap_or_else(|_| format!("{}/src", env!("CARGO_MANIFEST_DIR")));
);
let file = format!("{}/bindings.inc.rs", path);
let file = Path::new(&file);
if !Path::new(&file).exists() {
panic!(concat!(
"No generated C bindings found! If you want to run `cargo`, start a subshell\n",
"with `meson devenv`, or point MESON_BUILD_ROOT to the top of the build tree."
));
} }
let out_dir = env::var("OUT_DIR").unwrap();
let dest_path = format!("{}/bindings.inc.rs", out_dir);
let dest_path = Path::new(&dest_path);
if dest_path.symlink_metadata().is_ok() {
remove_file(dest_path)?;
}
symlink_file(file, dest_path)?;
// Check for available rustc features // Check for available rustc features
if rustc::is_min_version("1.77.0").unwrap_or(false) { if rustc::is_min_version("1.77.0").unwrap_or(false) {
println!("cargo:rustc-cfg=has_offset_of"); println!("cargo:rustc-cfg=has_offset_of");
} }
println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=build.rs");
Ok(())
} }

View file

@ -9,6 +9,7 @@ _qemu_api_rs = static_library(
structured_sources( structured_sources(
[ [
'src/lib.rs', 'src/lib.rs',
'src/bindings.rs',
'src/c_str.rs', 'src/c_str.rs',
'src/definitions.rs', 'src/definitions.rs',
'src/device_class.rs', 'src/device_class.rs',

View file

@ -0,0 +1,29 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#![allow(
dead_code,
improper_ctypes_definitions,
improper_ctypes,
non_camel_case_types,
non_snake_case,
non_upper_case_globals,
unsafe_op_in_unsafe_fn,
clippy::missing_const_for_fn,
clippy::too_many_arguments,
clippy::approx_constant,
clippy::use_self,
clippy::useless_transmute,
clippy::missing_safety_doc
)]
#[cfg(MESON)]
include!("bindings.inc.rs");
#[cfg(not(MESON))]
include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
unsafe impl Send for Property {}
unsafe impl Sync for Property {}
unsafe impl Sync for TypeInfo {}
unsafe impl Sync for VMStateDescription {}
unsafe impl Sync for VMStateField {}
unsafe impl Sync for VMStateInfo {}

View file

@ -4,31 +4,9 @@
#![cfg_attr(not(MESON), doc = include_str!("../README.md"))] #![cfg_attr(not(MESON), doc = include_str!("../README.md"))]
#[allow(
dead_code,
improper_ctypes_definitions,
improper_ctypes,
non_camel_case_types,
non_snake_case,
non_upper_case_globals,
unsafe_op_in_unsafe_fn,
clippy::missing_const_for_fn,
clippy::too_many_arguments,
clippy::approx_constant,
clippy::use_self,
clippy::useless_transmute,
clippy::missing_safety_doc,
)]
#[rustfmt::skip] #[rustfmt::skip]
pub mod bindings; pub mod bindings;
unsafe impl Send for bindings::Property {}
unsafe impl Sync for bindings::Property {}
unsafe impl Sync for bindings::TypeInfo {}
unsafe impl Sync for bindings::VMStateDescription {}
unsafe impl Sync for bindings::VMStateField {}
unsafe impl Sync for bindings::VMStateInfo {}
pub mod c_str; pub mod c_str;
pub mod definitions; pub mod definitions;
pub mod device_class; pub mod device_class;