From: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
Add bindings_rs target for generating rust bindings to target-independent
qemu C APIs.
The bindings need be created before any rust crate that uses them is
compiled.
The bindings.rs file will end up in BUILDDIR/bindings.rs and have the
same name as a target:
ninja bindings.rs
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
Link: https://lore.kernel.org/r/1be89a27719049b7203eaf2eca8bbb75b33f18d4.1727961605.git.manos.pitsidianakis@linaro.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
MAINTAINERS | 4 ++
configure | 7 ++++
meson.build | 68 ++++++++++++++++++++++++++++++
rust/wrapper.h | 47 +++++++++++++++++++++
rust/.gitignore | 3 ++
rust/meson.build | 0
scripts/rust/rustc_args.py | 84 ++++++++++++++++++++++++++++++++++++++
7 files changed, 213 insertions(+)
create mode 100644 rust/wrapper.h
create mode 100644 rust/.gitignore
create mode 100644 rust/meson.build
create mode 100644 scripts/rust/rustc_args.py
diff --git a/MAINTAINERS b/MAINTAINERS
index fb3188c053e..f09029560b6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4191,7 +4191,11 @@ F: docs/devel/docs.rst
Rust build system integration
M: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
S: Maintained
+F: scripts/rust/
+F: rust/.gitignore
F: rust/Kconfig
+F: rust/meson.build
+F: rust/wrapper.h
Miscellaneous
-------------
diff --git a/configure b/configure
index e832f211dc6..99d57a6ef07 100755
--- a/configure
+++ b/configure
@@ -2060,3 +2060,10 @@ echo ' "$@"' >>config.status
chmod +x config.status
rm -r "$TMPDIR1"
+
+if test "$rust" != disabled; then
+ echo '\nINFO: Rust bindings generation with `bindgen` might fail in some cases where'
+ echo 'the detected `libclang` does not match the expected `clang` version/target. In'
+ echo 'this case you must pass the path to `clang` and `libclang` to your build'
+ echo 'command invocation using the environment variables CLANG_PATH and LIBCLANG_PATH'
+fi
diff --git a/meson.build b/meson.build
index f76f01f065f..b9a83dfc744 100644
--- a/meson.build
+++ b/meson.build
@@ -3892,6 +3892,74 @@ common_all = static_library('common',
implicit_include_directories: false,
dependencies: common_ss.all_dependencies())
+if have_rust and have_system
+ rustc_args = run_command(
+ find_program('scripts/rust/rustc_args.py'),
+ '--config-headers', meson.project_build_root() / 'config-host.h',
+ capture : true,
+ check: true).stdout().strip().split()
+ rustc_args += ['-D', 'unsafe_op_in_unsafe_fn']
+ bindgen_args = [
+ '--disable-header-comment',
+ '--raw-line', '// @generated',
+ '--ctypes-prefix', 'core::ffi',
+ '--formatter', 'rustfmt',
+ '--generate-block',
+ '--generate-cstr',
+ '--impl-debug',
+ '--merge-extern-blocks',
+ '--no-doc-comments',
+ '--use-core',
+ '--with-derive-default',
+ '--no-size_t-is-usize',
+ '--no-layout-tests',
+ '--no-prepend-enum-name',
+ '--allowlist-file', meson.project_source_root() + '/include/.*',
+ '--allowlist-file', meson.project_source_root() + '/.*',
+ '--allowlist-file', meson.project_build_root() + '/.*'
+ ]
+ c_enums = [
+ 'DeviceCategory',
+ 'GpioPolarity',
+ 'MachineInitPhase',
+ 'MemoryDeviceInfoKind',
+ 'MigrationPolicy',
+ 'MigrationPriority',
+ 'QEMUChrEvent',
+ 'QEMUClockType',
+ 'device_endian',
+ 'module_init_type',
+ ]
+ foreach enum : c_enums
+ bindgen_args += ['--rustified-enum', enum]
+ endforeach
+ c_bitfields = [
+ 'ClockEvent',
+ 'VMStateFlags',
+ ]
+ foreach enum : c_bitfields
+ bindgen_args += ['--bitfield-enum', enum]
+ endforeach
+
+ # TODO: Remove this comment when the clang/libclang mismatch issue is solved.
+ #
+ # Rust bindings generation with `bindgen` might fail in some cases where the
+ # detected `libclang` does not match the expected `clang` version/target. In
+ # this case you must pass the path to `clang` and `libclang` to your build
+ # command invocation using the environment variables CLANG_PATH and
+ # LIBCLANG_PATH
+ bindings_rs = import('rust').bindgen(
+ input: 'rust/wrapper.h',
+ dependencies: common_ss.all_dependencies(),
+ output: 'bindings.rs',
+ include_directories: include_directories('.', 'include'),
+ bindgen_version: ['>=0.69.4'],
+ args: bindgen_args,
+ )
+ subdir('rust')
+endif
+
+
feature_to_c = find_program('scripts/feature_to_c.py')
if host_os == 'darwin'
diff --git a/rust/wrapper.h b/rust/wrapper.h
new file mode 100644
index 00000000000..77e40213efb
--- /dev/null
+++ b/rust/wrapper.h
@@ -0,0 +1,47 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2024 Linaro Ltd.
+ *
+ * Authors: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+/*
+ * This header file is meant to be used as input to the `bindgen` application
+ * in order to generate C FFI compatible Rust bindings.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/module.h"
+#include "qemu-io.h"
+#include "sysemu/sysemu.h"
+#include "hw/sysbus.h"
+#include "exec/memory.h"
+#include "chardev/char-fe.h"
+#include "hw/clock.h"
+#include "hw/qdev-clock.h"
+#include "hw/qdev-properties.h"
+#include "hw/qdev-properties-system.h"
+#include "hw/irq.h"
+#include "qapi/error.h"
+#include "migration/vmstate.h"
+#include "chardev/char-serial.h"
diff --git a/rust/.gitignore b/rust/.gitignore
new file mode 100644
index 00000000000..1bf71b1f68e
--- /dev/null
+++ b/rust/.gitignore
@@ -0,0 +1,3 @@
+# Ignore any cargo development build artifacts; for qemu-wide builds, all build
+# artifacts will go to the meson build directory.
+target
diff --git a/rust/meson.build b/rust/meson.build
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/scripts/rust/rustc_args.py b/scripts/rust/rustc_args.py
new file mode 100644
index 00000000000..e4cc9720e16
--- /dev/null
+++ b/scripts/rust/rustc_args.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python3
+
+"""Generate rustc arguments for meson rust builds.
+
+This program generates --cfg compile flags for the configuration headers passed
+as arguments.
+
+Copyright (c) 2024 Linaro Ltd.
+
+Authors:
+ Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""
+
+import argparse
+import logging
+
+from typing import List
+
+
+def generate_cfg_flags(header: str) -> List[str]:
+ """Converts defines from config[..].h headers to rustc --cfg flags."""
+
+ def cfg_name(name: str) -> str:
+ """Filter function for C #defines"""
+ if (
+ name.startswith("CONFIG_")
+ or name.startswith("TARGET_")
+ or name.startswith("HAVE_")
+ ):
+ return name
+ return ""
+
+ with open(header, encoding="utf-8") as cfg:
+ config = [l.split()[1:] for l in cfg if l.startswith("#define")]
+
+ cfg_list = []
+ for cfg in config:
+ name = cfg_name(cfg[0])
+ if not name:
+ continue
+ if len(cfg) >= 2 and cfg[1] != "1":
+ continue
+ cfg_list.append("--cfg")
+ cfg_list.append(name)
+ return cfg_list
+
+
+def main() -> None:
+ # pylint: disable=missing-function-docstring
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-v", "--verbose", action="store_true")
+ parser.add_argument(
+ "--config-headers",
+ metavar="CONFIG_HEADER",
+ action="append",
+ dest="config_headers",
+ help="paths to any configuration C headers (*.h files), if any",
+ required=False,
+ default=[],
+ )
+ args = parser.parse_args()
+ if args.verbose:
+ logging.basicConfig(level=logging.DEBUG)
+ logging.debug("args: %s", args)
+ for header in args.config_headers:
+ for tok in generate_cfg_flags(header):
+ print(tok)
+
+
+if __name__ == "__main__":
+ main()
--
2.46.2