import logging import os from pathlib import Path import shutil import subprocess import re import sys from typing import Sequence _invalid_file_char_regex = re.compile(r'[^a-zA-Z0-9_]') _variable_regex = re.compile(r'@([A-Z_]+)@') _logger = logging.getLogger(__name__) _root: Path def _exec_checked(args: Sequence[str], **kwargs) -> None: subprocess.run(args, stdout=sys.stdout, stderr=sys.stderr, check=True, **kwargs) def _exec_get_output(args: Sequence[str], **kwargs) -> str: return subprocess.run(args, text=True, check=True, capture_output=True).stdout def _prompt(prefix: str = '> ') -> str: sys.stdout.write(prefix) sys.stdout.flush() return sys.stdin.readline() def _escape_filename(name: str) -> str: return _invalid_file_char_regex.sub('_', name) def verify_tools() -> None: _logger.debug('Verifying all required tools are available...') success = True if shutil.which('git') is None: _logger.error('git not found in PATH') success = False if shutil.which('scons') is None: _logger.error('SCons not found in PATH') success = False if not success: raise RuntimeError('one or more required tools could not be found') def download_spp() -> None: _logger.debug('Checking if Scons++ is checked out...') output = _exec_get_output(['git', 'submodule', 'status', 'external/scons-plus-plus']) if output[0] in ('+', ' '): return assert output[0] == '-' _logger.info('SCons++ not checkout out yet, doing it now.') _exec_checked(['git', 'submodule', 'init']) _exec_checked(['git', 'submodule', 'update', 'external/scons-plus-plus']) def update_spp() -> None: _logger.debug('Updating SCons++ submodule...') os.chdir(_root / 'external/scons-plus-plus') try: _exec_checked(['git', 'fetch', 'origin', 'master']) _exec_checked(['git', 'checkout', 'master']) output = _exec_get_output(['git', 'status', '--porcelain']) if output.strip() == '': return finally: os.chdir(_root) _logger.info('Changes in SCons++ detected, creating commit.') _exec_checked(['git', 'commit', '-m', 'Updated Scons++', 'external/scons-plus-plus']) def _replace_in_file(file: Path, **replacements) -> None: _logger.debug('Patching file "%s"...', file) bakfile = file.with_suffix(f'{file.suffix}.bak') bakfile.unlink(missing_ok=True) file.rename(bakfile) with bakfile.open('r') as fin, file.open('w') as fout: for line in fin: def _repl(match: re.Match) -> str: return replacements.get(match.group(1), '') fout.write(_variable_regex.sub(_repl, line)) bakfile.unlink() def setup_project() -> None: project_name = '' while project_name == '': print('Please enter a name for your project.') project_name = _prompt().strip() module_name = project_name print(f'Please enter a name for the first module. Leave empty to use the project name ("{module_name}").') new_name = _prompt().strip() if new_name != '': module_name = new_name module_folder_name = _escape_filename(project_name.lower()) print(f'Please enter a folder name for the first module. Leave empty for "{module_folder_name}". Anything but [A-Za-z0-9_] will be replaced with underscores.') new_name = _prompt().strip() if new_name != '': module_folder_name = _escape_filename(new_name) # just enforce nice names module_exe_name = _escape_filename(module_name.lower()) print(f'Please enter a file name for the module executable. Leave empty for "{module_exe_name}". Omit the file suffix. Anything but [A-Za-z0-9_] will be replaced with underscores.') new_name = _prompt().strip() if new_name != '': module_exe_name = _escape_filename(new_name) _replace_in_file(_root / 'SConstruct', PROJECT_NAME=project_name, MODULE_FOLDER_NAME=module_folder_name) _replace_in_file(_root / 'private/spp_template/SModule', MODULE_NAME=module_name, EXE_NAME=module_exe_name) template_folder = _root / 'private/spp_template' module_folder = _root / 'private' / module_folder_name _logger.debug(f'Moving {template_folder} to {module_folder}.') _logger.info('Creating a git commit for the setup.') _exec_checked(['git', 'commit', '-m', 'Project setup', 'SConstruct', 'private']) if __name__ == '__main__': logging.basicConfig(level=logging.DEBUG, format='%(message)s') _root = Path(__file__).parent.parent os.chdir(_root) #try: verify_tools() download_spp() update_spp() setup_project() #except Exception as e: # _logger.error('There was an error running the script: %s.', e) # sys.exit(1)