spp_template/tools/init_project.py

140 lines
5.1 KiB
Python

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), '<invalid variable>')
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'])
def setup_git_remote() -> None:
current_url = _exec_get_output(['git', 'remote', 'get-url', 'origin'])
if 'spp_template' not in current_url:
_logger.debug('Remote already set up.')
return
remote_url = ''
while remote_url == '':
print('Please enter a new URL for your git remote.')
remote_url = _prompt().strip()
_exec_checked(['git', 'remote', 'set-url', 'origin', remote_url])
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()
setup_git_remote()
except Exception as e:
_logger.error('There was an error running the script: %s.', e)
sys.exit(1)