Re: [PATCH 12/12] meson: link ARM and RISC-V into qemu-system

Pierrick Bouvier <[email protected]>
Newsgroups org.nongnu.qemu-riscv,org.nongnu.qemu-arm,org.nongnu.qemu-devel
Message-ID <[email protected]>
On 8/23/2026 8:07 AM, Yonggang Luo wrote:
> - Build qemu-system (and qemu-systemw on Windows) from aarch64
>   common/system objects plus RISC-V arch objects taken from the
>   existing riscv64 lib.
> - Pull 32-bit TargetInfo and 32-only boards from the arm/riscv32
>   libs so hw/arm and target/riscv are not compiled twice.
> - Skip linking qemu-system-{arm,aarch64,riscv32,riscv64} and the
>   Windows *w variants; symlink those names to qemu-system so
>   argv[0] still selects TargetInfo.
> 
> Signed-off-by: Yonggang Luo <[email protected]>
> ---
>  meson.build | 156 +++++++++++++++++++++++++++++++++++++++++++++++++---
>  1 file changed, 147 insertions(+), 9 deletions(-)
> 
> diff --git a/meson.build b/meson.build
> index 449d50f2983..b4f435f6a22 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -4250,6 +4250,10 @@ endif
>  
>  traceable = []
>  emulators = {}
> +target_softmmu = {}
> +combo_softmmu = ['aarch64-softmmu', 'arm-softmmu', 'riscv32-softmmu', 'riscv64-softmmu']
> +have_qemu_system_combo = ('aarch64-softmmu' in target_dirs
> +                          and 'riscv64-softmmu' in target_dirs)
>  foreach target : target_dirs
>    config_target = config_target_mak[target]
>    target_name = config_target['TARGET_NAME']
> @@ -4399,20 +4403,37 @@ foreach target : target_dirs
>                   c_args: c_args,
>                   build_by_default: false)
>  
> +  if target in combo_softmmu
> +    target_softmmu += {target: {
> +      'lib': lib,
> +      'arch_srcs': arch_srcs,
> +      'arch_deps': arch_deps,
> +      'c_args': c_args,
> +      'link_args': link_args,
> +      'target_stubs': target_stubs,
> +      'main_rs': main_rs,
> +      'config_target': config_target,
> +      'target_base_arch': target_base_arch,
> +    }}
> +  endif
> +
>    if target.endswith('-softmmu')
> -    execs = [{
> -      'name': 'qemu-system-' + target_name,
> -      'win_subsystem': 'console',
> -      'sources': [main_rs, files('system/main.c')],
> -      'dependencies': [sdl, target_stubs],
> -    }]
> -    if host_os == 'windows' and (sdl.found() or gtk.found())
> +    execs = []
> +    if not (have_qemu_system_combo and target in combo_softmmu)
>        execs += [{
> -        'name': 'qemu-system-' + target_name + 'w',
> -        'win_subsystem': 'windows',
> +        'name': 'qemu-system-' + target_name,
> +        'win_subsystem': 'console',
>          'sources': [main_rs, files('system/main.c')],
>          'dependencies': [sdl, target_stubs],
>        }]
> +      if host_os == 'windows' and (sdl.found() or gtk.found())
> +        execs += [{
> +          'name': 'qemu-system-' + target_name + 'w',
> +          'win_subsystem': 'windows',
> +          'sources': [main_rs, files('system/main.c')],
> +          'dependencies': [sdl, target_stubs],
> +        }]
> +      endif
>      endif
>      if get_option('fuzzing')
>        specific_fuzz = specific_fuzz_ss.apply(config_target, strict: false)
> @@ -4481,6 +4502,123 @@ foreach target : target_dirs
>    endforeach
>  endforeach
>  
> +# Combined ARM + RISC-V system emulator (32 and 64).
> +# Common/system objects come from aarch64. RISC-V arch objects come
> +# from the existing riscv64 lib. 32-bit TargetInfo and 32-only boards
> +# come from the arm/riscv32 libs. Per-target qemu-system-* names are
> +# symlinks to qemu-system (argv[0] selects TargetInfo).
> +if have_qemu_system_combo
> +  a64 = target_softmmu['aarch64-softmmu']
> +  rv = target_softmmu['riscv64-softmmu']
> +  extra_objs = []
> +  target_info_def = files('target-info-def.c')
> +  foreach spec : [[common_ss, common_all], [system_ss, libsystem]]
> +    rv_src = spec[0].apply(rv['config_target'], strict: false)
> +    a64_src = spec[0].apply(a64['config_target'], strict: false)
> +    extra = []
> +    foreach f : rv_src.sources()
> +      if f not in a64_src.sources() and not f.full_path().endswith('_stubs.c')
> +        extra += f
> +      endif
> +    endforeach
> +    if extra.length() > 0
> +      extra_objs += spec[1].extract_objects(extra)
> +    endif
> +  endforeach
> +  rv_base = rv['target_base_arch']
> +  if rv_base in target_common_arch_libs
> +    src = target_common_arch[rv_base].apply(rv['config_target'], strict: false)
> +    extra_objs += target_common_arch_libs[rv_base].extract_objects(src.sources())
> +  endif
> +  if rv_base in target_common_system_arch_libs
> +    src = target_common_system_arch[rv_base].apply(rv['config_target'], strict: false)
> +    extra_objs += target_common_system_arch_libs[rv_base].extract_objects(src.sources())
> +  endif
> +  foreach pair : [['arm-softmmu', 'aarch64-softmmu'], ['riscv32-softmmu', 'riscv64-softmmu']]
> +    t32 = pair[0]
> +    t64 = pair[1]
> +    if t32 in target_softmmu
> +      extra_objs += target_softmmu[t32]['lib'].extract_objects(target_info_def)
> +      base32 = target_softmmu[t32]['target_base_arch']
> +      if base32 in hw_arch
> +        hw32 = hw_arch[base32].apply(target_softmmu[t32]['config_target'], strict: false)
> +        hw64 = hw_arch[base32].apply(target_softmmu[t64]['config_target'], strict: false)
> +        extra_hw = []
> +        foreach f : hw32.sources()
> +          if f not in hw64.sources()
> +            extra_hw += f
> +          endif
> +        endforeach
> +        if extra_hw.length() > 0
> +          extra_objs += target_softmmu[t32]['lib'].extract_objects(extra_hw)
> +        endif
> +      endif
> +    endif
> +  endforeach
> +  combo_objects = extra_objs
> +  combo_objects += a64['lib'].extract_all_objects(recursive: true)
> +  combo_objects += rv['lib'].extract_objects(rv['arch_srcs'])
> +  combo_deps = a64['arch_deps'] + rv['arch_deps'] + [sdl, a64['target_stubs'], rv['target_stubs']]
> +  if 'arm-softmmu' in target_softmmu
> +    combo_deps += target_softmmu['arm-softmmu']['arch_deps']
> +  endif
> +  if 'riscv32-softmmu' in target_softmmu
> +    combo_deps += target_softmmu['riscv32-softmmu']['arch_deps']
> +  endif
> +  combo_execs = [{
> +    'name': 'qemu-system',
> +    'win_subsystem': 'console',
> +  }]
> +  if host_os == 'windows' and (sdl.found() or gtk.found())
> +    combo_execs += [{
> +      'name': 'qemu-systemw',
> +      'win_subsystem': 'windows',
> +    }]
> +  endif
> +  combo_exe_suffix = host_os == 'windows' ? '.exe' : ''
> +  combo_alias_py = 'import os, sys; src, dst = sys.argv[1:3]; os.path.exists(dst) and os.remove(dst); os.symlink(os.path.basename(src), dst)'
> +  foreach exe : combo_execs
> +    emulator = executable(exe['name'], [a64['main_rs'], files('system/main.c')],
> +               install: true,
> +               c_args: a64['c_args'],
> +               dependencies: combo_deps,
> +               objects: combo_objects,
> +               link_depends: [block_syms, qemu_syms],
> +               link_args: a64['link_args'],
> +               win_subsystem: exe['win_subsystem'])
> +    emulators += {exe['name']: emulator}
> +    traceable += [{
> +      'exe': exe['name'],
> +      'probe-prefix': 'qemu.system.multi',
> +    }]
> +    foreach t : combo_softmmu
> +      if t not in target_softmmu
> +        continue
> +      endif
> +      tname = target_softmmu[t]['config_target']['TARGET_NAME']
> +      if exe['name'] == 'qemu-systemw'
> +        alias = 'qemu-system-' + tname + 'w'
> +      else
> +        alias = 'qemu-system-' + tname
> +      endif
> +      alias_tgt = custom_target(alias,
> +                                input: emulator,
> +                                output: alias + combo_exe_suffix,
> +                                command: [python, '-c', combo_alias_py, '@INPUT@', '@OUTPUT@'],
> +                                build_by_default: true,
> +                                install: false)
> +      emulators += {alias: alias_tgt}
> +      install_symlink(alias + combo_exe_suffix,
> +                      pointing_to: exe['name'] + combo_exe_suffix,
> +                      install_dir: get_option('bindir'))
> +      traceable += [{
> +        'exe': alias,
> +        'probe-prefix': 'qemu.system.' + tname,
> +      }]
> +    endforeach
> +  endforeach
> +endif
> +
>  # Other build targets
>  
>  if get_option('plugins')

Please find attached a much simpler implementation, which only requires
a single line update as we add new targets in single-binary.

Regards,
Pierrick
6261b0c255e1a8f0df2b41928b8dfb365d0fa488.patch.txt (text/plain, 2.9 KB)
From 6261b0c255e1a8f0df2b41928b8dfb365d0fa488 Mon Sep 17 00:00:00 2001
From: Pierrick Bouvier <[email protected]>
Date: Tue, 14 Apr 2026 10:56:15 -0700
Subject: [PATCH] meson: build single binary

This is not built by default.
Build with: ninja -C build qemu-system

All available *-softmmu targets are aggregated, ./configure
--target-list can be used to restrict which targets are included.

In case user has targets that are not supported, we emit an error
message when build qemu-system target. This way, it's not necessary to
have a custom configure flag, and qemu-system can live next to existing
binaries without complexifying the workflow more than it is already.

Signed-off-by: Pierrick Bouvier <[email protected]>
---
 meson.build | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/meson.build b/meson.build
index 553df3e7f053..4a31a520d9eb 100644
--- a/meson.build
+++ b/meson.build
@@ -4264,6 +4264,9 @@ if host_os == 'darwin'
 endif
 
 traceable = []
+single_binary_libs = []
+single_binary_deps = []
+single_binary_crates = []
 emulators = {}
 foreach target : target_dirs
   config_target = config_target_mak[target]
@@ -4419,6 +4422,10 @@ foreach target : target_dirs
                  build_by_default: false)
 
   if target.endswith('-softmmu')
+    single_binary_libs += lib
+    single_binary_deps += arch_deps
+    single_binary_deps += target_stubs
+    single_binary_crates += main_rs
     execs = [{
       'name': 'qemu-system-' + target_name,
       'win_subsystem': 'console',
@@ -4500,6 +4507,47 @@ foreach target : target_dirs
   endforeach
 endforeach
 
+single_binary_objects = []
+foreach lib: single_binary_libs
+  single_binary_objects += lib.extract_all_objects(recursive: true)
+endforeach
+
+single_binary_link_args = emulator_link_args
+single_binary_link_args += enable_modules ? ['@block.syms', '@qemu.syms'] : []
+
+supported_targets = [
+  'aarch64-softmmu',
+  'arm-softmmu',
+]
+unsupported_targets = []
+foreach target : target_dirs
+  if target.endswith('-softmmu') and target not in supported_targets
+    unsupported_targets += target
+  endif
+endforeach
+
+if unsupported_targets.length() == 0
+  executable('qemu-system',
+             sources: [single_binary_crates, files('system/main.c')],
+             dependencies: single_binary_deps,
+             objects: single_binary_objects,
+             link_depends: [block_syms, qemu_syms],
+             link_args: single_binary_link_args,
+             build_by_default: false)
+else
+  error_str = f'''qemu-system only support targets:
+@supported_targets@
+
+unsupported targets:
+@unsupported_targets@
+
+use ./configure --target-list= to choose list of targets'''
+  t = custom_target(command: ['sh', '-c', 'echo "' + error_str + '" && exit 1'],
+                    output: 'none',
+                    build_by_default: false)
+  alias_target('qemu-system', t)
+endif
+
 # Other build targets
 
 if get_option('plugins')
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.