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:
commit
363412cd72
@ -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
|
||||
|
@ -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
115
dist/tools/programmer/programmer.py
vendored
Executable 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())
|
@ -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
|
||||
|
26
makefiles/tools/programmer.inc.mk
Normal file
26
makefiles/tools/programmer.inc.mk
Normal 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
|
@ -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)`.
|
||||
|
Loading…
Reference in New Issue
Block a user