diff mbox series

[RFC,v3,2/5] rust: add bindgen step as a meson dependency

Message ID 6bf311a35e6d3bfa8b3bfd10d8f896a9e655fa30.1718827153.git.manos.pitsidianakis@linaro.org (mailing list archive)
State New, archived
Headers show
Series Implement ARM PL011 in Rust | expand

Commit Message

Manos Pitsidianakis June 19, 2024, 8:13 p.m. UTC
Add mechanism to generate rust hw targets that depend on a custom
bindgen target for rust bindings to C.

This way bindings will be created before the rust crate is compiled.

The bindings will end up in BUILDDIR/{target}-generated.rs and have the same name
as a target:

ninja aarch64-softmmu-generated.rs

The way the bindings are generated is:

1. All required C headers are included in a single file, in our case
   rust/wrapper.h for convenience. Otherwise we'd have to provide a list
   of headers every time to the bindgen tool.

2. Meson creates a generated_rs target that runs bindgen making sure
   the architecture etc header dependencies are present.

3. The generated_rs target takes a list of files, type symbols,
   function symbols to block from being generated. This is not necessary
   for the bindings to work, but saves us time and space.

4. Meson creates rust hardware target dependencies from the rust_targets
   dictionary defined in rust/meson.build.

   Since we cannot declare a dependency on generated_rs before it is
   declared in meson.build, the rust crate targets must be defined after
   the generated_rs target for each target architecture is defined. This
   way meson sets up the dependency tree properly.

5. After compiling each rust crate with the cargo_wrapper.py script,
   its static library artifact is linked as a `whole-archive` with the
   final binary.

Signed-off-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
---
 MAINTAINERS              |   3 +
 meson.build              |  56 +++++++++++++++++
 rust/.gitignore          |   3 +
 rust/meson.build         | 129 +++++++++++++++++++++++++++++++++++++++
 rust/wrapper.h           |  39 ++++++++++++
 scripts/cargo_wrapper.py |  10 +++
 6 files changed, 240 insertions(+)
 create mode 100644 rust/.gitignore
 create mode 100644 rust/meson.build
 create mode 100644 rust/wrapper.h

Comments

Alex Bennée June 20, 2024, 11:10 a.m. UTC | #1
Manos Pitsidianakis <manos.pitsidianakis@linaro.org> writes:

> Add mechanism to generate rust hw targets that depend on a custom
> bindgen target for rust bindings to C.
>
> This way bindings will be created before the rust crate is compiled.
>
> The bindings will end up in BUILDDIR/{target}-generated.rs and have the same name
> as a target:
>
> ninja aarch64-softmmu-generated.rs
>
<snip>
> +
> +# FIXME: These are the latest stable versions, refine to actual minimum ones.
> +msrv = {
> +  'rustc': '1.79.0',
> +  'cargo': '1.79.0',
> +  'bindgen': '0.69.4',
> +}

So for Debian Bookworm this comes out as:

  msrv = {
    'rustc': '1.79.0',
    'cargo': '1.79.0',
    'bindgen': '0.69.4',
  }

I shall have to see how close Trixie is ;-)
Alex Bennée June 20, 2024, 12:32 p.m. UTC | #2
Manos Pitsidianakis <manos.pitsidianakis@linaro.org> writes:

> Add mechanism to generate rust hw targets that depend on a custom
> bindgen target for rust bindings to C.
>
> This way bindings will be created before the rust crate is compiled.
>
> The bindings will end up in BUILDDIR/{target}-generated.rs and have the same name
> as a target:
>
> ninja aarch64-softmmu-generated.rs
>
<snip>
> +
> +
> +rust_targets = {}
> +
> +cargo_wrapper = [
> +  find_program(meson.global_source_root() / 'scripts/cargo_wrapper.py'),
> +  '--config-headers', meson.project_build_root() / 'config-host.h',
> +  '--meson-build-root', meson.project_build_root(),
> +  '--meson-build-dir', meson.current_build_dir(),
> +  '--meson-source-dir', meson.current_source_dir(),
> +]

I'm unclear what the difference between meson-build-root and
meson-build-dir is?

We also end up defining crate-dir and outdir. Aren't these all
derivable from whatever module we are building?

> +
> +if get_option('b_colorout') != 'never'
> +  cargo_wrapper += ['--color', 'always']
> +endif
> +
> +if get_option('optimization') in ['0', '1', 'g']
> +  rs_build_type = 'debug'
> +else
> +  rs_build_type = 'release'
> +endif
> +
> +# Collect metadata for each (crate,qemu-target,compiler-target) combination.
> +# Rust meson targets cannot be defined a priori because they depend on bindgen
> +# generation that is created for each emulation target separately. Thus Rust
> +# meson targets will be defined for each target after the target-specific
> +# bindgen dependency is declared.
> +rust_hw_target_list = {}
> +
> +foreach rust_hw_target, rust_hws: rust_hw_target_list
> +  foreach rust_hw_dev: rust_hws
> +    output = meson.current_build_dir() / rust_target_triple / rs_build_type / rust_hw_dev['output']
> +    crate_metadata = {
> +      'name': rust_hw_dev['name'],
> +      'output': [rust_hw_dev['output']],
> +      'output-path': output,
> +      'command': [cargo_wrapper,
> +        '--crate-dir', meson.current_source_dir() / rust_hw_dev['dirname'],
> +        '--profile', rs_build_type,
> +        '--target-triple', rust_target_triple,
> +        '--outdir', '@OUTDIR@',
> +        'build-lib'
> +        ]
> +      }
> +    rust_targets += { rust_hw_target: [crate_metadata] }
> +  endforeach
> +endforeach
> diff --git a/rust/wrapper.h b/rust/wrapper.h
> new file mode 100644
> index 0000000000..bcf808c8d7
> --- /dev/null
> +++ b/rust/wrapper.h
> @@ -0,0 +1,39 @@
> +/*
> + * QEMU System Emulator
> + *
> + * Copyright (c) 2003-2020 Fabrice Bellard
> + *
> + * 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.
> + */
> +
> +#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/scripts/cargo_wrapper.py b/scripts/cargo_wrapper.py
> index 927336f80e..833e0e55f8 100644
> --- a/scripts/cargo_wrapper.py
> +++ b/scripts/cargo_wrapper.py
> @@ -111,6 +111,8 @@ def get_cargo_rustc(args: argparse.Namespace) -> tuple[Dict[str, Any], List[str]
>  
>      env = os.environ
>      env["CARGO_ENCODED_RUSTFLAGS"] = cfg
> +    env["MESON_BUILD_DIR"] = str(target_dir)
> +    env["MESON_BUILD_ROOT"] = str(args.meson_build_root)
>  
>      return (env, cargo_cmd)
>  
> @@ -234,6 +236,14 @@ def main() -> None:
>          required=True,
>      )
>      parser.add_argument(
> +        "--meson-build-root",
> +        metavar="BUILD_ROOT",
> +        help="meson.project_build_root()",
> +        type=Path,
> +        dest="meson_build_root",
> +        required=True,
> +    )
> +    parser.add_argument(
>          "--meson-source-dir",
>          metavar="SOURCE_DIR",
>          help="meson.current_source_dir()",
Paolo Bonzini June 20, 2024, 12:34 p.m. UTC | #3
On Thu, Jun 20, 2024 at 1:10 PM Alex Bennée <alex.bennee@linaro.org> wrote:
> > +# FIXME: These are the latest stable versions, refine to actual minimum ones.
> > +msrv = {
> > +  'rustc': '1.79.0',
> > +  'cargo': '1.79.0',
> > +  'bindgen': '0.69.4',
> > +}
>
> So for Debian Bookworm this comes out as:
>
>   msrv = {
>     'rustc': '1.79.0',
>     'cargo': '1.79.0',
>     'bindgen': '0.69.4',
>   }

I think it's 0.60.1 bindgen and 1.63.0 rustc/cargo? That means we
don't have generic associated types (1.65), which are nice to have but
not absolutely necessary.

The only other one with an old version is Ubuntu 22.04 (1.58.1), but
it has 1.75.0 in updates

Paolo
Richard Henderson June 20, 2024, 2:01 p.m. UTC | #4
On 6/19/24 13:13, Manos Pitsidianakis wrote:
> +# FIXME: These are the latest stable versions, refine to actual minimum ones.
> +msrv = {
> +  'rustc': '1.79.0',
> +  'cargo': '1.79.0',
> +  'bindgen': '0.69.4',
> +}

A note for other rust newbies:

These versions are pretty darn close to actual minima.  Ubuntu 24.04 packages rust 1.77, 
which does not support (but has a warning reserving syntax for)

> +    println!("cargo::rerun-if-env-changed=MESON_BUILD_DIR");

Since even the newest distros will not have current enough rust versions, we must rely on 
'rustup'.  This may be available even on older distros; for instance Ubuntu 22.04 has 
rustup via 'snap'.

I think this is good enough for rust development within qemu, but it may require that the 
configure switch be opt-in: default no rather than default auto.


r~
Manos Pitsidianakis June 20, 2024, 6:18 p.m. UTC | #5
On Thu, 20 Jun 2024 15:34, Paolo Bonzini <pbonzini@redhat.com> wrote:
>On Thu, Jun 20, 2024 at 1:10 PM Alex Bennée <alex.bennee@linaro.org> wrote:
>> > +# FIXME: These are the latest stable versions, refine to actual minimum ones.
>> > +msrv = {
>> > +  'rustc': '1.79.0',
>> > +  'cargo': '1.79.0',
>> > +  'bindgen': '0.69.4',
>> > +}
>>
>> So for Debian Bookworm this comes out as:
>>
>>   msrv = {
>>     'rustc': '1.79.0',
>>     'cargo': '1.79.0',
>>     'bindgen': '0.69.4',
>>   }
>
>I think it's 0.60.1 bindgen and 1.63.0 rustc/cargo? That means we
>don't have generic associated types (1.65), which are nice to have but
>not absolutely necessary.
>
>The only other one with an old version is Ubuntu 22.04 (1.58.1), but
>it has 1.75.0 in updates
>
>Paolo

1.63 is definitely old at this point but we might still be in luck, 
(Except for bindgen). I will try running cargo-msrv[0] which finds the 
actual minimum supported version of a codebase with a binary search and 
see if we can give up features if necessary.

[0]: https://github.com/foresterre/cargo-msrv
Manos Pitsidianakis June 20, 2024, 6:22 p.m. UTC | #6
On Thu, 20 Jun 2024 15:32, Alex Bennée <alex.bennee@linaro.org> wrote:
>Manos Pitsidianakis <manos.pitsidianakis@linaro.org> writes:
>
>> Add mechanism to generate rust hw targets that depend on a custom
>> bindgen target for rust bindings to C.
>>
>> This way bindings will be created before the rust crate is compiled.
>>
>> The bindings will end up in BUILDDIR/{target}-generated.rs and have the same name
>> as a target:
>>
>> ninja aarch64-softmmu-generated.rs
>>
><snip>
>> +
>> +
>> +rust_targets = {}
>> +
>> +cargo_wrapper = [
>> +  find_program(meson.global_source_root() / 'scripts/cargo_wrapper.py'),
>> +  '--config-headers', meson.project_build_root() / 'config-host.h',
>> +  '--meson-build-root', meson.project_build_root(),
>> +  '--meson-build-dir', meson.current_build_dir(),
>> +  '--meson-source-dir', meson.current_source_dir(),
>> +]
>
>I'm unclear what the difference between meson-build-root and
>meson-build-dir is?

Build-dir is the subdir of the current subdir(...) meson.build file

So if we are building under qemu/build, meson_build_root is qemu/build 
and meson_build_dir is qemu/build/rust

>
>We also end up defining crate-dir and outdir. Aren't these all
>derivable from whatever module we are building?

Crate dir is the source directory (i.e. qemu/rust/pl011) that contains 
the crate's manifest file Cargo.toml.

Outdir is where to put the final build artifact for meson to find. We 
could derive that from the build directories and package names somehow 
but I chose to be explicit instead of doing indirect logic to make the 
process less magic.

I know it's a lot so I'm open to simplifications. The only problem is 
that all of these directories, except the crate source code, are defined 
from meson and can change with any refactor we do from the meson side of 
things.
Manos Pitsidianakis June 20, 2024, 6:36 p.m. UTC | #7
On Thu, 20 Jun 2024 17:01, Richard Henderson <richard.henderson@linaro.org> wrote:
>On 6/19/24 13:13, Manos Pitsidianakis wrote:
>> +# FIXME: These are the latest stable versions, refine to actual minimum ones.
>> +msrv = {
>> +  'rustc': '1.79.0',
>> +  'cargo': '1.79.0',
>> +  'bindgen': '0.69.4',
>> +}
>
>A note for other rust newbies:
>
>These versions are pretty darn close to actual minima.  Ubuntu 24.04 packages rust 1.77, 
>which does not support (but has a warning reserving syntax for)
>
>> +    println!("cargo::rerun-if-env-changed=MESON_BUILD_DIR");
>


rerun-if-env-changed is not new, I think, but the `cargo::` instead of 
`cargo:` syntax is. Is this what the warning is saying?

Source 
<https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script>:

  Note: The old invocation prefix cargo: (one colon only) is deprecated
  and won’t get any new features. To migrate, use two-colons prefix
  cargo::, which was added in Rust 1.77. If you were using
  cargo:KEY=VALUE for arbitrary links manifest key-value pairs, it is
  encouraged to switch to cargo::metadata=KEY=VALUE. Stick to cargo:
  only if the support of Rust version older than 1.77 is required.

But this is not in any way necessary for us, we can ignore cargo's stale 
build detection and force it from meson.
Richard Henderson June 20, 2024, 7:30 p.m. UTC | #8
On 6/20/24 11:36, Manos Pitsidianakis wrote:
> On Thu, 20 Jun 2024 17:01, Richard Henderson <richard.henderson@linaro.org> wrote:
>> On 6/19/24 13:13, Manos Pitsidianakis wrote:
>>> +# FIXME: These are the latest stable versions, refine to actual minimum ones.
>>> +msrv = {
>>> +  'rustc': '1.79.0',
>>> +  'cargo': '1.79.0',
>>> +  'bindgen': '0.69.4',
>>> +}
>>
>> A note for other rust newbies:
>>
>> These versions are pretty darn close to actual minima.  Ubuntu 24.04 packages rust 1.77, 
>> which does not support (but has a warning reserving syntax for)
>>
>>> +    println!("cargo::rerun-if-env-changed=MESON_BUILD_DIR");
>>
> 
> 
> rerun-if-env-changed is not new, I think, but the `cargo::` instead of `cargo:` syntax is. 
> Is this what the warning is saying?
> 
> Source 
> <https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script>:
> 
>   Note: The old invocation prefix cargo: (one colon only) is deprecated
>   and won’t get any new features. To migrate, use two-colons prefix
>   cargo::, which was added in Rust 1.77. If you were using
>   cargo:KEY=VALUE for arbitrary links manifest key-value pairs, it is
>   encouraged to switch to cargo::metadata=KEY=VALUE. Stick to cargo:
>   only if the support of Rust version older than 1.77 is required.
> 
> But this is not in any way necessary for us, we can ignore cargo's stale build detection 
> and force it from meson.

Yes indeed.  The exact error reported is

error: unsupported output in build script of `pl011 v0.1.0 
(/home/rth/qemu/src/rust/pl011)`: `cargo::rerun-if-env-changed=MESON_BUILD_DIR`
Found a `cargo::key=value` build directive which is reserved for future use.
Either change the directive to `cargo:key=value` syntax (note the single `:`) or upgrade 
your version of Rust.


r~
Manos Pitsidianakis June 24, 2024, 10:02 a.m. UTC | #9
Hello Zhao!

On Mon, 24 Jun 2024 13:12, Zhao Liu <zhao1.liu@intel.com> wrote:
>Hi Manos,
>
>Thanks for your patch. I have a few comments below:
>
>> diff --git a/meson.build b/meson.build
>> index 3533889852..2b305e745a 100644
>> --- a/meson.build
>> +++ b/meson.build
>> @@ -3876,6 +3876,62 @@ foreach target : target_dirs
>>      lib_deps += dep.partial_dependency(compile_args: true, includes: true)
>>    endforeach
>>  
>> +  if with_rust and target_type == 'system'
>> +       # FIXME:
>
>Just nit, FIXME looks a bit ambiguous, is it intended to fix the
>following warning? It could be a bit more specific.


Yes it could, that's true. Basically meson prints this warning when it 
reconfigures the build targets. So to use these newer features in our 
minimum supported meson version the code should be fixed to be backwards 
compatible.

I will add a sentence explaining that this is meson's output.

>
>> +       # > WARNING: Project specifies a minimum meson_version '>=0.63.0' but
>> +       # > uses features which were added in newer versions:
>> +       # > * 0.64.0: {'fs.copyfile'}
>> +       # > * 1.0.0: {'dependencies arg in rust.bindgen', 'module rust as stable module'}
>> +      rust_bindgen = import('rust')
>> +
>> +      # We need one generated_rs target per target, so give them
>> +      # target-specific names.
>> +      copy = fs.copyfile('rust/wrapper.h',
>> +                         target + '_wrapper.h')
>> +      generated_rs = rust_bindgen.bindgen(
>> +        input: copy,
>> +        dependencies: arch_deps + lib_deps,
>> +        output: target + '-generated.rs',
>> +        include_directories: include_directories('.', 'include'),
>> +        args: [
>> +          '--ctypes-prefix', 'core::ffi',
>> +          '--formatter', 'rustfmt',
>> +          '--generate-block',
>> +          '--generate-cstr',
>> +          '--impl-debug',
>> +          '--merge-extern-blocks',
>> +          '--no-doc-comments',
>> +          '--no-include-path-detection',
>> +          '--use-core',
>> +          '--with-derive-default',
>> +          '--allowlist-file', meson.project_source_root() + '/include/.*',
>> +          '--allowlist-file', meson.project_source_root() + '/.*',
>> +          '--allowlist-file', meson.project_build_root() + '/.*'
>> +        ],
>> +      )
>> +
>> +      if target in rust_targets
>> +        rust_hw = ss.source_set()
>> +        foreach t: rust_targets[target]
>> +          rust_device_cargo = custom_target(t['name'],
>> +                                       output: t['output'],
>> +                                       depends: [generated_rs],
>> +                                       build_always_stale: true,
>> +                                       command: t['command'])
>> +          rust_dep = declare_dependency(link_args: [
>> +                                          '-Wl,--whole-archive',
>> +                                          t['output-path'],
>> +                                          '-Wl,--no-whole-archive'
>> +                                          ],
>> +                                          sources: [rust_device_cargo])
>> +          rust_hw.add(rust_dep)
>> +        endforeach
>> +        rust_hw_config = rust_hw.apply(config_target, strict: false)
>> +        arch_srcs += rust_hw_config.sources()
>> +        arch_deps += rust_hw_config.dependencies()
>> +      endif
>> +  endif
>> +
>>    lib = static_library('qemu-' + target,
>>                   sources: arch_srcs + genh,
>>                   dependencies: lib_deps,
>> diff --git a/rust/.gitignore b/rust/.gitignore
>> new file mode 100644
>> index 0000000000..1bf71b1f68
>> --- /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 0000000000..435abd3e1c
>> --- /dev/null
>> +++ b/rust/meson.build
>> @@ -0,0 +1,129 @@
>> +# Supported hosts
>> +rust_supported_oses = {
>> +  'linux': '-unknown-linux-gnu',
>> +  'darwin': '-apple-darwin',
>> +  'windows': '-pc-windows-gnu'
>> +}
>
>Does the current test cover windows and apple? If not, I think we can
>add these two platforms later on.

Do you mean the test for supported OSes?

This is for future-proofing the Rust integration in general. I haven't 
been able to compile under macos yet because bindgen cannot find the 
system clang header. I also don't have a windows pc to test it on. But 
it should work theoretically under all three.

>
>> +rust_supported_cpus = ['x86_64', 'aarch64']
>
>Similarly, here I have another question, x-pl011-rust in arm virt machine
>I understand should only run on ARM platform, right? So compiling to
>x86_64 doesn't seem necessary at the moment?

This refers to the host cpu not the virtualized ones, you can run 
qemu-system-aarch64 under x86_64


>> +# Future-proof the above definitions against any change in the root meson.build file:
>> +foreach rust_os: rust_supported_oses.keys()
>> +  if not supported_oses.contains(rust_os)
>> +    message()
>> +    warning('UNSUPPORTED OS VALUES IN ' + meson.current_source_dir() + '/meson.build')
>> +    message()
>> +    message('This meson.build file claims OS `+' + rust_os + '` is supported but')
>> +    message('it is not included in the global supported OSes list in')
>> +    message(meson.global_source_root() + '/meson.build.')
>> +  endif
>> +endforeach
>> +foreach rust_cpu: rust_supported_cpus
>> +  if not supported_cpus.contains(rust_cpu)
>> +    message()
>> +    warning('UNSUPPORTED CPU VALUES IN ' + meson.current_source_dir() + '/meson.build')
>> +    message()
>> +    message('This meson.build file claims CPU `+' + rust_cpu + '` is supported but')
>> +    message('it is not included in the global supported CPUs list in')
>> +    message(meson.global_source_root() + '/meson.build.')
>> +  endif
>> +endforeach
>> +
>> +# FIXME: retrieve target triple from meson and construct rust_target_triple
>> +if meson.is_cross_build()
>> +  message()
>> +  error('QEMU does not support cross compiling with Rust enabled.')
>> +endif
>
>Is the check here to avoid inconsistencies between cross-build's target
>and rust_target_triple?
>
>If target consistency is not guaranteed, then it seems custom rust_target_triple
>is useless, right? please educate me if I am wrong ;-)

It is, correct. I was hoping someone on the list would have advice on 
how to best do this (see the FIXME comment right above)

>
>> +# FIXME: These are the latest stable versions, refine to actual minimum ones.
>> +msrv = {
>> +  'rustc': '1.79.0',
>> +  'cargo': '1.79.0',
>> +  'bindgen': '0.69.4',
>> +}
>> +
>> +# rustup = find_program('rustup', required: false)
>> +foreach bin_dep: msrv.keys()
>> +  bin = find_program(bin_dep, required: true)
>> +  if bin.version() < msrv[bin_dep]
>> +    if bin_dep == 'bindgen' and get_option('wrap_mode') != 'nodownload'
>
>What about adding a download info here?
>
>It took a long time for my bindgen-cli to update quietly, and for a
>moment I thought it was stuck here...
>
>e.g., message('Installing bindgen-cli...')
>
>Or can we just report the error and let users install bindgen-cli
>themselves?

Now that I think about it again I agree it's better to not mess with 
user/system installations without asking.

>> +      run_command(cargo, 'install', 'bindgen-cli', capture: true, check: true)
>> +      bin = find_program(bin_dep, required: true)
>> +      if bin.version() >= msrv[bin_dep]
>> +        continue
>> +      endif
>> +    endif
>> +    message()
>> +    error(bin_dep + ' version ' + bin.version() + ' is unsupported: Please upgrade to at least ' + msrv[bin_dep])
>> +  endif
>> +endforeach
>> +
>> +rust_target_triple = get_option('with_rust_target_triple')
>> +
>> +if rust_target_triple == ''
>> +  if not supported_oses.contains(host_os)
>> +    message()
>> +    error('QEMU does not support `' + host_os +'` as a Rust platform.')
>> +  elif not supported_cpus.contains(host_arch)
>> +    message()
>> +    error('QEMU does not support `' + host_arch +'` as a Rust architecture.')
>> +  endif
>> +  rust_target_triple = host_arch + rust_supported_oses[host_os]
>> +  if host_os == 'windows' and host_arch == 'aarch64'
>> +    rust_target_triple += 'llvm'
>> +  endif
>> +else
>> +  # verify rust_target_triple if given as an option
>> +  rustc = find_program('rustc', required: true)
>> +  rustc_targets = run_command(rustc, '--print', 'target-list', capture: true, check: true).stdout().strip().split()
>> +  if not rustc_targets.contains(rust_target_triple)
>> +    message()
>> +    error('Given rust_target_triple ' + rust_target_triple + ' is not listed in rustc --print target-list output')
>> +  endif
>
>It seems rust_target_triple missed support check, we should use rust_supported_cpus
>+ rust_supported_oses to check rust_target_triple, right? ;-)
>
>> +endif
>> +
>> +
>
>An extra blank line :-)
>
>> +rust_targets = {}
>> +
>> +cargo_wrapper = [
>> +  find_program(meson.global_source_root() / 'scripts/cargo_wrapper.py'),
>> +  '--config-headers', meson.project_build_root() / 'config-host.h',
>> +  '--meson-build-root', meson.project_build_root(),
>> +  '--meson-build-dir', meson.current_build_dir(),
>> +  '--meson-source-dir', meson.current_source_dir(),
>> +]
>> +
>> +if get_option('b_colorout') != 'never'
>> +  cargo_wrapper += ['--color', 'always']
>> +endif
>> +
>> +if get_option('optimization') in ['0', '1', 'g']
>> +  rs_build_type = 'debug'
>> +else
>> +  rs_build_type = 'release'
>> +endif
>
>Could we decouple 'optimization' with cargo's opt-level (maybe in the future)?
>
>Or we could add new option to specify custom rust opt-level, which will
>set the environment varialbe CARGO_PROFILE_<profile_name>_OPT_LEVEL and
>override the default opt-level in Cargo.toml.

optimization here refers to LLVM's opt flags directly, so I think it 
makes sense to follow the project-wide compiler settings?
>
>> +# Collect metadata for each (crate,qemu-target,compiler-target) combination.
>> +# Rust meson targets cannot be defined a priori because they depend on bindgen
>> +# generation that is created for each emulation target separately. Thus Rust
>> +# meson targets will be defined for each target after the target-specific
>> +# bindgen dependency is declared.
>> +rust_hw_target_list = {}
>> +
>> +foreach rust_hw_target, rust_hws: rust_hw_target_list
>> +  foreach rust_hw_dev: rust_hws
>> +    output = meson.current_build_dir() / rust_target_triple / rs_build_type / rust_hw_dev['output']
>> +    crate_metadata = {
>> +      'name': rust_hw_dev['name'],
>> +      'output': [rust_hw_dev['output']],
>> +      'output-path': output,
>> +      'command': [cargo_wrapper,
>> +        '--crate-dir', meson.current_source_dir() / rust_hw_dev['dirname'],
>> +        '--profile', rs_build_type,
>> +        '--target-triple', rust_target_triple,
>> +        '--outdir', '@OUTDIR@',
>> +        'build-lib'
>> +        ]
>> +      }
>> +    rust_targets += { rust_hw_target: [crate_metadata] }
>> +  endforeach
>> +endforeach
>> diff --git a/rust/wrapper.h b/rust/wrapper.h
>> new file mode 100644
>> index 0000000000..bcf808c8d7
>> --- /dev/null
>> +++ b/rust/wrapper.h
>> @@ -0,0 +1,39 @@
>> +/*
>> + * QEMU System Emulator
>> + *
>> + * Copyright (c) 2003-2020 Fabrice Bellard
>
>Here should the author be yourself?

Technically I copy pasted this file from the main.c executable so it's a 
derived work :D

>
>> + *
>> + * 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.
>> + */
>> +
>> +#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"
>
>Thanks,
>Zhao
>
Zhao Liu June 24, 2024, 10:12 a.m. UTC | #10
Hi Manos,

Thanks for your patch. I have a few comments below:

> diff --git a/meson.build b/meson.build
> index 3533889852..2b305e745a 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -3876,6 +3876,62 @@ foreach target : target_dirs
>      lib_deps += dep.partial_dependency(compile_args: true, includes: true)
>    endforeach
>  
> +  if with_rust and target_type == 'system'
> +       # FIXME:

Just nit, FIXME looks a bit ambiguous, is it intended to fix the
following warning? It could be a bit more specific.

> +       # > WARNING: Project specifies a minimum meson_version '>=0.63.0' but
> +       # > uses features which were added in newer versions:
> +       # > * 0.64.0: {'fs.copyfile'}
> +       # > * 1.0.0: {'dependencies arg in rust.bindgen', 'module rust as stable module'}
> +      rust_bindgen = import('rust')
> +
> +      # We need one generated_rs target per target, so give them
> +      # target-specific names.
> +      copy = fs.copyfile('rust/wrapper.h',
> +                         target + '_wrapper.h')
> +      generated_rs = rust_bindgen.bindgen(
> +        input: copy,
> +        dependencies: arch_deps + lib_deps,
> +        output: target + '-generated.rs',
> +        include_directories: include_directories('.', 'include'),
> +        args: [
> +          '--ctypes-prefix', 'core::ffi',
> +          '--formatter', 'rustfmt',
> +          '--generate-block',
> +          '--generate-cstr',
> +          '--impl-debug',
> +          '--merge-extern-blocks',
> +          '--no-doc-comments',
> +          '--no-include-path-detection',
> +          '--use-core',
> +          '--with-derive-default',
> +          '--allowlist-file', meson.project_source_root() + '/include/.*',
> +          '--allowlist-file', meson.project_source_root() + '/.*',
> +          '--allowlist-file', meson.project_build_root() + '/.*'
> +        ],
> +      )
> +
> +      if target in rust_targets
> +        rust_hw = ss.source_set()
> +        foreach t: rust_targets[target]
> +          rust_device_cargo = custom_target(t['name'],
> +                                       output: t['output'],
> +                                       depends: [generated_rs],
> +                                       build_always_stale: true,
> +                                       command: t['command'])
> +          rust_dep = declare_dependency(link_args: [
> +                                          '-Wl,--whole-archive',
> +                                          t['output-path'],
> +                                          '-Wl,--no-whole-archive'
> +                                          ],
> +                                          sources: [rust_device_cargo])
> +          rust_hw.add(rust_dep)
> +        endforeach
> +        rust_hw_config = rust_hw.apply(config_target, strict: false)
> +        arch_srcs += rust_hw_config.sources()
> +        arch_deps += rust_hw_config.dependencies()
> +      endif
> +  endif
> +
>    lib = static_library('qemu-' + target,
>                   sources: arch_srcs + genh,
>                   dependencies: lib_deps,
> diff --git a/rust/.gitignore b/rust/.gitignore
> new file mode 100644
> index 0000000000..1bf71b1f68
> --- /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 0000000000..435abd3e1c
> --- /dev/null
> +++ b/rust/meson.build
> @@ -0,0 +1,129 @@
> +# Supported hosts
> +rust_supported_oses = {
> +  'linux': '-unknown-linux-gnu',
> +  'darwin': '-apple-darwin',
> +  'windows': '-pc-windows-gnu'
> +}

Does the current test cover windows and apple? If not, I think we can
add these two platforms later on.

> +rust_supported_cpus = ['x86_64', 'aarch64']

Similarly, here I have another question, x-pl011-rust in arm virt machine
I understand should only run on ARM platform, right? So compiling to
x86_64 doesn't seem necessary at the moment?

> +# Future-proof the above definitions against any change in the root meson.build file:
> +foreach rust_os: rust_supported_oses.keys()
> +  if not supported_oses.contains(rust_os)
> +    message()
> +    warning('UNSUPPORTED OS VALUES IN ' + meson.current_source_dir() + '/meson.build')
> +    message()
> +    message('This meson.build file claims OS `+' + rust_os + '` is supported but')
> +    message('it is not included in the global supported OSes list in')
> +    message(meson.global_source_root() + '/meson.build.')
> +  endif
> +endforeach
> +foreach rust_cpu: rust_supported_cpus
> +  if not supported_cpus.contains(rust_cpu)
> +    message()
> +    warning('UNSUPPORTED CPU VALUES IN ' + meson.current_source_dir() + '/meson.build')
> +    message()
> +    message('This meson.build file claims CPU `+' + rust_cpu + '` is supported but')
> +    message('it is not included in the global supported CPUs list in')
> +    message(meson.global_source_root() + '/meson.build.')
> +  endif
> +endforeach
> +
> +# FIXME: retrieve target triple from meson and construct rust_target_triple
> +if meson.is_cross_build()
> +  message()
> +  error('QEMU does not support cross compiling with Rust enabled.')
> +endif

Is the check here to avoid inconsistencies between cross-build's target
and rust_target_triple?

If target consistency is not guaranteed, then it seems custom rust_target_triple
is useless, right? please educate me if I am wrong ;-)

> +# FIXME: These are the latest stable versions, refine to actual minimum ones.
> +msrv = {
> +  'rustc': '1.79.0',
> +  'cargo': '1.79.0',
> +  'bindgen': '0.69.4',
> +}
> +
> +# rustup = find_program('rustup', required: false)
> +foreach bin_dep: msrv.keys()
> +  bin = find_program(bin_dep, required: true)
> +  if bin.version() < msrv[bin_dep]
> +    if bin_dep == 'bindgen' and get_option('wrap_mode') != 'nodownload'

What about adding a download info here?

It took a long time for my bindgen-cli to update quietly, and for a
moment I thought it was stuck here...

e.g., message('Installing bindgen-cli...')

Or can we just report the error and let users install bindgen-cli
themselves?

> +      run_command(cargo, 'install', 'bindgen-cli', capture: true, check: true)
> +      bin = find_program(bin_dep, required: true)
> +      if bin.version() >= msrv[bin_dep]
> +        continue
> +      endif
> +    endif
> +    message()
> +    error(bin_dep + ' version ' + bin.version() + ' is unsupported: Please upgrade to at least ' + msrv[bin_dep])
> +  endif
> +endforeach
> +
> +rust_target_triple = get_option('with_rust_target_triple')
> +
> +if rust_target_triple == ''
> +  if not supported_oses.contains(host_os)
> +    message()
> +    error('QEMU does not support `' + host_os +'` as a Rust platform.')
> +  elif not supported_cpus.contains(host_arch)
> +    message()
> +    error('QEMU does not support `' + host_arch +'` as a Rust architecture.')
> +  endif
> +  rust_target_triple = host_arch + rust_supported_oses[host_os]
> +  if host_os == 'windows' and host_arch == 'aarch64'
> +    rust_target_triple += 'llvm'
> +  endif
> +else
> +  # verify rust_target_triple if given as an option
> +  rustc = find_program('rustc', required: true)
> +  rustc_targets = run_command(rustc, '--print', 'target-list', capture: true, check: true).stdout().strip().split()
> +  if not rustc_targets.contains(rust_target_triple)
> +    message()
> +    error('Given rust_target_triple ' + rust_target_triple + ' is not listed in rustc --print target-list output')
> +  endif

It seems rust_target_triple missed support check, we should use rust_supported_cpus
+ rust_supported_oses to check rust_target_triple, right? ;-)

> +endif
> +
> +

An extra blank line :-)

> +rust_targets = {}
> +
> +cargo_wrapper = [
> +  find_program(meson.global_source_root() / 'scripts/cargo_wrapper.py'),
> +  '--config-headers', meson.project_build_root() / 'config-host.h',
> +  '--meson-build-root', meson.project_build_root(),
> +  '--meson-build-dir', meson.current_build_dir(),
> +  '--meson-source-dir', meson.current_source_dir(),
> +]
> +
> +if get_option('b_colorout') != 'never'
> +  cargo_wrapper += ['--color', 'always']
> +endif
> +
> +if get_option('optimization') in ['0', '1', 'g']
> +  rs_build_type = 'debug'
> +else
> +  rs_build_type = 'release'
> +endif

Could we decouple 'optimization' with cargo's opt-level (maybe in the future)?

Or we could add new option to specify custom rust opt-level, which will
set the environment varialbe CARGO_PROFILE_<profile_name>_OPT_LEVEL and
override the default opt-level in Cargo.toml.

> +# Collect metadata for each (crate,qemu-target,compiler-target) combination.
> +# Rust meson targets cannot be defined a priori because they depend on bindgen
> +# generation that is created for each emulation target separately. Thus Rust
> +# meson targets will be defined for each target after the target-specific
> +# bindgen dependency is declared.
> +rust_hw_target_list = {}
> +
> +foreach rust_hw_target, rust_hws: rust_hw_target_list
> +  foreach rust_hw_dev: rust_hws
> +    output = meson.current_build_dir() / rust_target_triple / rs_build_type / rust_hw_dev['output']
> +    crate_metadata = {
> +      'name': rust_hw_dev['name'],
> +      'output': [rust_hw_dev['output']],
> +      'output-path': output,
> +      'command': [cargo_wrapper,
> +        '--crate-dir', meson.current_source_dir() / rust_hw_dev['dirname'],
> +        '--profile', rs_build_type,
> +        '--target-triple', rust_target_triple,
> +        '--outdir', '@OUTDIR@',
> +        'build-lib'
> +        ]
> +      }
> +    rust_targets += { rust_hw_target: [crate_metadata] }
> +  endforeach
> +endforeach
> diff --git a/rust/wrapper.h b/rust/wrapper.h
> new file mode 100644
> index 0000000000..bcf808c8d7
> --- /dev/null
> +++ b/rust/wrapper.h
> @@ -0,0 +1,39 @@
> +/*
> + * QEMU System Emulator
> + *
> + * Copyright (c) 2003-2020 Fabrice Bellard

Here should the author be yourself?

> + *
> + * 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.
> + */
> +
> +#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"

Thanks,
Zhao
Stefan Hajnoczi June 24, 2024, 7:52 p.m. UTC | #11
On Thu, 20 Jun 2024 at 14:35, Manos Pitsidianakis
<manos.pitsidianakis@linaro.org> wrote:
>
> On Thu, 20 Jun 2024 15:32, Alex Bennée <alex.bennee@linaro.org> wrote:
> >Manos Pitsidianakis <manos.pitsidianakis@linaro.org> writes:
> >
> >> Add mechanism to generate rust hw targets that depend on a custom
> >> bindgen target for rust bindings to C.
> >>
> >> This way bindings will be created before the rust crate is compiled.
> >>
> >> The bindings will end up in BUILDDIR/{target}-generated.rs and have the same name
> >> as a target:
> >>
> >> ninja aarch64-softmmu-generated.rs
> >>
> ><snip>
> >> +
> >> +
> >> +rust_targets = {}
> >> +
> >> +cargo_wrapper = [
> >> +  find_program(meson.global_source_root() / 'scripts/cargo_wrapper.py'),
> >> +  '--config-headers', meson.project_build_root() / 'config-host.h',
> >> +  '--meson-build-root', meson.project_build_root(),
> >> +  '--meson-build-dir', meson.current_build_dir(),
> >> +  '--meson-source-dir', meson.current_source_dir(),
> >> +]
> >
> >I'm unclear what the difference between meson-build-root and
> >meson-build-dir is?
>
> Build-dir is the subdir of the current subdir(...) meson.build file
>
> So if we are building under qemu/build, meson_build_root is qemu/build
> and meson_build_dir is qemu/build/rust
>
> >
> >We also end up defining crate-dir and outdir. Aren't these all
> >derivable from whatever module we are building?
>
> Crate dir is the source directory (i.e. qemu/rust/pl011) that contains
> the crate's manifest file Cargo.toml.
>
> Outdir is where to put the final build artifact for meson to find. We
> could derive that from the build directories and package names somehow
> but I chose to be explicit instead of doing indirect logic to make the
> process less magic.
>
> I know it's a lot so I'm open to simplifications. The only problem is
> that all of these directories, except the crate source code, are defined
> from meson and can change with any refactor we do from the meson side of
> things.

Expanding the help text for these command-line options would make it
easier to understand. It would be great to include an example path
too.

Stefan
Zhao Liu June 25, 2024, 4 p.m. UTC | #12
> > > +       # > WARNING: Project specifies a minimum meson_version '>=0.63.0' but
> > > +       # > uses features which were added in newer versions:
> > > +       # > * 0.64.0: {'fs.copyfile'}
> > > +       # > * 1.0.0: {'dependencies arg in rust.bindgen', 'module rust as stable module'}
> > > +      rust_bindgen = import('rust')
> > > +
> > > +      # We need one generated_rs target per target, so give them
> > > +      # target-specific names.
> > > +      copy = fs.copyfile('rust/wrapper.h',
> > > +                         target + '_wrapper.h')
> > > +      generated_rs = rust_bindgen.bindgen(
> > > +        input: copy,
> > > +        dependencies: arch_deps + lib_deps,
> > > +        output: target + '-generated.rs',
> > > +        include_directories: include_directories('.', 'include'),
> > > +        args: [
> > > +          '--ctypes-prefix', 'core::ffi',
> > > +          '--formatter', 'rustfmt',
> > > +          '--generate-block',
> > > +          '--generate-cstr',
> > > +          '--impl-debug',
> > > +          '--merge-extern-blocks',
> > > +          '--no-doc-comments',
> > > +          '--no-include-path-detection',
> > > +          '--use-core',
> > > +          '--with-derive-default',
> > > +          '--allowlist-file', meson.project_source_root() + '/include/.*',
> > > +          '--allowlist-file', meson.project_source_root() + '/.*',
> > > +          '--allowlist-file', meson.project_build_root() + '/.*'
> > > +        ],
> > > +      )
> > > +
> > > +      if target in rust_targets
> > > +        rust_hw = ss.source_set()
> > > +        foreach t: rust_targets[target]
> > > +          rust_device_cargo = custom_target(t['name'],
> > > +                                       output: t['output'],
> > > +                                       depends: [generated_rs],
> > > +                                       build_always_stale: true,
> > > +                                       command: t['command'])
> > > +          rust_dep = declare_dependency(link_args: [
> > > +                                          '-Wl,--whole-archive',
> > > +                                          t['output-path'],
> > > +                                          '-Wl,--no-whole-archive'
> > > +                                          ],
> > > +                                          sources: [rust_device_cargo])
> > > +          rust_hw.add(rust_dep)
> > > +        endforeach
> > > +        rust_hw_config = rust_hw.apply(config_target, strict: false)
> > > +        arch_srcs += rust_hw_config.sources()
> > > +        arch_deps += rust_hw_config.dependencies()
> > > +      endif
> > > +  endif
> > > +
> > >    lib = static_library('qemu-' + target,
> > >                   sources: arch_srcs + genh,
> > >                   dependencies: lib_deps,
> > > diff --git a/rust/.gitignore b/rust/.gitignore
> > > new file mode 100644
> > > index 0000000000..1bf71b1f68
> > > --- /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 0000000000..435abd3e1c
> > > --- /dev/null
> > > +++ b/rust/meson.build
> > > @@ -0,0 +1,129 @@
> > > +# Supported hosts
> > > +rust_supported_oses = {
> > > +  'linux': '-unknown-linux-gnu',
> > > +  'darwin': '-apple-darwin',
> > > +  'windows': '-pc-windows-gnu'
> > > +}
> > 
> > Does the current test cover windows and apple? If not, I think we can
> > add these two platforms later on.
> 
> Do you mean the test for supported OSes?

Yes.

> This is for future-proofing the Rust integration in general. I haven't been
> able to compile under macos yet because bindgen cannot find the system clang
> header. I also don't have a windows pc to test it on. But it should work
> theoretically under all three.

Yes, they should work. EMM, but there is no particular need for them at
the moment, so just to be safe, we can put these two platforms on hold
for now, and they can be easily added when the tests are covered.

A TODO can remind support for them.

> > 
> > > +rust_supported_cpus = ['x86_64', 'aarch64']
> > 
> > Similarly, here I have another question, x-pl011-rust in arm virt machine
> > I understand should only run on ARM platform, right? So compiling to
> > x86_64 doesn't seem necessary at the moment?
> 
> This refers to the host cpu not the virtualized ones, you can run
> qemu-system-aarch64 under x86_64

Thanks, I see.

> > > +# Future-proof the above definitions against any change in the root meson.build file:
> > > +foreach rust_os: rust_supported_oses.keys()
> > > +  if not supported_oses.contains(rust_os)
> > > +    message()
> > > +    warning('UNSUPPORTED OS VALUES IN ' + meson.current_source_dir() + '/meson.build')
> > > +    message()
> > > +    message('This meson.build file claims OS `+' + rust_os + '` is supported but')
> > > +    message('it is not included in the global supported OSes list in')
> > > +    message(meson.global_source_root() + '/meson.build.')
> > > +  endif
> > > +endforeach
> > > +foreach rust_cpu: rust_supported_cpus
> > > +  if not supported_cpus.contains(rust_cpu)
> > > +    message()
> > > +    warning('UNSUPPORTED CPU VALUES IN ' + meson.current_source_dir() + '/meson.build')
> > > +    message()
> > > +    message('This meson.build file claims CPU `+' + rust_cpu + '` is supported but')
> > > +    message('it is not included in the global supported CPUs list in')
> > > +    message(meson.global_source_root() + '/meson.build.')
> > > +  endif
> > > +endforeach
> > > +
> > > +# FIXME: retrieve target triple from meson and construct rust_target_triple
> > > +if meson.is_cross_build()
> > > +  message()
> > > +  error('QEMU does not support cross compiling with Rust enabled.')
> > > +endif
> > 
> > Is the check here to avoid inconsistencies between cross-build's target
> > and rust_target_triple?
> > 
> > If target consistency is not guaranteed, then it seems custom rust_target_triple
> > is useless, right? please educate me if I am wrong ;-)
> 
> It is, correct. I was hoping someone on the list would have advice on how to
> best do this (see the FIXME comment right above)

Yes, Daniel has said what I would say in his reply.

> > 
> > > +# FIXME: These are the latest stable versions, refine to actual minimum ones.
> > > +msrv = {
> > > +  'rustc': '1.79.0',
> > > +  'cargo': '1.79.0',
> > > +  'bindgen': '0.69.4',
> > > +}
> > > +
> > > +# rustup = find_program('rustup', required: false)
> > > +foreach bin_dep: msrv.keys()
> > > +  bin = find_program(bin_dep, required: true)
> > > +  if bin.version() < msrv[bin_dep]
> > > +    if bin_dep == 'bindgen' and get_option('wrap_mode') != 'nodownload'
> > 
> > What about adding a download info here?
> > 
> > It took a long time for my bindgen-cli to update quietly, and for a
> > moment I thought it was stuck here...
> > 
> > e.g., message('Installing bindgen-cli...')
> > 
> > Or can we just report the error and let users install bindgen-cli
> > themselves?
> 
> Now that I think about it again I agree it's better to not mess with
> user/system installations without asking.

I agree. Thanks!

> > > +      run_command(cargo, 'install', 'bindgen-cli', capture: true, check: true)
> > > +      bin = find_program(bin_dep, required: true)
> > > +      if bin.version() >= msrv[bin_dep]
> > > +        continue
> > > +      endif
> > > +    endif
> > > +    message()
> > > +    error(bin_dep + ' version ' + bin.version() + ' is unsupported: Please upgrade to at least ' + msrv[bin_dep])
> > > +  endif
> > > +endforeach
> > > +
> > > +rust_target_triple = get_option('with_rust_target_triple')
> > > +
> > > +if rust_target_triple == ''
> > > +  if not supported_oses.contains(host_os)
> > > +    message()
> > > +    error('QEMU does not support `' + host_os +'` as a Rust platform.')
> > > +  elif not supported_cpus.contains(host_arch)
> > > +    message()
> > > +    error('QEMU does not support `' + host_arch +'` as a Rust architecture.')
> > > +  endif
> > > +  rust_target_triple = host_arch + rust_supported_oses[host_os]
> > > +  if host_os == 'windows' and host_arch == 'aarch64'
> > > +    rust_target_triple += 'llvm'
> > > +  endif
> > > +else
> > > +  # verify rust_target_triple if given as an option
> > > +  rustc = find_program('rustc', required: true)
> > > +  rustc_targets = run_command(rustc, '--print', 'target-list', capture: true, check: true).stdout().strip().split()
> > > +  if not rustc_targets.contains(rust_target_triple)
> > > +    message()
> > > +    error('Given rust_target_triple ' + rust_target_triple + ' is not listed in rustc --print target-list output')
> > > +  endif
> > 
> > It seems rust_target_triple missed support check, we should use rust_supported_cpus
> > + rust_supported_oses to check rust_target_triple, right? ;-)
> > 
> > > +endif
> > > +
> > > +
> > 
> > An extra blank line :-)
> > 
> > > +rust_targets = {}
> > > +
> > > +cargo_wrapper = [
> > > +  find_program(meson.global_source_root() / 'scripts/cargo_wrapper.py'),
> > > +  '--config-headers', meson.project_build_root() / 'config-host.h',
> > > +  '--meson-build-root', meson.project_build_root(),
> > > +  '--meson-build-dir', meson.current_build_dir(),
> > > +  '--meson-source-dir', meson.current_source_dir(),
> > > +]
> > > +
> > > +if get_option('b_colorout') != 'never'
> > > +  cargo_wrapper += ['--color', 'always']
> > > +endif
> > > +
> > > +if get_option('optimization') in ['0', '1', 'g']
> > > +  rs_build_type = 'debug'
> > > +else
> > > +  rs_build_type = 'release'
> > > +endif
> > 
> > Could we decouple 'optimization' with cargo's opt-level (maybe in the future)?
> > 
> > Or we could add new option to specify custom rust opt-level, which will
> > set the environment varialbe CARGO_PROFILE_<profile_name>_OPT_LEVEL and
> > override the default opt-level in Cargo.toml.
> 
> optimization here refers to LLVM's opt flags directly, so I think it makes
> sense to follow the project-wide compiler settings?

Oh, yes. But I think the choice of debug or release is best based on the
debug info. What about this?

if get_option('debug')
    rs_build_type = 'debug'
else
    rs_build_type = 'release'
endif

get_option('debug') would apply -g flag, and that flag is mapped to debuginfo=2,
which is the default debuginfo level for debug version.

> > > +# Collect metadata for each (crate,qemu-target,compiler-target) combination.
> > > +# Rust meson targets cannot be defined a priori because they depend on bindgen
> > > +# generation that is created for each emulation target separately. Thus Rust
> > > +# meson targets will be defined for each target after the target-specific
> > > +# bindgen dependency is declared.
> > > +rust_hw_target_list = {}
> > > +
> > > +foreach rust_hw_target, rust_hws: rust_hw_target_list
> > > +  foreach rust_hw_dev: rust_hws
> > > +    output = meson.current_build_dir() / rust_target_triple / rs_build_type / rust_hw_dev['output']
> > > +    crate_metadata = {
> > > +      'name': rust_hw_dev['name'],
> > > +      'output': [rust_hw_dev['output']],
> > > +      'output-path': output,
> > > +      'command': [cargo_wrapper,
> > > +        '--crate-dir', meson.current_source_dir() / rust_hw_dev['dirname'],
> > > +        '--profile', rs_build_type,
> > > +        '--target-triple', rust_target_triple,
> > > +        '--outdir', '@OUTDIR@',
> > > +        'build-lib'
> > > +        ]
> > > +      }
> > > +    rust_targets += { rust_hw_target: [crate_metadata] }
> > > +  endforeach
> > > +endforeach
> > > diff --git a/rust/wrapper.h b/rust/wrapper.h
> > > new file mode 100644
> > > index 0000000000..bcf808c8d7
> > > --- /dev/null
> > > +++ b/rust/wrapper.h
> > > @@ -0,0 +1,39 @@
> > > +/*
> > > + * QEMU System Emulator
> > > + *
> > > + * Copyright (c) 2003-2020 Fabrice Bellard
> > 
> > Here should the author be yourself?
> 
> Technically I copy pasted this file from the main.c executable so it's a
> derived work :D
>

It's a totally new file, and it is used to derive other files. These are
all significant changes. At least for 2024, I think you can declare
copyright here. :-)

Thanks,
Zhao
Manos Pitsidianakis June 25, 2024, 6:08 p.m. UTC | #13
On Tue, 25 Jun 2024 19:00, Zhao Liu <zhao1.liu@intel.com> wrote:
>[snip]
>> This is for future-proofing the Rust integration in general. I 
>> haven't been
>> able to compile under macos yet because bindgen cannot find the system clang
>> header. I also don't have a windows pc to test it on. But it should work
>> theoretically under all three.
>
>Yes, they should work. EMM, but there is no particular need for them at
>the moment, so just to be safe, we can put these two platforms on hold
>for now, and they can be easily added when the tests are covered.
>
>A TODO can remind support for them.

I'm still trying to figure out why bindgen doesn't find the /Library/*** 
include paths.. it's frustrating! I will remove them if I don't succeed 
and also no one volunteers to attempt a windows build. :)

>[snip]
>> > 
>> > Could we decouple 'optimization' with cargo's opt-level (maybe in the future)?
>> > 
>> > Or we could add new option to specify custom rust opt-level, which will
>> > set the environment varialbe CARGO_PROFILE_<profile_name>_OPT_LEVEL and
>> > override the default opt-level in Cargo.toml.
>> 
>> optimization here refers to LLVM's opt flags directly, so I think it makes
>> sense to follow the project-wide compiler settings?
>
>Oh, yes. But I think the choice of debug or release is best based on the
>debug info. What about this?
>
>if get_option('debug')
>    rs_build_type = 'debug'
>else
>    rs_build_type = 'release'
>endif
>
>get_option('debug') would apply -g flag, and that flag is mapped to debuginfo=2,
>which is the default debuginfo level for debug version.

But wait, I just noticed rs_build_type should be 'dev' not 'debug'. You 
can build with optimizations and keep debuginfo if you override the 
'release' profile or use a different profile altogether.

I will correct the optimization and debuginfo mappings in the next 
version.

>It's a totally new file, and it is used to derive other files. These 
>are
>all significant changes. At least for 2024, I think you can declare
>copyright here. :-)

Heh yes :)

>
>Thanks,
>Zhao
>
Pierrick Bouvier June 27, 2024, 11:47 p.m. UTC | #14
On 6/25/24 11:08, Manos Pitsidianakis wrote:
> On Tue, 25 Jun 2024 19:00, Zhao Liu <zhao1.liu@intel.com> wrote:
>> [snip]
>>> This is for future-proofing the Rust integration in general. I
>>> haven't been
>>> able to compile under macos yet because bindgen cannot find the system clang
>>> header. I also don't have a windows pc to test it on. But it should work
>>> theoretically under all three.
>>
>> Yes, they should work. EMM, but there is no particular need for them at
>> the moment, so just to be safe, we can put these two platforms on hold
>> for now, and they can be easily added when the tests are covered.
>>
>> A TODO can remind support for them.
> 
> I'm still trying to figure out why bindgen doesn't find the /Library/***
> include paths.. it's frustrating! I will remove them if I don't succeed
> and also no one volunteers to attempt a windows build. :)
> 

I'm currently doing it, and managed to run until bindgen step. Same 
problem that you found on MacOS, it can't locate some headers 
(strings.h, included from osdep.h). I'll try to dig into this, but if 
you found a solution already, you're welcome to share it.

'gcc | grep' command you used should work, but should be adapted because 
windows paths start with C:/ instead of /.

>> [snip]
>>>>
>>>> Could we decouple 'optimization' with cargo's opt-level (maybe in the future)?
>>>>
>>>> Or we could add new option to specify custom rust opt-level, which will
>>>> set the environment varialbe CARGO_PROFILE_<profile_name>_OPT_LEVEL and
>>>> override the default opt-level in Cargo.toml.
>>>
>>> optimization here refers to LLVM's opt flags directly, so I think it makes
>>> sense to follow the project-wide compiler settings?
>>
>> Oh, yes. But I think the choice of debug or release is best based on the
>> debug info. What about this?
>>
>> if get_option('debug')
>>     rs_build_type = 'debug'
>> else
>>     rs_build_type = 'release'
>> endif
>>
>> get_option('debug') would apply -g flag, and that flag is mapped to debuginfo=2,
>> which is the default debuginfo level for debug version.
> 
> But wait, I just noticed rs_build_type should be 'dev' not 'debug'. You
> can build with optimizations and keep debuginfo if you override the
> 'release' profile or use a different profile altogether.
> 
> I will correct the optimization and debuginfo mappings in the next
> version.
> 
>> It's a totally new file, and it is used to derive other files. These
>> are
>> all significant changes. At least for 2024, I think you can declare
>> copyright here. :-)
> 
> Heh yes :)
> 
>>
>> Thanks,
>> Zhao
>>
Pierrick Bouvier June 28, 2024, 7:12 p.m. UTC | #15
On 6/27/24 16:47, Pierrick Bouvier wrote:
> On 6/25/24 11:08, Manos Pitsidianakis wrote:
>> On Tue, 25 Jun 2024 19:00, Zhao Liu <zhao1.liu@intel.com> wrote:
>>> [snip]
>>>> This is for future-proofing the Rust integration in general. I
>>>> haven't been
>>>> able to compile under macos yet because bindgen cannot find the system clang
>>>> header. I also don't have a windows pc to test it on. But it should work
>>>> theoretically under all three.
>>>
>>> Yes, they should work. EMM, but there is no particular need for them at
>>> the moment, so just to be safe, we can put these two platforms on hold
>>> for now, and they can be easily added when the tests are covered.
>>>
>>> A TODO can remind support for them.
>>
>> I'm still trying to figure out why bindgen doesn't find the /Library/***
>> include paths.. it's frustrating! I will remove them if I don't succeed
>> and also no one volunteers to attempt a windows build. :)
>>
> 
> I'm currently doing it, and managed to run until bindgen step. Same
> problem that you found on MacOS, it can't locate some headers
> (strings.h, included from osdep.h). I'll try to dig into this, but if
> you found a solution already, you're welcome to share it.
> 
> 'gcc | grep' command you used should work, but should be adapted because
> windows paths start with C:/ instead of /.
> 

I've been able to build rust device on windows, with a few tweaks needed.

- specificy the target for libclang (used by bindgen), which targets 
MSVC by default (so different set of headers)
- additional headers (libclang searches its own header with a relative 
path instead of absolute)
- additional windows libs that must be linked in final executable

However, even tough I can build the executable, I get this error:
$ ./build/qemu-system-aarch64 -M virt
C:\w\qemu\build\qemu-system-aarch64.exe: unknown type 'x-pl011-rust'

Any idea of what could be missing here?

By the way, I noticed configure --enable-with-rust does not trigger 
error when not finding cargo, it just deactivates rust support, which is 
probably not what is expected.

---

QEMU Build instructions for windows are here:
https://wiki.qemu.org/Hosts/W32#Native_builds_with_MSYS2

Additional steps needed:
$ cargo install bindgen-cli
$ export PATH=/c/Users/user/.cargo/bin/:$PATH
$ wget 
https://github.com/llvm/llvm-project/releases/download/llvmorg-18.1.6/LLVM-18.1.6-win64.exe 
# for libclang.dll
$ pacman -S p7zip
$ mkdir llvm && cd llvm && 7z x ../LLVM-18.1.6-win64.exe && cd ..
$ export LIBCLANG_PATH=$(cygpath -m $(pwd)/llvm/bin/libclang.dll)

Additional libs to link can be found with:
$ touch empty.rs
$ rustc empty.rs --print=native-static-libs --crate-type=staticlib
note: Link against the following native artifacts when linking against 
this static library. The order and any duplication can be significant on so
me platforms.
note: native-static-libs: -lkernel32 -ladvapi32 -lkernel32 -lntdll 
-luserenv -lws2_32 -lkernel32 -lws2_32 -lkernel32

---

diff --git a/meson.build b/meson.build
index ca40a39ad7e..98faa4777b7 100644
--- a/meson.build
+++ b/meson.build
@@ -3896,7 +3896,8 @@ foreach target : target_dirs
          input: copy,
          dependencies: arch_deps + lib_deps,
          output: target + '-generated.rs',
-        include_directories: include_directories('.', 'include'),
+        include_directories: include_directories('.', 'include',
+        'llvm/lib/clang/18/include/'),
          args: [
            '--ctypes-prefix', 'core::ffi',
            '--formatter', 'rustfmt',
@@ -3910,7 +3911,10 @@ foreach target : target_dirs
            '--with-derive-default',
            '--allowlist-file', meson.project_source_root() + '/include/.*',
            '--allowlist-file', meson.project_source_root() + '/.*',
-          '--allowlist-file', meson.project_build_root() + '/.*'
+          '--allowlist-file', meson.project_build_root() + '/.*',
+        ],
+        c_args: [
+          '--target=x86_64-pc-windows-gnu'
          ],
        )

@@ -3925,7 +3929,12 @@ foreach target : target_dirs
            rust_dep = declare_dependency(link_args: [
                                            '-Wl,--whole-archive',
                                            t['output-path'],
-                                          '-Wl,--no-whole-archive'
+                                          '-Wl,--no-whole-archive',
+                                          '-lkernel32',
+                                          '-ladvapi32',
+                                          '-lntdll',
+                                          '-luserenv',
+                                          '-lws2_32',
                                            ],
                                            sources: [rust_device_cargo])
            rust_hw.add(rust_dep)
Paolo Bonzini June 28, 2024, 9:50 p.m. UTC | #16
On Fri, Jun 28, 2024 at 9:12 PM Pierrick Bouvier
<pierrick.bouvier@linaro.org> wrote:
> However, even tough I can build the executable, I get this error:
> $ ./build/qemu-system-aarch64 -M virt
> C:\w\qemu\build\qemu-system-aarch64.exe: unknown type 'x-pl011-rust'
>
> Any idea of what could be missing here?

Maybe the underlying mechanism to invoke constructors is different?

Perhaps we could use https://crates.io/crates/ctor instead?

Paolo
Manos Pitsidianakis June 29, 2024, 8:06 a.m. UTC | #17
On Fri, 28 Jun 2024 22:12, Pierrick Bouvier <pierrick.bouvier@linaro.org> wrote:
>I've been able to build rust device on windows, with a few tweaks 
>needed.
>
>- specificy the target for libclang (used by bindgen), which targets 
>MSVC by default (so different set of headers)
>- additional headers (libclang searches its own header with a relative 
>path instead of absolute)
>- additional windows libs that must be linked in final executable
>
>However, even tough I can build the executable, I get this error:
>$ ./build/qemu-system-aarch64 -M virt
>C:\w\qemu\build\qemu-system-aarch64.exe: unknown type 'x-pl011-rust'
>
>Any idea of what could be missing here?

Sounds like either the rust lib is not linked to the final binary (which 
you can confirm if you look at the included symbols and don't see 
anything with rust origin) or the constructor decorated with     
#[cfg_attr(target_os = "windows", link_section = ".CRT$XCU"] is not 
linked properly (you can add a puts() and see if it's invoked or add a 
breakpoint in gdb)
Pierrick Bouvier July 1, 2024, 6:53 p.m. UTC | #18
Seems like the using cfg_attr was taken from this crate, so it should be 
equivalent to using it.

On 6/28/24 14:50, Paolo Bonzini wrote:
> On Fri, Jun 28, 2024 at 9:12 PM Pierrick Bouvier
> <pierrick.bouvier@linaro.org> wrote:
>> However, even tough I can build the executable, I get this error:
>> $ ./build/qemu-system-aarch64 -M virt
>> C:\w\qemu\build\qemu-system-aarch64.exe: unknown type 'x-pl011-rust'
>>
>> Any idea of what could be missing here?
> 
> Maybe the underlying mechanism to invoke constructors is different?
> 
> Perhaps we could use https://crates.io/crates/ctor instead?
> 
> Paolo
>
Pierrick Bouvier July 1, 2024, 6:54 p.m. UTC | #19
The ctor is not run, but, interesting point, there is the same problem 
on Linux :)

Can you please confirm with a clean build this work on your machine with 
this v3, and report exact configure and run commands you use?

Thanks,
Pierrick

On 6/29/24 01:06, Manos Pitsidianakis wrote:
> On Fri, 28 Jun 2024 22:12, Pierrick Bouvier <pierrick.bouvier@linaro.org> wrote:
>> I've been able to build rust device on windows, with a few tweaks
>> needed.
>>
>> - specificy the target for libclang (used by bindgen), which targets
>> MSVC by default (so different set of headers)
>> - additional headers (libclang searches its own header with a relative
>> path instead of absolute)
>> - additional windows libs that must be linked in final executable
>>
>> However, even tough I can build the executable, I get this error:
>> $ ./build/qemu-system-aarch64 -M virt
>> C:\w\qemu\build\qemu-system-aarch64.exe: unknown type 'x-pl011-rust'
>>
>> Any idea of what could be missing here?
> 
> Sounds like either the rust lib is not linked to the final binary (which
> you can confirm if you look at the included symbols and don't see
> anything with rust origin) or the constructor decorated with
> #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU"] is not
> linked properly (you can add a puts() and see if it's invoked or add a
> breakpoint in gdb)
Manos Pitsidianakis July 2, 2024, 12:25 p.m. UTC | #20
On Mon, 01 Jul 2024 21:54, Pierrick Bouvier <pierrick.bouvier@linaro.org> wrote:
>The ctor is not run, but, interesting point, there is the same problem 
>on Linux :)
>
>Can you please confirm with a clean build this work on your machine with 
>this v3, and report exact configure and run commands you use?

On a clean checkout, I did exactly the following:

```
mkdir ./build && cd ./build
../configure --enable-system --enable-debug \
 --target-list=aarch64-softmmu --enable-with-rust
ninja qemu-system-aarch64
```

which selected the default cc/gcc on debian,
gcc (Debian 12.2.0-14) 12.2.0

and ran QEMU with like this:

```
./qemu-system-aarch64 -M virt \
  -machine virtualization=true -machine virt,gic-version=3 \
  -cpu max,pauth-impdef=on -smp 2 -m 4096 \
  [.. drive and img arguments ..] \
  -nographic
```


>
>Thanks,
>Pierrick
>
>On 6/29/24 01:06, Manos Pitsidianakis wrote:
>> On Fri, 28 Jun 2024 22:12, Pierrick Bouvier <pierrick.bouvier@linaro.org> wrote:
>>> I've been able to build rust device on windows, with a few tweaks
>>> needed.
>>>
>>> - specificy the target for libclang (used by bindgen), which targets
>>> MSVC by default (so different set of headers)
>>> - additional headers (libclang searches its own header with a relative
>>> path instead of absolute)
>>> - additional windows libs that must be linked in final executable
>>>
>>> However, even tough I can build the executable, I get this error:
>>> $ ./build/qemu-system-aarch64 -M virt
>>> C:\w\qemu\build\qemu-system-aarch64.exe: unknown type 'x-pl011-rust'
>>>
>>> Any idea of what could be missing here?
>> 
>> Sounds like either the rust lib is not linked to the final binary (which
>> you can confirm if you look at the included symbols and don't see
>> anything with rust origin) or the constructor decorated with
>> #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU"] is not
>> linked properly (you can add a puts() and see if it's invoked or add a
>> breakpoint in gdb)
Pierrick Bouvier July 2, 2024, 4:07 p.m. UTC | #21
On 7/2/24 05:25, Manos Pitsidianakis wrote:
> On Mon, 01 Jul 2024 21:54, Pierrick Bouvier <pierrick.bouvier@linaro.org> wrote:
>> The ctor is not run, but, interesting point, there is the same problem
>> on Linux :)
>>
>> Can you please confirm with a clean build this work on your machine with
>> this v3, and report exact configure and run commands you use?
> 
> On a clean checkout, I did exactly the following:
> 
> ```
> mkdir ./build && cd ./build
> ../configure --enable-system --enable-debug \
>   --target-list=aarch64-softmmu --enable-with-rust
> ninja qemu-system-aarch64
> ```
> 

Debug flag was the difference.
After looking with Manos, we identified an issue with release builds 
(unused symbols gets dropped, including device ctor), that will be 
solved in v4.

> which selected the default cc/gcc on debian,
> gcc (Debian 12.2.0-14) 12.2.0
> 
> and ran QEMU with like this:
> 
> ```
> ./qemu-system-aarch64 -M virt \
>    -machine virtualization=true -machine virt,gic-version=3 \
>    -cpu max,pauth-impdef=on -smp 2 -m 4096 \
>    [.. drive and img arguments ..] \
>    -nographic
> ```
> 
> 
>>
>> Thanks,
>> Pierrick
>>
>> On 6/29/24 01:06, Manos Pitsidianakis wrote:
>>> On Fri, 28 Jun 2024 22:12, Pierrick Bouvier <pierrick.bouvier@linaro.org> wrote:
>>>> I've been able to build rust device on windows, with a few tweaks
>>>> needed.
>>>>
>>>> - specificy the target for libclang (used by bindgen), which targets
>>>> MSVC by default (so different set of headers)
>>>> - additional headers (libclang searches its own header with a relative
>>>> path instead of absolute)
>>>> - additional windows libs that must be linked in final executable
>>>>
>>>> However, even tough I can build the executable, I get this error:
>>>> $ ./build/qemu-system-aarch64 -M virt
>>>> C:\w\qemu\build\qemu-system-aarch64.exe: unknown type 'x-pl011-rust'
>>>>
>>>> Any idea of what could be missing here?
>>>
>>> Sounds like either the rust lib is not linked to the final binary (which
>>> you can confirm if you look at the included symbols and don't see
>>> anything with rust origin) or the constructor decorated with
>>> #[cfg_attr(target_os = "windows", link_section = ".CRT$XCU"] is not
>>> linked properly (you can add a puts() and see if it's invoked or add a
>>> breakpoint in gdb)
diff mbox series

Patch

diff --git a/MAINTAINERS b/MAINTAINERS
index 431010ddbf..0f36bb3e9a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4227,6 +4227,9 @@  Rust build system integration
 M: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
 S: Maintained
 F: scripts/cargo_wrapper.py
+F: rust/meson.build
+F: rust/wrapper.h
+F: rust/.gitignore
 
 Miscellaneous
 -------------
diff --git a/meson.build b/meson.build
index 3533889852..2b305e745a 100644
--- a/meson.build
+++ b/meson.build
@@ -3876,6 +3876,62 @@  foreach target : target_dirs
     lib_deps += dep.partial_dependency(compile_args: true, includes: true)
   endforeach
 
+  if with_rust and target_type == 'system'
+       # FIXME:
+       # > WARNING: Project specifies a minimum meson_version '>=0.63.0' but
+       # > uses features which were added in newer versions:
+       # > * 0.64.0: {'fs.copyfile'}
+       # > * 1.0.0: {'dependencies arg in rust.bindgen', 'module rust as stable module'}
+      rust_bindgen = import('rust')
+
+      # We need one generated_rs target per target, so give them
+      # target-specific names.
+      copy = fs.copyfile('rust/wrapper.h',
+                         target + '_wrapper.h')
+      generated_rs = rust_bindgen.bindgen(
+        input: copy,
+        dependencies: arch_deps + lib_deps,
+        output: target + '-generated.rs',
+        include_directories: include_directories('.', 'include'),
+        args: [
+          '--ctypes-prefix', 'core::ffi',
+          '--formatter', 'rustfmt',
+          '--generate-block',
+          '--generate-cstr',
+          '--impl-debug',
+          '--merge-extern-blocks',
+          '--no-doc-comments',
+          '--no-include-path-detection',
+          '--use-core',
+          '--with-derive-default',
+          '--allowlist-file', meson.project_source_root() + '/include/.*',
+          '--allowlist-file', meson.project_source_root() + '/.*',
+          '--allowlist-file', meson.project_build_root() + '/.*'
+        ],
+      )
+
+      if target in rust_targets
+        rust_hw = ss.source_set()
+        foreach t: rust_targets[target]
+          rust_device_cargo = custom_target(t['name'],
+                                       output: t['output'],
+                                       depends: [generated_rs],
+                                       build_always_stale: true,
+                                       command: t['command'])
+          rust_dep = declare_dependency(link_args: [
+                                          '-Wl,--whole-archive',
+                                          t['output-path'],
+                                          '-Wl,--no-whole-archive'
+                                          ],
+                                          sources: [rust_device_cargo])
+          rust_hw.add(rust_dep)
+        endforeach
+        rust_hw_config = rust_hw.apply(config_target, strict: false)
+        arch_srcs += rust_hw_config.sources()
+        arch_deps += rust_hw_config.dependencies()
+      endif
+  endif
+
   lib = static_library('qemu-' + target,
                  sources: arch_srcs + genh,
                  dependencies: lib_deps,
diff --git a/rust/.gitignore b/rust/.gitignore
new file mode 100644
index 0000000000..1bf71b1f68
--- /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 0000000000..435abd3e1c
--- /dev/null
+++ b/rust/meson.build
@@ -0,0 +1,129 @@ 
+# Supported hosts
+rust_supported_oses = {
+  'linux': '-unknown-linux-gnu',
+  'darwin': '-apple-darwin',
+  'windows': '-pc-windows-gnu'
+}
+rust_supported_cpus = ['x86_64', 'aarch64']
+
+# Future-proof the above definitions against any change in the root meson.build file:
+foreach rust_os: rust_supported_oses.keys()
+  if not supported_oses.contains(rust_os)
+    message()
+    warning('UNSUPPORTED OS VALUES IN ' + meson.current_source_dir() + '/meson.build')
+    message()
+    message('This meson.build file claims OS `+' + rust_os + '` is supported but')
+    message('it is not included in the global supported OSes list in')
+    message(meson.global_source_root() + '/meson.build.')
+  endif
+endforeach
+foreach rust_cpu: rust_supported_cpus
+  if not supported_cpus.contains(rust_cpu)
+    message()
+    warning('UNSUPPORTED CPU VALUES IN ' + meson.current_source_dir() + '/meson.build')
+    message()
+    message('This meson.build file claims CPU `+' + rust_cpu + '` is supported but')
+    message('it is not included in the global supported CPUs list in')
+    message(meson.global_source_root() + '/meson.build.')
+  endif
+endforeach
+
+# FIXME: retrieve target triple from meson and construct rust_target_triple
+if meson.is_cross_build()
+  message()
+  error('QEMU does not support cross compiling with Rust enabled.')
+endif
+
+# FIXME: These are the latest stable versions, refine to actual minimum ones.
+msrv = {
+  'rustc': '1.79.0',
+  'cargo': '1.79.0',
+  'bindgen': '0.69.4',
+}
+
+# rustup = find_program('rustup', required: false)
+foreach bin_dep: msrv.keys()
+  bin = find_program(bin_dep, required: true)
+  if bin.version() < msrv[bin_dep]
+    if bin_dep == 'bindgen' and get_option('wrap_mode') != 'nodownload'
+      run_command(cargo, 'install', 'bindgen-cli', capture: true, check: true)
+      bin = find_program(bin_dep, required: true)
+      if bin.version() >= msrv[bin_dep]
+        continue
+      endif
+    endif
+    message()
+    error(bin_dep + ' version ' + bin.version() + ' is unsupported: Please upgrade to at least ' + msrv[bin_dep])
+  endif
+endforeach
+
+rust_target_triple = get_option('with_rust_target_triple')
+
+if rust_target_triple == ''
+  if not supported_oses.contains(host_os)
+    message()
+    error('QEMU does not support `' + host_os +'` as a Rust platform.')
+  elif not supported_cpus.contains(host_arch)
+    message()
+    error('QEMU does not support `' + host_arch +'` as a Rust architecture.')
+  endif
+  rust_target_triple = host_arch + rust_supported_oses[host_os]
+  if host_os == 'windows' and host_arch == 'aarch64'
+    rust_target_triple += 'llvm'
+  endif
+else
+  # verify rust_target_triple if given as an option
+  rustc = find_program('rustc', required: true)
+  rustc_targets = run_command(rustc, '--print', 'target-list', capture: true, check: true).stdout().strip().split()
+  if not rustc_targets.contains(rust_target_triple)
+    message()
+    error('Given rust_target_triple ' + rust_target_triple + ' is not listed in rustc --print target-list output')
+  endif
+endif
+
+
+rust_targets = {}
+
+cargo_wrapper = [
+  find_program(meson.global_source_root() / 'scripts/cargo_wrapper.py'),
+  '--config-headers', meson.project_build_root() / 'config-host.h',
+  '--meson-build-root', meson.project_build_root(),
+  '--meson-build-dir', meson.current_build_dir(),
+  '--meson-source-dir', meson.current_source_dir(),
+]
+
+if get_option('b_colorout') != 'never'
+  cargo_wrapper += ['--color', 'always']
+endif
+
+if get_option('optimization') in ['0', '1', 'g']
+  rs_build_type = 'debug'
+else
+  rs_build_type = 'release'
+endif
+
+# Collect metadata for each (crate,qemu-target,compiler-target) combination.
+# Rust meson targets cannot be defined a priori because they depend on bindgen
+# generation that is created for each emulation target separately. Thus Rust
+# meson targets will be defined for each target after the target-specific
+# bindgen dependency is declared.
+rust_hw_target_list = {}
+
+foreach rust_hw_target, rust_hws: rust_hw_target_list
+  foreach rust_hw_dev: rust_hws
+    output = meson.current_build_dir() / rust_target_triple / rs_build_type / rust_hw_dev['output']
+    crate_metadata = {
+      'name': rust_hw_dev['name'],
+      'output': [rust_hw_dev['output']],
+      'output-path': output,
+      'command': [cargo_wrapper,
+        '--crate-dir', meson.current_source_dir() / rust_hw_dev['dirname'],
+        '--profile', rs_build_type,
+        '--target-triple', rust_target_triple,
+        '--outdir', '@OUTDIR@',
+        'build-lib'
+        ]
+      }
+    rust_targets += { rust_hw_target: [crate_metadata] }
+  endforeach
+endforeach
diff --git a/rust/wrapper.h b/rust/wrapper.h
new file mode 100644
index 0000000000..bcf808c8d7
--- /dev/null
+++ b/rust/wrapper.h
@@ -0,0 +1,39 @@ 
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2020 Fabrice Bellard
+ *
+ * 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.
+ */
+
+#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/scripts/cargo_wrapper.py b/scripts/cargo_wrapper.py
index 927336f80e..833e0e55f8 100644
--- a/scripts/cargo_wrapper.py
+++ b/scripts/cargo_wrapper.py
@@ -111,6 +111,8 @@  def get_cargo_rustc(args: argparse.Namespace) -> tuple[Dict[str, Any], List[str]
 
     env = os.environ
     env["CARGO_ENCODED_RUSTFLAGS"] = cfg
+    env["MESON_BUILD_DIR"] = str(target_dir)
+    env["MESON_BUILD_ROOT"] = str(args.meson_build_root)
 
     return (env, cargo_cmd)
 
@@ -234,6 +236,14 @@  def main() -> None:
         required=True,
     )
     parser.add_argument(
+        "--meson-build-root",
+        metavar="BUILD_ROOT",
+        help="meson.project_build_root()",
+        type=Path,
+        dest="meson_build_root",
+        required=True,
+    )
+    parser.add_argument(
         "--meson-source-dir",
         metavar="SOURCE_DIR",
         help="meson.current_source_dir()",