ballistica/tools/batools/metamakefile.py
2021-06-13 11:41:15 -05:00

145 lines
5.0 KiB
Python
Executable File

# Released under the MIT License. See LICENSE for details.
#
"""Procedurally regenerates our code Makefile.
This Makefiles builds our generated code such as encrypted python strings,
node types, etc).
"""
from __future__ import annotations
import os
import sys
from typing import TYPE_CHECKING
from dataclasses import dataclass
from efro.terminal import Clr
if TYPE_CHECKING:
from typing import Optional, Set, List, Dict, Any, Tuple
# These paths need to be relative to the dir we're writing the Makefile to.
TOOLS_DIR = '../../tools'
ROOT_DIR = '../..'
OUT_DIR_CPP = '../ballistica/generated'
@dataclass
class Target:
"""A target to be added to the makefile."""
src: List[str]
dst: str
cmd: str
public: bool
mkdir: bool = False
def update(projroot: str, check: bool) -> None:
"""Main script entry point."""
from efrotools import get_public_license
# Operate out of root dist dir for consistency.
os.chdir(projroot)
targets = _generate_targets()
# Write Makefile.
fname = 'src/meta/Makefile'
existing: Optional[str]
try:
with open(fname, 'r') as infile:
existing = infile.read()
except Exception:
existing = None
out = (get_public_license('makefile') +
f'\n#\n# This file was generated by {__name__}.\n\n')
public_dsts: Set[str] = set()
private_dsts: Set[str] = set()
for target in targets:
(public_dsts if target.public else private_dsts).add(target.dst)
out += 'all: cpp_sources\n'
out += ('\n'
'cpp_sources: ' + ' \\\n '.join(
dst.replace(' ', '\\ ') for dst in sorted(public_dsts)) + '\n')
out += ('\n'
'#__PUBSYNC_STRIP_BEGIN__\n'
'cpp_sources: ' + ' \\\n '.join(
dst.replace(' ', '\\ ') for dst in sorted(private_dsts)) +
'\n' + '#__PUBSYNC_STRIP_END__\n')
out += f'\nclean:\n\t@rm -rf {OUT_DIR_CPP}\n\n'
var_num = 1
for public in [True, False]:
if not public:
out += '#__PUBSYNC_STRIP_BEGIN__\n\n'
for target in targets:
if target.public != public:
continue
if bool(False) and ' ' in target.dst:
out += ('TARGET_' + str(var_num) + ' = ' +
target.dst.replace(' ', '\\ ') + '\n${TARGET_' +
str(var_num) + '}')
var_num += 1
else:
out += target.dst.replace(' ', '\\ ')
out += ' : ' + ' '.join(s for s in target.src) + (
('\n\t@mkdir -p "' + os.path.dirname(target.dst) +
'"') if target.mkdir else '') + '\n\t@' + target.cmd + '\n\n'
if not public:
out += '#__PUBSYNC_STRIP_END__\n'
if out == existing:
print('Meta Makefile 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)
def _generate_targets() -> List[Target]:
targets: List[Target] = []
# Manually define a few entries and automatically add one for each
# of our ba_embedded_*.py files.
for pkg, public in [('bameta', True), ('bametainternal', False)]:
entries: List[Tuple[str, str]] = []
for fname in os.listdir(f'src/meta/{pkg}/python_embedded'):
if (fname.endswith('.py') and fname != '__init__.py'
and 'flycheck' not in fname):
entries.append((os.path.splitext(fname)[0],
'BINDING' if fname == 'binding.py' else
'FLAT_DATA' if public else 'ENCRYPTED_DATA'))
# Generate targets from our entries.
for name, out_type in entries:
extra_sources: List[str] = []
if out_type == 'FLAT_DATA':
cmd = os.path.join(
TOOLS_DIR,
f'pcommand gen_flat_data_code $< $@ {name}_code')
extra_sources.append(os.path.join(TOOLS_DIR,
'batools/meta.py'))
elif out_type == 'BINDING':
cmd = os.path.join(TOOLS_DIR,
'pcommand gen_binding_code $< $@')
extra_sources.append(os.path.join(TOOLS_DIR,
'batools/meta.py'))
else:
cmd = os.path.join(TOOLS_DIR,
'pcommand gen_encrypted_python_code $< $@')
extra_sources.append(
os.path.join(TOOLS_DIR, 'batoolsinternal/meta.py'))
targets.append(
Target(src=[f'{pkg}/python_embedded/{name}.py'] +
extra_sources,
dst=os.path.join(OUT_DIR_CPP, 'python_embedded',
f'{name}.inc'),
cmd=cmd,
public=public))
return targets