Skip to content

Commit a31f784

Browse files
fabiobaltierikartben
authored andcommitted
twister: add a --flash-command flag
Currently twister requires west to flash a board, either directly using west-flash or indirectly through the cmake flash target. Add an option to specify a custom flash script, this is passed a build-dir and board-id flags so it can be used to implement custom flashing scripts in a system that does not use west. Signed-off-by: Fabio Baltieri <[email protected]>
1 parent 5857aee commit a31f784

File tree

4 files changed

+86
-4
lines changed

4 files changed

+86
-4
lines changed

doc/develop/test/twister.rst

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1443,6 +1443,32 @@ work. It is equivalent to following west and twister commands.
14431443
manually according to above example. This is because the serial port
14441444
of the PTY is not fixed and being allocated in the system at runtime.
14451445

1446+
If west is not available or does not know how to flash your system, a custom
1447+
flash command can be specified using the ``flash-command`` flag. The script is
1448+
called with a ``--build-dir`` with the path of the current build, as well as a
1449+
``--board-id`` flag to identify the specific device when multiple are available
1450+
in a hardware map.
1451+
1452+
.. tabs::
1453+
1454+
.. group-tab:: Linux
1455+
1456+
.. code-block:: bash
1457+
1458+
twister -p npcx9m6f_evb --device-testing --device-serial /dev/ttyACM0
1459+
--flash-command './custom_flash_script.py,--flag,"complex,argument"'
1460+
1461+
.. group-tab:: Windows
1462+
1463+
.. note::
1464+
1465+
python .\scripts\twister -p npcx9m6f_evb --device-testing
1466+
--device-serial COM1
1467+
--flash-command 'custom_flash_script.py,--flag,"complex,argument"'
1468+
1469+
Would result in calling ``./custom_flash_script.py --flag complex,argument --build-dir
1470+
<build directory> --board-id <board identification>``.
1471+
14461472
Fixtures
14471473
+++++++++
14481474

scripts/pylib/twister/twisterlib/environment.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,15 @@ def add_parse_arguments(parser = None) -> argparse.ArgumentParser:
836836
will translate to "west flash --runner pyocd"
837837
"""
838838
)
839+
parser.add_argument(
840+
"--flash-command",
841+
help="""Instead of 'west flash', uses a custom flash command to flash
842+
when running with --device-testing. Supports comma-separated
843+
argument list, the script is also passed a --build-dir flag with
844+
the build directory as an argument, and a --board-id flag with the
845+
board or probe id if available.
846+
"""
847+
)
839848

840849
parser.add_argument(
841850
"-X", "--fixture", action="append", default=[],

scripts/pylib/twister/twisterlib/handlers.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import argparse
1111
import contextlib
12+
import csv
1213
import logging
1314
import math
1415
import os
@@ -564,7 +565,24 @@ def run_custom_script(script, timeout):
564565
proc.communicate()
565566
logger.error(f"{script} timed out")
566567

568+
def _create_flash_command(self, hardware):
569+
flash_command = next(csv.reader([self.options.flash_command]))
570+
571+
command = [flash_command[0]]
572+
command.extend(['--build-dir', self.build_dir])
573+
574+
board_id = hardware.probe_id or hardware.id
575+
if board_id:
576+
command.extend(['--board-id', board_id])
577+
578+
command.extend(flash_command[1:])
579+
580+
return command
581+
567582
def _create_command(self, runner, hardware):
583+
if self.options.flash_command:
584+
return self._create_flash_command(hardware)
585+
568586
command = ["west", "flash", "--skip-rebuild", "-d", self.build_dir]
569587
command_extra_args = []
570588

scripts/tests/twister/test_handlers.py

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,25 +1049,29 @@ def mock_availability(handler, instance, no=num_of_failures):
10491049
None,
10501050
None,
10511051
None,
1052+
None,
10521053
['west', 'flash', '--skip-rebuild', '-d', '$build_dir']
10531054
),
10541055
(
10551056
[],
10561057
None,
10571058
None,
1059+
None,
10581060
['west', 'flash', '--skip-rebuild', '-d', '$build_dir']
10591061
),
10601062
(
10611063
'--dummy',
10621064
None,
10631065
None,
1066+
None,
10641067
['west', 'flash', '--skip-rebuild', '-d', '$build_dir',
10651068
'--', '--dummy']
10661069
),
10671070
(
10681071
'--dummy1,--dummy2',
10691072
None,
10701073
None,
1074+
None,
10711075
['west', 'flash', '--skip-rebuild', '-d', '$build_dir',
10721076
'--', '--dummy1', '--dummy2']
10731077
),
@@ -1076,6 +1080,7 @@ def mock_availability(handler, instance, no=num_of_failures):
10761080
None,
10771081
'runner',
10781082
'product',
1083+
None,
10791084
['west', 'flash', '--skip-rebuild', '-d', '$build_dir',
10801085
'--runner', 'runner', 'param1', 'param2']
10811086
),
@@ -1084,20 +1089,23 @@ def mock_availability(handler, instance, no=num_of_failures):
10841089
None,
10851090
'pyocd',
10861091
'product',
1092+
None,
10871093
['west', 'flash', '--skip-rebuild', '-d', '$build_dir',
10881094
'--runner', 'pyocd', 'param1', 'param2', '--', '--dev-id', 12345]
10891095
),
10901096
(
10911097
None,
10921098
'nrfjprog',
10931099
'product',
1100+
None,
10941101
['west', 'flash', '--skip-rebuild', '-d', '$build_dir',
10951102
'--runner', 'nrfjprog', 'param1', 'param2', '--', '--dev-id', 12345]
10961103
),
10971104
(
10981105
None,
10991106
'openocd',
11001107
'STM32 STLink',
1108+
None,
11011109
['west', 'flash', '--skip-rebuild', '-d', '$build_dir',
11021110
'--runner', 'openocd', 'param1', 'param2',
11031111
'--', '--cmd-pre-init', 'hla_serial 12345']
@@ -1106,6 +1114,7 @@ def mock_availability(handler, instance, no=num_of_failures):
11061114
None,
11071115
'openocd',
11081116
'STLINK-V3',
1117+
None,
11091118
['west', 'flash', '--skip-rebuild', '-d', '$build_dir',
11101119
'--runner', 'openocd', 'param1', 'param2',
11111120
'--', '--cmd-pre-init', 'hla_serial 12345']
@@ -1114,6 +1123,7 @@ def mock_availability(handler, instance, no=num_of_failures):
11141123
None,
11151124
'openocd',
11161125
'EDBG CMSIS-DAP',
1126+
None,
11171127
['west', 'flash', '--skip-rebuild', '-d', '$build_dir',
11181128
'--runner', 'openocd', 'param1', 'param2',
11191129
'--', '--cmd-pre-init', 'cmsis_dap_serial 12345']
@@ -1122,6 +1132,7 @@ def mock_availability(handler, instance, no=num_of_failures):
11221132
None,
11231133
'jlink',
11241134
'product',
1135+
None,
11251136
['west', 'flash', '--skip-rebuild', '-d', '$build_dir',
11261137
'--runner', 'jlink', '--dev-id', 12345,
11271138
'param1', 'param2']
@@ -1130,23 +1141,39 @@ def mock_availability(handler, instance, no=num_of_failures):
11301141
None,
11311142
'stm32cubeprogrammer',
11321143
'product',
1144+
None,
11331145
['west', 'flash', '--skip-rebuild', '-d', '$build_dir',
11341146
'--runner', 'stm32cubeprogrammer', '--tool-opt=sn=12345',
11351147
'param1', 'param2']
11361148
),
1137-
1149+
(
1150+
None,
1151+
None,
1152+
None,
1153+
'flash_command',
1154+
['flash_command', '--build-dir', '$build_dir', '--board-id', 12345]
1155+
),
1156+
(
1157+
None,
1158+
None,
1159+
None,
1160+
'path to/flash_command,with,args,"1,2,3",4',
1161+
['path to/flash_command', '--build-dir', '$build_dir', '--board-id',
1162+
12345, 'with', 'args', '1,2,3', '4']
1163+
),
11381164
]
11391165

11401166
TESTDATA_13_2 = [(True), (False)]
11411167

11421168
@pytest.mark.parametrize(
11431169
'self_west_flash, runner,' \
1144-
' hardware_product_name, expected',
1170+
' hardware_product_name, self_flash_command, expected',
11451171
TESTDATA_13,
11461172
ids=['default', '--west-flash', 'one west flash value',
11471173
'multiple west flash values', 'generic runner', 'pyocd',
11481174
'nrfjprog', 'openocd, STM32 STLink', 'openocd, STLINK-v3',
1149-
'openocd, EDBG CMSIS-DAP', 'jlink', 'stm32cubeprogrammer']
1175+
'openocd, EDBG CMSIS-DAP', 'jlink', 'stm32cubeprogrammer',
1176+
'flash_command', 'flash_command with args']
11501177
)
11511178
@pytest.mark.parametrize('hardware_probe', TESTDATA_13_2, ids=['probe', 'id'])
11521179
def test_devicehandler_create_command(
@@ -1155,10 +1182,12 @@ def test_devicehandler_create_command(
11551182
runner,
11561183
hardware_probe,
11571184
hardware_product_name,
1185+
self_flash_command,
11581186
expected
11591187
):
11601188
handler = DeviceHandler(mocked_instance, 'build', mock.Mock())
1161-
handler.options = mock.Mock(west_flash=self_west_flash)
1189+
handler.options = mock.Mock(west_flash=self_west_flash,
1190+
flash_command=self_flash_command)
11621191
handler.generator_cmd = 'generator_cmd'
11631192

11641193
expected = [handler.build_dir if val == '$build_dir' else \

0 commit comments

Comments
 (0)