From 976eda9cbbce4a4fad759c4197b630209dd5a2bd Mon Sep 17 00:00:00 2001 From: Alexandre Abadie Date: Wed, 17 Feb 2021 21:55:26 +0100 Subject: [PATCH 1/5] dist/tools: add programmer wrapper script --- dist/tools/programmer/programmer.py | 115 ++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100755 dist/tools/programmer/programmer.py diff --git a/dist/tools/programmer/programmer.py b/dist/tools/programmer/programmer.py new file mode 100755 index 000000000000..eddcac61d200 --- /dev/null +++ b/dist/tools/programmer/programmer.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2021 Inria +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +import sys +import time +import shlex +import subprocess +import argparse + +from contextlib import contextmanager + + +SUCCESS = "\033[32;1m✓\033[0m" +FAILED = "\033[31;1m×\033[0m" +SPIN = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"] + + +class Programmer: + + @contextmanager + def spawn_process(self): + """Yield a subprocess running in background.""" + kwargs = {} if self.verbose else { + "stdout": subprocess.PIPE, + "stderr": subprocess.STDOUT + } + yield subprocess.Popen(shlex.split(self.cmd), **kwargs) + + def spin(self, process): + """Print a spinning icon while programmer process is running.""" + while process.poll() is None: + for index in range(len(SPIN)): + sys.stdout.write( + "\r \033[36;1m{}\033[0m {} in progress " + "(programmer: '{}')" + .format(SPIN[index], self.action, self.programmer) + ) + sys.stdout.flush() + time.sleep(0.1) + + def print_status(self, process, elapsed): + """Print status of background programmer process.""" + print( + "\r \u001b[2K{} {} {} (programmer: '{}' - duration: {:0.2f}s)" + .format( + FAILED if process.returncode != 0 else SUCCESS, + self.action, + "failed!" if process.returncode != 0 else "done!", + self.programmer, + elapsed + ) + ) + # Print content of stdout (which also contain stderr) when the + # subprocess failed + if process.returncode != 0: + print(process.stdout.read().decode()) + else: + print( + "(for full programmer output add PROGRAMMER_QUIET=0 or " + "QUIET=0 to the make command line)" + ) + + def run(self): + """Run the programmer in a background process.""" + if not self.cmd.strip(): + # Do nothing if programmer command is empty + return 0 + + if self.verbose: + print(self.cmd) + start = time.time() + with self.spawn_process() as proc: + try: + if self.verbose: + proc.communicate() + else: + self.spin(proc) + except KeyboardInterrupt: + proc.terminate() + proc.kill() + elapsed = time.time() - start + if not self.verbose: + # When using the spinning icon, print the programmer status + self.print_status(proc, elapsed) + + return proc.returncode + + +def main(parser): + """Main function.""" + programmer = Programmer() + parser.parse_args(namespace=programmer) + # Return with same return code as subprocess + sys.exit(programmer.run()) + + +def parser(): + """Return an argument parser.""" + parser = argparse.ArgumentParser() + parser.add_argument("--action", help="Programmer action") + parser.add_argument("--cmd", help="Programmer command") + parser.add_argument("--programmer", help="Programmer") + parser.add_argument( + "--verbose", action='store_true', default=False, help="Verbose output" + ) + return parser + + +if __name__ == "__main__": + main(parser()) From c181ba2fe4def5caf0dd1c3b86b7f6890ef9283f Mon Sep 17 00:00:00 2001 From: Alexandre Abadie Date: Tue, 9 Feb 2021 17:49:56 +0100 Subject: [PATCH 2/5] Makefile.include: use programmer wrapper to silent output --- Makefile.include | 7 +++++-- makefiles/tools/programmer.inc.mk | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 makefiles/tools/programmer.inc.mk diff --git a/Makefile.include b/Makefile.include index 763c0aa00920..9bb29c5ad766 100644 --- a/Makefile.include +++ b/Makefile.include @@ -709,10 +709,13 @@ distclean: -@for i in $(USEPKG) ; do "$(MAKE)" -C $(RIOTPKG)/$$i distclean ; done -@rm -rf $(BINDIRBASE) +# Include PROGRAMMER_FLASH/PROGRAMMER_RESET variables +include $(RIOTMAKE)/tools/programmer.inc.mk + # Define flash-recipe with a default value define default-flash-recipe $(call check_cmd,$(FLASHER),Flash program) - $(FLASHER) $(FFLAGS) + $(PROGRAMMER_FLASH) endef flash-recipe ?= $(default-flash-recipe) @@ -774,7 +777,7 @@ endif reset: $(call check_cmd,$(RESET),Reset program) - $(RESET) $(RESET_FLAGS) + $(PROGRAMMER_RESET) # tests related targets and variables include $(RIOTMAKE)/tests/tests.inc.mk diff --git a/makefiles/tools/programmer.inc.mk b/makefiles/tools/programmer.inc.mk new file mode 100644 index 000000000000..48c288561c1a --- /dev/null +++ b/makefiles/tools/programmer.inc.mk @@ -0,0 +1,26 @@ +# Configure the programmer related variables + +PROGRAMMER_QUIET ?= $(QUIET) +ifeq (0,$(PROGRAMMER_QUIET)) + PROGRAMMER_VERBOSE_OPT ?= --verbose +endif + +# Don't use the programmer wrapper for the CI (where speed and verbose output +# are important) +ifneq (1,$(RIOT_CI_BUILD)) + USE_PROGRAMMER_WRAPPER_SCRIPT ?= 1 +else + USE_PROGRAMMER_WRAPPER_SCRIPT ?= 0 +endif + +ifeq (1,$(USE_PROGRAMMER_WRAPPER_SCRIPT)) + PROGRAMMER_FLASH ?= @$(RIOTTOOLS)/programmer/programmer.py \ + --action Flashing --cmd "$(FLASHER) $(FFLAGS)" \ + --programmer "$(PROGRAMMER)" $(PROGRAMMER_VERBOSE_OPT) + PROGRAMMER_RESET ?= @$(RIOTTOOLS)/programmer/programmer.py \ + --action Resetting --cmd "$(RESET) $(RESET_FLAGS)" \ + --programmer "$(PROGRAMMER)" $(PROGRAMMER_VERBOSE_OPT) +else + PROGRAMMER_FLASH ?= $(FLASHER) $(FFLAGS) + PROGRAMMER_RESET ?= $(RESET) $(RESET_FLAGS) +endif From a47bf127194185f2b2f524bb60580e9cb170e6e7 Mon Sep 17 00:00:00 2001 From: Alexandre Abadie Date: Tue, 9 Feb 2021 18:00:57 +0100 Subject: [PATCH 3/5] makefiles/vars.inc.mk: document PROGRAMMER_QUIET variable --- makefiles/vars.inc.mk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/makefiles/vars.inc.mk b/makefiles/vars.inc.mk index b460be459cb8..82e7b8e1df4b 100644 --- a/makefiles/vars.inc.mk +++ b/makefiles/vars.inc.mk @@ -107,6 +107,10 @@ export HEXFILE # The 'intel hex' stripped result of the compilatio # RESET_FLAGS # The parameters to supply to RESET. # PROGRAMMER # The programmer to use when flashing, resetting or debugging # PROGRAMMERS_SUPPORTED # The list of programmers supported by a board +# PROGRAMMER_QUIET # Change verbosity of programmer output (only used with flash and reset targets). + # Default is 1, not verbose. Use 0 to get normal programmer output. +# USE_PROGRAMMER_WRAPPER_SCRIPT # Use the programmer wrapper Python script. Default is 1 (0 if RIOT_CI_BUILD is set). + export DLCACHE # directory used to cache http downloads export DOWNLOAD_TO_FILE # Use `$(DOWNLOAD_TO_FILE) $(DESTINATION) $(URL)` to download `$(URL)` to `$(DESTINATION)`. From af37f5b9bc3877a07363af7d6b9919944f0a4411 Mon Sep 17 00:00:00 2001 From: Alexandre Abadie Date: Thu, 18 Feb 2021 12:00:04 +0100 Subject: [PATCH 4/5] makefiles/boot: map PROGRAMMER_QUIET to riotboot/flash% --- makefiles/boot/riotboot.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefiles/boot/riotboot.mk b/makefiles/boot/riotboot.mk index 286b5373cdde..9f68aa2eeac7 100644 --- a/makefiles/boot/riotboot.mk +++ b/makefiles/boot/riotboot.mk @@ -81,7 +81,7 @@ riotboot/bootloader/%: $(BUILDDEPS) QUIET=$(QUIET) PATH="$(PATH)"\ EXTERNAL_BOARD_DIRS="$(EXTERNAL_BOARD_DIRS)" BOARD=$(BOARD)\ DEBUG_ADAPTER_ID=$(DEBUG_ADAPTER_ID) \ - PROGRAMMER=$(PROGRAMMER) \ + PROGRAMMER=$(PROGRAMMER) PROGRAMMER_QUIET=$(PROGRAMMER_QUIET) \ $(MAKE) --no-print-directory -C $(RIOTBOOT_DIR) $* # Generate a binary file from the bootloader which fills all the From 5a8094ea0a2d5d07d6c1028cc4fdcc6f4c67a7dc Mon Sep 17 00:00:00 2001 From: Alexandre Abadie Date: Tue, 23 Feb 2021 11:30:55 +0100 Subject: [PATCH 5/5] dist/iotlab: return ret code within a single call --- dist/testbed-support/makefile.iotlab.single.inc.mk | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/dist/testbed-support/makefile.iotlab.single.inc.mk b/dist/testbed-support/makefile.iotlab.single.inc.mk index 5b94584b5558..cf90d3f2dbd4 100644 --- a/dist/testbed-support/makefile.iotlab.single.inc.mk +++ b/dist/testbed-support/makefile.iotlab.single.inc.mk @@ -172,17 +172,14 @@ info-iotlab-node: # Configure FLASHER, RESET, TERMPROG depending on BOARD and if on frontend -# Command to check if 'stdin' is 0. Cannot use 'cmp - <(echo 0)' without bash shell -_STDIN_EQ_0 = grep 0 - ifneq (iotlab-a8-m3,$(BOARD)) # M3 nodes FLASHER = iotlab-node RESET = iotlab-node - _NODE_FMT = --jmespath='keys(@)[0]' --format='int' - FFLAGS = $(_NODE_FMT) $(_IOTLAB_EXP_ID) $(_IOTLAB_NODELIST) $(_NODES_FLASH_OPTION) $(FLASHFILE) | $(_STDIN_EQ_0) - RESET_FLAGS = $(_NODE_FMT) $(_IOTLAB_EXP_ID) $(_IOTLAB_NODELIST) --reset | $(_STDIN_EQ_0) + _NODE_FMT = --jmespath='keys(@)[0]' --format='lambda ret: exit(int(ret))' + FFLAGS = $(_NODE_FMT) $(_IOTLAB_EXP_ID) $(_IOTLAB_NODELIST) $(_NODES_FLASH_OPTION) $(FLASHFILE) + RESET_FLAGS = $(_NODE_FMT) $(_IOTLAB_EXP_ID) $(_IOTLAB_NODELIST) --reset ifeq (,$(_IOTLAB_ON_FRONTEND)) TERMPROG = ssh @@ -198,8 +195,8 @@ else FLASHER = iotlab-ssh RESET = iotlab-ssh _NODE_FMT = --jmespath='keys(values(@)[0])[0]' --fmt='int' - FFLAGS = $(_NODE_FMT) $(_IOTLAB_EXP_ID) flash-m3 $(_IOTLAB_NODELIST) $(FLASHFILE) | $(_STDIN_EQ_0) - RESET_FLAGS = $(_NODE_FMT) $(_IOTLAB_EXP_ID) reset-m3 $(_IOTLAB_NODELIST) | $(_STDIN_EQ_0) + FFLAGS = $(_NODE_FMT) $(_IOTLAB_EXP_ID) flash-m3 $(_IOTLAB_NODELIST) $(FLASHFILE) + RESET_FLAGS = $(_NODE_FMT) $(_IOTLAB_EXP_ID) reset-m3 $(_IOTLAB_NODELIST) TERMPROG = ssh ifeq (,$(_IOTLAB_ON_FRONTEND))