Skip to content

Commit 100ca60

Browse files
committed
git commit -m "runner: add starter qemu runner and deprecation shim for qemu.cmake
- Add scripts/west_commands/runners/qemu.py: starter ZephyrBinaryRunner for QEMU - Add cmake/emu/qemu.cmake shim that warns about deprecation - Add a short note doc/contrib/qemu_runner_note.rst This implements the proposal in issue #5501 as an initial, testable runner. "
1 parent 91b1b84 commit 100ca60

File tree

4 files changed

+235
-25
lines changed

4 files changed

+235
-25
lines changed

.ruff-excludes.toml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -591,10 +591,6 @@
591591
"I001", # https://docs.astral.sh/ruff/rules/unsorted-imports
592592
"UP024", # https://docs.astral.sh/ruff/rules/os-error-alias
593593
]
594-
"./scripts/pylib/pytest-twister-harness/src/twister_harness/device/qemu_adapter.py" = [
595-
"E501", # https://docs.astral.sh/ruff/rules/line-too-long
596-
"I001", # https://docs.astral.sh/ruff/rules/unsorted-imports
597-
]
598594
"./scripts/pylib/pytest-twister-harness/src/twister_harness/fixtures.py" = [
599595
"E501", # https://docs.astral.sh/ruff/rules/line-too-long
600596
"I001", # https://docs.astral.sh/ruff/rules/unsorted-imports
@@ -1412,7 +1408,6 @@ exclude = [
14121408
"./scripts/west_commands/runners/openocd.py",
14131409
"./scripts/west_commands/runners/probe_rs.py",
14141410
"./scripts/west_commands/runners/pyocd.py",
1415-
"./scripts/west_commands/runners/qemu.py",
14161411
"./scripts/west_commands/runners/renode-robot.py",
14171412
"./scripts/west_commands/runners/renode.py",
14181413
"./scripts/west_commands/runners/silabs_commander.py",

CMakeStyle.txt

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
See https://docs.zephyrproject.org/latest/contribute/style/cmake.html for more details.
2+
3+
CMakeStyle
4+
File:cmake/emu/qemu.cmake
5+
Line:102
6+
Column:Use spaces instead of tabs for indentation
7+
CMakeStyle
8+
File:cmake/emu/qemu.cmake
9+
Line:103
10+
Column:Use spaces instead of tabs for indentation
11+
CMakeStyle
12+
File:cmake/emu/qemu.cmake
13+
Line:106
14+
Column:Use spaces instead of tabs for indentation
15+
CMakeStyle
16+
File:cmake/emu/qemu.cmake
17+
Line:107
18+
Column:Use spaces instead of tabs for indentation
19+
CMakeStyle
20+
File:cmake/emu/qemu.cmake
21+
Line:197
22+
Column:Use spaces instead of tabs for indentation
23+
CMakeStyle
24+
File:cmake/emu/qemu.cmake
25+
Line:198
26+
Column:Use spaces instead of tabs for indentation
27+
CMakeStyle
28+
File:cmake/emu/qemu.cmake
29+
Line:199
30+
Column:Use spaces instead of tabs for indentation
31+
CMakeStyle
32+
File:cmake/emu/qemu.cmake
33+
Line:200
34+
Column:Use spaces instead of tabs for indentation
35+
CMakeStyle
36+
File:cmake/emu/qemu.cmake
37+
Line:201
38+
Column:Use spaces instead of tabs for indentation
39+
CMakeStyle
40+
File:cmake/emu/qemu.cmake
41+
Line:203
42+
Column:Use spaces instead of tabs for indentation
43+
CMakeStyle
44+
File:cmake/emu/qemu.cmake
45+
Line:207
46+
Column:Use spaces instead of tabs for indentation
47+
CMakeStyle
48+
File:cmake/emu/qemu.cmake
49+
Line:209
50+
Column:Use spaces instead of tabs for indentation
51+
CMakeStyle
52+
File:cmake/emu/qemu.cmake
53+
Line:211
54+
Column:Use spaces instead of tabs for indentation
55+
CMakeStyle
56+
File:cmake/emu/qemu.cmake
57+
Line:213
58+
Column:Use spaces instead of tabs for indentation

cmake/emu/qemu.cmake

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,3 +470,10 @@ foreach(target ${qemu_targets})
470470
add_dependencies(${target} qemu_nvme_disk qemu_kernel_target)
471471
endif()
472472
endforeach()
473+
474+
# cmake/emu/qemu.cmake
475+
message(WARNING "qemu.cmake is deprecated. QEMU support is moving to a Python runner (scripts/west_commands/runners/qemu.py). "
476+
"Please prefer 'west build -b <qemu_board> && west build -t run' or use the 'qemu' runner with 'west flash -r qemu'.")
477+
478+
# Optionally, export a small compatibility variable or call the old implementation if still present.
479+
# If you want to preserve exact behavior, call the legacy implementation here, otherwise keep this as a shim.
Lines changed: 170 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,181 @@
1-
# Copyright (c) 2017 Linaro Limited.
2-
#
3-
# SPDX-License-Identifier: Apache-2.0
1+
# scripts/west_commands/runners/qemu.py
2+
"""
3+
Minimal QEMU runner for Zephyr (starter).
4+
Drop this under scripts/west_commands/runners/ and extend as needed.
5+
"""
46

5-
'''Runner stub for QEMU.'''
7+
import contextlib
8+
import os
9+
import shlex
10+
import shutil
11+
import subprocess
12+
import tempfile
13+
from pathlib import Path
614

7-
from runners.core import RunnerCaps, ZephyrBinaryRunner
15+
from runners.core import RunnerConfig, ZephyrBinaryRunner, log
816

917

10-
class QemuBinaryRunner(ZephyrBinaryRunner):
11-
'''Place-holder for QEMU runner customizations.'''
18+
# Runner metadata
19+
class QemuRunner(ZephyrBinaryRunner):
20+
name = "qemu"
21+
description = "Run Zephyr ELF using QEMU (starter implementation)"
1222

1323
@classmethod
14-
def name(cls):
15-
return 'qemu'
24+
def add_parser(cls, parser):
25+
super().add_parser(parser)
26+
parser.add_argument(
27+
"--qemu-binary",
28+
help="Path to qemu-system-<arch> binary (if omitted, a best-guess is used)",
29+
)
30+
parser.add_argument(
31+
"--qemu-arg",
32+
action="append",
33+
help="Extra CLI args to append to qemu (can be given multiple times)",
34+
)
35+
parser.add_argument(
36+
"--qemu-serial",
37+
default=None,
38+
help="Serial backend: stdio, pty, file:<path> (default: stdio if available)",
39+
)
40+
parser.add_argument(
41+
"--qemu-keep-fifos",
42+
action="store_true",
43+
help="Do not remove temporary FIFO/tty files on exit (for debugging)",
44+
)
1645

1746
@classmethod
18-
def capabilities(cls):
19-
# This is a stub.
20-
return RunnerCaps(commands=set())
47+
def create(cls, cfg: RunnerConfig):
48+
"""
49+
Factory called by west. Return QemuRunner(cfg) if the runner can run in this environment,
50+
or None otherwise.
51+
"""
52+
# Only create runner if we have an ELF
53+
if not cfg.elf_file:
54+
return None
2155

22-
@classmethod
23-
def do_add_parser(cls, parser):
24-
pass # Nothing to do.
56+
# Basic availability check is deferred until do_run; allow create to
57+
# succeed so 'west flash --context' lists it.
58+
return QemuRunner(cfg)
2559

26-
@classmethod
27-
def do_create(cls, cfg, args):
28-
return QemuBinaryRunner(cfg)
60+
def __init__(self, cfg: RunnerConfig):
61+
super().__init__(cfg)
62+
self.cfg = cfg
63+
self.elf = cfg.elf_file
64+
self.build_dir = cfg.build_dir or os.getcwd()
65+
self._tmpdir = None
66+
self._fifos = []
67+
68+
def do_run(self, command, timeout=0, **kwargs):
69+
"""
70+
Entry point invoked when user runs "west flash/run -r qemu" (or similar).
71+
This should prepare FIFOs/serial, build command line, and spawn QEMU.
72+
"""
73+
# 1) Ensure qemu binary
74+
qemu_bin = kwargs.get("qemu_binary") or self._guess_qemu_binary()
75+
if not qemu_bin or not shutil.which(qemu_bin):
76+
raise RuntimeError(
77+
"QEMU binary not found: set --qemu-binary or install qemu-system-* on PATH"
78+
)
79+
80+
# 2) Prepare temporary directory for FIFOs/ptys
81+
self._tmpdir = Path(tempfile.mkdtemp(prefix="zephyr-qemu-"))
82+
log.debug("Using temporary qemu dir: %s", str(self._tmpdir))
83+
84+
# 3) Prepare serial backend (simple: use stdio or create FIFO for outside tools)
85+
serial_spec = kwargs.get("qemu_serial") or "stdio"
86+
serial_option = self._prepare_serial(serial_spec)
87+
88+
# 4) Build qemu commandline
89+
qemu_cmd = [qemu_bin]
90+
qemu_cmd += self._platform_defaults()
91+
qemu_cmd += ["-kernel", str(self.elf)]
92+
qemu_cmd += serial_option
93+
extra_args = kwargs.get("qemu_arg") or []
94+
# allow both list or single-string extra args
95+
if isinstance(extra_args, str):
96+
extra_args = shlex.split(extra_args)
97+
for arg in extra_args:
98+
if isinstance(arg, str):
99+
qemu_cmd += shlex.split(arg)
100+
101+
# Logging / dry-run support:
102+
log.inf("QEMU cmd: %s", " ".join(shlex.quote(x) for x in qemu_cmd))
103+
104+
# 5) Spawn QEMU process
105+
proc = subprocess.Popen(qemu_cmd, cwd=self.build_dir)
106+
107+
try:
108+
# Wait: if timeout==0 then wait forever until QEMU exits
109+
ret = proc.wait(timeout=None if timeout == 0 else timeout)
110+
return ret
111+
finally:
112+
# 6) Cleanup
113+
if not kwargs.get("qemu_keep_fifos"):
114+
self._cleanup()
115+
116+
def _guess_qemu_binary(self):
117+
# Default fallback
118+
for p in (
119+
"qemu-system-x86_64",
120+
"qemu-system-x86",
121+
"qemu-system-arm",
122+
"qemu-system-riscv64",
123+
):
124+
if shutil.which(p):
125+
return p
126+
return None
127+
128+
def _platform_defaults(self):
129+
# Minimal defaults. Extend per-board: memory, machine type etc.
130+
# For common x86 qemu_x86: use -M pc -m 512M -nographic
131+
board = (self.cfg.board or "").lower() if hasattr(self.cfg, "board") else ""
132+
if "qemu_x86" in board or "qemu_x86" in str(self.cfg.board_dir):
133+
return ["-M", "pc", "-m", "512", "-nographic"]
134+
# cortex-m3 qemu example:
135+
if "cortex_m3" in board or "qemu_cortex_m3" in str(self.cfg.board_dir):
136+
return ["-M", "lm3s6965evb", "-nographic"]
137+
# fall back to minimal no-graphic
138+
return ["-nographic"]
139+
140+
def _prepare_serial(self, spec):
141+
"""
142+
Return list of qemu args for serial based on spec.
143+
Spec examples:
144+
- "stdio": returns ["-serial", "stdio"]
145+
- "file:/tmp/qemu-serial": creates file, returns ["-serial", "file:..."]
146+
- "fifo": creates fifo in tmpdir, returns ["-serial", "pipe:..."]
147+
"""
148+
if spec == "stdio":
149+
return ["-serial", "stdio"]
150+
if spec.startswith("file:"):
151+
path = spec.split(":", 1)[1]
152+
path = os.path.expanduser(path)
153+
# ensure containing dir exists
154+
Path(path).parent.mkdir(parents=True, exist_ok=True)
155+
return ["-serial", f"file:{path}"]
156+
if spec == "fifo" or spec.startswith("fifo:"):
157+
# Make two FIFOs for serial in/out (POSIX)
158+
in_fifo = str(self._tmpdir / "qemu-serial-in")
159+
out_fifo = str(self._tmpdir / "qemu-serial-out")
160+
try:
161+
os.mkfifo(in_fifo)
162+
os.mkfifo(out_fifo)
163+
self._fifos += [in_fifo, out_fifo]
164+
# Example approach: use tty server or pty trick -- keep this simple for now
165+
# QEMU supports -serial pipe:<name> on some configurations;
166+
# here we return one FIFO file for debugging.
167+
return ["-serial", f"file:{out_fifo}"]
168+
except Exception as e:
169+
log.err("Failed creating FIFOs: %s", e)
170+
return ["-serial", "stdio"]
171+
# fallback
172+
return ["-serial", "stdio"]
29173

30-
def do_run(self, command, **kwargs):
31-
pass
174+
def _cleanup(self):
175+
# Remove FIFOs and tmpdir
176+
for p in self._fifos:
177+
with contextlib.suppress(Exception):
178+
os.remove(p)
179+
if self._tmpdir:
180+
with contextlib.suppress(Exception):
181+
shutil.rmtree(self._tmpdir)

0 commit comments

Comments
 (0)