Files
TransPyC/lib/core/stub_generator.py
2026-07-18 19:25:40 +08:00

762 lines
31 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
C/H 文件到 Python 存根文件生成器
将 C 头文件 (.h) 或 C 源文件 (.c) 转换为 Python 存根文件 (.pyi)
生成的 Python 代码使用 TransPyC 语法,包含类型注解
"""
from __future__ import annotations
import os
import re
import sys
from typing import Any
from lib.core.VLogger import get_logger as _vlog
class CTypeMapper:
# 硬编码 C 类型名 → Python 类型映射表
# 这是 stub 生成器C 头文件 → Python stub专用的固定映射
# 不再依赖运行时 CTypeRegistry._cname_to_class该映射已删除
_CNAME_TO_PY: dict[str, str] = {
# 基本类型
'char': 't.CChar', 'int': 't.CInt', 'short': 't.CShort',
'long': 't.CLong', 'long long': 't.CLongLong',
'float': 't.CFloat', 'double': 't.CDouble', 'void': 't.CVoid',
# unsigned / signed 变体
'unsigned': 't.CUnsigned', 'unsigned char': 't.CUnsignedChar',
'unsigned int': 't.CUnsignedInt', 'unsigned short': 't.CUnsignedShort',
'unsigned long': 't.CUnsignedLong',
'unsigned long long': 't.CUnsignedLongLong',
'signed char': 't.CSignedChar',
# bool / size_t
'bool': 't.CBool', '_Bool': 't.CBool', 'size_t': 't.CSizeT',
'ssize_t': 't.CLong',
# stdint 固定宽度类型
'int8_t': 't.CInt8T', 'int16_t': 't.CInt16T',
'int32_t': 't.CInt32T', 'int64_t': 't.CInt64T',
'uint8_t': 't.CUInt8T', 'uint16_t': 't.CUInt16T',
'uint32_t': 't.CUInt32T', 'uint64_t': 't.CUInt64T',
'intptr_t': 't.CIntPtrT', 'uintptr_t': 't.CUIntPtrT',
'ptrdiff_t': 't.CPtrDiffT',
# 宽字符 / 字符类型
'wchar_t': 't.CWCharT', 'char8_t': 't.CChar8T',
'char16_t': 't.CChar16T', 'char32_t': 't.CChar32T',
# 浮点特化类型
'float8_t': 't.CFloat8T', 'float16_t': 't.CFloat16T',
'float32_t': 't.CFloat32T', 'float64_t': 't.CFloat64T',
'float128_t': 't.CFloat128T',
# signed 速记
'signed': 't.CInt', 'signed int': 't.CInt',
'signed short': 't.CShort', 'signed long': 't.CLong',
}
@classmethod
def _ResolveCNameToPyType(cls, c_type: str) -> str:
"""将 C 类型名解析为 Python 类型字符串(如 'int''t.CInt'
使用硬编码映射表,不再依赖 CTypeRegistry._cname_to_class。
未识别的类型原样返回(用于 struct/union/enum/typedef 用户自定义名)。
"""
py: str | None = cls._CNAME_TO_PY.get(c_type)
if py is not None:
return py
return c_type
@classmethod
def MapType(cls, c_type: str, IsPtr: bool = False, IsArray: bool = False) -> str:
c_type = c_type.strip()
IsConst: bool = 'const ' in c_type
c_type = c_type.replace('const ', '').replace('const', '').strip()
IsVolatile: bool = 'volatile ' in c_type
c_type = c_type.replace('volatile ', '').replace('volatile', '').strip()
IsStatic: bool = 'static ' in c_type
c_type = c_type.replace('static ', '').replace('static', '').strip()
IsExtern: bool = 'extern ' in c_type
c_type = c_type.replace('extern ', '').replace('extern', '').strip()
IsRegister: bool = 'register ' in c_type
c_type = c_type.replace('register ', '').replace('register', '').strip()
IsInline: bool = 'inline ' in c_type
c_type = c_type.replace('inline ', '').replace('inline', '').strip()
PtrSuffix: str = ''
if IsPtr or c_type.endswith('*'):
PtrSuffix = ' | t.CPtr'
c_type = c_type.rstrip('*').strip()
if c_type.startswith('struct '):
c_type = c_type[7:].strip()
elif c_type.startswith('union '):
c_type = c_type[6:].strip()
elif c_type.startswith('enum '):
c_type = c_type[5:].strip()
PyType: str = cls._ResolveCNameToPyType(c_type)
result: str = PyType + PtrSuffix
if IsArray:
result += ' | t.CArrayPtr'
return result
def _find_matching_brace(text: str, start: int) -> int:
depth: int = 0
i: int = start
while i < len(text):
if text[i] == '{':
depth += 1
elif text[i] == '}':
depth -= 1
if depth == 0:
return i
elif text[i] == '"' or text[i] == "'":
quote: str = text[i]
i += 1
while i < len(text) and text[i] != quote:
if text[i] == '\\':
i += 1
i += 1
elif text[i] == '/' and i + 1 < len(text) and text[i + 1] == '/':
while i < len(text) and text[i] != '\n':
i += 1
continue
elif text[i] == '/' and i + 1 < len(text) and text[i + 1] == '*':
i += 2
while i + 1 < len(text) and not (text[i] == '*' and text[i + 1] == '/'):
i += 1
i += 1
i += 1
return -1
def _strip_comments(content: str) -> str:
result: list[str] = []
i: int = 0
while i < len(content):
if content[i] == '/' and i + 1 < len(content) and content[i + 1] == '/':
while i < len(content) and content[i] != '\n':
i += 1
elif content[i] == '/' and i + 1 < len(content) and content[i + 1] == '*':
i += 2
while i + 1 < len(content) and not (content[i] == '*' and content[i + 1] == '/'):
i += 1
i += 2
elif content[i] == '"' or content[i] == "'":
quote: str = content[i]
result.append(content[i])
i += 1
while i < len(content) and content[i] != quote:
if content[i] == '\\' and i + 1 < len(content):
result.append(content[i])
i += 1
result.append(content[i])
i += 1
if i < len(content):
result.append(content[i])
i += 1
else:
result.append(content[i])
i += 1
return ''.join(result)
def _strip_preprocessor_guards(content: str) -> str:
lines: list[str] = content.split('\n')
filtered: list[str] = []
for line in lines:
stripped: str = line.strip()
if stripped.startswith('#ifndef') and '_H' in stripped.upper():
continue
if stripped.startswith('#define') and '_H' in stripped.upper():
continue
if stripped == '#endif':
continue
filtered.append(line)
return '\n'.join(filtered)
class CHeaderParser:
"""C 头文件解析器"""
def __init__(self) -> None:
self.defines: list[dict[str, str | None]] = []
self.structs: dict[str, dict[str, Any]] = {}
self.enums: dict[str, dict[str, Any]] = {}
self.typedefs: list[dict[str, str]] = []
self.functions: dict[str, dict[str, Any]] = {}
self.variables: dict[str, dict[str, Any]] = {}
self.unions: dict[str, dict[str, Any]] = {}
self.func_ptr_typedefs: dict[str, dict[str, Any]] = {}
self._typedef_struct_map: dict[str, str] = {}
self._typedef_enum_map: dict[str, str] = {}
self._typedef_union_map: dict[str, str] = {}
def ParseFile(self, FilePath: str) -> None:
with open(FilePath, 'r', encoding='utf-8') as f:
content: str = f.read()
self.ParseContent(content)
def ParseContent(self, content: str) -> None:
content = _strip_comments(content)
content = _strip_preprocessor_guards(content)
self._ParseDefines(content)
self._ParseTypedefStructs(content)
self._ParseTypedefEnums(content)
self._ParseTypedefUnions(content)
self._ParseFuncPtrTypedefs(content)
self._ParseSimpleTypedefs(content)
self._ParseStandaloneStructs(content)
self._ParseStandaloneEnums(content)
self._ParseStandaloneUnions(content)
stripped: str = self._StripBodies(content)
self._ParseFunctions(stripped)
self._ParseVariables(stripped)
def _StripBodies(self, content: str) -> str:
result: str = content
while True:
pos: int = result.find('{')
if pos < 0:
break
end: int = _find_matching_brace(result, pos)
if end < 0:
break
result = result[:pos] + result[end + 1:]
return result
def _ParseDefines(self, content: str) -> None:
for match in re.finditer(r'#define\s+(\w+)(?:\s+(.+?))?\s*$', content, re.MULTILINE):
Name: str = match.group(1)
Value: str | None = match.group(2)
if Name.startswith('_') and Name.endswith('_'):
continue
if Name.startswith('__'):
continue
self.defines.append({
'name': Name,
'value': Value.strip() if Value else None,
})
def _ParseTypedefStructs(self, content: str) -> None:
pattern: str = r'typedef\s+struct\s*(\w*)\s*\{'
for match in re.finditer(pattern, content):
brace_start: int = content.index('{', match.start())
brace_end: int = _find_matching_brace(content, brace_start)
if brace_end < 0:
continue
body: str = content[brace_start + 1:brace_end]
rest: str = content[brace_end + 1:].lstrip()
AliasMatch: re.Match[str] | None = re.match(r'(\w+)\s*;', rest)
if not AliasMatch:
continue
AliasName: str = AliasMatch.group(1)
TagName: str | None = match.group(1) if match.group(1) else None
members: list[dict[str, Any]] = self._ParseStructMembers(body)
self.structs[AliasName] = {
'name': AliasName,
'tag': TagName,
'members': members,
}
if TagName:
self._typedef_struct_map[TagName] = AliasName
def _ParseTypedefEnums(self, content: str) -> None:
pattern: str = r'typedef\s+enum\s*(\w*)\s*\{'
for match in re.finditer(pattern, content):
brace_start: int = content.index('{', match.start())
brace_end: int = _find_matching_brace(content, brace_start)
if brace_end < 0:
continue
body: str = content[brace_start + 1:brace_end]
rest: str = content[brace_end + 1:].lstrip()
AliasMatch: re.Match[str] | None = re.match(r'(\w+)\s*;', rest)
if not AliasMatch:
continue
AliasName: str = AliasMatch.group(1)
TagName: str | None = match.group(1) if match.group(1) else None
values: list[dict[str, str | None]] = self._ParseEnumValues(body)
self.enums[AliasName] = {
'name': AliasName,
'tag': TagName,
'values': values,
}
if TagName:
self._typedef_enum_map[TagName] = AliasName
def _ParseTypedefUnions(self, content: str) -> None:
pattern: str = r'typedef\s+union\s*(\w*)\s*\{'
for match in re.finditer(pattern, content):
brace_start: int = content.index('{', match.start())
brace_end: int = _find_matching_brace(content, brace_start)
if brace_end < 0:
continue
body: str = content[brace_start + 1:brace_end]
rest: str = content[brace_end + 1:].lstrip()
AliasMatch: re.Match[str] | None = re.match(r'(\w+)\s*;', rest)
if not AliasMatch:
continue
AliasName: str = AliasMatch.group(1)
TagName: str | None = match.group(1) if match.group(1) else None
members: list[dict[str, Any]] = self._ParseStructMembers(body)
self.unions[AliasName] = {
'name': AliasName,
'tag': TagName,
'members': members,
}
if TagName:
self._typedef_union_map[TagName] = AliasName
def _ParseFuncPtrTypedefs(self, content: str) -> None:
pattern: str = r'typedef\s+([\w\s\*]+?)\s*\(\s*\*\s*(\w+)\s*\)\s*\(([^)]*)\)\s*;'
for match in re.finditer(pattern, content):
ReturnType: str = match.group(1).strip()
Name: str = match.group(2)
ParamsStr: str = match.group(3).strip()
params: list[dict[str, Any]] = self._ParseFuncParams(ParamsStr)
self.func_ptr_typedefs[Name] = {
'name': Name,
'return_type': ReturnType,
'params': params,
}
def _ParseSimpleTypedefs(self, content: str) -> None:
stripped: str = self._StripBodies(content)
pattern: str = r'typedef\s+([\w\s\*]+?)\s+(\w+)\s*;'
for match in re.finditer(pattern, stripped):
BaseType: str = match.group(1).strip()
AliasName: str = match.group(2)
if AliasName in self.structs:
continue
if AliasName in self.enums:
continue
if AliasName in self.unions:
continue
if AliasName in self.func_ptr_typedefs:
continue
if BaseType.startswith('typedef'):
continue
self.typedefs.append({'name': AliasName, 'base': BaseType})
def _ParseStandaloneStructs(self, content: str) -> None:
pattern: str = r'(?<!typedef\s)struct\s+(\w+)\s*\{'
for match in re.finditer(pattern, content):
TagName: str = match.group(1)
if TagName in self._typedef_struct_map:
continue
if TagName in self.structs:
continue
brace_start: int = content.index('{', match.start())
brace_end: int = _find_matching_brace(content, brace_start)
if brace_end < 0:
continue
body: str = content[brace_start + 1:brace_end]
members: list[dict[str, Any]] = self._ParseStructMembers(body)
self.structs[TagName] = {
'name': TagName,
'tag': None,
'members': members,
}
def _ParseStandaloneEnums(self, content: str) -> None:
pattern: str = r'(?<!typedef\s)enum\s+(\w+)\s*\{'
for match in re.finditer(pattern, content):
TagName: str = match.group(1)
if TagName in self._typedef_enum_map:
continue
if TagName in self.enums:
continue
brace_start: int = content.index('{', match.start())
brace_end: int = _find_matching_brace(content, brace_start)
if brace_end < 0:
continue
body: str = content[brace_start + 1:brace_end]
values: list[dict[str, str | None]] = self._ParseEnumValues(body)
self.enums[TagName] = {
'name': TagName,
'tag': None,
'values': values,
}
def _ParseStandaloneUnions(self, content: str) -> None:
pattern: str = r'(?<!typedef\s)union\s+(\w+)\s*\{'
for match in re.finditer(pattern, content):
TagName: str = match.group(1)
if TagName in self._typedef_union_map:
continue
if TagName in self.unions:
continue
brace_start: int = content.index('{', match.start())
brace_end: int = _find_matching_brace(content, brace_start)
if brace_end < 0:
continue
body: str = content[brace_start + 1:brace_end]
members: list[dict[str, Any]] = self._ParseStructMembers(body)
self.unions[TagName] = {
'name': TagName,
'tag': None,
'members': members,
}
def _ParseFunctions(self, content: str) -> None:
pattern: str = r'(?:(?:static|extern|inline)\s+)*([\w][\w\s\*]*?)\b(\w+)\s*\(([^)]*)\)\s*;'
for match in re.finditer(pattern, content):
ReturnType: str = match.group(1).strip()
FuncName: str = match.group(2)
ParamsStr: str = match.group(3).strip()
if FuncName in self.structs or FuncName in self.enums or FuncName in self.unions:
continue
if FuncName in self.func_ptr_typedefs:
continue
if FuncName in self.defines:
continue
if ReturnType in ('struct', 'union', 'enum', 'typedef'):
continue
if re.match(r'^\d', ReturnType):
continue
if not re.match(r'^[A-Za-z_]', FuncName):
continue
params: list[dict[str, Any]] = self._ParseFuncParams(ParamsStr)
self.functions[FuncName] = {
'name': FuncName,
'return_type': ReturnType,
'params': params,
}
def _ParseVariables(self, content: str) -> None:
pattern: str = r'(?:(?:static|extern|volatile|const)\s+)*([\w][\w\s\*]*?)\s+(\w+)\s*(?:\[(\d*)\])?\s*;'
for match in re.finditer(pattern, content):
TypeStr: str = match.group(1).strip()
VarName: str = match.group(2)
ArraySize: str | None = match.group(3)
if VarName in self.functions or VarName in self.structs or VarName in self.enums:
continue
if VarName in self.unions or VarName in self.func_ptr_typedefs:
continue
if VarName in self.defines:
continue
if TypeStr.startswith('typedef'):
continue
if TypeStr in ('struct', 'union', 'enum', 'typedef'):
continue
if re.match(r'^(if|else|while|for|return|switch|case|break|continue|do|goto)$', VarName):
continue
if not re.match(r'^[A-Za-z_]', VarName):
continue
IsPtr: bool = '*' in TypeStr
IsArray: bool = ArraySize is not None
self.variables[VarName] = {
'name': VarName,
'type': TypeStr,
'IsPtr': IsPtr,
'is_array': IsArray,
'array_size': int(ArraySize) if ArraySize and ArraySize.isdigit() else None,
}
def _ParseStructMembers(self, body: str) -> list[dict[str, Any]]:
members: list[dict[str, Any]] = []
for line in body.split(';'):
line = line.strip()
if not line:
continue
arr_match: re.Match[str] | None = re.match(r'([\w\s\*]+?)\s+(\w+)\s*\[(\d*)\]', line)
if arr_match:
MemberType: str = arr_match.group(1).strip()
MemberName: str = arr_match.group(2)
ArraySize: str = arr_match.group(3)
members.append({
'name': MemberName,
'type': MemberType,
'is_array': True,
'array_size': int(ArraySize) if ArraySize and ArraySize.isdigit() else None,
})
continue
ptr_match: re.Match[str] | None = re.match(r'([\w\s\*]+?)\s*\*\s*(\w+)', line)
if ptr_match:
MemberType: str = ptr_match.group(1).strip() + ' *'
MemberName: str = ptr_match.group(2)
members.append({
'name': MemberName,
'type': MemberType,
'IsPtr': True,
})
continue
simple_match: re.Match[str] | None = re.match(r'([\w\s]+?)\s+(\w+)', line)
if simple_match:
MemberType: str = simple_match.group(1).strip()
MemberName: str = simple_match.group(2)
members.append({
'name': MemberName,
'type': MemberType,
})
return members
def _ParseEnumValues(self, body: str) -> list[dict[str, str | None]]:
values: list[dict[str, str | None]] = []
for item in body.split(','):
item = item.strip()
if not item:
continue
eq_pos: int = item.find('=')
if eq_pos >= 0:
Name: str = item[:eq_pos].strip()
Value: str | None = item[eq_pos + 1:].strip()
else:
Name = item.strip()
Value = None
if Name and re.match(r'^[A-Za-z_]\w*$', Name):
values.append({'name': Name, 'value': Value})
return values
def _ParseFuncParams(self, params_str: str) -> list[dict[str, Any]]:
params: list[dict[str, Any]] = []
if not params_str or params_str.strip() == 'void' or params_str.strip() == '':
return params
for param in params_str.split(','):
param = param.strip()
if not param:
continue
func_ptr_match: re.Match[str] | None = re.match(r'([\w\s\*]+?)\s*\(\s*\*\s*(\w*)\s*\)\s*\([^)]*\)', param)
if func_ptr_match:
RetType: str = func_ptr_match.group(1).strip()
ParamName: str = func_ptr_match.group(2) or 'callback'
params.append({
'name': ParamName,
'type': RetType + ' (*)',
'is_func_ptr': True,
})
continue
arr_match: re.Match[str] | None = re.match(r'([\w\s\*]+?)\s+(\w+)\s*\[\d*\]', param)
if arr_match:
ParamType: str = arr_match.group(1).strip()
ParamName: str = arr_match.group(2)
params.append({
'name': ParamName,
'type': ParamType,
'is_array': True,
})
continue
tokens: list[str] = param.split()
if len(tokens) >= 2:
LastToken: str = tokens[-1]
PtrCount: int = 0
while LastToken.startswith('*'):
PtrCount += 1
LastToken = LastToken[1:]
TypeTokens: list[str] = tokens[:-1]
for _ in range(PtrCount):
TypeTokens.append('*')
ParamName: str = LastToken
ParamType: str = ' '.join(TypeTokens)
if not ParamName:
ParamName = 'arg'
ParamType = param
params.append({
'name': ParamName,
'type': ParamType,
})
elif len(tokens) == 1:
params.append({
'name': 'arg',
'type': tokens[0],
})
return params
class PythonStubGenerator:
"""Python 存根文件生成器"""
def __init__(self, parser: CHeaderParser) -> None:
self.parser: CHeaderParser = parser
self.OutputLines: list[str] = []
def generate(self, ModuleName: str | None = None) -> str:
self.OutputLines = []
self._AddHeader(ModuleName)
self._GenerateImports()
self._GenerateDefines()
self._GenerateFuncPtrTypedefs()
self._GenerateSimpleTypedefs()
self._GenerateStructs()
self._GenerateUnions()
self._GenerateEnums()
self._GenerateVariables()
self._GenerateFunctions()
return '\n'.join(self.OutputLines)
def _AddHeader(self, ModuleName: str | None = None) -> None:
self.OutputLines.append('"""')
self.OutputLines.append('Auto-generated Python stub file from C header')
if ModuleName:
self.OutputLines.append(f'Module: {ModuleName}')
self.OutputLines.append('"""')
self.OutputLines.append('')
def _GenerateImports(self) -> None:
self.OutputLines.append('import t')
self.OutputLines.append('import c')
self.OutputLines.append('')
def _GenerateDefines(self) -> None:
if not self.parser.defines:
return
for define in self.parser.defines:
Name: str = define['name']
Value: str | None = define['value']
if Value is not None:
self.OutputLines.append(f'{Name}: t.CDefine = {Value}')
else:
self.OutputLines.append(f'{Name}: t.CDefine')
self.OutputLines.append('')
def _GenerateFuncPtrTypedefs(self) -> None:
if not self.parser.func_ptr_typedefs:
return
for Name, Info in self.parser.func_ptr_typedefs.items():
RetType: str = CTypeMapper.MapType(Info['return_type'])
ParamTypes: list[str] = []
for p in Info['params']:
ParamTypes.append(CTypeMapper.MapType(p['type']))
ParamStr: str = ', '.join(ParamTypes) if ParamTypes else ''
self.OutputLines.append(f'{Name}: t.CTypedef = t.Callable[[{ParamStr}], {RetType}]')
self.OutputLines.append('')
def _GenerateSimpleTypedefs(self) -> None:
if not self.parser.typedefs:
return
for td in self.parser.typedefs:
AliasName: str = td['name']
BaseType: str = td['base']
PyType: str = CTypeMapper.MapType(BaseType)
self.OutputLines.append(f'{AliasName}: t.CTypedef | {PyType}')
self.OutputLines.append('')
def _GenerateStructs(self) -> None:
if not self.parser.structs:
return
for StructName, StructInfo in self.parser.structs.items():
self.OutputLines.append(f'@c.Attribute(t.attr.packed)')
self.OutputLines.append(f'class {StructName}(t.CStruct):')
if StructInfo['members']:
for member in StructInfo['members']:
MemberName: str = member['name']
IsPtr: bool = member.get('IsPtr', False)
IsArray: bool = member.get('is_array', False)
ArraySize: int | None = member.get('array_size')
PyType: str = CTypeMapper.MapType(member['type'], IsPtr=IsPtr)
if IsArray and ArraySize is not None:
self.OutputLines.append(f' {MemberName}: list[{PyType}, {ArraySize}]')
elif IsArray:
self.OutputLines.append(f' {MemberName}: {PyType} | t.CArrayPtr')
else:
self.OutputLines.append(f' {MemberName}: {PyType}')
else:
self.OutputLines.append(' pass')
self.OutputLines.append('')
def _GenerateUnions(self) -> None:
if not self.parser.unions:
return
for UnionName, UnionInfo in self.parser.unions.items():
self.OutputLines.append(f'@c.Attribute(t.attr.packed)')
self.OutputLines.append(f'class {UnionName}(t.CUnion):')
if UnionInfo['members']:
for member in UnionInfo['members']:
PyType: str = CTypeMapper.MapType(member['type'], IsPtr=member.get('IsPtr', False))
self.OutputLines.append(f' {member["name"]}: {PyType}')
else:
self.OutputLines.append(' pass')
self.OutputLines.append('')
def _GenerateEnums(self) -> None:
if not self.parser.enums:
return
for EnumName, EnumInfo in self.parser.enums.items():
self.OutputLines.append(f'class __{EnumName.lower()}(t.CEnum):')
if EnumInfo['values']:
for value in EnumInfo['values']:
if value['value'] is not None:
self.OutputLines.append(f' {value["name"]}: t.CDefine = {value["value"]}')
else:
self.OutputLines.append(f' {value["name"]}: t.CDefine')
else:
self.OutputLines.append(' pass')
self.OutputLines.append(f'{EnumName}: t.CTypedef = __{EnumName.lower()}')
self.OutputLines.append('')
def _GenerateVariables(self) -> None:
if not self.parser.variables:
return
for VarName, VarInfo in self.parser.variables.items():
PyType: str = CTypeMapper.MapType(VarInfo['type'], IsPtr=VarInfo['IsPtr'], IsArray=VarInfo['is_array'])
self.OutputLines.append(f'{VarName}: {PyType}')
self.OutputLines.append('')
def _GenerateFunctions(self) -> None:
if not self.parser.functions:
return
for FuncName, FuncInfo in self.parser.functions.items():
params: list[str] = []
for param in FuncInfo['params']:
if param.get('is_func_ptr'):
PyType: str = CTypeMapper.MapType(param['type'])
elif param.get('is_array'):
PyType: str = CTypeMapper.MapType(param['type'], IsPtr=True)
else:
PyType: str = CTypeMapper.MapType(param['type'])
params.append(f'{param["name"]}: {PyType}')
ParamStr: str = ', '.join(params) if params else ''
ReturnType: str = CTypeMapper.MapType(FuncInfo['return_type'])
self.OutputLines.append(f'def {FuncName}({ParamStr}) -> {ReturnType}: pass')
self.OutputLines.append('')
def GenerateStubFromC(InputFile: str, OutputFile: str | None = None) -> str:
parser: CHeaderParser = CHeaderParser()
parser.ParseFile(InputFile)
generator: PythonStubGenerator = PythonStubGenerator(parser)
ModuleName: str = os.path.splitext(os.path.basename(InputFile))[0]
content: str = generator.generate(ModuleName)
if OutputFile:
with open(OutputFile, 'w', encoding='utf-8') as f:
f.write(content)
_vlog().success(f"生成: {OutputFile}")
return content
if __name__ == '__main__':
if len(sys.argv) >= 3:
GenerateStubFromC(sys.argv[1], sys.argv[2])
elif len(sys.argv) == 2:
_vlog().info(GenerateStubFromC(sys.argv[1]))
else:
test_code: str = '''
struct memory_block {
uint64_t size;
uint8_t state;
uint8_t order;
struct memory_block *next;
struct memory_block *prev;
};
typedef struct memory_block memory_block_t;
enum color_format {
COLOR_FORMAT_BGRA,
COLOR_FORMAT_RGBA,
COLOR_FORMAT_BGR,
COLOR_FORMAT_RGB
};
void *malloc(size_t size);
void free(void *ptr);
void *memcpy(void *dest, const void *src, size_t n);
'''
parser: CHeaderParser = CHeaderParser()
parser.ParseContent(test_code)
generator: PythonStubGenerator = PythonStubGenerator(parser)
_vlog().info(generator.generate("test_header"))