1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

Merge pull request #15970 from aabadie/pr/make/redirect_flash_output

make: print spinning icon while flashing/resetting
This commit is contained in:
Kaspar Schleiser 2021-02-25 21:39:45 +01:00 committed by GitHub
commit 363412cd72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 156 additions and 11 deletions

View File

@ -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

View File

@ -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))

115
dist/tools/programmer/programmer.py vendored Executable file
View File

@ -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())

View File

@ -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

View File

@ -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

View File

@ -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)`.