mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-01-26 17:03:14 +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>memfunctions</w>
|
||||
<w>menubar</w>
|
||||
<w>metamakefile</w>
|
||||
<w>metaprogramming</w>
|
||||
<w>metascan</w>
|
||||
<w>meteorshower</w>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
# 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
|
||||
|
||||
|
||||
@ -607,6 +607,7 @@
|
||||
<w>memcpy</w>
|
||||
<w>meshdata</w>
|
||||
<w>messagebox</w>
|
||||
<w>metamakefile</w>
|
||||
<w>meth</w>
|
||||
<w>mhbegin</w>
|
||||
<w>mhend</w>
|
||||
|
||||
@ -895,3 +895,9 @@ def xcode_build_path() -> None:
|
||||
project_path=project_path,
|
||||
configuration=configuration)
|
||||
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
|
||||
|
||||
def _update_python_enums_module(self) -> None:
|
||||
if os.path.exists('tools/update_python_enums_module'):
|
||||
if os.system('tools/update_python_enums_module' +
|
||||
self._checkarg) != 0:
|
||||
print(f'{Clr.RED}Error checking/updating'
|
||||
f' python enums module.{Clr.RST}')
|
||||
sys.exit(255)
|
||||
# FIXME: should support running this in public too.
|
||||
if not self._public:
|
||||
try:
|
||||
subprocess.run(
|
||||
['tools/pcommand', 'update_python_enums_module'] +
|
||||
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,
|
||||
wsl_build_check_win_drive, win_ci_binary_build, genchangelog,
|
||||
android_sdk_utils, update_resources_makefile, update_meta_makefile,
|
||||
xcode_build_path)
|
||||
xcode_build_path, update_python_enums_module)
|
||||
# pylint: enable=unused-import
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user