Re: [PATCH 10/12] target-info: add -target and program-name selection
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: > - Parse -target before TargetInfo is fixed; CLI overrides the program > basename. > - Tokens are target_name or target_name-be / -le; -target help lists > registered names in sorted order. > - When -target is omitted, infer the token from g_get_prgname(): strip > .exe, take the longest dash-separated registered suffix, and retry > without a trailing w for Windows GUI binaries. > - A basename that matches no registered token falls through to the > unique default. Combined binaries with no suffix require -target > when more than one default TargetInfo is registered. > > Signed-off-by: Yonggang Luo <[email protected]> > --- > include/qemu/target-info-qom.h | 10 +- > qemu-options.hx | 13 +++ > system/vl.c | 28 ++++-- > target-info-qom.c | 167 ++++++++++++++++++++++++++++++--- > tests/qtest/fuzz/fuzz.c | 2 +- > 5 files changed, 197 insertions(+), 23 deletions(-) > > diff --git a/include/qemu/target-info-qom.h b/include/qemu/target-info-qom.h > index 842cf241e0a..3b59cf82d65 100644 > --- a/include/qemu/target-info-qom.h > +++ b/include/qemu/target-info-qom.h > @@ -40,7 +40,15 @@ typedef struct TargetInfoQomClass { > > OBJECT_DECLARE_TYPE(TargetInfoQom, TargetInfoQomClass, TARGET_INFO) > > -void target_info_qom_set_target(void); > +/** > + * target_info_qom_set_target: > + * @name: -target token, or NULL/%empty to infer from the program > + * basename or the unique default > + * > + * Tokens are target_name (arch default endian) or target_name-be / > + * target_name-le. -target overrides the program basename. > + */ > +void target_info_qom_set_target(const char *name); > > /** > * get_machine_types_available: > diff --git a/qemu-options.hx b/qemu-options.hx > index 34970fffc94..638b531aa49 100644 > --- a/qemu-options.hx > +++ b/qemu-options.hx > @@ -223,6 +223,19 @@ SRST > as -machine. > ERST > > +DEF("target", HAS_ARG, QEMU_OPTION_target, > + "-target name select target ('-target help' for list)\n", QEMU_ARCH_ALL) > +SRST > +``-target name`` > + Select the emulation target (``-target help`` for the list). The > + default endian is the architecture default (``riscv64`` is little > + endian). The other endian uses a ``-be`` or ``-le`` suffix > + (``riscv64-be``, ``ppc64-le``). If omitted, a matching suffix of > + the program basename is used (``qemu-system-riscv64-be``, and > + ``qemu-system-aarch64w.exe`` on Windows). ``-target`` overrides > + the program name. > +ERST > + > DEF("cpu", HAS_ARG, QEMU_OPTION_cpu, > "-cpu cpu select CPU ('-cpu help' for list)\n", QEMU_ARCH_ALL) > SRST > diff --git a/system/vl.c b/system/vl.c > index 83b9425de9b..405f434e036 100644 > --- a/system/vl.c > +++ b/system/vl.c > @@ -166,6 +166,7 @@ typedef struct DeviceOption { > QTAILQ_ENTRY(DeviceOption) next; > } DeviceOption; > > +static const char *target_option; > static const char *cpu_option; > static const char *mem_path; > static const char *incoming; > @@ -2899,19 +2900,12 @@ void qemu_init(int argc, char **argv) > > os_setup_limits(); > > - module_call_init(MODULE_INIT_TARGET_INFO); > - target_info_qom_set_target(); > - > - module_init_info(qemu_modinfo); > - module_allow_arch(target_name()); > - > - qemu_init_subsystems(); > - > - /* first pass of option parsing */ > + /* > + * First pass: options needed before TargetInfo is fixed. > + */ > optind = 1; > while (optind < argc) { > if (argv[optind][0] != '-') { > - /* disk image */ > optind++; > } else { > const QEMUOption *popt; > @@ -2921,10 +2915,21 @@ void qemu_init(int argc, char **argv) > case QEMU_OPTION_nouserconfig: > userconfig = false; > break; > + case QEMU_OPTION_target: > + target_option = optarg; > + break; > } > } > } > > + module_call_init(MODULE_INIT_TARGET_INFO); > + target_info_qom_set_target(target_option); > + > + module_init_info(qemu_modinfo); > + module_allow_arch(target_name()); > + > + qemu_init_subsystems(); > + > machine_opts_dict = qdict_new(); > if (userconfig) { > qemu_read_default_config_file(&error_fatal); > @@ -3687,6 +3692,9 @@ void qemu_init(int argc, char **argv) > case QEMU_OPTION_nouserconfig: > /* Nothing to be parsed here. Especially, do not error out below. */ > break; > + case QEMU_OPTION_target: > + /* Parsed before TargetInfo is selected. */ > + break; > #if defined(CONFIG_POSIX) && !defined(EMSCRIPTEN) > case QEMU_OPTION_daemonize: > os_set_daemonize(true); > diff --git a/target-info-qom.c b/target-info-qom.c > index 2c1c57b05fd..be03573a699 100644 > --- a/target-info-qom.c > +++ b/target-info-qom.c > @@ -7,6 +7,7 @@ > */ > > #include "qemu/osdep.h" > +#include "qemu/help_option.h" > #include "qapi/error.h" > #include "qom/object.h" > #include "qemu/target-info-impl.h" > @@ -87,7 +88,139 @@ const TargetInfo *target_info(void) > return target_info_ptr; > } > > -void target_info_qom_set_target(void) > +static const TargetInfo *target_info_from_class(ObjectClass *oc) > +{ > + return TARGET_INFO_CLASS(oc)->target_info; > +} > + > +/* Default token is target_name; the other endian is target_name-le or -be. */ > +static char *target_info_option_name(const TargetInfo *ti) > +{ > + if (ti->is_default) { > + return g_strdup(ti->target_name); > + } > + if (ti->endianness == ENDIAN_MODE_BIG) { > + return g_strdup_printf("%s-be", ti->target_name); > + } > + return g_strdup_printf("%s-le", ti->target_name); > +} > + > +static gint target_info_token_name_cmp(gconstpointer a, gconstpointer b, > + gpointer data) > +{ > + const TargetInfo *ta = target_info_from_class((ObjectClass *)a); > + const TargetInfo *tb = target_info_from_class((ObjectClass *)b); > + g_autofree char *na = target_info_option_name(ta); > + g_autofree char *nb = target_info_option_name(tb); > + > + return strcmp(na, nb); > +} > + > +static void G_NORETURN target_info_list_and_exit(GSList *targets) > +{ > + printf("Supported targets:\n"); > + for (GSList *l = targets; l; l = l->next) { > + const TargetInfo *ti = target_info_from_class(l->data); > + g_autofree char *opt_name = target_info_option_name(ti); > + > + printf(" %s%s\n", opt_name, ti->is_default ? " (default)" : ""); > + } > + exit(0); > +} > + > +static const TargetInfo *target_info_find_default(GSList *targets, > + size_t *num_default) > +{ > + const TargetInfo *chosen = NULL; > + > + *num_default = 0; > + for (GSList *l = targets; l; l = l->next) { > + const TargetInfo *ti = target_info_from_class(l->data); > + > + if (ti->is_default) { > + (*num_default)++; > + chosen = ti; > + } > + } > + return chosen; > +} > + > +static const TargetInfo *target_info_find_token(GSList *targets, > + const char *token) > +{ > + for (GSList *l = targets; l; l = l->next) { > + const TargetInfo *ti = target_info_from_class(l->data); > + g_autofree char *opt_name = target_info_option_name(ti); > + > + if (!strcmp(opt_name, token)) { > + return ti; > + } > + } > + return NULL; > +} > + > +static bool target_info_base_match_token(const char *base, const char *token) > +{ > + size_t nlen = strlen(token); > + size_t blen; > + > + if (!base || !token[0] || !g_str_has_suffix(base, token)) { > + return false; > + } > + blen = strlen(base); > + return blen == nlen || base[blen - nlen - 1] == '-'; > +} > + > +static const TargetInfo *target_info_find_prgname(GSList *targets, > + const char *base) > +{ > + const TargetInfo *chosen = NULL; > + size_t best_len = 0; > + > + if (!base || !base[0]) { > + return NULL; > + } > + > + for (GSList *l = targets; l; l = l->next) { > + const TargetInfo *ti = target_info_from_class(l->data); > + g_autofree char *opt_name = target_info_option_name(ti); > + size_t nlen = strlen(opt_name); > + > + if (nlen > best_len && > + target_info_base_match_token(base, opt_name)) { > + chosen = ti; > + best_len = nlen; > + } > + } > + return chosen; > +} > + > +static const TargetInfo *target_info_from_prgname(GSList *targets) > +{ > + const char *prg = g_get_prgname(); > + g_autofree char *base = NULL; > + char *dot; > + const TargetInfo *chosen; > + > + if (!prg || !prg[0]) { > + return NULL; > + } > + > + base = g_path_get_basename(prg); > + dot = strrchr(base, '.'); > + if (dot && g_ascii_strcasecmp(dot, ".exe") == 0) { > + *dot = '\0'; > + } > + > + chosen = target_info_find_prgname(targets, base); > + if (!chosen && strlen(base) > 1 && g_str_has_suffix(base, "w")) { > + base[strlen(base) - 1] = '\0'; > + chosen = target_info_find_prgname(targets, base); > + } > + return chosen; > +} > + > +void target_info_qom_set_target(const char *name) > { > g_autoptr(GSList) targets = object_class_get_list(TYPE_TARGET_INFO, false); > const TargetInfo *chosen = NULL; > @@ -98,25 +231,37 @@ void target_info_qom_set_target(void) > error_setg(&error_fatal, "no target-info is available"); > } > > - if (num_found == 1) { > - target_info_ptr = TARGET_INFO_CLASS(targets->data)->target_info; > + if (name && is_help_option(name)) { > + targets = g_slist_sort_with_data(targets, target_info_token_name_cmp, > + NULL); > + target_info_list_and_exit(targets); > + } > + > + if (name && name[0]) { > + chosen = target_info_find_token(targets, name); > + if (!chosen) { > + error_setg(&error_fatal, "unknown target '%s'", name); > + } > + target_info_ptr = chosen; > return; > } > > - for (GSList *l = targets; l; l = l->next) { > - const TargetInfo *ti = TARGET_INFO_CLASS(l->data)->target_info; > + chosen = target_info_from_prgname(targets); > + if (chosen) { > + target_info_ptr = chosen; > + return; > + } > > - if (ti->is_default) { > - num_default++; > - chosen = ti; > - } > + if (num_found == 1) { > + target_info_ptr = target_info_from_class(targets->data); > + return; > } > > + chosen = target_info_find_default(targets, &num_default); > if (num_default != 1) { > error_setg(&error_fatal, num_default == 0 ? > "no default target-info is available" : > - "more than one default target-info " > - "is available"); > + "multiple default targets; use -target"); > } > > target_info_ptr = chosen; > diff --git a/tests/qtest/fuzz/fuzz.c b/tests/qtest/fuzz/fuzz.c > index a3a131c80f8..489c7138bb9 100644 > --- a/tests/qtest/fuzz/fuzz.c > +++ b/tests/qtest/fuzz/fuzz.c > @@ -174,7 +174,7 @@ int LLVMFuzzerInitialize(int *argc, char ***argv, char ***envp) > /* Initialize qgraph and modules */ > qos_graph_init(); > module_call_init(MODULE_INIT_TARGET_INFO); > - target_info_qom_set_target(); > + target_info_qom_set_target(NULL); > module_call_init(MODULE_INIT_FUZZ_TARGET); > module_call_init(MODULE_INIT_QOM); > module_call_init(MODULE_INIT_LIBQOS); It could at least be useful to list available targets for users when the one passed is incorrect, or when they must pick one. See patch attached for inspiration. Regards, Pierrick
a3e5a8f350e14ce533262b29e7dc37af6d76425d.patch.txt
(text/plain, 5.5 KB)
From a3e5a8f350e14ce533262b29e7dc37af6d76425d Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier <[email protected]> Date: Sun, 17 May 2026 21:40:22 +0000 Subject: [PATCH] system/vl: add new option -target This commit adds a new option -target, which can be allowed to set current target, or retrieve list of targets available with 'help'. This option is the first we need to handle, as all the rest of initialization depends on it. Signed-off-by: Pierrick Bouvier <[email protected]> --- include/qemu/target-info-qom.h | 1 + qemu-options.hx | 8 ++++++++ system/vl.c | 34 +++++++++++++++++++++++++++++++++- target-info-qom.c | 33 ++++++++++++++++++++++++++++++++- 4 files changed, 74 insertions(+), 2 deletions(-) diff --git a/include/qemu/target-info-qom.h b/include/qemu/target-info-qom.h index 91be415ed33f2..87f3a51ccd4af 100644 --- a/include/qemu/target-info-qom.h +++ b/include/qemu/target-info-qom.h @@ -26,5 +26,6 @@ typedef struct TargetInfoQomClass { OBJECT_DECLARE_TYPE(TargetInfoQom, TargetInfoQomClass, TARGET_INFO) void target_info_qom_set_target(void); +void target_info_qom_set_target_from_name(const char *name); #endif /* QEMU_TARGET_INFO_QOM_H */ diff --git a/qemu-options.hx b/qemu-options.hx index b8e2710a2cbd4..1d719b237cb60 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -24,6 +24,14 @@ SRST Display version information and exit ERST +DEF("target", HAS_ARG, QEMU_OPTION_target, \ + "-target target selects the target architecture ('-target help' for list)\n", + QEMU_ARCH_ALL) +SRST +``-target target`` + Selects target architecture ('-target help' for list) +ERST + DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ "-machine [type=]name[,prop=value[,...]]\n" " selects emulated machine ('-machine help' for list)\n" diff --git a/system/vl.c b/system/vl.c index bd7b9a8f14ac2..f889594c88d84 100644 --- a/system/vl.c +++ b/system/vl.c @@ -284,6 +284,15 @@ static QemuOptsList qemu_accel_opts = { }, }; +static QemuOptsList qemu_target_opts = { + .name = "target", + .implied_opt_name = "target", + .head = QTAILQ_HEAD_INITIALIZER(qemu_target_opts.head), + .desc = { + { /* end of list */ }, + }, +}; + static QemuOptsList qemu_boot_opts = { .name = "boot-opts", .implied_opt_name = "order", @@ -2901,6 +2910,7 @@ void qemu_init(int argc, char **argv) qemu_add_opts(&qemu_semihosting_config_opts); qemu_add_opts(&qemu_fw_cfg_opts); qemu_add_opts(&qemu_action_opts); + qemu_add_opts(&qemu_target_opts); qemu_add_run_with_opts(); module_call_init(MODULE_INIT_OPTS); @@ -2910,7 +2920,26 @@ void qemu_init(int argc, char **argv) os_setup_limits(); module_call_init(MODULE_INIT_TARGET_INFO); - target_info_qom_set_target(); + + /* + * Identify target: first from option, then from argv[0]. + * This happens even before handling --help option, because it may contain + * entries that are target specific. + */ + for (int idx = 1; idx < argc;) { + if (argv[idx][0] != '-') { + idx++; + } else { + const QEMUOption *popt = lookup_opt(argc, argv, &optarg, &idx); + if (popt->index == QEMU_OPTION_target) { + target_info_qom_set_target_from_name(optarg); + break; + } + } + } + if (!target_info()) { + target_info_qom_set_target(); + } module_init_info(qemu_modinfo); module_allow_arch(target_name()); @@ -2958,6 +2987,9 @@ void qemu_init(int argc, char **argv) exit(1); } switch(popt->index) { + case QEMU_OPTION_target: + /* handled previously, ignore it here */ + break; case QEMU_OPTION_cpu: /* hw initialization will check this */ cpu_option = optarg; diff --git a/target-info-qom.c b/target-info-qom.c index 7958a5cc685f9..5a3abeceb7672 100644 --- a/target-info-qom.c +++ b/target-info-qom.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qom/object.h" +#include "qemu/error-report.h" #include "qemu/target-info-impl.h" #include "qemu/target-info-init.h" #include "qemu/target-info-qom.h" @@ -73,6 +74,36 @@ void target_info_qom_set_target(void) "no target-info is available" : "more than one target-info is available"); } - target_info_ptr = TARGET_INFO_CLASS(targets->data)->target_info; } + +static void list_targets_available(void) +{ + printf("List of targets available:\n"); + g_autoptr(GSList) targets = object_class_get_list_sorted(TYPE_TARGET_INFO, false); + for (GSList *elem = targets; elem; elem = elem->next) { + const TargetInfo *ti = TARGET_INFO_CLASS(elem->data)->target_info; + printf("- %s\n", ti->target_name); + } +} + +void target_info_qom_set_target_from_name(const char *name) +{ + if (!strcmp(name, "help")) { + list_targets_available(); + exit(0); + } + + g_autoptr(GSList) targets = object_class_get_list(TYPE_TARGET_INFO, false); + for (GSList *elem = targets; elem; elem = elem->next) { + const TargetInfo *ti = TARGET_INFO_CLASS(elem->data)->target_info; + if (!strcmp(name, ti->target_name)) { + target_info_ptr = ti; + return; + } + } + + error_report("target '%s' is not available", name); + list_targets_available(); + exit(1); +}