| 
									
										
										
										
											2024-05-10 18:55:43 +07:00
										 |  |  | import argparse | 
					
						
							| 
									
										
										
										
											2024-05-13 20:27:49 +07:00
										 |  |  | import random | 
					
						
							| 
									
										
										
										
											2024-05-09 20:43:46 +07:00
										 |  |  | import os | 
					
						
							|  |  |  | import sys | 
					
						
							|  |  |  | import time | 
					
						
							|  |  |  | import subprocess | 
					
						
							|  |  |  | from pathlib import Path | 
					
						
							|  |  |  | from multiprocessing import Pool | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import build_utils | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  | STATUS_OK = "\033[32mOK\033[0m" | 
					
						
							|  |  |  | STATUS_FAILED = "\033[31mFailed\033[0m" | 
					
						
							|  |  |  | STATUS_SKIPPED = "\033[33mSkipped\033[0m" | 
					
						
							| 
									
										
										
										
											2024-05-09 20:43:46 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  | RET_OK = 0 | 
					
						
							|  |  |  | RET_FAILED = 1 | 
					
						
							|  |  |  | RET_SKIPPED = 2 | 
					
						
							| 
									
										
										
										
											2024-05-09 20:43:46 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  | build_format = '| {:30} | {:40} | {:16} | {:5} |' | 
					
						
							|  |  |  | build_separator = '-' * 95 | 
					
						
							|  |  |  | build_status = [STATUS_OK, STATUS_FAILED, STATUS_SKIPPED] | 
					
						
							| 
									
										
										
										
											2024-05-09 20:43:46 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  | 
 | 
					
						
							|  |  |  | # ----------------------------- | 
					
						
							|  |  |  | # Helper | 
					
						
							|  |  |  | # ----------------------------- | 
					
						
							| 
									
										
										
										
											2024-05-09 20:43:46 +07:00
										 |  |  | def run_cmd(cmd): | 
					
						
							|  |  |  |     #print(cmd) | 
					
						
							|  |  |  |     r = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  |     title = f'Command Error: {cmd}' | 
					
						
							| 
									
										
										
										
											2024-05-09 20:43:46 +07:00
										 |  |  |     if r.returncode != 0: | 
					
						
							|  |  |  |         # print build output if failed | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  |         if os.getenv('GITHUB_ACTIONS'): | 
					
						
							| 
									
										
										
										
											2024-05-09 20:43:46 +07:00
										 |  |  |             print(f"::group::{title}") | 
					
						
							|  |  |  |             print(r.stdout.decode("utf-8")) | 
					
						
							|  |  |  |             print(f"::endgroup::") | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             print(title) | 
					
						
							|  |  |  |             print(r.stdout.decode("utf-8")) | 
					
						
							|  |  |  |     return r | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-09 20:43:46 +07:00
										 |  |  | def find_family(board): | 
					
						
							|  |  |  |     bsp_dir = Path("hw/bsp") | 
					
						
							|  |  |  |     for family_dir in bsp_dir.iterdir(): | 
					
						
							|  |  |  |         if family_dir.is_dir(): | 
					
						
							|  |  |  |             board_dir = family_dir / 'boards' / board | 
					
						
							|  |  |  |             if board_dir.exists(): | 
					
						
							|  |  |  |                 return family_dir.name | 
					
						
							|  |  |  |     return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_examples(family): | 
					
						
							|  |  |  |     all_examples = [] | 
					
						
							|  |  |  |     for d in os.scandir("examples"): | 
					
						
							|  |  |  |         if d.is_dir() and 'cmake' not in d.name and 'build_system' not in d.name: | 
					
						
							|  |  |  |             for entry in os.scandir(d.path): | 
					
						
							|  |  |  |                 if entry.is_dir() and 'cmake' not in entry.name: | 
					
						
							|  |  |  |                     if family != 'espressif' or 'freertos' in entry.name: | 
					
						
							|  |  |  |                         all_examples.append(d.name + '/' + entry.name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if family == 'espressif': | 
					
						
							|  |  |  |         all_examples.append('device/board_test') | 
					
						
							|  |  |  |         all_examples.append('device/video_capture') | 
					
						
							|  |  |  |     all_examples.sort() | 
					
						
							|  |  |  |     return all_examples | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  | def print_build_result(board, example, status, duration): | 
					
						
							|  |  |  |     if isinstance(duration, (int, float)): | 
					
						
							|  |  |  |         duration = "{:.2f}s".format(duration) | 
					
						
							|  |  |  |     print(build_format.format(board, example, build_status[status], duration)) | 
					
						
							| 
									
										
										
										
											2024-05-09 20:43:46 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  | # ----------------------------- | 
					
						
							|  |  |  | # CMake | 
					
						
							|  |  |  | # ----------------------------- | 
					
						
							|  |  |  | def cmake_board(board, toolchain): | 
					
						
							|  |  |  |     ret = [0, 0, 0] | 
					
						
							|  |  |  |     start_time = time.monotonic() | 
					
						
							| 
									
										
										
										
											2024-05-09 20:43:46 +07:00
										 |  |  |     build_dir = f"cmake-build/cmake-build-{board}" | 
					
						
							|  |  |  |     family = find_family(board) | 
					
						
							|  |  |  |     if family == 'espressif': | 
					
						
							|  |  |  |         # for espressif, we have to build example individually | 
					
						
							|  |  |  |         all_examples = get_examples(family) | 
					
						
							|  |  |  |         for example in all_examples: | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  |             rcmd = run_cmd(f'cmake examples/{example} -B {build_dir}/{example} -G "Ninja" -DBOARD={board} -DMAX3421_HOST=1') | 
					
						
							|  |  |  |             if rcmd.returncode == 0: | 
					
						
							|  |  |  |                 rcmd = run_cmd(f'cmake --build {build_dir}/{example}') | 
					
						
							|  |  |  |             ret[0 if rcmd.returncode == 0 else 1] += 1 | 
					
						
							| 
									
										
										
										
											2024-05-09 20:43:46 +07:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  |         rcmd = run_cmd(f'cmake examples -B {build_dir} -G "Ninja" -DBOARD={board} -DCMAKE_BUILD_TYPE=MinSizeRel -DTOOLCHAIN={toolchain}') | 
					
						
							|  |  |  |         if rcmd.returncode == 0: | 
					
						
							|  |  |  |             rcmd = run_cmd(f"cmake --build {build_dir}") | 
					
						
							|  |  |  |         ret[0 if rcmd.returncode == 0 else 1] += 1 | 
					
						
							| 
									
										
										
										
											2024-05-09 20:43:46 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  |     example = 'all' | 
					
						
							|  |  |  |     print_build_result(board, example, 0 if ret[1] == 0 else 1, time.monotonic() - start_time) | 
					
						
							|  |  |  |     return ret | 
					
						
							| 
									
										
										
										
											2024-05-09 20:43:46 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  | 
 | 
					
						
							|  |  |  | # ----------------------------- | 
					
						
							|  |  |  | # Make | 
					
						
							|  |  |  | # ----------------------------- | 
					
						
							|  |  |  | def make_one_example(example, board, make_option): | 
					
						
							|  |  |  |     # Check if board is skipped | 
					
						
							|  |  |  |     if build_utils.skip_example(example, board): | 
					
						
							|  |  |  |         print_build_result(board, example, 2, '-') | 
					
						
							|  |  |  |         r = 2 | 
					
						
							| 
									
										
										
										
											2024-05-09 20:43:46 +07:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  |         start_time = time.monotonic() | 
					
						
							|  |  |  |         # skip -j for circleci | 
					
						
							|  |  |  |         if not os.getenv('CIRCLECI'): | 
					
						
							|  |  |  |             make_option += ' -j' | 
					
						
							|  |  |  |         make_cmd = f"make -C examples/{example} BOARD={board} {make_option}" | 
					
						
							|  |  |  |         # run_cmd(f"{make_cmd} clean") | 
					
						
							|  |  |  |         build_result = run_cmd(f"{make_cmd} all") | 
					
						
							|  |  |  |         r = 0 if build_result.returncode == 0 else 1 | 
					
						
							|  |  |  |         print_build_result(board, example, r, time.monotonic() - start_time) | 
					
						
							| 
									
										
										
										
											2024-05-09 20:43:46 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  |     ret = [0, 0, 0] | 
					
						
							|  |  |  |     ret[r] = 1 | 
					
						
							| 
									
										
										
										
											2024-05-09 20:43:46 +07:00
										 |  |  |     return ret | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  | def make_board(board, toolchain): | 
					
						
							|  |  |  |     print(build_separator) | 
					
						
							|  |  |  |     all_examples = get_examples(find_family(board)) | 
					
						
							| 
									
										
										
										
											2024-05-13 20:27:49 +07:00
										 |  |  |     start_time = time.monotonic() | 
					
						
							|  |  |  |     ret = [0, 0, 0] | 
					
						
							|  |  |  |     with Pool(processes=os.cpu_count()) as pool: | 
					
						
							|  |  |  |         pool_args = list((map(lambda e, b=board, o=f"TOOLCHAIN={toolchain}": [e, b, o], all_examples))) | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  |         r = pool.starmap(make_one_example, pool_args) | 
					
						
							| 
									
										
										
										
											2024-05-13 20:27:49 +07:00
										 |  |  |         # sum all element of same index (column sum) | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  |         ret = list(map(sum, list(zip(*r)))) | 
					
						
							| 
									
										
										
										
											2024-05-13 20:27:49 +07:00
										 |  |  |     example = 'all' | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  |     print_build_result(board, example, 0 if ret[1] == 0 else 1, time.monotonic() - start_time) | 
					
						
							|  |  |  |     return ret | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # ----------------------------- | 
					
						
							|  |  |  | # Build Family | 
					
						
							|  |  |  | # ----------------------------- | 
					
						
							|  |  |  | def build_boards_list(boards, toolchain, build_system): | 
					
						
							|  |  |  |     ret = [0, 0, 0] | 
					
						
							|  |  |  |     for b in boards: | 
					
						
							|  |  |  |         r = [0, 0, 0] | 
					
						
							|  |  |  |         if build_system == 'cmake': | 
					
						
							|  |  |  |             r = cmake_board(b, toolchain) | 
					
						
							|  |  |  |         elif build_system == 'make': | 
					
						
							|  |  |  |             r = make_board(b, toolchain) | 
					
						
							|  |  |  |         ret[0] += r[0] | 
					
						
							|  |  |  |         ret[1] += r[1] | 
					
						
							|  |  |  |         ret[2] += r[2] | 
					
						
							| 
									
										
										
										
											2024-05-13 20:27:49 +07:00
										 |  |  |     return ret | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def build_family(family, toolchain, build_system, one_per_family, boards): | 
					
						
							| 
									
										
										
										
											2024-05-09 20:43:46 +07:00
										 |  |  |     all_boards = [] | 
					
						
							|  |  |  |     for entry in os.scandir(f"hw/bsp/{family}/boards"): | 
					
						
							|  |  |  |         if entry.is_dir() and entry.name != 'pico_sdk': | 
					
						
							|  |  |  |             all_boards.append(entry.name) | 
					
						
							|  |  |  |     all_boards.sort() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ret = [0, 0, 0] | 
					
						
							| 
									
										
										
										
											2024-05-13 20:27:49 +07:00
										 |  |  |     # If only-one flag is set, select one random board | 
					
						
							|  |  |  |     if one_per_family: | 
					
						
							|  |  |  |         for b in boards: | 
					
						
							|  |  |  |             # skip if -b already specify one in this family | 
					
						
							|  |  |  |             if find_family(b) == family: | 
					
						
							|  |  |  |                 return ret | 
					
						
							|  |  |  |         all_boards = [random.choice(all_boards)] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  |     ret = build_boards_list(all_boards, toolchain, build_system) | 
					
						
							| 
									
										
										
										
											2024-05-09 20:43:46 +07:00
										 |  |  |     return ret | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  | # ----------------------------- | 
					
						
							|  |  |  | # Main | 
					
						
							|  |  |  | # ----------------------------- | 
					
						
							| 
									
										
										
										
											2024-05-10 18:55:43 +07:00
										 |  |  | def main(): | 
					
						
							|  |  |  |     parser = argparse.ArgumentParser() | 
					
						
							|  |  |  |     parser.add_argument('families', nargs='*', default=[], help='Families to build') | 
					
						
							|  |  |  |     parser.add_argument('-b', '--board', action='append', default=[], help='Boards to build') | 
					
						
							|  |  |  |     parser.add_argument('-t', '--toolchain', default='gcc', help='Toolchain to use, default is gcc') | 
					
						
							|  |  |  |     parser.add_argument('-s', '--build-system', default='cmake', help='Build system to use, default is cmake') | 
					
						
							| 
									
										
										
										
											2024-05-13 20:27:49 +07:00
										 |  |  |     parser.add_argument('-1', '--one-per-family', action='store_true', default=False, help='Build only one random board inside a family') | 
					
						
							| 
									
										
										
										
											2024-05-10 18:55:43 +07:00
										 |  |  |     args = parser.parse_args() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     families = args.families | 
					
						
							|  |  |  |     boards = args.board | 
					
						
							|  |  |  |     toolchain = args.toolchain | 
					
						
							|  |  |  |     build_system = args.build_system | 
					
						
							| 
									
										
										
										
											2024-05-13 20:27:49 +07:00
										 |  |  |     one_per_family = args.one_per_family | 
					
						
							| 
									
										
										
										
											2024-05-10 18:55:43 +07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if len(families) == 0 and len(boards) == 0: | 
					
						
							| 
									
										
										
										
											2024-05-09 20:43:46 +07:00
										 |  |  |         print("Please specify families or board to build") | 
					
						
							|  |  |  |         return 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     print(build_separator) | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  |     print(build_format.format('Board', 'Example', '\033[39mResult\033[0m', 'Time')) | 
					
						
							| 
									
										
										
										
											2024-05-09 20:43:46 +07:00
										 |  |  |     total_time = time.monotonic() | 
					
						
							| 
									
										
										
										
											2024-05-13 20:27:49 +07:00
										 |  |  |     result = [0, 0, 0] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # build families | 
					
						
							|  |  |  |     all_families = [] | 
					
						
							|  |  |  |     if 'all' in families: | 
					
						
							|  |  |  |         for entry in os.scandir("hw/bsp"): | 
					
						
							|  |  |  |             if entry.is_dir() and entry.name != 'espressif' and os.path.isfile(entry.path + "/family.cmake"): | 
					
						
							|  |  |  |                 all_families.append(entry.name) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         all_families = list(families) | 
					
						
							|  |  |  |     all_families.sort() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  |     # succeeded, failed, skipped | 
					
						
							| 
									
										
										
										
											2024-05-13 20:27:49 +07:00
										 |  |  |     for f in all_families: | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  |         r = build_family(f, toolchain, build_system, one_per_family, boards) | 
					
						
							| 
									
										
										
										
											2024-05-13 20:27:49 +07:00
										 |  |  |         result[0] += r[0] | 
					
						
							|  |  |  |         result[1] += r[1] | 
					
						
							|  |  |  |         result[2] += r[2] | 
					
						
							| 
									
										
										
										
											2024-05-09 20:43:46 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  |     # build boards | 
					
						
							|  |  |  |     r = build_boards_list(boards, toolchain, build_system) | 
					
						
							|  |  |  |     result[0] += r[0] | 
					
						
							|  |  |  |     result[1] += r[1] | 
					
						
							|  |  |  |     result[2] += r[2] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-09 20:43:46 +07:00
										 |  |  |     total_time = time.monotonic() - total_time | 
					
						
							|  |  |  |     print(build_separator) | 
					
						
							| 
									
										
										
										
											2024-08-21 19:09:37 +07:00
										 |  |  |     print(f"Build Summary: {result[0]} {STATUS_OK}, {result[1]} {STATUS_FAILED} and took {total_time:.2f}s") | 
					
						
							| 
									
										
										
										
											2024-05-09 20:43:46 +07:00
										 |  |  |     print(build_separator) | 
					
						
							| 
									
										
										
										
											2024-05-13 20:27:49 +07:00
										 |  |  |     return result[1] | 
					
						
							| 
									
										
										
										
											2024-05-09 20:43:46 +07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     sys.exit(main()) |