From f95039b3babd34d65d2eedf935e20e72d75e4ef8 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 24 Jul 2025 18:33:43 +0100 Subject: [PATCH 01/14] Add --boards-local-txt argument --- build_platform.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/build_platform.py b/build_platform.py index 1a361cf..ec5d5be 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 option to copy boards.local.txt +# to the appropriate package folder after installing the platform +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\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! From 7a10d6d6b26ad81d7299ae20128f82fe8c97076d Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 24 Jul 2025 18:48:31 +0100 Subject: [PATCH 02/14] Add copy step for boards.local.txt --- build_platform.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/build_platform.py b/build_platform.py index ec5d5be..c3d6904 100755 --- a/build_platform.py +++ b/build_platform.py @@ -473,8 +473,30 @@ 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]) + install_platform(core_fqbn, ALL_PLATFORMS[platform]) # take only first two elements + + # Inject boards.local.txt if enabled + if COPY_BOARDS_LOCAL_TXT and boards_local_txt: + try: + # Find platform path via `arduino-cli core list --format json` + import json + output = subprocess.check_output(["arduino-cli", "core", "list", "--format", "json"]).decode() + core_list = json.loads(output) + for core in core_list: + if core["id"] == core_fqbn: + platform_path = core["install_dir"] + 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}") + break + else: + ColorPrint.print_warn(f"Could not find platform path for {core_fqbn} - not copying boards.local.txt") + except Exception as e: + ColorPrint.print_fail(f"Error injecting boards.local.txt for {core_fqbn}: {e}") print('#'*80) + + # Test examples in the platform folder if not IS_LEARNING_SYS: test_examples_in_folder(platform, BUILD_DIR+"/examples") else: From d0c35074c518d14eaac3cb7c855c9eaae4038c6e Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 24 Jul 2025 19:01:54 +0100 Subject: [PATCH 03/14] clean up comments --- build_platform.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build_platform.py b/build_platform.py index c3d6904..d69d29c 100755 --- a/build_platform.py +++ b/build_platform.py @@ -29,8 +29,8 @@ sys.argv.pop(sys.argv.index("--build_timeout") + 1) sys.argv.remove("--build_timeout") -# optional --boards-local-txt option to copy boards.local.txt -# to the appropriate package folder after installing the platform +# 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: @@ -473,10 +473,10 @@ def main(): fqbn = ALL_PLATFORMS[platform][0] print('#'*80) ColorPrint.print_info("SWITCHING TO "+fqbn) - core_fqbn = ":".join(fqbn.split(':', 2)[0:2]) - install_platform(core_fqbn, 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 enabled + # Inject boards.local.txt if requested if COPY_BOARDS_LOCAL_TXT and boards_local_txt: try: # Find platform path via `arduino-cli core list --format json` From 6f2978d54f8712726aae72e0578db3c4f69eb184 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 24 Jul 2025 19:07:21 +0100 Subject: [PATCH 04/14] Correct drill-down to platforms --- build_platform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_platform.py b/build_platform.py index d69d29c..8d8ca32 100755 --- a/build_platform.py +++ b/build_platform.py @@ -482,7 +482,7 @@ def main(): # Find platform path via `arduino-cli core list --format json` import json output = subprocess.check_output(["arduino-cli", "core", "list", "--format", "json"]).decode() - core_list = json.loads(output) + core_list = json.loads(output)["platforms"] for core in core_list: if core["id"] == core_fqbn: platform_path = core["install_dir"] From 7d7d5308f62ae925fefd8c155a6b8573edc672c7 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 24 Jul 2025 19:42:25 +0100 Subject: [PATCH 05/14] Use config dump for data directory + generate platform path --- build_platform.py | 51 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/build_platform.py b/build_platform.py index 8d8ca32..03c1209 100755 --- a/build_platform.py +++ b/build_platform.py @@ -479,19 +479,54 @@ def main(): # Inject boards.local.txt if requested if COPY_BOARDS_LOCAL_TXT and boards_local_txt: try: - # Find platform path via `arduino-cli core list --format json` + # Get arduino-cli data directory import json - output = subprocess.check_output(["arduino-cli", "core", "list", "--format", "json"]).decode() - core_list = json.loads(output)["platforms"] - for core in core_list: - if core["id"] == core_fqbn: - platform_path = core["install_dir"] + 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: + ColorPrint.print_warn("No data directory found in arduino-cli config, using fallback locations.") + # Fallback to common default locations + if os.name == 'nt': # Windows + data_dir = os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Arduino15') + else: # Linux/macOS + data_dir = os.path.join(os.environ.get('HOME', ''), '.arduino15') + 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 + platform_base = os.path.join(data_dir, "packages", vendor, "hardware", architecture) + + # Find the latest version directory + if os.path.exists(platform_base): + versions = [d for d in os.listdir(platform_base) if os.path.isdir(os.path.join(platform_base, d))] + ColorPrint.print_info(f"Found 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}") - break + else: + ColorPrint.print_warn(f"No version directories found in {platform_base}") else: - ColorPrint.print_warn(f"Could not find platform path for {core_fqbn} - not copying boards.local.txt") + 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}") print('#'*80) From b48303f918508c766431b527b1977fdfcbf80978 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 24 Jul 2025 20:08:15 +0100 Subject: [PATCH 06/14] Fallback to expected CI arduino folder /home/runner/Arduino --- build_platform.py | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/build_platform.py b/build_platform.py index 03c1209..5822642 100755 --- a/build_platform.py +++ b/build_platform.py @@ -479,21 +479,30 @@ def main(): # Inject boards.local.txt if requested if COPY_BOARDS_LOCAL_TXT and boards_local_txt: try: + local_app_data_dir = os.environ.get('HOME', '') + data_dir = None + if os.path.exists(os.path.join(local_app_data_dir, 'Arduino')): + data_dir = os.path.join(local_app_data_dir, 'Arduino') + elif 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 - config_output = subprocess.check_output(["arduino-cli", "config", "dump", "--format", "json"]).decode() + 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", "") + data_dir = config.get("directories", {}).get("data", data_dir) if not data_dir: - ColorPrint.print_warn("No data directory found in arduino-cli config, using fallback locations.") - # Fallback to common default locations - if os.name == 'nt': # Windows - data_dir = os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Arduino15') - else: # Linux/macOS - data_dir = os.path.join(os.environ.get('HOME', ''), '.arduino15') + ColorPrint.print_warn("No valid data directory found, cannot copy boards.local.txt") + continue + ColorPrint.print_info(f"Using data directory: {data_dir}") # Parse platform vendor and architecture from core_fqbn (e.g., "adafruit:samd") @@ -507,8 +516,10 @@ def main(): ColorPrint.print_info(f"Using vendor: {vendor}, architecture: {architecture}") - # Construct base platform path - platform_base = os.path.join(data_dir, "packages", vendor, "hardware", 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) # Find the latest version directory if os.path.exists(platform_base): From 909fc32885ebe9130526a874be1baa27313e6e1a Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 24 Jul 2025 20:23:57 +0100 Subject: [PATCH 07/14] Super robust/hacky fallbacks to the correct CI folder of /home/runner/Arduino/hardware/espressif/esp32/ --- build_platform.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/build_platform.py b/build_platform.py index 5822642..b6e6616 100755 --- a/build_platform.py +++ b/build_platform.py @@ -519,8 +519,13 @@ def main(): # 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) - + 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): versions = [d for d in os.listdir(platform_base) if os.path.isdir(os.path.join(platform_base, d))] @@ -533,6 +538,9 @@ def main(): 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}") + elif 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: ColorPrint.print_warn(f"No version directories found in {platform_base}") else: From 66622baed39f16d4e3d1c3bef27c52dc39ba58e3 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 24 Jul 2025 20:43:14 +0100 Subject: [PATCH 08/14] Be more robust about version folders, but check for boards.txt first --- build_platform.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/build_platform.py b/build_platform.py index b6e6616..3b9f54a 100755 --- a/build_platform.py +++ b/build_platform.py @@ -528,21 +528,25 @@ def main(): # Find the latest version directory if os.path.exists(platform_base): - versions = [d for d in os.listdir(platform_base) if os.path.isdir(os.path.join(platform_base, d))] - ColorPrint.print_info(f"Found 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}") - elif os.path.exists(os.path.join(platform_base, "boards.txt")): + 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: - ColorPrint.print_warn(f"No version directories found in {platform_base}") + versions = [d for d in os.listdir(platform_base) if os.path.isdir(os.path.join(platform_base, d))] + ColorPrint.print_info(f"Found versions: {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)] + 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") From a51bab0b56f7ab97921b6d0c681c114f16007cde Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 24 Jul 2025 21:00:03 +0100 Subject: [PATCH 09/14] Refactor to install_boards_local_txt method --- build_platform.py | 159 ++++++++++++++++++++++++---------------------- 1 file changed, 84 insertions(+), 75 deletions(-) diff --git a/build_platform.py b/build_platform.py index 3b9f54a..b047c8b 100755 --- a/build_platform.py +++ b/build_platform.py @@ -40,7 +40,7 @@ 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\n") + 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 @@ -450,6 +450,88 @@ 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, 'Arduino')): + data_dir = os.path.join(local_app_data_dir, 'Arduino') + elif 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", data_dir) + if not data_dir: + 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 versions: {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)] + 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 = [] @@ -478,80 +560,7 @@ def main(): # Inject boards.local.txt if requested if COPY_BOARDS_LOCAL_TXT and boards_local_txt: - try: - local_app_data_dir = os.environ.get('HOME', '') - data_dir = None - if os.path.exists(os.path.join(local_app_data_dir, 'Arduino')): - data_dir = os.path.join(local_app_data_dir, 'Arduino') - elif 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", data_dir) - if not data_dir: - ColorPrint.print_warn("No valid data directory found, cannot copy boards.local.txt") - continue - - 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 versions: {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)] - 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}") + install_boards_local_txt(core_fqbn, boards_local_txt) print('#'*80) # Test examples in the platform folder From ea8dfa1677846f87d8a77b245cc60b3a9bf2aca5 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 24 Jul 2025 22:49:59 +0100 Subject: [PATCH 10/14] Use config folder, but fallback to known path for esp --- build_platform.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/build_platform.py b/build_platform.py index b047c8b..8909688 100755 --- a/build_platform.py +++ b/build_platform.py @@ -459,9 +459,7 @@ def install_boards_local_txt(core_fqbn, boards_local_txt): try: local_app_data_dir = os.environ.get('HOME', '') data_dir = None - if os.path.exists(os.path.join(local_app_data_dir, 'Arduino')): - data_dir = os.path.join(local_app_data_dir, 'Arduino') - elif os.path.exists(os.path.join(local_app_data_dir, '.arduino15')): + 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') @@ -476,10 +474,13 @@ def install_boards_local_txt(core_fqbn, boards_local_txt): 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", data_dir) + data_dir = config.get("directories", {}).get("data", "") if not data_dir: - ColorPrint.print_warn("No valid data directory found, cannot copy boards.local.txt") - return + 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}") @@ -501,8 +502,7 @@ def install_boards_local_txt(core_fqbn, boards_local_txt): 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) + os.path.join(data_dir, "hardware", architecture, architecture) # Find the latest version directory if os.path.exists(platform_base): @@ -511,9 +511,10 @@ def install_boards_local_txt(core_fqbn, 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 versions: {versions}") + 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] From e74ec1640f422064677e8178eb99238c946c9618 Mon Sep 17 00:00:00 2001 From: tyeth Date: Thu, 24 Jul 2025 22:51:59 +0100 Subject: [PATCH 11/14] Support example boards.local.txt and board specific .platform.boards.local.txt --- build_platform.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/build_platform.py b/build_platform.py index 8909688..197f364 100755 --- a/build_platform.py +++ b/build_platform.py @@ -402,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] From 0c51dc01c735b2aa0bab541ebf30addaab76959a Mon Sep 17 00:00:00 2001 From: Tyeth Gundry Date: Mon, 18 Aug 2025 17:19:15 +0100 Subject: [PATCH 12/14] fix(args): better safety around sys.argv access --- build_platform.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/build_platform.py b/build_platform.py index 197f364..5b37dad 100755 --- a/build_platform.py +++ b/build_platform.py @@ -35,7 +35,7 @@ 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): + 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" @@ -43,7 +43,14 @@ 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 + # check second arg is file or another flag: + if sys.argv[sys.argv.index("--boards-local-txt") + 1].startswith("--"): + 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) + # get the boards.local.txt file from the command line (index exists checked earlier) 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) @@ -482,7 +489,7 @@ def install_boards_local_txt(core_fqbn, boards_local_txt): 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: @@ -529,7 +536,7 @@ def install_boards_local_txt(core_fqbn, boards_local_txt): # 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}") @@ -573,7 +580,7 @@ def main(): 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") From 13e4c5177caaa6589815783ff6345794cd8a6fc7 Mon Sep 17 00:00:00 2001 From: Tyeth Gundry Date: Mon, 18 Aug 2025 17:22:50 +0100 Subject: [PATCH 13/14] add(log): say if example specific boards.local.txt not found --- build_platform.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build_platform.py b/build_platform.py index 5b37dad..4f2ef40 100755 --- a/build_platform.py +++ b/build_platform.py @@ -418,6 +418,8 @@ def test_examples_in_folder(platform, folderpath): 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") + else: + ColorPrint.print_info("No example-specific boards.local.txt found to copy, using root version") if BUILD_WARN: if os.path.exists(gen_file_name): From cf23f456c27022f49caa27cf3454ed8f2d4bfdc6 Mon Sep 17 00:00:00 2001 From: Tyeth Gundry Date: Mon, 18 Aug 2025 19:09:24 +0100 Subject: [PATCH 14/14] Update boards.local.txt example specific file logic --- build_platform.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/build_platform.py b/build_platform.py index 4f2ef40..5fb5b1f 100755 --- a/build_platform.py +++ b/build_platform.py @@ -409,17 +409,23 @@ 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 + + # check if root or example-specific 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") - else: + root_boards_local_txt = boards_local_txt + example_boards_local_txt = folderpath+"/boards.local.txt" + board_specific_example_boards_local_txt = folderpath+"/."+platform+".boards.local.txt" + + if os.path.exists(board_specific_example_boards_local_txt): + ColorPrint.print_info("Copying boards.local.txt from "+board_specific_example_boards_local_txt) + install_boards_local_txt(":".join(fqbn.split(':')[0:2]), board_specific_example_boards_local_txt) + elif os.path.exists(example_boards_local_txt): + ColorPrint.print_info("Copying boards.local.txt from "+example_boards_local_txt) + install_boards_local_txt(":".join(fqbn.split(':')[0:2]), example_boards_local_txt) + else: # effectively does the revert if subsequent example doesn't have a specific boards.local.txt ColorPrint.print_info("No example-specific boards.local.txt found to copy, using root version") + install_boards_local_txt(":".join(fqbn.split(':')[0:2]), root_boards_local_txt) + if BUILD_WARN: if os.path.exists(gen_file_name): @@ -475,9 +481,9 @@ def install_boards_local_txt(core_fqbn, boards_local_txt): name, vendor:architecture (e.g., "adafruit:samd"). :param str boards_local_txt: The path to the boards.local.txt file. """ + local_app_data_dir = os.environ.get('HOME', '') + data_dir = None 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')):