109 lines
3.6 KiB
Python
109 lines
3.6 KiB
Python
|
import jinja2
|
||
|
import argparse
|
||
|
import os
|
||
|
import io
|
||
|
import re
|
||
|
import shlex
|
||
|
|
||
|
def validate_cpp_ver(ver: str) -> str:
|
||
|
if re.match(r'^[0-9]+$', ver) is not None: return ver
|
||
|
else: raise argparse.ArgumentTypeError('invalid version of C++ standard.')
|
||
|
|
||
|
def write_line(f: io.TextIOWrapper, val: str) -> None:
|
||
|
f.write(val)
|
||
|
f.write('\n')
|
||
|
|
||
|
# Reference: https://stackoverflow.com/questions/29213106/how-to-securely-escape-command-line-arguments-for-the-cmd-exe-shell-on-windows
|
||
|
def escape_for_cmd_exe(arg):
|
||
|
meta_re = re.compile(r'([()%!^"<>&|])')
|
||
|
return meta_re.sub('^\1', arg)
|
||
|
def escape_cmd_argument(arg):
|
||
|
if not arg or re.search(r'(["\s])', arg):
|
||
|
arg = '"' + arg.replace('"', r'\"') + '"'
|
||
|
return escape_for_cmd_exe(arg)
|
||
|
def escape_sh_argument(arg):
|
||
|
return shlex.quote(arg)
|
||
|
|
||
|
class ScriptSettings:
|
||
|
m_CppVersion: str
|
||
|
m_BuildDoc: bool
|
||
|
m_PIC: bool
|
||
|
|
||
|
def __init__(self, cpp_ver: str, build_doc: bool, pic: bool):
|
||
|
self.m_CppVersion = cpp_ver
|
||
|
self.m_BuildDoc = build_doc
|
||
|
self.m_PIC = pic
|
||
|
|
||
|
class TemplateRender:
|
||
|
m_Loader: jinja2.BaseLoader
|
||
|
m_Environment: jinja2.Environment
|
||
|
|
||
|
m_WinTemplate: jinja2.Template
|
||
|
m_LinuxTemplate: jinja2.Template
|
||
|
|
||
|
m_Settings: ScriptSettings
|
||
|
|
||
|
def __init__(self, settings: ScriptSettings) -> None:
|
||
|
self.m_Loader = jinja2.FileSystemLoader(self.__get_dir())
|
||
|
self.m_Environment = jinja2.Environment(loader=self.m_Loader)
|
||
|
|
||
|
self.m_WinTemplate = self.m_Environment.get_template('win_build.template.bat')
|
||
|
self.m_LinuxTemplate = self.m_Environment.get_template('linux_build.template.sh')
|
||
|
|
||
|
self.m_Settings = settings
|
||
|
|
||
|
def __get_dir(self) -> str:
|
||
|
return os.path.dirname(__file__)
|
||
|
|
||
|
def __escape_path(self, val: str, is_win: bool) -> str:
|
||
|
if is_win: return escape_cmd_argument(val)
|
||
|
else: return escape_sh_argument(val)
|
||
|
|
||
|
def __render(self, template: jinja2.Template, dest_file: str, is_win: bool) -> None:
|
||
|
with open(os.path.join(self.__get_dir(), dest_file), 'w', encoding='utf-8') as f:
|
||
|
f.write(template.render(
|
||
|
repo_root_dir = self.__escape_path(os.path.dirname(self.__get_dir()), is_win),
|
||
|
cpp_version = self.m_Settings.m_CppVersion,
|
||
|
build_doc = self.m_Settings.m_BuildDoc,
|
||
|
pic = settings.m_PIC
|
||
|
))
|
||
|
|
||
|
def render_win_script(self) -> None:
|
||
|
self.__render(self.m_WinTemplate, 'win_build.bat', True)
|
||
|
|
||
|
def render_linux_script(self) -> None:
|
||
|
self.__render(self.m_LinuxTemplate, 'linux_build.sh', False)
|
||
|
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
# parse argument
|
||
|
parser = argparse.ArgumentParser(
|
||
|
prog='YYCC Windows Build Script Generator',
|
||
|
description='YYCC Windows Build Script Generator'
|
||
|
)
|
||
|
parser.add_argument(
|
||
|
'-c', '--cpp',
|
||
|
action='store', default='17', dest='cpp', type=validate_cpp_ver,
|
||
|
help='The version of C++ standard used when building.'
|
||
|
)
|
||
|
parser.add_argument(
|
||
|
'-d', '--build-doc',
|
||
|
action='store_true', dest='build_doc',
|
||
|
help='Build YYCC without documentation.'
|
||
|
)
|
||
|
parser.add_argument(
|
||
|
'-p', '--pic',
|
||
|
action='store_true', dest='pic',
|
||
|
help='Enable Position Independent Code flag on non-Windows platform. This is crucial for compiling dynamic library using this library.'
|
||
|
)
|
||
|
args = parser.parse_args()
|
||
|
|
||
|
# build settings
|
||
|
settings = ScriptSettings(args.cpp, args.build_doc, args.pic)
|
||
|
# build template render and render result
|
||
|
render = TemplateRender(settings)
|
||
|
render.render_win_script()
|
||
|
render.render_linux_script()
|
||
|
|
||
|
|