diff options
-rw-r--r-- | .gitignore | 6 | ||||
-rwxr-xr-x | configure.py | 3 | ||||
-rw-r--r-- | src/editors/README.md | 4 | ||||
-rw-r--r-- | src/editors/editorconfig/.editorconfig | 17 | ||||
-rw-r--r-- | src/editors/sublime/README.md | 19 | ||||
-rwxr-xr-x | src/editors/sublime/build.py | 148 | ||||
-rw-r--r-- | src/editors/sublime/sublime-project | 57 | ||||
-rwxr-xr-x | src/scripts/ci_build.py | 3 |
8 files changed, 255 insertions, 2 deletions
diff --git a/.gitignore b/.gitignore index 8c7a071b7..7973cba7c 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,11 @@ vgcore.* \#*\# .\#* +# Editor configuration files (top level) +/*.sublime-project +/*.sublime-workspace +/.editorconfig + # Archive files *.tgz *.tar @@ -41,6 +46,7 @@ vgcore.* *.pyc .DS_Store *.swp +/*.cache # ctags/etags files /TAGS diff --git a/configure.py b/configure.py index 66a64149f..beed9bbc6 100755 --- a/configure.py +++ b/configure.py @@ -1948,7 +1948,8 @@ def create_template_vars(source_paths, build_paths, options, modules, cc, arch, # Cut absolute path from main executable (e.g. configure.py or python interpreter) # to get the same result when configuring the same thing on different machines main_executable = os.path.basename(sys.argv[0]) - return ' '.join([main_executable] + sys.argv[1:]) + quoted_args = [arg if ' ' not in arg else '\'' + arg + '\'' for arg in sys.argv[1:]] + return ' '.join([main_executable] + quoted_args) def cmake_escape(s): return s.replace('(', '\\(').replace(')', '\\)') diff --git a/src/editors/README.md b/src/editors/README.md new file mode 100644 index 000000000..30f4e3d14 --- /dev/null +++ b/src/editors/README.md @@ -0,0 +1,4 @@ +# Editor Configurations + +Those editor configurations and/or project files can be used to set up your favourite editor for developing Botan. +To enable any of those, just symlink the respective files into the repository root. diff --git a/src/editors/editorconfig/.editorconfig b/src/editors/editorconfig/.editorconfig new file mode 100644 index 000000000..0dab9d754 --- /dev/null +++ b/src/editors/editorconfig/.editorconfig @@ -0,0 +1,17 @@ +# see https://EditorConfig.org +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_style = space +trim_trailing_whitespace = true + +[*.{cpp,h}] +indent_size = 3 +tab_width = 3 + +[*.py] +indent_size = 4 +tab_width = 47 diff --git a/src/editors/sublime/README.md b/src/editors/sublime/README.md new file mode 100644 index 000000000..84edfbd55 --- /dev/null +++ b/src/editors/sublime/README.md @@ -0,0 +1,19 @@ +# Sublime Text Project + +This provides a few convenience integrations for building, testing and debugging the library. + +## Configuring and building + +The build integrations simply run an already pre-configured `Makefile`. Hence, before build integrations work, a manual `./configure.py` must be executed. After that, the sublime integration will opportunistically re-configure using the same `./configure.py` arguments. + +## Running a specific test + +There is a special build command that runs the unit test currently in Sublime's active focus. Note that this command opportunistically re-configures and builds the test binary first. Usage of a compiler cache is hence strongly recommended. + +## Running all tests + +Builds and executes all unit tests. + +## Applying Source Formatting + +Use Botan's astyle formatting rules on the C++ or header file that is currently in active focus. 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) diff --git a/src/editors/sublime/sublime-project b/src/editors/sublime/sublime-project new file mode 100644 index 000000000..3708a9cd2 --- /dev/null +++ b/src/editors/sublime/sublime-project @@ -0,0 +1,57 @@ +{ + "folders": + [ + { + "path": ".", + "folder_exclude_patterns": [ ".cache", "build"] + } + ], + "settings": + { + "default_encoding": "UTF-8", + "detect_indentation": false, + "rulers": [80], + "translate_tabs_to_spaces": true, + "trim_automatic_white_space": true, + "trim_trailing_white_space_on_save": "all" + }, + "build_systems": [ + { + "name": "Build all Targets", + "cmd": [ + "$project_path/src/editors/sublime/build.py", + "--project-root", "$project_path", + "all"], + "cancel": { "kill": true } + }, + { + "name": "Apply Formatting to Current File", + "cmd": [ + "$project_path/src/editors/sublime/build.py", + "--project-root", "$project_path", + "--format-file", "$file", + "format"], + "file_patterns": [ "*.cpp", "*.h" ] + }, + { + "name": "Run Tests", + "cmd": [ + "$project_path/src/editors/sublime/build.py", + "--project-root", "$project_path", + "test" + ], + "cancel": { "kill": true } + }, + { + "name": "Run Current Test File", + "cmd":[ + "$project_path/src/editors/sublime/build.py", + "--project-root", "$project_path", + "--test-file", "$file", + "test" + ], + "file_patterns": [ "test_*.cpp", "unit_*.cpp" ], + "cancel": { "kill": true } + } + ] +} diff --git a/src/scripts/ci_build.py b/src/scripts/ci_build.py index 700203471..9d7ee7863 100755 --- a/src/scripts/ci_build.py +++ b/src/scripts/ci_build.py @@ -522,7 +522,8 @@ def main(args=None): 'src/scripts/test_fuzzers.py', 'src/scripts/test_cli.py', 'src/scripts/python_unittests.py', - 'src/scripts/python_unittests_unix.py'] + 'src/scripts/python_unittests_unix.py', + 'src/editors/sublime/build.py'] full_paths = [os.path.join(root_dir, s) for s in py_scripts] |