Skip to content

Ci wippersnapper boards local txt #217

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 14 commits into
base: ci-wippersnapper
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 138 additions & 1 deletion build_platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,35 @@
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:
# 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)
boards_local_txt = sys.argv[sys.argv.index("--boards-local-txt") + 1]
Comment on lines +47 to +57
Copy link
Member Author

@tyeth tyeth Aug 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

second if should be elif but negated with the line57 body, and first if should confirm there is a period (".") in the argument that follows --boards-local-txt, and finally the error as else body.

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!
Expand Down Expand Up @@ -380,6 +409,24 @@ def test_examples_in_folder(platform, folderpath):
if os.path.exists(gen_file_name):
ColorPrint.print_info("generating")


# check if root or example-specific boards.local.txt should be copied
if COPY_BOARDS_LOCAL_TXT:
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):
cmd = ['arduino-cli', 'compile', '--warnings', 'all', '--fqbn', fqbn, '-e', folderpath]
Expand Down Expand Up @@ -428,6 +475,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.
"""
local_app_data_dir = os.environ.get('HOME', '')
data_dir = None
try:
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 = []
Expand All @@ -451,8 +581,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:
Expand Down