From 72ab64461cf74fb1aab30d88871783c15a45dbc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Tue, 28 Feb 2023 21:14:23 +0100 Subject: [PATCH] [builder] Add library queue to manage cloned environments --- builder/family/beken-72xx.py | 51 ++++---- builder/frameworks/arduino.py | 98 +++++++------- builder/frameworks/base.py | 95 +++++++------- builder/main.py | 9 +- builder/utils/cores.py | 63 +++++---- builder/utils/flash.py | 12 +- builder/utils/libs-external.py | 7 +- builder/utils/libs-queue.py | 210 ++++++++++++++++++++++++++++++ builder/utils/libs.py | 115 ---------------- cores/beken-72xx/base/lt_family.h | 2 + 10 files changed, 383 insertions(+), 279 deletions(-) create mode 100644 builder/utils/libs-queue.py delete mode 100644 builder/utils/libs.py diff --git a/builder/family/beken-72xx.py b/builder/family/beken-72xx.py index 458faa9..1976cb0 100644 --- a/builder/family/beken-72xx.py +++ b/builder/family/beken-72xx.py @@ -7,6 +7,8 @@ from SCons.Script import DefaultEnvironment, Environment env: Environment = DefaultEnvironment() board: PlatformBoardConfig = env.BoardConfig() +queue = env.AddLibraryQueue("beken-72xx") +env.ConfigureFamily() ROOT_DIR = join("$SDK_DIR", "beken378") APP_DIR = join(ROOT_DIR, "app") @@ -36,7 +38,7 @@ SOC = env.Cfg("CFG_SOC_NAME") WPA_VERSION = "wpa_supplicant_2_9" if env.Cfg("CFG_USE_WPA_29") else "hostapd-2.5" # Flags -env.Append( +queue.AppendPublic( CCFLAGS=[ "-mcpu=arm968e-s", "-march=armv5te", @@ -119,8 +121,9 @@ srcs_core = [] # Fix for BK7231T's bootloader compatibility if board.get("build.bkboot_version") == "1.0.5-bk7231s": - env.Append(CPPDEFINES=[("CFG_SUPPORT_BOOTLOADER", "1")]) - env.AddLibrary( + # this has to be public, so that fixups/intc.c sees it + queue.AppendPublic(CPPDEFINES=[("CFG_SUPPORT_BOOTLOADER", "1")]) + queue.AddLibrary( name="bdk_boot", base_dir="$FAMILY_DIR/base/fixups", srcs=["+"], @@ -129,7 +132,7 @@ else: srcs_core.append("+") # Sources - from framework-beken-bdk/beken378/beken_src.mk -env.AddLibrary( +queue.AddLibrary( name="bdk_core", base_dir=ROOT_DIR, srcs=[ @@ -151,7 +154,7 @@ env.AddLibrary( ) # Sources - app module -env.AddLibrary( +queue.AddLibrary( name="bdk_app", base_dir=APP_DIR, srcs=[ @@ -171,7 +174,7 @@ env.AddLibrary( ) # Sources - drivers -env.AddLibrary( +queue.AddLibrary( name="bdk_driver", base_dir=DRIVER_DIR, srcs=[ @@ -228,7 +231,7 @@ env.AddLibrary( ) # Sources - functional components -env.AddLibrary( +queue.AddLibrary( name="bdk_func", base_dir=FUNC_DIR, srcs=[ @@ -292,7 +295,7 @@ env.AddLibrary( ) # Sources - FreeRTOS -env.AddLibrary( +queue.AddLibrary( name="bdk_freertos_thumb", base_dir=ROOT_DIR, srcs=[ @@ -302,7 +305,7 @@ env.AddLibrary( "+", ], ) -env.AddLibrary( +queue.AddLibrary( name="bdk_freertos_arm", base_dir="$SDK_DIR", srcs=[ @@ -322,10 +325,10 @@ env.AddLibrary( ) # Sources - lwIP -env.AddExternalLibrary("lwip", port="bdk") +queue.AddExternalLibrary("lwip", port="bdk") # Sources - mbedTLS 2.6.0 -env.AddLibrary( +queue.AddLibrary( name="bdk_mbedtls", base_dir=join(FUNC_DIR, "mbedtls"), srcs=[ @@ -349,7 +352,7 @@ env.AddLibrary( # Sources - chip-specific drivers if SOC in [SOC_BK7231U, SOC_BK7251]: - env.AddLibrary( + queue.AddLibrary( name="bdk_driver_spi", base_dir=join(DRIVER_DIR, "spi"), srcs=[ @@ -359,7 +362,7 @@ if SOC in [SOC_BK7231U, SOC_BK7251]: ], ) if SOC in [SOC_BK7231N]: - env.AddLibrary( + queue.AddLibrary( name="bdk_driver_spi", base_dir=join(DRIVER_DIR, "spi"), srcs=[ @@ -369,7 +372,7 @@ if SOC in [SOC_BK7231N]: ], ) if SOC in [SOC_BK7251]: - env.AddLibrary( + queue.AddLibrary( name="bdk_bk7251", base_dir=ROOT_DIR, srcs=[ @@ -386,7 +389,7 @@ if SOC in [SOC_BK7251]: # Sources - enabled through config if env.Cfg("CFG_SDIO"): - env.AddLibrary( + queue.AddLibrary( name="bdk_driver_sdio", base_dir=ROOT_DIR, srcs=[ @@ -395,7 +398,7 @@ if env.Cfg("CFG_SDIO"): ], ) if env.Cfg("CFG_BK_AWARE"): - env.AddLibrary( + queue.AddLibrary( name="bdk_driver_sdio", base_dir="$SDK_DIR", srcs=[ @@ -407,7 +410,7 @@ if env.Cfg("CFG_BK_AWARE"): ], ) if env.Cfg("CFG_USE_SDCARD_HOST"): - env.AddLibrary( + queue.AddLibrary( name="bdk_func_fatfs", base_dir=join(FUNC_DIR, "fatfs"), srcs=[ @@ -419,7 +422,7 @@ if env.Cfg("CFG_USE_SDCARD_HOST"): ], ) if env.Cfg("CFG_WPA3"): - env.AddLibrary( + queue.AddLibrary( name="bdk_wolfssl", base_dir=join(FUNC_DIR, "wolfssl"), srcs=[ @@ -437,7 +440,7 @@ if env.Cfg("CFG_WPA3"): if env.Cfg("CFG_SUPPORT_BLE") and env.Cfg("CFG_BLE_VERSION") == env.Cfg( "BLE_VERSION_4_2" ): - env.AddLibrary( + queue.AddLibrary( name="bdk_ble_4_2", base_dir=join(DRIVER_DIR, "ble"), srcs=[ @@ -457,7 +460,7 @@ if env.Cfg("CFG_SUPPORT_BLE") and env.Cfg("CFG_BLE_VERSION") == env.Cfg( if env.Cfg("CFG_SUPPORT_BLE") and env.Cfg("CFG_BLE_VERSION") == env.Cfg( "BLE_VERSION_5_x" ): - env.AddLibrary( + queue.AddLibrary( name="bdk_ble_5_x", base_dir=join(DRIVER_DIR, "ble_5_x_rw"), srcs=[ @@ -479,7 +482,7 @@ if env.Cfg("CFG_SUPPORT_BLE") and env.Cfg("CFG_BLE_VERSION") == env.Cfg( ], ) if env.Cfg("ATSVR_CFG"): - env.AddLibrary( + queue.AddLibrary( name="bdk_atsvr", base_dir=join(FUNC_DIR, "at_server"), srcs=[ @@ -491,7 +494,7 @@ if env.Cfg("ATSVR_CFG"): ], ) if env.Cfg("CFG_USB") or env.Cfg("CFG_USE_SDCARD_HOST"): - env.AddLibrary( + queue.AddLibrary( name="bdk_driver_usb", base_dir=ROOT_DIR, srcs=[ @@ -504,7 +507,7 @@ if env.Cfg("CFG_USB") or env.Cfg("CFG_USE_SDCARD_HOST"): ) # Libs & linker config -env.Append( +queue.AppendPublic( LIBPATH=[ join("$SDK_DIR", "beken378", "lib"), join("$SDK_DIR", "beken378", "func", "airkiss"), @@ -549,7 +552,7 @@ rbl_offs = to_offset(app_size) - 102 env.Replace(FLASH_RBL_OFFSET=f"0x{app_offs + rbl_offs:06X}") # Build all libraries -env.BuildLibraries() +queue.BuildLibraries() # Main firmware outputs and actions env.Replace( diff --git a/builder/frameworks/arduino.py b/builder/frameworks/arduino.py index fa7874d..b9dbdbb 100644 --- a/builder/frameworks/arduino.py +++ b/builder/frameworks/arduino.py @@ -9,17 +9,67 @@ from SCons.Script import DefaultEnvironment, Environment # Let everyone know we're using the Arduino framework env: Environment = DefaultEnvironment() env["ARDUINO"] = True +family: Family = env["FAMILY_OBJ"] # Add base cores' sources first env.SConscript("base.py") +# Build a safe environment for this script +queue = env.AddLibraryQueue("arduino", prepend_includes=True) + +# Add sources common among all families +env.AddCoreSources( + queue=queue, + name="common_arduino", + path=join("$COMMON_DIR", "arduino", "src"), +) +env.AddArduinoLibraries( + queue=queue, + name="common_arduino", + path=join("$COMMON_DIR", "arduino", "libraries"), +) +# Add sources for this family and each parent +found = False +for f in family.inheritance: + code = f"{f.code}_arduino" + path = join("$CORES_DIR", f.name, "arduino") + + found = found or env.AddCoreSources(queue, name=code, path=join(path, "src")) + env.AddArduinoLibraries(queue, name=code, path=join(path, "libraries")) + + if f.short_name: + env.Prepend(CPPDEFINES=[(f"ARDUINO_ARCH_{f.short_name}", "1")]) + if f.code: + env.Prepend(CPPDEFINES=[(f"ARDUINO_ARCH_{f.code.upper()}", "1")]) + +# Fail if Arduino core wasn't found +if not found: + click.secho( + f"Platform '{family.name}' doesn't support Arduino framework - " + "the Arduino core source files are absent.", + fg="red", + ) + exit(1) + +# Sources - ArduinoCore-API +queue.AddExternalLibrary("arduino_api") + +# Sources - board variant +queue.AddLibrary( + name="board_${VARIANT}", + base_dir="$BOARD_DIR", + srcs=[ + "+", + ], + # not adding includes since they're added with the base core +) + # Flags & linker options -env.Append( +queue.AppendPublic( CPPDEFINES=[ ("LIBRETUYA_ARDUINO", 1), ("ARDUINO", 10812), ("ARDUINO_SDK", 1), - ("ARDUINO_ARCH_${FAMILY_CODE}", 1), ], LINKFLAGS=[ "--specs=nosys.specs", @@ -35,47 +85,5 @@ env.Append( ], ) -family: Family = env["FAMILY_OBJ"] - -# Add common sources among all families -env.AddCoreSources( - name="common_arduino", - path=join("$COMMON_DIR", "arduino", "src"), -) -env.AddArduinoLibraries( - name="common_arduino", - path=join("$COMMON_DIR", "arduino", "libraries"), -) -# Add sources for this family and each parent -found = False -for f in family.inheritance: - code = f"{f.code}_arduino" - path = join("$CORES_DIR", f.name, "arduino") - # Add libraries first, to put the include paths after core sources - env.AddArduinoLibraries(name=code, path=join(path, "libraries")) - found = found or env.AddCoreSources(name=code, path=join(path, "src")) - -# Fail if Arduino core wasn't found -if not found: - click.secho( - f"Platform '{family.name}' doesn't support Arduino framework - " - "the Arduino core source files are absent.", - fg="red", - ) - exit(1) - -# Sources - ArduinoCore-API -env.AddExternalLibrary("arduino_api") - -# Sources - board variant -env.AddLibrary( - name="board_${VARIANT}", - base_dir="$BOARD_DIR", - srcs=[ - "+", - ], - # not adding includes since they're added in base.py -) - # Build all libraries -env.BuildLibraries() +queue.BuildLibraries() diff --git a/builder/frameworks/base.py b/builder/frameworks/base.py index e97fe23..7423ae3 100644 --- a/builder/frameworks/base.py +++ b/builder/frameworks/base.py @@ -12,53 +12,10 @@ from SCons.Script import DefaultEnvironment, Environment env: Environment = DefaultEnvironment() board: PlatformBoardConfig = env.BoardConfig() platform: PlatformBase = env.PioPlatform() - -# Environment variables, include paths, etc. -family: Family = env.ConfigureEnvironment(platform, board) -# Flash layout defines -env.AddFlashLayout(board) - -# Flags & linker options -env.Append( - CFLAGS=[ - "-Werror=implicit-function-declaration", - ], - CPPDEFINES=[ - ("LIBRETUYA", 1), - ("LT_VERSION", env.ReadLTVersion(platform.get_dir(), platform.version)), - ("LT_BOARD", "${VARIANT}"), - ("F_CPU", board.get("build.f_cpu")), - ("MCU", "${MCU}"), - ("FAMILY", "F_${FAMILY}"), - ], - CPPPATH=[ - "$BOARD_DIR", - ], - LINKFLAGS=[ - '"-Wl,-Map=' + join("$BUILD_DIR", "${PROGNAME}.map") + '"', - ], - LIBS=[ - "stdc++", - "supc++", - ], -) - -# Build a core list to add sources, flags, etc. -cores = { - "common": "$COMMON_DIR", -} -# Configure each family first (add CPP defines) -for f in family.inheritance: - cores[f.code] = env.AddFamily(f) - -# Add fixups & config for each core -for name, path in cores.items(): - env.AddCoreConfig(path=join(path, "base")) - if "ARDUINO" in env: - env.AddCoreConfig(path=join(path, "arduino", "src")) +family: Family = env["FAMILY_OBJ"] # Include SDK builder scripts -# The script will call BuildLibraries(safe=True) to secure the include paths +# No environment options that follow later will be considered found = False for f in family.inheritance: try: @@ -75,14 +32,50 @@ if not found: ) exit(1) +# Build a safe environment for this script +queue = env.AddLibraryQueue("base", prepend_includes=True) # Add sources & include paths for each core -for name, path in cores.items(): - env.AddCoreSources(name=name, path=join(path, "base")) +env.AddCoreSources(queue, name="common", path=join("$COMMON_DIR", "base")) +for f in family.inheritance: + env.AddCoreSources(queue, name=f.code, path=join("$CORES_DIR", f.name, "base")) + + if f.short_name: + env.Prepend(CPPDEFINES=[(f"LT_{f.short_name}", "1")]) + if f.code: + env.Prepend(CPPDEFINES=[(f"LT_{f.code.upper()}", "1")]) + env.Prepend(LIBPATH=[join("$CORES_DIR", f.name, "misc")]) # Sources - external libraries -env.AddExternalLibrary("ltchiptool") # uf2ota source code -env.AddExternalLibrary("flashdb") -env.AddExternalLibrary("printf") +queue.AddExternalLibrary("ltchiptool") # uf2ota source code +queue.AddExternalLibrary("flashdb") +queue.AddExternalLibrary("printf") + +# Flags & linker options +queue.AppendPublic( + CFLAGS=[ + "-Werror=implicit-function-declaration", + ], + CPPDEFINES=[ + ("LIBRETUYA", 1), + ("LT_VERSION", env.ReadLTVersion(platform.get_dir(), platform.version)), + ("LT_BOARD", "${VARIANT}"), + ("F_CPU", board.get("build.f_cpu")), + ("MCU", "${MCU}"), + ("FAMILY", "F_${FAMILY}"), + # Add flash layout defines created in env.AddFlashLayout() + *env["FLASH_DEFINES"].items(), + ], + CPPPATH=[ + "$BOARD_DIR", + ], + LINKFLAGS=[ + '"-Wl,-Map=' + join("$BUILD_DIR", "${PROGNAME}.map") + '"', + ], + LIBS=[ + "stdc++", + "supc++", + ], +) # Build everything from the base core -env.BuildLibraries() +queue.BuildLibraries() diff --git a/builder/main.py b/builder/main.py index 348c571..0108c7b 100644 --- a/builder/main.py +++ b/builder/main.py @@ -2,10 +2,12 @@ import sys +from platformio.platform.base import PlatformBase from platformio.platform.board import PlatformBoardConfig from SCons.Script import Default, DefaultEnvironment, Environment env: Environment = DefaultEnvironment() +platform: PlatformBase = env.PioPlatform() board: PlatformBoardConfig = env.BoardConfig() # Utilities @@ -14,7 +16,7 @@ env.SConscript("utils/cores.py", exports="env") env.SConscript("utils/env.py", exports="env") env.SConscript("utils/flash.py", exports="env") env.SConscript("utils/libs-external.py", exports="env") -env.SConscript("utils/libs.py", exports="env") +env.SConscript("utils/libs-queue.py", exports="env") env.SConscript("utils/ltchiptool.py", exports="env") # Firmware name @@ -38,6 +40,11 @@ env.Replace( SIZETOOL=prefix + "size", ) +# Environment variables, include paths, etc. +env.ConfigureEnvironment(platform, board) +# Flash layout defines +env.AddFlashLayout(board) + # Family builders details: # - call env.AddLibrary("lib name", "base dir", [sources]) to add lib sources # - call env.BuildLibraries() to build lib targets with safe envs diff --git a/builder/utils/cores.py b/builder/utils/cores.py index 3ecaab3..d16c924 100644 --- a/builder/utils/cores.py +++ b/builder/utils/cores.py @@ -8,44 +8,40 @@ from SCons.Script import DefaultEnvironment, Environment env: Environment = DefaultEnvironment() -def env_add_family(env: Environment, family: Family) -> str: - if family.short_name: - env.Prepend(CPPDEFINES=[(f"LT_{family.short_name}", "1")]) - if family.code: - env.Prepend(CPPDEFINES=[(f"LT_{family.code.upper()}", "1")]) - path = join("$CORES_DIR", family.name) - if not isdir(env.subst(path)): - return path - env.Prepend( - LIBPATH=[join(path, "misc")], - ) - return path - - -def env_add_core_config(env: Environment, path: str) -> bool: - if not isdir(env.subst(path)): - return False +def env_configure_family(env: Environment): env.Prepend( CPPPATH=[ - join(path, "compat"), - join(path, "config"), - join(path, "fixups"), - ], - LIBPATH=[ - join(path, "fixups"), + join("$COMMON_DIR", "base", "fixups"), + join("$COMMON_DIR", "base", "config"), + join("$COMMON_DIR", "base", "compat"), ], ) + + family: Family = env["FAMILY_OBJ"] + for f in family.inheritance: + path = join("$CORES_DIR", f.name, "base") + if not isdir(env.subst(path)): + continue + env.Prepend( + CPPPATH=[ + join(path, "fixups"), + join(path, "config"), + join(path, "compat"), + ], + LIBPATH=[ + join(path, "fixups"), + ], + ) + + +def env_add_core_sources(env: Environment, queue, name: str, path: str) -> bool: + if not isdir(env.subst(path)): + return False try: env.LoadDefines(join(path, "lt_defs.h")) except FileNotFoundError: pass - return True - - -def env_add_core_sources(env: Environment, name: str, path: str) -> bool: - if not isdir(env.subst(path)): - return False - env.AddLibrary( + queue.AddLibrary( name=f"core_{name}", base_dir=path, srcs=[ @@ -69,10 +65,10 @@ def env_add_core_sources(env: Environment, name: str, path: str) -> bool: return True -def env_add_arduino_libraries(env: Environment, name: str, path: str) -> bool: +def env_add_arduino_libraries(env: Environment, queue, name: str, path: str) -> bool: if not isdir(env.subst(path)): return False - env.AddLibrary( + queue.AddLibrary( name=f"core_{name}_libraries", base_dir=path, srcs=[ @@ -85,7 +81,6 @@ def env_add_arduino_libraries(env: Environment, name: str, path: str) -> bool: return True -env.AddMethod(env_add_family, "AddFamily") -env.AddMethod(env_add_core_config, "AddCoreConfig") +env.AddMethod(env_configure_family, "ConfigureFamily") env.AddMethod(env_add_core_sources, "AddCoreSources") env.AddMethod(env_add_arduino_libraries, "AddArduinoLibraries") diff --git a/builder/utils/flash.py b/builder/utils/flash.py index 6d50fad..a25c63f 100644 --- a/builder/utils/flash.py +++ b/builder/utils/flash.py @@ -12,22 +12,22 @@ def env_add_flash_layout(env: Environment, board): flash_size = 0 fal_items = "" # add "root" partition - fal_items += "FAL_PART_TABLE_ITEM(root, ROOT)" + fal_items += "FAL_PART_TABLE_ITEM(root,ROOT)" # add all partitions for name, layout in flash_layout.items(): name = name.upper() (offset, _, length) = layout.partition("+") defines[f"FLASH_{name}_OFFSET"] = offset defines[f"FLASH_{name}_LENGTH"] = length - fal_items += f"FAL_PART_TABLE_ITEM({name.lower()}, {name})" + fal_items += f"FAL_PART_TABLE_ITEM({name.lower()},{name})" flash_size = max(flash_size, int(offset, 16) + int(length, 16)) - defines["FLASH_LENGTH"] = flash_size + defines["FLASH_LENGTH"] = f"0x{flash_size:06X}" # for "root" partition - defines["FLASH_ROOT_OFFSET"] = 0 - defines["FLASH_ROOT_LENGTH"] = flash_size + defines["FLASH_ROOT_OFFSET"] = "0x000000" + defines["FLASH_ROOT_LENGTH"] = f"0x{flash_size:06X}" # add partition table array defines["FAL_PART_TABLE"] = "{" + fal_items + "}" - env.Append(CPPDEFINES=list(defines.items())) + env.Replace(FLASH_DEFINES=defines) env.Replace(**defines) diff --git a/builder/utils/libs-external.py b/builder/utils/libs-external.py index 0c8adbe..e91f951 100644 --- a/builder/utils/libs-external.py +++ b/builder/utils/libs-external.py @@ -12,7 +12,7 @@ platform: PlatformBase = env.PioPlatform() @dataclass -class Library: +class ExternalLibrary: package: str sources: List[str] includes: List[str] @@ -28,13 +28,14 @@ class Library: def env_add_external_library( env: Environment, + queue, name: str, port: Optional[str] = None, ): if port: name += f"-{port}" external_libs = env["EXTERNAL_LIBS"] - lib = Library(**external_libs[name]) + lib = ExternalLibrary(**external_libs[name]) version = platform.versions.get(lib.package, None) package: PackageItem = platform.pm.get_package( @@ -45,7 +46,7 @@ def env_add_external_library( f"Version '{version}' of library '{name}' ({lib.package}) is not installed" ) - env.AddLibrary( + queue.AddLibrary( name=name.replace("-", "_"), base_dir=package.path, srcs=lib.sources, diff --git a/builder/utils/libs-queue.py b/builder/utils/libs-queue.py new file mode 100644 index 0000000..99cfda4 --- /dev/null +++ b/builder/utils/libs-queue.py @@ -0,0 +1,210 @@ +# Copyright (c) Kuba SzczodrzyƄski 2023-02-28. + +import fnmatch +from dataclasses import InitVar, dataclass, field +from glob import glob +from os.path import isdir, join +from typing import Dict, Generator, List, Optional, Tuple + +from ltchiptool.util.dict import merge_dicts +from SCons.Script import DefaultEnvironment, Environment + +env: Environment = DefaultEnvironment() + + +def add_base_dir( + env: Environment, + base_dir: str, + expressions: List[str], + subst: bool = False, +): + out = [] + for expr in expressions: + if expr == False: + # support '[cond] and [path]' logical expressions + continue + if expr[1] != "<" or expr[-1] != ">": + raise ValueError(f"Not a valid glob: {expr}") + if expr[2] == "$": + # do not append base path + path = expr[2:-1] + else: + path = join(base_dir, expr[2:-1]) + if subst: + path = env.subst(path) + out.append(expr[0] + "<" + path + ">") + return out + + +def iter_expressions(expressions: List[str]) -> Generator[Tuple[str, str], None, None]: + for expr in expressions: + if expr[1:2] != "<" or expr[-1:] != ">": + yield ("+", expr) + continue + yield (expr[0], expr[2:-1]) + + +def apply_options(env: Environment, options: Dict[str, List[str]]): + non_expr_keys = ["CPPDEFINES"] + for key, values in options.items(): + if not values: + continue + if key in non_expr_keys or not isinstance(values[0], str): + env.Append(**{key: values}) + continue + for dir, value in iter_expressions(values): + if dir == "+": + env.Append(**{key: [value]}) + elif dir == "!": + env.Prepend(**{key: [value]}) + elif dir == "-": + if value not in env[key]: + raise ValueError(f"Invalid option; {value} is not in {key}") + env[key].remove(value) + + +@dataclass +class Library: + env: InitVar[Environment] + name: str + base_dir: str + srcs: List[str] + includes: List[str] = field(default_factory=lambda: []) + options: Dict[str, List[str]] = field(default_factory=lambda: {}) + + def __post_init__(self, env: Environment): + # add base dir to all globs + self.srcs = add_base_dir(env, self.base_dir, self.srcs) + self.includes = add_base_dir(env, self.base_dir, self.includes, subst=True) + + +class LibraryQueue: + env: Environment + name: str + queue: List[Library] + includes: List[str] + options_public: dict + options_private: dict + prepend_includes: bool + built: bool = False + + def __init__( + self, + env: Environment, + name: str, + prepend_includes: bool = False, + ) -> None: + self.env = env + self.name = name + self.queue = [] + self.includes = [] + self.options_public = {} + self.options_private = {} + self.prepend_includes = prepend_includes + + def AddLibrary(self, **kwargs): + lib = Library(env=self.env, **kwargs) + # search all include paths + for dir, expr in iter_expressions(lib.includes): + if dir == "-": + for item in fnmatch.filter(self.includes, expr): + if item in self.includes: + self.includes.remove(item) + continue + for item in glob(expr, recursive=True): + if not isdir(item): + continue + if dir == "!": + self.includes.insert(0, item) + else: + self.includes.append(item) + self.queue.append(lib) + + def AddExternalLibrary(self, name: str, port: Optional[str] = None): + return self.env.AddExternalLibrary(self, name, port) + + def AppendPublic(self, **kwargs): + if "CPPPATH" in kwargs: + self.includes += kwargs["CPPPATH"] + kwargs.pop("CPPPATH") + self.options_public = merge_dicts(self.options_public, kwargs) + + def AppendPrivate(self, **kwargs): + public_only = ["CPPPATH", "LIBPATH", "LIBS", "LINKFLAGS"] + if any(key in public_only for key in kwargs.keys()): + raise ValueError("Cannot set these as private options") + self.options_private = merge_dicts(self.options_private, kwargs) + + def Print(self): + def print_list(items): + print( + "\n".join( + f"{i+1: 4d}. {self.env.subst(str(item))}" + for i, item in enumerate(items) + ) + ) + + print() + print(f"Library Queue - {self.name}") + print("Environment paths:") + print_list(self.env["CPPPATH"]) + print( + "Include paths (%s):" % ("prepend" if self.prepend_includes else "append") + ) + print_list(self.includes) + print("Environment options:") + opts = ["CFLAGS", "CCFLAGS", "CXXFLAGS", "CPPDEFINES", "ASFLAGS"] + opts = {k: v for k, v in self.env.items() if k in opts} + print_list(opts.items()) + print("Options - public:") + print_list(self.options_public.items()) + print("Options - private:") + print_list(self.options_private.items()) + print("Libraries:") + print_list(self.queue) + + def BuildLibraries(self): + if self.built: + raise RuntimeError("Cannot build a library queue twice") + self.Print() + + # add public options to the environment + apply_options(self.env, self.options_public) + # treat all include paths as public + if self.prepend_includes: + self.env.Prepend(CPPPATH=self.includes) + else: + self.env.Append(CPPPATH=self.includes) + + # clone the environment for the whole library queue + queue_env = self.env.Clone() + # add private options to the cloned environment + apply_options(queue_env, self.options_private) + + for lib in self.queue: + if lib.options: + # clone the environment separately for each library + lib_env = queue_env.Clone() + # add library-scoped options + apply_options(lib_env, lib.options) + else: + # no library-scoped options, just use the base env + lib_env = queue_env + # build library with (name, base_dir, sources) options + target = lib_env.BuildLibrary( + join("$BUILD_DIR", lib.name), lib.base_dir, lib.srcs + ) + self.env.Prepend(LIBS=[target]) + + self.built = True + + +def env_add_library_queue( + env: Environment, + name: str, + prepend_includes: bool = False, +) -> LibraryQueue: + return LibraryQueue(env, name, prepend_includes) + + +env.AddMethod(env_add_library_queue, "AddLibraryQueue") diff --git a/builder/utils/libs.py b/builder/utils/libs.py deleted file mode 100644 index 77e32f7..0000000 --- a/builder/utils/libs.py +++ /dev/null @@ -1,115 +0,0 @@ -# Copyright (c) Kuba SzczodrzyƄski 2022-05-04. - -import fnmatch -from glob import glob -from os.path import isdir, join -from typing import Dict, Generator, List, Tuple - -from SCons.Script import DefaultEnvironment, Environment - -env: Environment = DefaultEnvironment() - - -def add_base_dir( - env: Environment, - base_dir: str, - expressions: List[str], - subst: bool = False, -): - out = [] - for expr in expressions: - if expr == False: - # support '[cond] and [path]' logical expressions - continue - if expr[1] != "<" or expr[-1] != ">": - raise ValueError(f"Not a valid glob: {expr}") - if expr[2] == "$": - # do not append base path - path = expr[2:-1] - else: - path = join(base_dir, expr[2:-1]) - if subst: - path = env.subst(path) - out.append(expr[0] + "<" + path + ">") - return out - - -def iter_expressions(expressions: List[str]) -> Generator[Tuple[str, str], None, None]: - for expr in expressions: - if expr[1] != "<" or expr[-1] != ">": - yield ("+", expr) - continue - yield (expr[0], expr[2:-1]) - - -def env_add_library( - env: Environment, - name: str, - base_dir: str, - srcs: List[str], - includes: List[str] = [], - options: Dict[str, List[str]] = {}, -): - name = env.subst(name) - # add base dir to all globs - srcs = add_base_dir(env, base_dir, srcs) - includes = add_base_dir(env, base_dir, includes, subst=True) - - # allow removing sources from parent builders - key = f"LIB_{name.upper()}_SKIP" - if key in env: - for expr in env[key]: - srcs.append("-<" + expr + ">") - - # queue library for further env clone and build - if srcs: - env.Prepend( - LIBQUEUE=[ - (join("$BUILD_DIR", name), base_dir, srcs, options), - ] - ) - - # search all include paths - for dir, expr in iter_expressions(includes): - if dir == "-": - for item in fnmatch.filter(env["CPPPATH"], expr): - if item in env["CPPPATH"]: - env["CPPPATH"].remove(item) - else: - for item in glob(expr, recursive=True): - if not isdir(item): - continue - if dir == "!": - env.Prepend(CPPPATH=[item]) - else: - env.Append(CPPPATH=[item]) - - -def env_build_libraries(env: Environment, safe: bool = True): - # add lib targets and clone safe envs - if not "LIBQUEUE" in env: - return - queue = env["LIBQUEUE"] - env["LIBQUEUE"] = [] - for lib in queue: - envsafe = env.Clone() if safe else env - # get env options to add/remove - options: Dict[str, List[str]] = lib[3] - # change only safe env options - for key, values in options.items(): - for dir, value in iter_expressions(values): - if dir == "+": - envsafe.Append(**{key: [value]}) - elif dir == "!": - envsafe.Prepend(**{key: [value]}) - elif dir == "-": - if value not in envsafe[key]: - raise ValueError(f"Invalid option; {value} is not in {key}") - envsafe[key].remove(value) - # build library with (name, base_dir, sources) options - target = envsafe.BuildLibrary(*lib[0:3]) - env.Prepend(LIBS=[target]) - - -env.AddMethod(env_add_library, "AddLibrary") -env.AddMethod(env_build_libraries, "BuildLibraries") diff --git a/cores/beken-72xx/base/lt_family.h b/cores/beken-72xx/base/lt_family.h index 4ffe4d6..5223d7f 100644 --- a/cores/beken-72xx/base/lt_family.h +++ b/cores/beken-72xx/base/lt_family.h @@ -2,6 +2,8 @@ #pragma once +#include + // Choose the main UART output port #ifndef LT_UART_DEFAULT_PORT #if HAS_SERIAL2