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

115 lines
3.4 KiB
Python
Raw Normal View History

#!/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())