148 lines
5.5 KiB
Python
148 lines
5.5 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 verify_unchanged() -> None:
|
|
output = _exec_get_output(['git', 'status', '--porcelain'])
|
|
if output != '':
|
|
raise RuntimeError('There are uncommitted changes. Commit, stash or revert them before running this script.')
|
|
|
|
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}.')
|
|
shutil.move(template_folder, module_folder)
|
|
|
|
_logger.info('Creating a git commit for the setup.')
|
|
_exec_checked(['git', 'add', '.'])
|
|
_exec_checked(['git', 'commit', '-m', 'Project setup'])
|
|
|
|
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()
|
|
verify_unchanged()
|
|
setup_project()
|
|
setup_git_remote()
|
|
except Exception as e:
|
|
_logger.error('There was an error running the script: %s', e)
|
|
sys.exit(1)
|