ardupilot/Tools/scripts/test_new_boards.py
2025-12-09 11:55:49 +11:00

163 lines
5.5 KiB
Python
Executable File

#!/usr/bin/env python3
from __future__ import annotations
'''
Look at the difference between this and its merge-base with master
for each new hwdef file, build the board
AP_FLAKE8_CLEAN
'''
import optparse
import os
import shutil
from dataclasses import dataclass
from typing import Set
import board_list
from build_script_base import BuildScriptBase
class TestNewBoards(BuildScriptBase):
def __init__(self, master_branch: str = "master") -> None:
super().__init__()
self.master_branch: str = master_branch
def progress_prefix(self) -> str:
'''pretty-print progress'''
return 'TNB'
def get_new_hwdef_paths(self) -> Set[str]:
'''returns list of newly added hwdef filepaths relative to the root.
Only .dat files for both main and bootloader firmwares'''
current_commitish = self.find_current_git_branch_or_sha1()
merge_base = self.find_git_branch_merge_base(current_commitish, self.master_branch)
delta_files = self.run_git([
'diff',
'--name-status',
f"{merge_base}",
], show_output=False)
ret = set()
for line in delta_files.split("\n"):
if not line:
continue
parts = line.split(None, 1)
if len(parts) != 2:
continue
status, filepath = parts
# Only process added files (status 'A')
if status != 'A':
continue
if "/hwdef/" not in filepath:
continue
if not filepath.endswith(("hwdef.dat", "hwdef-bl.dat")):
continue
ret.add(filepath)
return ret
def build_board(self, board : board_list.Board):
self.run_waf(["configure", "--board", board.name], show_output=False)
if board.is_ap_periph:
self.run_waf(["AP_Periph"], show_output=False)
else:
for t in board.autobuild_targets:
t = t.lower()
if t == 'tracker':
t = 'antennatracker'
self.run_waf([t], show_output=False)
def build_bootloader(self, board : board_list.Board) -> None:
if not board.is_ap_periph:
# hopefully temporary hack so you can build bootloader
# after building other vehicles without a clean:
dsdl_generated_path = os.path.join('build', board.name, "modules", "DroneCAN", "libcanard", "dsdlc_generated")
self.progress(f"HACK: Removing ({dsdl_generated_path})")
shutil.rmtree(dsdl_generated_path, ignore_errors=True)
self.run_waf([
"configure",
"--board",
board.name,
"--bootloader"
], show_output=False)
self.run_waf(["bootloader"], show_output=False)
def run(self) -> None:
hwdef_filepaths = self.get_new_hwdef_paths()
@dataclass
class BoardToTest():
test_bootloader: bool = False
test_vehicles: bool = False
# Extract unique board names and track if bootloader hwdef was added
boards_to_test = {} # board_name -> has_bootloader
for hwdef_filepath in hwdef_filepaths:
self.progress(f"New hwdef: {hwdef_filepath}")
# Extract board name from path like libraries/AP_HAL_ChibiOS/hwdef/BoardName/hwdef.dat
parts = hwdef_filepath.split('/')
if 'hwdef' not in parts:
raise ValueError(f"hwdef not found in path: {hwdef_filepath}")
hwdef_idx = parts.index('hwdef')
if hwdef_idx + 1 >= len(parts):
raise ValueError(f"No board name after hwdef in path: {hwdef_filepath}")
board_name = parts[hwdef_idx + 1]
if board_name not in boards_to_test:
boards_to_test[board_name] = BoardToTest()
if hwdef_filepath.endswith('hwdef-bl.dat'):
boards_to_test[board_name].test_bootloader = True
else:
boards_to_test[board_name].test_vehicles = True
# Build each unique board (find it in board list as an additional check)
bl = board_list.BoardList()
for board_name in sorted(boards_to_test.keys()):
# Find the board object
board = None
for b in bl.boards:
if b.name == board_name:
board = b
break
if board is None:
raise ValueError(f"Board {board_name} not found in board list")
# Skip ESP32 boards - CI machine can't build them
if board.hal == "ESP32":
self.progress(f"Skipping ESP32 board {board.name}")
continue
self.progress(f"Building board {board.name}")
if boards_to_test[board_name].test_vehicles:
self.build_board(board)
# Build bootloader if a bootloader hwdef file was added
if boards_to_test[board_name].test_bootloader:
self.progress(f"Building bootloader for {board.name}")
self.build_bootloader(board)
def main():
parser = optparse.OptionParser("test_new_boards.py")
parser.add_option("",
"--master-branch",
type="string",
default="master",
help="master branch to use")
cmd_opts, cmd_args = parser.parse_args()
tnb = TestNewBoards(
master_branch=cmd_opts.master_branch,
)
tnb.run()
if __name__ == '__main__':
main()