aboutsummaryrefslogtreecommitdiffstats
path: root/src/editors/sublime/build.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/editors/sublime/build.py')
-rwxr-xr-xsrc/editors/sublime/build.py148
1 files changed, 148 insertions, 0 deletions
diff --git a/src/editors/sublime/build.py b/src/editors/sublime/build.py
new file mode 100755
index 000000000..3081fdc50
--- /dev/null
+++ b/src/editors/sublime/build.py
@@ -0,0 +1,148 @@
+#!/usr/bin/env python3
+
+"""
+Build helper script for Botan's Sublime Text integration
+
+(C) 2022 Jack Lloyd
+(C) 2022 René Meusel (neXenio GmbH)
+
+Botan is released under the Simplified BSD License (see license.txt)
+"""
+
+import argparse
+import multiprocessing
+import subprocess
+import sys
+import os
+import re
+
+
+class BuildError(Exception):
+ pass
+
+
+def run_cmd(cmd):
+ if isinstance(cmd, str):
+ print('> running: ' + cmd)
+ shell = True
+ else:
+ print('> running: ' + ' '.join(cmd))
+ shell = False
+ sys.stdout.flush()
+
+ try:
+ subprocess.run(cmd, shell=shell, check=True)
+ except subprocess.CalledProcessError:
+ raise BuildError('Command failed, aborting...')
+
+
+def _find_regex_in_makefile(regex):
+ if not os.path.exists('Makefile'):
+ raise BuildError(
+ 'No Makefile found. Initial ./configure.py invocation must be performed manually.')
+
+ with open('Makefile', 'r') as f:
+ return re.search(regex, f.read())
+
+
+def _retrieve_test_binary_name():
+ match = _find_regex_in_makefile(r'TEST\s*=\s*([^\n]+)\n')
+ if not match:
+ raise BuildError('Test binary name not found in Makefile')
+ test_file = os.path.split(match.group(1))[1]
+ if not test_file:
+ raise BuildError(
+ 'Cannot make sense of test binary name: ' + match.group(0))
+
+ return test_file
+
+
+def _retrieve_configure_command():
+ match = _find_regex_in_makefile(r'\'(configure\.py.+)\'\n')
+ if not match:
+ raise BuildError('configure.py command not found in Makefile')
+ return match.group(1)
+
+
+def reconfigure():
+ run_cmd("./" + _retrieve_configure_command())
+
+
+def build(target=''):
+ reconfigure()
+ cmd = ['make', '-j', str(multiprocessing.cpu_count())]
+ if target:
+ cmd.append(target)
+ run_cmd(cmd)
+
+
+def _parse_test_file(test_file):
+ if not re.search(r'.+/tests/.+\.cpp', test_file):
+ raise BuildError(
+ 'Given file path is not a Botan unit test: ' + test_file)
+
+ with open(test_file, 'r') as f:
+ find_test_registration = \
+ re.compile(
+ r'BOTAN_REGISTER_TEST(_FN)?\s*\(\s*\"(.+)\",\s*\"(.+)\",[^)]+\)')
+
+ matches = find_test_registration.findall(f.read())
+ tests = [match[-1] for match in matches]
+
+ if not tests:
+ raise BuildError(
+ 'Failed to find a BOTAN_REGISTER_TEST in the given test file: ' + test_file)
+
+ return tests
+
+
+def unittests(test_file):
+ tests = _parse_test_file(test_file) if test_file else []
+
+ build('tests')
+ run_cmd(['./' + _retrieve_test_binary_name()] + tests)
+
+
+def apply_astyle_format(format_file):
+ ext = os.path.splitext(format_file)[1]
+ if ext not in ['.cpp', '.h']:
+ raise BuildError(
+ "Refuse to format source files that appear to be non-C++")
+
+ try:
+ run_cmd(['astyle',
+ '--suffix=none', # do not create a backup copy of the unformatted file
+ '--project=src/configs/astyle.rc',
+ format_file])
+ except FileNotFoundError:
+ raise BuildError(
+ "astyle utility not installed, cannot apply formatting")
+
+
+def main():
+ parser = argparse.ArgumentParser(description='Sublime build helper')
+ parser.add_argument('job', type=str)
+ parser.add_argument('--project-root', type=str, required=True)
+ parser.add_argument('--test-file', type=str, default='')
+ parser.add_argument('--format-file', type=str, default='')
+
+ opts = parser.parse_args()
+
+ os.chdir(opts.project_root)
+
+ if opts.job == 'all':
+ build()
+ elif opts.job == 'test':
+ unittests(opts.test_file)
+ elif opts.job == 'format':
+ apply_astyle_format(opts.format_file)
+ else:
+ raise RuntimeError('Unknown build job: ' + opts.job)
+
+
+if __name__ == '__main__':
+ try:
+ main()
+ except BuildError as msg:
+ print(msg, file=sys.stderr)
+ sys.exit(1)