diff --git a/docs/shared_bindings_matrix.py b/docs/shared_bindings_matrix.py index 5b9b63d8fd..761e3e29f2 100644 --- a/docs/shared_bindings_matrix.py +++ b/docs/shared_bindings_matrix.py @@ -27,6 +27,7 @@ import pathlib import re import subprocess import sys +import functools from concurrent.futures import ThreadPoolExecutor @@ -80,12 +81,11 @@ This is the same list as in the preprocess_frozen_modules script.""" repository_urls = {} """Cache of repository URLs for frozen modules.""" +root_dir = pathlib.Path(__file__).resolve().parent.parent + def get_circuitpython_root_dir(): """ The path to the root './circuitpython' directory. """ - file_path = pathlib.Path(__file__).resolve() - root_dir = file_path.parent.parent - return root_dir def get_shared_bindings(): @@ -102,7 +102,7 @@ def get_board_mapping(): """ boards = {} for port in SUPPORTED_PORTS: - board_path = os.path.join("../ports", port, "boards") + board_path = root_dir / "ports" / port / "boards" for board_path in os.scandir(board_path): if board_path.is_dir(): board_files = os.listdir(board_path.path) @@ -276,6 +276,7 @@ def lookup_setting(settings, key, default=''): key = value[2:-1] return value +@functools.cache def all_ports_all_boards(ports=SUPPORTED_PORTS): for port in ports: diff --git a/tools/ci_set_matrix.py b/tools/ci_set_matrix.py old mode 100644 new mode 100755 index e165c23c27..f22840fad7 --- a/tools/ci_set_matrix.py +++ b/tools/ci_set_matrix.py @@ -12,6 +12,13 @@ on the event that triggered run. Pull request runs will compare to the base branch while pushes will compare to the current ref. We override this for the adafruit/circuitpython repo so we build all docs/boards for pushes. +When making changes to the script it is useful to manually test it. +You can for instance run +```shell +tools/ci_set_matrix ports/raspberrypi/common-hal/socket/SSLSocket.c +``` +and (at the time this comment was written) get a series of messages indicating +that only the single board raspberry_pi_pico_w would be built. """ import re @@ -19,9 +26,21 @@ import os import sys import json import yaml +import pathlib +from concurrent.futures import ThreadPoolExecutor + +tools_dir = pathlib.Path(__file__).resolve().parent +top_dir = tools_dir.parent + +sys.path.insert(0, str(tools_dir / "adabot")) +sys.path.insert(0, str(top_dir / "docs")) import build_board_info -from shared_bindings_matrix import get_settings_from_makefile +from shared_bindings_matrix import ( + get_settings_from_makefile, + SUPPORTED_PORTS, + all_ports_all_boards, +) PORT_TO_ARCH = { "atmel-samd": "arm", @@ -40,12 +59,17 @@ IGNORE = [ "tools/ci_check_duplicate_usb_vid_pid.py", ] -changed_files = {} -try: - changed_files = json.loads(os.environ["CHANGED_FILES"]) -except json.decoder.JSONDecodeError as exc: - if exc.msg != "Expecting value": - raise +if len(sys.argv) > 1: + print("Using files list on commandline") + changed_files = sys.argv[1:] +else: + c = os.environ["CHANGED_FILES"] + if c == "": + print("CHANGED_FILES is in environment, but value is empty") + changed_files = [] + else: + print("Using files list in CHANGED_FILES") + changed_files = json.loads(os.environ["CHANGED_FILES"]) def set_output(name, value): @@ -74,13 +98,29 @@ def set_boards_to_build(build_all): port_to_boards[port].add(board_id) board_to_port[board_id] = port + def compute_board_settings(boards): + need = set(boards) - set(board_settings.keys()) + if not need: + return + + def get_settings(board): + return ( + board, + get_settings_from_makefile(str(top_dir / "ports" / board_to_port[board]), board), + ) + + with ThreadPoolExecutor(max_workers=os.cpu_count()) as ex: + board_settings.update(ex.map(get_settings, need)) + boards_to_build = all_board_ids if not build_all: boards_to_build = set() board_pattern = re.compile(r"^ports/[^/]+/boards/([^/]+)/") port_pattern = re.compile(r"^ports/([^/]+)/") - module_pattern = re.compile(r"^(ports/[^/]+/shared-bindings|shared-module)/([^/]+)/") + module_pattern = re.compile( + r"^(ports/[^/]+/(?:common-hal|bindings)|shared-bindings|shared-module)/([^/]+)/" + ) for p in changed_files: # See if it is board specific board_matches = board_pattern.search(p) @@ -91,9 +131,9 @@ def set_boards_to_build(build_all): # See if it is port specific port_matches = port_pattern.search(p) + port = port_matches.group(1) if port_matches else None module_matches = module_pattern.search(p) - if port_matches and not module_matches: - port = port_matches.group(1) + if port and not module_matches: if port != "unix": boards_to_build.update(port_to_boards[port]) continue @@ -109,11 +149,12 @@ def set_boards_to_build(build_all): # As a (nearly) last resort, for some certain files, we compute the settings from the # makefile for each board and determine whether to build them that way. if p.startswith("frozen") or p.startswith("supervisor") or module_matches: - for board in all_board_ids: - if board not in board_settings: - board_settings[board] = get_settings_from_makefile( - "../ports/" + board_to_port[board], board - ) + if port: + board_ids = port_to_boards[port] + else: + board_ids = all_board_ids + compute_board_settings(board_ids) + for board in board_ids: settings = board_settings[board] # Check frozen files to see if they are in each board.