[builder] Add library queue to manage cloned environments

This commit is contained in:
Kuba Szczodrzyński
2023-02-28 21:14:23 +01:00
parent 26b393e059
commit 72ab64461c
10 changed files with 383 additions and 279 deletions

View File

@@ -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=["+<boot_handlers_105_bk7231s.S>"],
@@ -129,7 +132,7 @@ else:
srcs_core.append("+<driver/entry/boot_handlers.S>")
# 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(
"+<os/*>",
],
)
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(

View File

@@ -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=[
"+<variant.cpp>",
],
# 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=[
"+<variant.cpp>",
],
# not adding includes since they're added in base.py
)
# Build all libraries
env.BuildLibraries()
queue.BuildLibraries()

View File

@@ -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()

View File

@@ -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

View File

@@ -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("$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, "compat"),
join(path, "config"),
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")

View File

@@ -21,13 +21,13 @@ def env_add_flash_layout(env: Environment, board):
defines[f"FLASH_{name}_LENGTH"] = length
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)

View File

@@ -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,

210
builder/utils/libs-queue.py Normal file
View File

@@ -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")

View File

@@ -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")

View File

@@ -2,6 +2,8 @@
#pragma once
#include <variant.h>
// Choose the main UART output port
#ifndef LT_UART_DEFAULT_PORT
#if HAS_SERIAL2