mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
6a705d95b3
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.
115 lines
3.4 KiB
Python
Executable File
115 lines
3.4 KiB
Python
Executable File
#!/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())
|