| #!/usr/bin/env python3 | |
| # | |
| # Copyright (c) 2024 Raspberry Pi (Trading) Ltd. | |
| # | |
| # SPDX-License-Identifier: BSD-3-Clause | |
| # | |
| # Common helpers and variables shared across Bazel-related Python scripts. | |
| import argparse | |
| import logging | |
| import os | |
| from pathlib import Path | |
| import shlex | |
| import shutil | |
| import subprocess | |
| import sys | |
| _LOG = logging.getLogger(__file__) | |
| SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) | |
| SDK_ROOT = subprocess.run( | |
| ( | |
| "git", | |
| "rev-parse", | |
| "--show-toplevel", | |
| ), | |
| cwd=SCRIPT_DIR, | |
| text=True, | |
| check=True, | |
| capture_output=True, | |
| ).stdout.strip() | |
| def parse_common_args(): | |
| parser = argparse.ArgumentParser() | |
| add_common_args(parser) | |
| return parser.parse_args() | |
| def add_common_args(parser): | |
| parser.add_argument( | |
| "--picotool-dir", | |
| help="Use a local copy of Picotool rather than the dynamically fetching it", | |
| default=None, | |
| type=Path, | |
| ) | |
| def override_picotool_arg(picotool_dir): | |
| return f"--override_module=picotool={picotool_dir.resolve()}" | |
| def bazel_command() -> str: | |
| """Return the path to bazelisk or bazel.""" | |
| if shutil.which("bazelisk"): | |
| return shutil.which("bazelisk") | |
| if shutil.which("bazel"): | |
| return "bazel" | |
| raise FileNotFoundError( | |
| "Cannot find 'bazel' or 'bazelisk' in the current system PATH" | |
| ) | |
| def run_bazel(args, check=False, **kwargs): | |
| command = ( | |
| bazel_command(), | |
| *args, | |
| ) | |
| _LOG.info("Running Bazel command: %s", shlex.join(command)) | |
| proc = subprocess.run( | |
| command, | |
| cwd=SDK_ROOT, | |
| **kwargs, | |
| ) | |
| if proc.returncode != 0: | |
| _LOG.error("Command invocation failed with return code %d!", proc.returncode) | |
| _LOG.error( | |
| "Failing command: %s", | |
| " ".join(shlex.quote(str(arg)) for arg in args), | |
| ) | |
| if kwargs.get("capture_output", False): | |
| output = ( | |
| proc.stderr if isinstance(proc.stderr, str) else proc.stderr.decode() | |
| ) | |
| _LOG.error( | |
| "Output:\n%s", | |
| output, | |
| ) | |
| if check: | |
| raise subprocess.CalledProcessError( | |
| returncode=proc.returncode, | |
| cmd=command, | |
| output=proc.stdout, | |
| stderr=proc.stderr, | |
| ) | |
| return proc | |
| def print_to_stderr(*args, **kwargs): | |
| print(*args, file=sys.stderr, **kwargs) | |
| def print_framed_string(s): | |
| """Frames a string of text and prints it to highlight script steps.""" | |
| header_spacer = "#" * (len(s) + 12) | |
| print_to_stderr(header_spacer) | |
| print_to_stderr("### " + s + " ###") | |
| print_to_stderr(header_spacer) | |
| def setup_logging(): | |
| log_levels = [ | |
| (logging.ERROR, "\x1b[31m[ERROR]\x1b[0m"), | |
| (logging.WARNING, "\x1b[33m[WARNING]\x1b[0m"), | |
| (logging.INFO, "\x1b[35m[INFO]\x1b[0m"), | |
| (logging.DEBUG, "\x1b[34m[DEBUG]\x1b[0m"), | |
| ] | |
| for level, level_text in log_levels: | |
| logging.addLevelName(level, level_text) | |
| logging.basicConfig(format="%(levelname)s %(message)s", level=logging.DEBUG) |