* 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>
		
			
				
	
	
		
			239 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			239 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
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
 |