Added script for initial project setup.
This commit is contained in:
parent
6c9e111a6c
commit
d9371e9cd4
125
tools/init_project.py
Normal file
125
tools/init_project.py
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
|
||||||
|
|
||||||
|
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'])
|
||||||
|
|
||||||
|
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)
|
Loading…
x
Reference in New Issue
Block a user