diff --git a/build_platform.py b/build_platform.py index 1a361cf..197f364 100755 --- a/build_platform.py +++ b/build_platform.py @@ -29,6 +29,28 @@ sys.argv.pop(sys.argv.index("--build_timeout") + 1) sys.argv.remove("--build_timeout") +# optional --boards-local-txt argument to copy boards.local.txt +# to the appropriate folder after installing the platform BSP. +COPY_BOARDS_LOCAL_TXT = False +boards_local_txt = None +if "--boards-local-txt" in sys.argv: + COPY_BOARDS_LOCAL_TXT = True + if sys.argv.index("--boards-local-txt") + 1 >= len(sys.argv): + # check if in cwd + if os.path.exists("boards.local.txt"): + boards_local_txt = "boards.local.txt" + else: + sys.stderr.write("Error: --boards-local-txt option requires a path to boards.local.txt file or to be present in current directory\n") + sys.exit(1) + else: + # get the boards.local.txt file from the command line + if not os.path.exists(sys.argv[sys.argv.index("--boards-local-txt") + 1]): + sys.stderr.write("Error: boards.local.txt file does not exist\n") + sys.exit(1) + boards_local_txt = sys.argv[sys.argv.index("--boards-local-txt") + 1] + sys.argv.pop(sys.argv.index("--boards-local-txt") + 1) + sys.argv.remove("--boards-local-txt") + # add user bin to path! BUILD_DIR = '' # add user bin to path! @@ -380,6 +402,16 @@ def test_examples_in_folder(platform, folderpath): if os.path.exists(gen_file_name): ColorPrint.print_info("generating") + # check if boards.local.txt should be copied + if COPY_BOARDS_LOCAL_TXT: + boards_local_txt = folderpath+"/."+platform+".boards.local.txt" + if os.path.exists(boards_local_txt): + ColorPrint.print_info("Copying boards.local.txt from "+boards_local_txt) + install_boards_local_txt(":".join(fqbn.split(':')[0:2]), boards_local_txt) + elif os.path.exists(folderpath+"/boards.local.txt"): + ColorPrint.print_info("Copying boards.local.txt from "+folderpath+"/boards.local.txt") + install_boards_local_txt(":".join(fqbn.split(':')[0:2]), folderpath+"/boards.local.txt") + if BUILD_WARN: if os.path.exists(gen_file_name): cmd = ['arduino-cli', 'compile', '--warnings', 'all', '--fqbn', fqbn, '-e', folderpath] @@ -428,6 +460,89 @@ def test_examples_in_folder(platform, folderpath): success = 1 +def install_boards_local_txt(core_fqbn, boards_local_txt): + """Copies boards.local.txt to the appropriate folder for the given core_fqbn. + :param str core_fqbn: The first two segments of fully qualified board + name, vendor:architecture (e.g., "adafruit:samd"). + :param str boards_local_txt: The path to the boards.local.txt file. + """ + try: + local_app_data_dir = os.environ.get('HOME', '') + data_dir = None + if os.path.exists(os.path.join(local_app_data_dir, '.arduino15')): + data_dir = os.path.join(local_app_data_dir, '.arduino15') + elif os.path.exists(os.path.join(local_app_data_dir, '.arduino')): + data_dir = os.path.join(local_app_data_dir, '.arduino') + + # Get arduino-cli data directory + import json + if data_dir: + config_output = subprocess.check_output(["arduino-cli", "config", "dump", "--format", "json", "--config-dir", data_dir]).decode() + else: + config_output = subprocess.check_output(["arduino-cli", "config", "dump", "--format", "json"]).decode() + config = json.loads(config_output) + ColorPrint.print_info(f"Using arduino-cli config: {config_output.strip()}") + + # Extract data directory, with fallback to default + data_dir = config.get("directories", {}).get("data", "") + if not data_dir: + if os.path.exists(os.path.join(local_app_data_dir, 'Arduino')): + data_dir = os.path.join(local_app_data_dir, 'Arduino') + else: + ColorPrint.print_warn("No valid data directory found, cannot copy boards.local.txt") + return + + ColorPrint.print_info(f"Using data directory: {data_dir}") + + # Parse platform vendor and architecture from core_fqbn (e.g., "adafruit:samd") + parts = core_fqbn.split(':') + if len(parts) >= 2: + vendor = parts[0] + architecture = parts[1] + else: + vendor = core_fqbn + architecture = vendor + + ColorPrint.print_info(f"Using vendor: {vendor}, architecture: {architecture}") + + # Construct base platform path, fall back to architecture if vendor rebadged BSP. + platform_base = os.path.join(data_dir, "packages", vendor, "hardware", architecture) if \ + os.path.exists(os.path.join(data_dir, "packages", vendor, "hardware", architecture)) else \ + os.path.join(data_dir, "packages", architecture, "hardware", architecture) if \ + os.path.exists(os.path.join(data_dir, "packages", architecture, "hardware", architecture)) else \ + os.path.join(data_dir, "hardware", vendor, architecture) if \ + os.path.exists(os.path.join(data_dir, "hardware", vendor, architecture)) else \ + os.path.join(data_dir, "hardware", architecture, architecture) + + # Find the latest version directory + if os.path.exists(platform_base): + if os.path.exists(os.path.join(platform_base, "boards.txt")): + shutil.copyfile(boards_local_txt, os.path.join(platform_base, "boards.local.txt")) + ColorPrint.print_info(f"Copied boards.local.txt to {os.path.join(platform_base, 'boards.local.txt')}") + else: + versions = [d for d in os.listdir(platform_base) if os.path.isdir(os.path.join(platform_base, d))] + ColorPrint.print_info(f"Found subdirectories for {platform_base}:\n {versions}") + # Filter out non-version directories (e.g., 'tools', 'libraries') while supporting 1.0-dev 1.0.0-offline-mode.102 etc + versions = [v for v in versions if re.match(r'^(v)?\d+\.\d+(\.\d+(-\w+)?)?(\.\d+)?$', v)] + ColorPrint.print_info(f"Filtered versions: {versions}") + if versions: + # Sort versions and take the latest (could be improved with proper version sorting) + latest_version = sorted(versions)[-1] + platform_path = os.path.join(platform_base, latest_version) + + dest_path = os.path.join(platform_path, "boards.local.txt") + shutil.copyfile(boards_local_txt, dest_path) + ColorPrint.print_info(f"Copied boards.local.txt to {dest_path}") + + else: + ColorPrint.print_warn(f"No version directories found in {platform_base}") + else: + ColorPrint.print_warn(f"Platform path {platform_base} does not exist") + + except Exception as e: + ColorPrint.print_fail(f"Error injecting boards.local.txt for {core_fqbn}: {e}") + + def main(): # Test platforms platforms = [] @@ -451,8 +566,15 @@ def main(): fqbn = ALL_PLATFORMS[platform][0] print('#'*80) ColorPrint.print_info("SWITCHING TO "+fqbn) - install_platform(":".join(fqbn.split(':', 2)[0:2]), ALL_PLATFORMS[platform]) # take only first two elements + core_fqbn = ":".join(fqbn.split(':', 2)[0:2]) # take only first two elements + install_platform(core_fqbn, ALL_PLATFORMS[platform]) + + # Inject boards.local.txt if requested + if COPY_BOARDS_LOCAL_TXT and boards_local_txt: + install_boards_local_txt(core_fqbn, boards_local_txt) print('#'*80) + + # Test examples in the platform folder if not IS_LEARNING_SYS: test_examples_in_folder(platform, BUILD_DIR+"/examples") else: