1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/dist/tools/programmer/programmer.py
Benjamin Valentin 6a705d95b3 dist/tools: always print how to disable programmer wrapper
If the help is only printed on failure, if the script does not detect failure
and programming still fails, the user has no idea how to get the full information.

Always print help on how to disable this feature.
2021-03-02 10:42:39 +01:00

115 lines
3.4 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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."""
print(
"For full programmer output add PROGRAMMER_QUIET=0 or "
"QUIET=0 to the make command line."
)
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())
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())