mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-02-06 15:47:06 +08:00
consolidating enums generation code
This commit is contained in:
parent
1ae25e469b
commit
b85f2ea0d1
1
.idea/dictionaries/ericf.xml
generated
1
.idea/dictionaries/ericf.xml
generated
@ -1341,6 +1341,7 @@
|
|||||||
<w>megalint</w>
|
<w>megalint</w>
|
||||||
<w>memfunctions</w>
|
<w>memfunctions</w>
|
||||||
<w>menubar</w>
|
<w>menubar</w>
|
||||||
|
<w>metamakefile</w>
|
||||||
<w>metaprogramming</w>
|
<w>metaprogramming</w>
|
||||||
<w>metascan</w>
|
<w>metascan</w>
|
||||||
<w>meteorshower</w>
|
<w>meteorshower</w>
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
# Released under the MIT License. See LICENSE for details.
|
# Released under the MIT License. See LICENSE for details.
|
||||||
"""Enums generated by tools/update_python_enums_module in ba-internal."""
|
"""Enum vals generated by batools.pythonenumsmodule; do not edit by hand."""
|
||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
|
|||||||
@ -607,6 +607,7 @@
|
|||||||
<w>memcpy</w>
|
<w>memcpy</w>
|
||||||
<w>meshdata</w>
|
<w>meshdata</w>
|
||||||
<w>messagebox</w>
|
<w>messagebox</w>
|
||||||
|
<w>metamakefile</w>
|
||||||
<w>meth</w>
|
<w>meth</w>
|
||||||
<w>mhbegin</w>
|
<w>mhbegin</w>
|
||||||
<w>mhend</w>
|
<w>mhend</w>
|
||||||
|
|||||||
@ -895,3 +895,9 @@ def xcode_build_path() -> None:
|
|||||||
project_path=project_path,
|
project_path=project_path,
|
||||||
configuration=configuration)
|
configuration=configuration)
|
||||||
print(path)
|
print(path)
|
||||||
|
|
||||||
|
|
||||||
|
def update_python_enums_module() -> None:
|
||||||
|
"""Update our procedurally generated python enums."""
|
||||||
|
from batools.pythonenumsmodule import update
|
||||||
|
update(projroot=str(PROJROOT), check='--check' in sys.argv)
|
||||||
|
|||||||
172
tools/batools/pythonenumsmodule.py
Executable file
172
tools/batools/pythonenumsmodule.py
Executable file
@ -0,0 +1,172 @@
|
|||||||
|
#!/usr/bin/env python3.8
|
||||||
|
# Released under the MIT License. See LICENSE for details.
|
||||||
|
#
|
||||||
|
"""Procedurally regenerates our python enums module.
|
||||||
|
|
||||||
|
This scans core/types.h for tagged C++ enum types and generates corresponding
|
||||||
|
python enums for them.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from efro.terminal import Clr
|
||||||
|
from efrotools import get_public_license
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from typing import Optional, List, Tuple
|
||||||
|
|
||||||
|
OUTPUT_FILENAME = 'assets/src/ba_data/python/ba/_enums.py'
|
||||||
|
|
||||||
|
|
||||||
|
def camel_case_convert(name: str) -> str:
|
||||||
|
"""Convert camel-case text to upcase-with-underscores."""
|
||||||
|
str1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
|
||||||
|
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', str1).upper()
|
||||||
|
|
||||||
|
|
||||||
|
def _gen_enums() -> str:
|
||||||
|
out = ''
|
||||||
|
enum_lnums: List[int] = []
|
||||||
|
with open('src/ballistica/core/types.h') as infile:
|
||||||
|
lines = infile.read().splitlines()
|
||||||
|
|
||||||
|
# Tally up all places tagged for exporting python enums.
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
if '// BA_EXPORT_PYTHON_ENUM' in line:
|
||||||
|
enum_lnums.append(i + 1)
|
||||||
|
|
||||||
|
# Now export each of them.
|
||||||
|
for lnum in enum_lnums:
|
||||||
|
|
||||||
|
doclines, lnum = _parse_doc_lines(lines, lnum)
|
||||||
|
enum_name = _parse_name(lines, lnum)
|
||||||
|
|
||||||
|
out += f'\n\nclass {enum_name}(Enum):\n """'
|
||||||
|
out += '\n '.join(doclines)
|
||||||
|
if len(doclines) > 1:
|
||||||
|
out += '\n """\n'
|
||||||
|
else:
|
||||||
|
out += '"""\n'
|
||||||
|
|
||||||
|
lnumend = _find_enum_end(lines, lnum)
|
||||||
|
out = _parse_values(lines, lnum, lnumend, out)
|
||||||
|
|
||||||
|
# Clear lines with only spaces.
|
||||||
|
return ('\n'.join('' if line == ' ' else line
|
||||||
|
for line in out.splitlines()) + '\n')
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_name(lines: List[str], lnum: int) -> str:
|
||||||
|
bits = lines[lnum].split(' ')
|
||||||
|
if (len(bits) != 4 or bits[0] != 'enum' or bits[1] != 'class'
|
||||||
|
or bits[3] != '{'):
|
||||||
|
raise Exception(f'Unexpected format for enum on line {lnum + 1}.')
|
||||||
|
enum_name = bits[2]
|
||||||
|
return enum_name
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_values(lines: List[str], lnum: int, lnumend: int, out: str) -> str:
|
||||||
|
val = 0
|
||||||
|
for i in range(lnum + 1, lnumend):
|
||||||
|
line = lines[i]
|
||||||
|
if line.strip().startswith('//'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Strip off any trailing comment.
|
||||||
|
if '//' in line:
|
||||||
|
line = line.split('//')[0].strip()
|
||||||
|
|
||||||
|
# Strip off any trailing comma.
|
||||||
|
if line.endswith(','):
|
||||||
|
line = line[:-1].strip()
|
||||||
|
|
||||||
|
# If they're explicitly assigning a value, parse it.
|
||||||
|
if '=' in line:
|
||||||
|
splits = line.split()
|
||||||
|
if (len(splits) != 3 or splits[1] != '='
|
||||||
|
or not splits[2].isnumeric()):
|
||||||
|
raise RuntimeError(f'Unable to parse enum value for: {line}')
|
||||||
|
name = splits[0]
|
||||||
|
val = int(splits[2])
|
||||||
|
else:
|
||||||
|
name = line
|
||||||
|
|
||||||
|
# name = line.split(',')[0].split('//')[0].strip()
|
||||||
|
if not name.startswith('k') or len(name) < 2:
|
||||||
|
raise RuntimeError(f"Expected name to start with 'k'; got {name}")
|
||||||
|
|
||||||
|
# We require kLast to be the final value
|
||||||
|
# (C++ requires this for bounds checking)
|
||||||
|
if i == lnumend - 1:
|
||||||
|
if name != 'kLast':
|
||||||
|
raise RuntimeError(
|
||||||
|
f'Expected last enum value of kLast; found {name}.')
|
||||||
|
continue
|
||||||
|
name = camel_case_convert(name[1:])
|
||||||
|
out += f' {name} = {val}\n'
|
||||||
|
val += 1
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def _find_enum_end(lines: List[str], lnum: int) -> int:
|
||||||
|
lnumend = lnum + 1
|
||||||
|
while True:
|
||||||
|
if lnumend > len(lines) - 1:
|
||||||
|
raise Exception(f'No end found for enum on line {lnum + 1}.')
|
||||||
|
if '};' in lines[lnumend]:
|
||||||
|
break
|
||||||
|
lnumend += 1
|
||||||
|
return lnumend
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_doc_lines(lines: List[str], lnum: int) -> Tuple[List[str], int]:
|
||||||
|
|
||||||
|
# First parse the doc-string
|
||||||
|
doclines: List[str] = []
|
||||||
|
lnumorig = lnum
|
||||||
|
while True:
|
||||||
|
if lnum > len(lines) - 1:
|
||||||
|
raise Exception(
|
||||||
|
f'No end found for enum docstr line {lnumorig + 1}.')
|
||||||
|
if lines[lnum].startswith('enum class '):
|
||||||
|
break
|
||||||
|
if not lines[lnum].startswith('///'):
|
||||||
|
raise Exception(f'Invalid docstr at line {lnum + 1}.')
|
||||||
|
doclines.append(lines[lnum][4:])
|
||||||
|
lnum += 1
|
||||||
|
return doclines, lnum
|
||||||
|
|
||||||
|
|
||||||
|
def update(projroot: str, check: bool) -> None:
|
||||||
|
"""Main script entry point."""
|
||||||
|
|
||||||
|
# Operate out of root dist dir for consistency.
|
||||||
|
os.chdir(projroot)
|
||||||
|
|
||||||
|
fname = OUTPUT_FILENAME
|
||||||
|
existing: Optional[str]
|
||||||
|
try:
|
||||||
|
with open(fname, 'r') as infile:
|
||||||
|
existing = infile.read()
|
||||||
|
except Exception:
|
||||||
|
existing = None
|
||||||
|
|
||||||
|
out = (get_public_license('python') +
|
||||||
|
f'\n"""Enum vals generated by {__name__}; do not edit by hand."""'
|
||||||
|
f'\n\nfrom enum import Enum\n')
|
||||||
|
|
||||||
|
out += _gen_enums()
|
||||||
|
|
||||||
|
if out == existing:
|
||||||
|
print('Python enums module is up to date.')
|
||||||
|
else:
|
||||||
|
if check:
|
||||||
|
print(Clr.SRED + 'ERROR: file out of date: ' + fname + Clr.RST)
|
||||||
|
sys.exit(255)
|
||||||
|
print(Clr.SBLU + 'Generating: ' + fname + Clr.RST)
|
||||||
|
with open(fname, 'w') as outfile:
|
||||||
|
outfile.write(out)
|
||||||
@ -691,9 +691,13 @@ class Updater:
|
|||||||
'Error checking/updating resources Makefile.') from exc
|
'Error checking/updating resources Makefile.') from exc
|
||||||
|
|
||||||
def _update_python_enums_module(self) -> None:
|
def _update_python_enums_module(self) -> None:
|
||||||
if os.path.exists('tools/update_python_enums_module'):
|
# FIXME: should support running this in public too.
|
||||||
if os.system('tools/update_python_enums_module' +
|
if not self._public:
|
||||||
self._checkarg) != 0:
|
try:
|
||||||
print(f'{Clr.RED}Error checking/updating'
|
subprocess.run(
|
||||||
f' python enums module.{Clr.RST}')
|
['tools/pcommand', 'update_python_enums_module'] +
|
||||||
sys.exit(255)
|
self._checkarglist,
|
||||||
|
check=True)
|
||||||
|
except Exception as exc:
|
||||||
|
raise CleanError(
|
||||||
|
'Error checking/updating python enums module.') from exc
|
||||||
|
|||||||
@ -41,7 +41,7 @@ from batools.pcommand import (
|
|||||||
cmake_prep_dir, gen_binding_code, gen_flat_data_code, wsl_path_to_win,
|
cmake_prep_dir, gen_binding_code, gen_flat_data_code, wsl_path_to_win,
|
||||||
wsl_build_check_win_drive, win_ci_binary_build, genchangelog,
|
wsl_build_check_win_drive, win_ci_binary_build, genchangelog,
|
||||||
android_sdk_utils, update_resources_makefile, update_meta_makefile,
|
android_sdk_utils, update_resources_makefile, update_meta_makefile,
|
||||||
xcode_build_path)
|
xcode_build_path, update_python_enums_module)
|
||||||
# pylint: enable=unused-import
|
# pylint: enable=unused-import
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user