fbt: fixes for ufbt compat (#1940)
* fbt: split sdk management code * scripts: fixed import handling * fbt: sdk: reformatted paths * scrips: dist: bundling libs as a build artifact * fbt: sdk: better path management * typo fix * fbt: sdk: minor path handling fixes * toolchain: fixed windows toolchain download Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
parent
9cd0592aaf
commit
4b921803cb
@ -178,23 +178,6 @@ sources.extend(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
fwenv.AppendUnique(
|
|
||||||
LINKFLAGS=[
|
|
||||||
"-specs=nano.specs",
|
|
||||||
"-specs=nosys.specs",
|
|
||||||
"-Wl,--gc-sections",
|
|
||||||
"-Wl,--undefined=uxTopUsedPriority",
|
|
||||||
"-Wl,--wrap,_malloc_r",
|
|
||||||
"-Wl,--wrap,_free_r",
|
|
||||||
"-Wl,--wrap,_calloc_r",
|
|
||||||
"-Wl,--wrap,_realloc_r",
|
|
||||||
"-n",
|
|
||||||
"-Xlinker",
|
|
||||||
"-Map=${TARGET}.map",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
# Debug
|
# Debug
|
||||||
# print(fwenv.Dump())
|
# print(fwenv.Dump())
|
||||||
|
|
||||||
|
44
scripts/fbt/sdk/__init__.py
Normal file
44
scripts/fbt/sdk/__init__.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
from typing import Set, ClassVar
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class ApiEntryFunction:
|
||||||
|
name: str
|
||||||
|
returns: str
|
||||||
|
params: str
|
||||||
|
|
||||||
|
csv_type: ClassVar[str] = "Function"
|
||||||
|
|
||||||
|
def dictify(self):
|
||||||
|
return dict(name=self.name, type=self.returns, params=self.params)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class ApiEntryVariable:
|
||||||
|
name: str
|
||||||
|
var_type: str
|
||||||
|
|
||||||
|
csv_type: ClassVar[str] = "Variable"
|
||||||
|
|
||||||
|
def dictify(self):
|
||||||
|
return dict(name=self.name, type=self.var_type, params=None)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class ApiHeader:
|
||||||
|
name: str
|
||||||
|
|
||||||
|
csv_type: ClassVar[str] = "Header"
|
||||||
|
|
||||||
|
def dictify(self):
|
||||||
|
return dict(name=self.name, type=None, params=None)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ApiEntries:
|
||||||
|
# These are sets, to avoid creating duplicates when we have multiple
|
||||||
|
# declarations with same signature
|
||||||
|
functions: Set[ApiEntryFunction] = field(default_factory=set)
|
||||||
|
variables: Set[ApiEntryVariable] = field(default_factory=set)
|
||||||
|
headers: Set[ApiHeader] = field(default_factory=set)
|
@ -4,284 +4,18 @@ import csv
|
|||||||
import operator
|
import operator
|
||||||
|
|
||||||
from enum import Enum, auto
|
from enum import Enum, auto
|
||||||
from typing import List, Set, ClassVar, Any
|
from typing import Set, ClassVar, Any
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from ansi.color import fg
|
from ansi.color import fg
|
||||||
|
|
||||||
from cxxheaderparser.parser import CxxParser
|
from . import (
|
||||||
|
ApiEntries,
|
||||||
|
ApiEntryFunction,
|
||||||
# 'Fixing' complaints about typedefs
|
ApiEntryVariable,
|
||||||
CxxParser._fundamentals.discard("wchar_t")
|
ApiHeader,
|
||||||
|
|
||||||
from cxxheaderparser.types import (
|
|
||||||
EnumDecl,
|
|
||||||
Field,
|
|
||||||
ForwardDecl,
|
|
||||||
FriendDecl,
|
|
||||||
Function,
|
|
||||||
Method,
|
|
||||||
Typedef,
|
|
||||||
UsingAlias,
|
|
||||||
UsingDecl,
|
|
||||||
Variable,
|
|
||||||
Pointer,
|
|
||||||
Type,
|
|
||||||
PQName,
|
|
||||||
NameSpecifier,
|
|
||||||
FundamentalSpecifier,
|
|
||||||
Parameter,
|
|
||||||
Array,
|
|
||||||
Value,
|
|
||||||
Token,
|
|
||||||
FunctionType,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
from cxxheaderparser.parserstate import (
|
|
||||||
State,
|
|
||||||
EmptyBlockState,
|
|
||||||
ClassBlockState,
|
|
||||||
ExternBlockState,
|
|
||||||
NamespaceBlockState,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class ApiEntryFunction:
|
|
||||||
name: str
|
|
||||||
returns: str
|
|
||||||
params: str
|
|
||||||
|
|
||||||
csv_type: ClassVar[str] = "Function"
|
|
||||||
|
|
||||||
def dictify(self):
|
|
||||||
return dict(name=self.name, type=self.returns, params=self.params)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class ApiEntryVariable:
|
|
||||||
name: str
|
|
||||||
var_type: str
|
|
||||||
|
|
||||||
csv_type: ClassVar[str] = "Variable"
|
|
||||||
|
|
||||||
def dictify(self):
|
|
||||||
return dict(name=self.name, type=self.var_type, params=None)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class ApiHeader:
|
|
||||||
name: str
|
|
||||||
|
|
||||||
csv_type: ClassVar[str] = "Header"
|
|
||||||
|
|
||||||
def dictify(self):
|
|
||||||
return dict(name=self.name, type=None, params=None)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ApiEntries:
|
|
||||||
# These are sets, to avoid creating duplicates when we have multiple
|
|
||||||
# declarations with same signature
|
|
||||||
functions: Set[ApiEntryFunction] = field(default_factory=set)
|
|
||||||
variables: Set[ApiEntryVariable] = field(default_factory=set)
|
|
||||||
headers: Set[ApiHeader] = field(default_factory=set)
|
|
||||||
|
|
||||||
|
|
||||||
class SymbolManager:
|
|
||||||
def __init__(self):
|
|
||||||
self.api = ApiEntries()
|
|
||||||
self.name_hashes = set()
|
|
||||||
|
|
||||||
# Calculate hash of name and raise exception if it already is in the set
|
|
||||||
def _name_check(self, name: str):
|
|
||||||
name_hash = gnu_sym_hash(name)
|
|
||||||
if name_hash in self.name_hashes:
|
|
||||||
raise Exception(f"Hash collision on {name}")
|
|
||||||
self.name_hashes.add(name_hash)
|
|
||||||
|
|
||||||
def add_function(self, function_def: ApiEntryFunction):
|
|
||||||
if function_def in self.api.functions:
|
|
||||||
return
|
|
||||||
self._name_check(function_def.name)
|
|
||||||
self.api.functions.add(function_def)
|
|
||||||
|
|
||||||
def add_variable(self, variable_def: ApiEntryVariable):
|
|
||||||
if variable_def in self.api.variables:
|
|
||||||
return
|
|
||||||
self._name_check(variable_def.name)
|
|
||||||
self.api.variables.add(variable_def)
|
|
||||||
|
|
||||||
def add_header(self, header: str):
|
|
||||||
self.api.headers.add(ApiHeader(header))
|
|
||||||
|
|
||||||
|
|
||||||
def gnu_sym_hash(name: str):
|
|
||||||
h = 0x1505
|
|
||||||
for c in name:
|
|
||||||
h = (h << 5) + h + ord(c)
|
|
||||||
return str(hex(h))[-8:]
|
|
||||||
|
|
||||||
|
|
||||||
class SdkCollector:
|
|
||||||
def __init__(self):
|
|
||||||
self.symbol_manager = SymbolManager()
|
|
||||||
|
|
||||||
def add_header_to_sdk(self, header: str):
|
|
||||||
self.symbol_manager.add_header(header)
|
|
||||||
|
|
||||||
def process_source_file_for_sdk(self, file_path: str):
|
|
||||||
visitor = SdkCxxVisitor(self.symbol_manager)
|
|
||||||
with open(file_path, "rt") as f:
|
|
||||||
content = f.read()
|
|
||||||
parser = CxxParser(file_path, content, visitor, None)
|
|
||||||
parser.parse()
|
|
||||||
|
|
||||||
def get_api(self):
|
|
||||||
return self.symbol_manager.api
|
|
||||||
|
|
||||||
|
|
||||||
def stringify_array_dimension(size_descr):
|
|
||||||
if not size_descr:
|
|
||||||
return ""
|
|
||||||
return stringify_descr(size_descr)
|
|
||||||
|
|
||||||
|
|
||||||
def stringify_array_descr(type_descr):
|
|
||||||
assert isinstance(type_descr, Array)
|
|
||||||
return (
|
|
||||||
stringify_descr(type_descr.array_of),
|
|
||||||
stringify_array_dimension(type_descr.size),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def stringify_descr(type_descr):
|
|
||||||
if isinstance(type_descr, (NameSpecifier, FundamentalSpecifier)):
|
|
||||||
return type_descr.name
|
|
||||||
elif isinstance(type_descr, PQName):
|
|
||||||
return "::".join(map(stringify_descr, type_descr.segments))
|
|
||||||
elif isinstance(type_descr, Pointer):
|
|
||||||
# Hack
|
|
||||||
if isinstance(type_descr.ptr_to, FunctionType):
|
|
||||||
return stringify_descr(type_descr.ptr_to)
|
|
||||||
return f"{stringify_descr(type_descr.ptr_to)}*"
|
|
||||||
elif isinstance(type_descr, Type):
|
|
||||||
return (
|
|
||||||
f"{'const ' if type_descr.const else ''}"
|
|
||||||
f"{'volatile ' if type_descr.volatile else ''}"
|
|
||||||
f"{stringify_descr(type_descr.typename)}"
|
|
||||||
)
|
|
||||||
elif isinstance(type_descr, Parameter):
|
|
||||||
return stringify_descr(type_descr.type)
|
|
||||||
elif isinstance(type_descr, Array):
|
|
||||||
# Hack for 2d arrays
|
|
||||||
if isinstance(type_descr.array_of, Array):
|
|
||||||
argtype, dimension = stringify_array_descr(type_descr.array_of)
|
|
||||||
return (
|
|
||||||
f"{argtype}[{stringify_array_dimension(type_descr.size)}][{dimension}]"
|
|
||||||
)
|
|
||||||
return f"{stringify_descr(type_descr.array_of)}[{stringify_array_dimension(type_descr.size)}]"
|
|
||||||
elif isinstance(type_descr, Value):
|
|
||||||
return " ".join(map(stringify_descr, type_descr.tokens))
|
|
||||||
elif isinstance(type_descr, FunctionType):
|
|
||||||
return f"{stringify_descr(type_descr.return_type)} (*)({', '.join(map(stringify_descr, type_descr.parameters))})"
|
|
||||||
elif isinstance(type_descr, Token):
|
|
||||||
return type_descr.value
|
|
||||||
elif type_descr is None:
|
|
||||||
return ""
|
|
||||||
else:
|
|
||||||
raise Exception("unsupported type_descr: %s" % type_descr)
|
|
||||||
|
|
||||||
|
|
||||||
class SdkCxxVisitor:
|
|
||||||
def __init__(self, symbol_manager: SymbolManager):
|
|
||||||
self.api = symbol_manager
|
|
||||||
|
|
||||||
def on_variable(self, state: State, v: Variable) -> None:
|
|
||||||
if not v.extern:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.api.add_variable(
|
|
||||||
ApiEntryVariable(
|
|
||||||
stringify_descr(v.name),
|
|
||||||
stringify_descr(v.type),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def on_function(self, state: State, fn: Function) -> None:
|
|
||||||
if fn.inline or fn.has_body:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.api.add_function(
|
|
||||||
ApiEntryFunction(
|
|
||||||
stringify_descr(fn.name),
|
|
||||||
stringify_descr(fn.return_type),
|
|
||||||
", ".join(map(stringify_descr, fn.parameters))
|
|
||||||
+ (", ..." if fn.vararg else ""),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def on_define(self, state: State, content: str) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_pragma(self, state: State, content: str) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_include(self, state: State, filename: str) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_empty_block_start(self, state: EmptyBlockState) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_empty_block_end(self, state: EmptyBlockState) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_extern_block_start(self, state: ExternBlockState) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_extern_block_end(self, state: ExternBlockState) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_namespace_start(self, state: NamespaceBlockState) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_namespace_end(self, state: NamespaceBlockState) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_forward_decl(self, state: State, fdecl: ForwardDecl) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_typedef(self, state: State, typedef: Typedef) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_using_namespace(self, state: State, namespace: List[str]) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_using_alias(self, state: State, using: UsingAlias) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_using_declaration(self, state: State, using: UsingDecl) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_enum(self, state: State, enum: EnumDecl) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_class_start(self, state: ClassBlockState) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_class_field(self, state: State, f: Field) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_class_method(self, state: ClassBlockState, method: Method) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_class_friend(self, state: ClassBlockState, friend: FriendDecl) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_class_end(self, state: ClassBlockState) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class SdkVersion:
|
class SdkVersion:
|
238
scripts/fbt/sdk/collector.py
Normal file
238
scripts/fbt/sdk/collector.py
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
from typing import List
|
||||||
|
|
||||||
|
from cxxheaderparser.parser import CxxParser
|
||||||
|
from . import (
|
||||||
|
ApiEntries,
|
||||||
|
ApiEntryFunction,
|
||||||
|
ApiEntryVariable,
|
||||||
|
ApiHeader,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# 'Fixing' complaints about typedefs
|
||||||
|
CxxParser._fundamentals.discard("wchar_t")
|
||||||
|
|
||||||
|
from cxxheaderparser.types import (
|
||||||
|
EnumDecl,
|
||||||
|
Field,
|
||||||
|
ForwardDecl,
|
||||||
|
FriendDecl,
|
||||||
|
Function,
|
||||||
|
Method,
|
||||||
|
Typedef,
|
||||||
|
UsingAlias,
|
||||||
|
UsingDecl,
|
||||||
|
Variable,
|
||||||
|
Pointer,
|
||||||
|
Type,
|
||||||
|
PQName,
|
||||||
|
NameSpecifier,
|
||||||
|
FundamentalSpecifier,
|
||||||
|
Parameter,
|
||||||
|
Array,
|
||||||
|
Value,
|
||||||
|
Token,
|
||||||
|
FunctionType,
|
||||||
|
)
|
||||||
|
|
||||||
|
from cxxheaderparser.parserstate import (
|
||||||
|
State,
|
||||||
|
EmptyBlockState,
|
||||||
|
ClassBlockState,
|
||||||
|
ExternBlockState,
|
||||||
|
NamespaceBlockState,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SymbolManager:
|
||||||
|
def __init__(self):
|
||||||
|
self.api = ApiEntries()
|
||||||
|
self.name_hashes = set()
|
||||||
|
|
||||||
|
# Calculate hash of name and raise exception if it already is in the set
|
||||||
|
def _name_check(self, name: str):
|
||||||
|
name_hash = gnu_sym_hash(name)
|
||||||
|
if name_hash in self.name_hashes:
|
||||||
|
raise Exception(f"Hash collision on {name}")
|
||||||
|
self.name_hashes.add(name_hash)
|
||||||
|
|
||||||
|
def add_function(self, function_def: ApiEntryFunction):
|
||||||
|
if function_def in self.api.functions:
|
||||||
|
return
|
||||||
|
self._name_check(function_def.name)
|
||||||
|
self.api.functions.add(function_def)
|
||||||
|
|
||||||
|
def add_variable(self, variable_def: ApiEntryVariable):
|
||||||
|
if variable_def in self.api.variables:
|
||||||
|
return
|
||||||
|
self._name_check(variable_def.name)
|
||||||
|
self.api.variables.add(variable_def)
|
||||||
|
|
||||||
|
def add_header(self, header: str):
|
||||||
|
self.api.headers.add(ApiHeader(header))
|
||||||
|
|
||||||
|
|
||||||
|
def gnu_sym_hash(name: str):
|
||||||
|
h = 0x1505
|
||||||
|
for c in name:
|
||||||
|
h = (h << 5) + h + ord(c)
|
||||||
|
return str(hex(h))[-8:]
|
||||||
|
|
||||||
|
|
||||||
|
class SdkCollector:
|
||||||
|
def __init__(self):
|
||||||
|
self.symbol_manager = SymbolManager()
|
||||||
|
|
||||||
|
def add_header_to_sdk(self, header: str):
|
||||||
|
self.symbol_manager.add_header(header)
|
||||||
|
|
||||||
|
def process_source_file_for_sdk(self, file_path: str):
|
||||||
|
visitor = SdkCxxVisitor(self.symbol_manager)
|
||||||
|
with open(file_path, "rt") as f:
|
||||||
|
content = f.read()
|
||||||
|
parser = CxxParser(file_path, content, visitor, None)
|
||||||
|
parser.parse()
|
||||||
|
|
||||||
|
def get_api(self):
|
||||||
|
return self.symbol_manager.api
|
||||||
|
|
||||||
|
|
||||||
|
def stringify_array_dimension(size_descr):
|
||||||
|
if not size_descr:
|
||||||
|
return ""
|
||||||
|
return stringify_descr(size_descr)
|
||||||
|
|
||||||
|
|
||||||
|
def stringify_array_descr(type_descr):
|
||||||
|
assert isinstance(type_descr, Array)
|
||||||
|
return (
|
||||||
|
stringify_descr(type_descr.array_of),
|
||||||
|
stringify_array_dimension(type_descr.size),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def stringify_descr(type_descr):
|
||||||
|
if isinstance(type_descr, (NameSpecifier, FundamentalSpecifier)):
|
||||||
|
return type_descr.name
|
||||||
|
elif isinstance(type_descr, PQName):
|
||||||
|
return "::".join(map(stringify_descr, type_descr.segments))
|
||||||
|
elif isinstance(type_descr, Pointer):
|
||||||
|
# Hack
|
||||||
|
if isinstance(type_descr.ptr_to, FunctionType):
|
||||||
|
return stringify_descr(type_descr.ptr_to)
|
||||||
|
return f"{stringify_descr(type_descr.ptr_to)}*"
|
||||||
|
elif isinstance(type_descr, Type):
|
||||||
|
return (
|
||||||
|
f"{'const ' if type_descr.const else ''}"
|
||||||
|
f"{'volatile ' if type_descr.volatile else ''}"
|
||||||
|
f"{stringify_descr(type_descr.typename)}"
|
||||||
|
)
|
||||||
|
elif isinstance(type_descr, Parameter):
|
||||||
|
return stringify_descr(type_descr.type)
|
||||||
|
elif isinstance(type_descr, Array):
|
||||||
|
# Hack for 2d arrays
|
||||||
|
if isinstance(type_descr.array_of, Array):
|
||||||
|
argtype, dimension = stringify_array_descr(type_descr.array_of)
|
||||||
|
return (
|
||||||
|
f"{argtype}[{stringify_array_dimension(type_descr.size)}][{dimension}]"
|
||||||
|
)
|
||||||
|
return f"{stringify_descr(type_descr.array_of)}[{stringify_array_dimension(type_descr.size)}]"
|
||||||
|
elif isinstance(type_descr, Value):
|
||||||
|
return " ".join(map(stringify_descr, type_descr.tokens))
|
||||||
|
elif isinstance(type_descr, FunctionType):
|
||||||
|
return f"{stringify_descr(type_descr.return_type)} (*)({', '.join(map(stringify_descr, type_descr.parameters))})"
|
||||||
|
elif isinstance(type_descr, Token):
|
||||||
|
return type_descr.value
|
||||||
|
elif type_descr is None:
|
||||||
|
return ""
|
||||||
|
else:
|
||||||
|
raise Exception("unsupported type_descr: %s" % type_descr)
|
||||||
|
|
||||||
|
|
||||||
|
class SdkCxxVisitor:
|
||||||
|
def __init__(self, symbol_manager: SymbolManager):
|
||||||
|
self.api = symbol_manager
|
||||||
|
|
||||||
|
def on_variable(self, state: State, v: Variable) -> None:
|
||||||
|
if not v.extern:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.api.add_variable(
|
||||||
|
ApiEntryVariable(
|
||||||
|
stringify_descr(v.name),
|
||||||
|
stringify_descr(v.type),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_function(self, state: State, fn: Function) -> None:
|
||||||
|
if fn.inline or fn.has_body:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.api.add_function(
|
||||||
|
ApiEntryFunction(
|
||||||
|
stringify_descr(fn.name),
|
||||||
|
stringify_descr(fn.return_type),
|
||||||
|
", ".join(map(stringify_descr, fn.parameters))
|
||||||
|
+ (", ..." if fn.vararg else ""),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_define(self, state: State, content: str) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_pragma(self, state: State, content: str) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_include(self, state: State, filename: str) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_empty_block_start(self, state: EmptyBlockState) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_empty_block_end(self, state: EmptyBlockState) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_extern_block_start(self, state: ExternBlockState) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_extern_block_end(self, state: ExternBlockState) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_namespace_start(self, state: NamespaceBlockState) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_namespace_end(self, state: NamespaceBlockState) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_forward_decl(self, state: State, fdecl: ForwardDecl) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_typedef(self, state: State, typedef: Typedef) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_using_namespace(self, state: State, namespace: List[str]) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_using_alias(self, state: State, using: UsingAlias) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_using_declaration(self, state: State, using: UsingDecl) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_enum(self, state: State, enum: EnumDecl) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_class_start(self, state: ClassBlockState) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_class_field(self, state: State, f: Field) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_class_method(self, state: ClassBlockState, method: Method) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_class_friend(self, state: ClassBlockState, friend: FriendDecl) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_class_end(self, state: ClassBlockState) -> None:
|
||||||
|
pass
|
@ -8,7 +8,7 @@ import os
|
|||||||
import pathlib
|
import pathlib
|
||||||
from fbt.elfmanifest import assemble_manifest_data
|
from fbt.elfmanifest import assemble_manifest_data
|
||||||
from fbt.appmanifest import FlipperApplication, FlipperManifestException
|
from fbt.appmanifest import FlipperApplication, FlipperManifestException
|
||||||
from fbt.sdk import SdkCache
|
from fbt.sdk.cache import SdkCache
|
||||||
import itertools
|
import itertools
|
||||||
from ansi.color import fg
|
from ansi.color import fg
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ from SCons.Action import Action
|
|||||||
from SCons.Errors import UserError
|
from SCons.Errors import UserError
|
||||||
|
|
||||||
# from SCons.Scanner import C
|
# from SCons.Scanner import C
|
||||||
from SCons.Script import Mkdir, Copy, Delete, Entry
|
from SCons.Script import Entry
|
||||||
from SCons.Util import LogicalLines
|
from SCons.Util import LogicalLines
|
||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
@ -12,7 +12,8 @@ import posixpath
|
|||||||
import pathlib
|
import pathlib
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from fbt.sdk import SdkCollector, SdkCache
|
from fbt.sdk.collector import SdkCollector
|
||||||
|
from fbt.sdk.cache import SdkCache
|
||||||
|
|
||||||
|
|
||||||
def ProcessSdkDepends(env, filename):
|
def ProcessSdkDepends(env, filename):
|
||||||
@ -49,15 +50,19 @@ def prebuild_sdk_create_origin_file(target, source, env):
|
|||||||
|
|
||||||
|
|
||||||
class SdkMeta:
|
class SdkMeta:
|
||||||
def __init__(self, env):
|
def __init__(self, env, tree_builder: "SdkTreeBuilder"):
|
||||||
self.env = env
|
self.env = env
|
||||||
|
self.treebuilder = tree_builder
|
||||||
|
|
||||||
def save_to(self, json_manifest_path: str):
|
def save_to(self, json_manifest_path: str):
|
||||||
meta_contents = {
|
meta_contents = {
|
||||||
"sdk_symbols": self.env["SDK_DEFINITION"].name,
|
"sdk_symbols": self.treebuilder.build_sdk_file_path(
|
||||||
|
self.env["SDK_DEFINITION"].path
|
||||||
|
),
|
||||||
"cc_args": self._wrap_scons_vars("$CCFLAGS $_CCCOMCOM"),
|
"cc_args": self._wrap_scons_vars("$CCFLAGS $_CCCOMCOM"),
|
||||||
"cpp_args": self._wrap_scons_vars("$CXXFLAGS $CCFLAGS $_CCCOMCOM"),
|
"cpp_args": self._wrap_scons_vars("$CXXFLAGS $CCFLAGS $_CCCOMCOM"),
|
||||||
"linker_args": self._wrap_scons_vars("$LINKFLAGS"),
|
"linker_args": self._wrap_scons_vars("$LINKFLAGS"),
|
||||||
|
"linker_script": self.env.subst("${LINKER_SCRIPT_PATH}"),
|
||||||
}
|
}
|
||||||
with open(json_manifest_path, "wt") as f:
|
with open(json_manifest_path, "wt") as f:
|
||||||
json.dump(meta_contents, f, indent=4)
|
json.dump(meta_contents, f, indent=4)
|
||||||
@ -68,6 +73,8 @@ class SdkMeta:
|
|||||||
|
|
||||||
|
|
||||||
class SdkTreeBuilder:
|
class SdkTreeBuilder:
|
||||||
|
SDK_DIR_SUBST = "SDK_ROOT_DIR"
|
||||||
|
|
||||||
def __init__(self, env, target, source) -> None:
|
def __init__(self, env, target, source) -> None:
|
||||||
self.env = env
|
self.env = env
|
||||||
self.target = target
|
self.target = target
|
||||||
@ -88,6 +95,8 @@ class SdkTreeBuilder:
|
|||||||
self.header_depends = list(
|
self.header_depends = list(
|
||||||
filter(lambda fname: fname.endswith(".h"), depends.split()),
|
filter(lambda fname: fname.endswith(".h"), depends.split()),
|
||||||
)
|
)
|
||||||
|
self.header_depends.append(self.env.subst("${LINKER_SCRIPT_PATH}"))
|
||||||
|
self.header_depends.append(self.env.subst("${SDK_DEFINITION}"))
|
||||||
self.header_dirs = sorted(
|
self.header_dirs = sorted(
|
||||||
set(map(os.path.normpath, map(os.path.dirname, self.header_depends)))
|
set(map(os.path.normpath, map(os.path.dirname, self.header_depends)))
|
||||||
)
|
)
|
||||||
@ -102,17 +111,33 @@ class SdkTreeBuilder:
|
|||||||
)
|
)
|
||||||
|
|
||||||
sdk_dirs = ", ".join(f"'{dir}'" for dir in self.header_dirs)
|
sdk_dirs = ", ".join(f"'{dir}'" for dir in self.header_dirs)
|
||||||
for dir in full_fw_paths:
|
filtered_paths.extend(
|
||||||
if dir in sdk_dirs:
|
map(
|
||||||
filtered_paths.append(
|
self.build_sdk_file_path,
|
||||||
posixpath.normpath(posixpath.join(self.target_sdk_dir_name, dir))
|
filter(lambda path: path in sdk_dirs, full_fw_paths),
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
sdk_env = self.env.Clone()
|
sdk_env = self.env.Clone()
|
||||||
sdk_env.Replace(CPPPATH=filtered_paths)
|
sdk_env.Replace(
|
||||||
meta = SdkMeta(sdk_env)
|
CPPPATH=filtered_paths,
|
||||||
|
LINKER_SCRIPT=self.env.subst("${APP_LINKER_SCRIPT}"),
|
||||||
|
ORIG_LINKER_SCRIPT_PATH=self.env["LINKER_SCRIPT_PATH"],
|
||||||
|
LINKER_SCRIPT_PATH=self.build_sdk_file_path("${ORIG_LINKER_SCRIPT_PATH}"),
|
||||||
|
)
|
||||||
|
|
||||||
|
meta = SdkMeta(sdk_env, self)
|
||||||
meta.save_to(self.target[0].path)
|
meta.save_to(self.target[0].path)
|
||||||
|
|
||||||
|
def build_sdk_file_path(self, orig_path: str) -> str:
|
||||||
|
return posixpath.normpath(
|
||||||
|
posixpath.join(
|
||||||
|
self.SDK_DIR_SUBST,
|
||||||
|
self.target_sdk_dir_name,
|
||||||
|
orig_path,
|
||||||
|
)
|
||||||
|
).replace("\\", "/")
|
||||||
|
|
||||||
def emitter(self, target, source, env):
|
def emitter(self, target, source, env):
|
||||||
target_folder = target[0]
|
target_folder = target[0]
|
||||||
target = [target_folder.File("sdk.opts")]
|
target = [target_folder.File("sdk.opts")]
|
||||||
@ -128,8 +153,6 @@ class SdkTreeBuilder:
|
|||||||
for sdkdir in dirs_to_create:
|
for sdkdir in dirs_to_create:
|
||||||
os.makedirs(sdkdir, exist_ok=True)
|
os.makedirs(sdkdir, exist_ok=True)
|
||||||
|
|
||||||
shutil.copy2(self.env["SDK_DEFINITION"].path, self.sdk_root_dir.path)
|
|
||||||
|
|
||||||
for header in self.header_depends:
|
for header in self.header_depends:
|
||||||
shutil.copy2(header, self.sdk_deploy_dir.File(header).path)
|
shutil.copy2(header, self.sdk_deploy_dir.File(header).path)
|
||||||
|
|
||||||
|
@ -48,48 +48,52 @@ class Main(App):
|
|||||||
)
|
)
|
||||||
self.parser_copy.set_defaults(func=self.copy)
|
self.parser_copy.set_defaults(func=self.copy)
|
||||||
|
|
||||||
def get_project_filename(self, project, filetype):
|
def get_project_file_name(self, project: ProjectDir, filetype: str) -> str:
|
||||||
# Temporary fix
|
# Temporary fix
|
||||||
project_name = project.project
|
project_name = project.project
|
||||||
if project_name == "firmware":
|
if project_name == "firmware" and filetype != "elf":
|
||||||
if filetype == "zip":
|
project_name = "full"
|
||||||
project_name = "sdk"
|
|
||||||
elif filetype != "elf":
|
|
||||||
project_name = "full"
|
|
||||||
|
|
||||||
return f"{self.DIST_FILE_PREFIX}{self.target}-{project_name}-{self.args.suffix}.{filetype}"
|
return self.get_dist_file_name(project_name, filetype)
|
||||||
|
|
||||||
def get_dist_filepath(self, filename):
|
def get_dist_file_name(self, dist_artifact_type: str, filetype: str) -> str:
|
||||||
|
return f"{self.DIST_FILE_PREFIX}{self.target}-{dist_artifact_type}-{self.args.suffix}.{filetype}"
|
||||||
|
|
||||||
|
def get_dist_file_path(self, filename: str) -> str:
|
||||||
return join(self.output_dir_path, filename)
|
return join(self.output_dir_path, filename)
|
||||||
|
|
||||||
def copy_single_project(self, project):
|
def copy_single_project(self, project: ProjectDir) -> None:
|
||||||
obj_directory = join("build", project.dir)
|
obj_directory = join("build", project.dir)
|
||||||
|
|
||||||
for filetype in ("elf", "bin", "dfu", "json"):
|
for filetype in ("elf", "bin", "dfu", "json"):
|
||||||
if exists(src_file := join(obj_directory, f"{project.project}.{filetype}")):
|
if exists(src_file := join(obj_directory, f"{project.project}.{filetype}")):
|
||||||
shutil.copyfile(
|
shutil.copyfile(
|
||||||
src_file,
|
src_file,
|
||||||
self.get_dist_filepath(
|
self.get_dist_file_path(
|
||||||
self.get_project_filename(project, filetype)
|
self.get_project_file_name(project, filetype)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
if exists(sdk_folder := join(obj_directory, "sdk")):
|
for foldertype in ("sdk", "lib"):
|
||||||
with zipfile.ZipFile(
|
if exists(sdk_folder := join(obj_directory, foldertype)):
|
||||||
self.get_dist_filepath(self.get_project_filename(project, "zip")),
|
self.package_zip(foldertype, sdk_folder)
|
||||||
"w",
|
|
||||||
zipfile.ZIP_DEFLATED,
|
|
||||||
) as zf:
|
|
||||||
for root, dirs, files in walk(sdk_folder):
|
|
||||||
for file in files:
|
|
||||||
zf.write(
|
|
||||||
join(root, file),
|
|
||||||
relpath(
|
|
||||||
join(root, file),
|
|
||||||
sdk_folder,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
def copy(self):
|
def package_zip(self, foldertype, sdk_folder):
|
||||||
|
with zipfile.ZipFile(
|
||||||
|
self.get_dist_file_path(self.get_dist_file_name(foldertype, "zip")),
|
||||||
|
"w",
|
||||||
|
zipfile.ZIP_DEFLATED,
|
||||||
|
) as zf:
|
||||||
|
for root, _, files in walk(sdk_folder):
|
||||||
|
for file in files:
|
||||||
|
zf.write(
|
||||||
|
join(root, file),
|
||||||
|
relpath(
|
||||||
|
join(root, file),
|
||||||
|
sdk_folder,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def copy(self) -> int:
|
||||||
self.projects = dict(
|
self.projects = dict(
|
||||||
map(
|
map(
|
||||||
lambda pd: (pd.project, pd),
|
lambda pd: (pd.project, pd),
|
||||||
@ -144,12 +148,12 @@ class Main(App):
|
|||||||
"-t",
|
"-t",
|
||||||
self.target,
|
self.target,
|
||||||
"--dfu",
|
"--dfu",
|
||||||
self.get_dist_filepath(
|
self.get_dist_file_path(
|
||||||
self.get_project_filename(self.projects["firmware"], "dfu")
|
self.get_project_file_name(self.projects["firmware"], "dfu")
|
||||||
),
|
),
|
||||||
"--stage",
|
"--stage",
|
||||||
self.get_dist_filepath(
|
self.get_dist_file_path(
|
||||||
self.get_project_filename(self.projects["updater"], "bin")
|
self.get_project_file_name(self.projects["updater"], "bin")
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
if self.args.resources:
|
if self.args.resources:
|
||||||
|
@ -23,12 +23,12 @@ if (!(Test-Path -LiteralPath "$repo_root\toolchain")) {
|
|||||||
New-Item "$repo_root\toolchain" -ItemType Directory
|
New-Item "$repo_root\toolchain" -ItemType Directory
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host -NoNewline "Unziping Windows toolchain.."
|
Write-Host -NoNewline "Extracting Windows toolchain.."
|
||||||
Add-Type -Assembly "System.IO.Compression.Filesystem"
|
Add-Type -Assembly "System.IO.Compression.Filesystem"
|
||||||
[System.IO.Compression.ZipFile]::ExtractToDirectory("$toolchain_zip", "$repo_root\")
|
[System.IO.Compression.ZipFile]::ExtractToDirectory("$repo_root\$toolchain_zip", "$repo_root\")
|
||||||
Move-Item -Path "$repo_root\$toolchain_dir" -Destination "$repo_root\toolchain\x86_64-windows"
|
Move-Item -Path "$repo_root\$toolchain_dir" -Destination "$repo_root\toolchain\x86_64-windows"
|
||||||
Write-Host "done!"
|
Write-Host "done!"
|
||||||
|
|
||||||
Write-Host -NoNewline "Clearing temporary files.."
|
Write-Host -NoNewline "Cleaning up temporary files.."
|
||||||
Remove-Item -LiteralPath "$repo_root\$toolchain_zip" -Force
|
Remove-Item -LiteralPath "$repo_root\$toolchain_zip" -Force
|
||||||
Write-Host "done!"
|
Write-Host "done!"
|
||||||
|
@ -21,7 +21,7 @@ appenv = ENV.Clone(
|
|||||||
)
|
)
|
||||||
|
|
||||||
appenv.Replace(
|
appenv.Replace(
|
||||||
LINKER_SCRIPT="application_ext",
|
LINKER_SCRIPT=appenv.subst("$APP_LINKER_SCRIPT"),
|
||||||
)
|
)
|
||||||
|
|
||||||
appenv.AppendUnique(
|
appenv.AppendUnique(
|
||||||
|
@ -32,12 +32,27 @@ else:
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
ENV.Append(
|
ENV.AppendUnique(
|
||||||
LINKFLAGS=[
|
LINKFLAGS=[
|
||||||
"-Tfirmware/targets/f${TARGET_HW}/${LINKER_SCRIPT}.ld",
|
"-specs=nano.specs",
|
||||||
|
"-specs=nosys.specs",
|
||||||
|
"-Wl,--gc-sections",
|
||||||
|
"-Wl,--undefined=uxTopUsedPriority",
|
||||||
|
"-Wl,--wrap,_malloc_r",
|
||||||
|
"-Wl,--wrap,_free_r",
|
||||||
|
"-Wl,--wrap,_calloc_r",
|
||||||
|
"-Wl,--wrap,_realloc_r",
|
||||||
|
"-n",
|
||||||
|
"-Xlinker",
|
||||||
|
"-Map=${TARGET}.map",
|
||||||
|
"-T${LINKER_SCRIPT_PATH}",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ENV.SetDefault(
|
||||||
|
LINKER_SCRIPT_PATH="firmware/targets/f${TARGET_HW}/${LINKER_SCRIPT}.ld",
|
||||||
|
)
|
||||||
|
|
||||||
if ENV["FIRMWARE_BUILD_CFG"] == "updater":
|
if ENV["FIRMWARE_BUILD_CFG"] == "updater":
|
||||||
ENV.Append(
|
ENV.Append(
|
||||||
IMAGE_BASE_ADDRESS="0x20000000",
|
IMAGE_BASE_ADDRESS="0x20000000",
|
||||||
@ -47,4 +62,5 @@ else:
|
|||||||
ENV.Append(
|
ENV.Append(
|
||||||
IMAGE_BASE_ADDRESS="0x8000000",
|
IMAGE_BASE_ADDRESS="0x8000000",
|
||||||
LINKER_SCRIPT="stm32wb55xx_flash",
|
LINKER_SCRIPT="stm32wb55xx_flash",
|
||||||
|
APP_LINKER_SCRIPT="application_ext",
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user