rust: add bindgen step as a meson dependency

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>
This commit is contained in:
Manos Pitsidianakis 2024-10-03 16:28:46 +03:00 committed by Paolo Bonzini
parent 1a6ef6ff62
commit 6fdc5bc173
7 changed files with 213 additions and 0 deletions

View File

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

7
configure vendored
View File

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

View File

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

3
rust/.gitignore vendored Normal file
View File

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

0
rust/meson.build Normal file
View File

47
rust/wrapper.h Normal file
View File

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

View File

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