# 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