* [platform] Install ltchiptool in separate virtual environment * [platform] Fix f-string syntax, set LibreTiny path in ltchiptool * [platform] Fix venv site-packages path * [platform] Fix installing pip without ensurepip * [platform] Install binary dependencies only
194 lines
6.6 KiB
Python
194 lines
6.6 KiB
Python
# Copyright (c) Kuba Szczodrzyński 2022-05-04.
|
|
|
|
import json
|
|
import sys
|
|
from os import makedirs
|
|
from os.path import isdir, join
|
|
from subprocess import PIPE, Popen
|
|
from typing import Dict
|
|
|
|
from ltchiptool import Family, get_version
|
|
from ltchiptool.util.lvm import LVM
|
|
from ltchiptool.util.misc import sizeof
|
|
from platformio.platform.base import PlatformBase
|
|
from platformio.platform.board import PlatformBoardConfig
|
|
from SCons.Script import DefaultEnvironment, Environment
|
|
|
|
env: Environment = DefaultEnvironment()
|
|
|
|
|
|
def read_version(platform_dir: str, version: str):
|
|
if not isdir(join(platform_dir, ".git")):
|
|
sys.stderr.write("Warning! Non-Git installations are NOT SUPPORTED.\n")
|
|
return version
|
|
try:
|
|
p = Popen(
|
|
["git", "rev-parse", "--short", "HEAD"], stdout=PIPE, cwd=platform_dir
|
|
)
|
|
if p.wait() != 0:
|
|
sys.stderr.write(
|
|
f"Warning! Non-zero return code received from Git: {p.returncode}\n"
|
|
)
|
|
return version
|
|
sha = p.stdout.read().decode().strip()
|
|
|
|
p = Popen(["git", "diff", "--quiet"], stdout=PIPE, cwd=platform_dir)
|
|
dirty = p.wait() != 0
|
|
except (FileNotFoundError, IndexError):
|
|
sys.stderr.write(
|
|
"Warning! Git executable not found, or unreadable data received. Cannot read version information.\n"
|
|
)
|
|
return version
|
|
|
|
ids = [
|
|
sha and "sha",
|
|
sha[:7] or None,
|
|
"dirty" if dirty else None,
|
|
]
|
|
build_str = ".".join(filter(None, ids))
|
|
return f"{version}+{build_str}" if build_str else version
|
|
|
|
|
|
def env_configure(
|
|
env: Environment,
|
|
platform: PlatformBase,
|
|
board: PlatformBoardConfig,
|
|
) -> Family:
|
|
# Read external libraries list
|
|
with open(join(platform.get_dir(), "external-libs.json")) as f:
|
|
external_libs = json.load(f)
|
|
# Get Family object for this board
|
|
family = Family.get(short_name=board.get("build.family"))
|
|
# Default environment variables
|
|
env.Replace(
|
|
SDK_DIR=platform.get_package_dir(board.get("package")),
|
|
LT_DIR=platform.get_dir(),
|
|
CORES_DIR=join("${LT_DIR}", "cores"),
|
|
COMMON_DIR=join("${LT_DIR}", "cores", "common"),
|
|
LT_VERSION=read_version(platform.get_dir(), platform.version),
|
|
# Build directories & paths
|
|
VARIANTS_DIR=join("${LT_DIR}", "boards", "variants"),
|
|
FAMILY_DIR=join("${LT_DIR}", "cores", "${FAMILY_NAME}"),
|
|
MISC_DIR=join("${FAMILY_DIR}", "misc"),
|
|
LDSCRIPT_PATH=[board.get("build.ldscript")],
|
|
# Board config variables
|
|
MCU=board.get("build.mcu").upper(),
|
|
MCULC=board.get("build.mcu").lower(),
|
|
VARIANT=board.get("build.variant"),
|
|
# ltchiptool config:
|
|
# -r output raw log messages
|
|
# -i 1 indent log messages
|
|
LTCHIPTOOL='"${LTPYTHONEXE}" -m ltchiptool -r -i 1 -L "${LT_DIR}"',
|
|
# Fix for link2bin to get tmpfile name in argv
|
|
LINKCOM="${LINK} ${LINKARGS}",
|
|
LINKARGS="${TEMPFILE('-o $TARGET $LINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS', '$LINKCOMSTR')}",
|
|
# Store the family object
|
|
FAMILY_OBJ=family,
|
|
EXTERNAL_LIBS=external_libs,
|
|
)
|
|
# Store family parameters as environment variables
|
|
env.Replace(**dict(family))
|
|
# Set platform directory in ltchiptool (for use in this process only)
|
|
LVM.add_path(platform.get_dir())
|
|
return family
|
|
|
|
|
|
def env_print_info(
|
|
env: Environment,
|
|
platform: PlatformBase,
|
|
board: PlatformBoardConfig,
|
|
):
|
|
TAB = " " * 4
|
|
|
|
def dump(k, v, indent=""):
|
|
k = k.replace("#", ".")
|
|
if isinstance(v, dict):
|
|
print(f"{indent} - {k}:")
|
|
for k, v in sorted(v.items()):
|
|
dump(k, v, indent + TAB)
|
|
elif isinstance(v, list):
|
|
print(f"{indent} - {k}:")
|
|
for k, v in enumerate(v):
|
|
dump(k, v, indent + TAB)
|
|
else:
|
|
print(f"{indent} - {k} = {v}")
|
|
|
|
# Print information about installed core versions
|
|
print("PLATFORM VERSIONS:")
|
|
print(" - libretiny @", env["LT_VERSION"])
|
|
print(" - ltchiptool @", get_version())
|
|
# Print custom platformio.ini options
|
|
if platform.custom_opts:
|
|
print("CUSTOM OPTIONS:")
|
|
for k, v in sorted(platform.custom_opts.items()):
|
|
dump(k, v)
|
|
# Print custom flash layout
|
|
if env.get("FLASH_IS_CUSTOM", False):
|
|
print("CUSTOM FLASH LAYOUT:")
|
|
for name, layout in board.get("flash").items():
|
|
(_, _, length) = v.partition("+")
|
|
length = int(length, 16)
|
|
print(f" - {name}: {layout} ({sizeof(length)})")
|
|
|
|
|
|
def env_parse_custom_options(env: Environment, platform: PlatformBase):
|
|
opts: dict = platform.custom_opts.get("options", None)
|
|
if not opts:
|
|
return
|
|
headers = {
|
|
"lwip": "lwipopts.h",
|
|
"freertos": "FreeRTOSConfig.h",
|
|
}
|
|
for header, options in list(opts.items()):
|
|
if not isinstance(options, str):
|
|
raise TypeError("Options value should be str")
|
|
options = options.strip().splitlines()
|
|
opts_dict = {}
|
|
for line in options:
|
|
if "=" not in line:
|
|
raise ValueError(f"Invalid option: {line}")
|
|
k, _, v = line.partition("=")
|
|
k = k.strip()
|
|
v = v.strip()
|
|
opts_dict[k] = v
|
|
# replace predefined header names
|
|
opts.pop(header)
|
|
header = headers.get(header, header)
|
|
header = header.replace(".", "#")
|
|
opts[header] = opts_dict
|
|
|
|
|
|
def env_apply_custom_options(env: Environment, platform: PlatformBase):
|
|
opts = platform.custom_opts.get("options", None)
|
|
if not opts:
|
|
return
|
|
header_dir = join("${BUILD_DIR}", "include")
|
|
real_dir = env.subst(header_dir)
|
|
makedirs(real_dir, exist_ok=True)
|
|
|
|
for header, options in opts.items():
|
|
header: str
|
|
options: Dict[str, str]
|
|
# open the header file for writing
|
|
header = header.replace("#", ".")
|
|
f = open(join(real_dir, header), "w")
|
|
f.write(f'#include_next "{header}"\n' "\n" "#pragma once\n" "\n")
|
|
# write all #defines
|
|
for k, v in options.items():
|
|
f.write(
|
|
f"// {k} = {v}\n"
|
|
f"#ifdef {k}\n"
|
|
f"#undef {k}\n"
|
|
f"#endif\n"
|
|
f"#define {k} {v}\n"
|
|
)
|
|
f.close()
|
|
# prepend newly created headers before any other
|
|
env.Prepend(CPPPATH=[header_dir])
|
|
|
|
|
|
env.AddMethod(env_configure, "ConfigureEnvironment")
|
|
env.AddMethod(env_print_info, "PrintInfo")
|
|
env.AddMethod(env_parse_custom_options, "ParseCustomOptions")
|
|
env.AddMethod(env_apply_custom_options, "ApplyCustomOptions")
|