From ff0286195eeb4dc659863af4afbdf9ddd4d53a1b Mon Sep 17 00:00:00 2001 From: aqua Date: Sun, 18 Aug 2024 15:08:13 +0300 Subject: Moved interface_generator script to bin --- tools/interface_generator/BUILD.bazel | 38 ---------- tools/interface_generator/bin/BUILD.bazel | 57 +++++++++++++++ .../bin/interface_definition.py | 47 ++++++++++++ .../interface_generator/bin/interface_generator.py | 84 ++++++++++++++++++++++ tools/interface_generator/bin/templates.py | 12 ++++ .../bin/templates/__c_functions.mako | 4 ++ .../bin/templates/__c_system_include.mako | 4 ++ .../bin/templates/__c_types.mako | 6 ++ .../bin/templates/__header.mako | 5 ++ .../bin/templates/interface.h.mako | 8 +++ .../bin/templates/interface_mock.cpp.mako | 32 +++++++++ .../bin/templates/interface_mock.hpp.mako | 26 +++++++ .../interface_generator/bin/templates_unittest.py | 26 +++++++ tools/interface_generator/defs.bzl | 2 +- tools/interface_generator/interface_definition.py | 47 ------------ tools/interface_generator/interface_generator.py | 83 --------------------- tools/interface_generator/templates.py | 12 ---- .../templates/__c_functions.mako | 4 -- .../templates/__c_system_include.mako | 4 -- tools/interface_generator/templates/__c_types.mako | 6 -- tools/interface_generator/templates/__header.mako | 5 -- .../interface_generator/templates/interface.h.mako | 8 --- .../templates/interface_mock.cpp.mako | 32 --------- .../templates/interface_mock.hpp.mako | 26 ------- tools/interface_generator/templates_unittest.py | 21 ------ 25 files changed, 312 insertions(+), 287 deletions(-) create mode 100644 tools/interface_generator/bin/BUILD.bazel create mode 100644 tools/interface_generator/bin/interface_definition.py create mode 100755 tools/interface_generator/bin/interface_generator.py create mode 100644 tools/interface_generator/bin/templates.py create mode 100644 tools/interface_generator/bin/templates/__c_functions.mako create mode 100644 tools/interface_generator/bin/templates/__c_system_include.mako create mode 100644 tools/interface_generator/bin/templates/__c_types.mako create mode 100644 tools/interface_generator/bin/templates/__header.mako create mode 100644 tools/interface_generator/bin/templates/interface.h.mako create mode 100644 tools/interface_generator/bin/templates/interface_mock.cpp.mako create mode 100644 tools/interface_generator/bin/templates/interface_mock.hpp.mako create mode 100755 tools/interface_generator/bin/templates_unittest.py delete mode 100644 tools/interface_generator/interface_definition.py delete mode 100755 tools/interface_generator/interface_generator.py delete mode 100644 tools/interface_generator/templates.py delete mode 100644 tools/interface_generator/templates/__c_functions.mako delete mode 100644 tools/interface_generator/templates/__c_system_include.mako delete mode 100644 tools/interface_generator/templates/__c_types.mako delete mode 100644 tools/interface_generator/templates/__header.mako delete mode 100644 tools/interface_generator/templates/interface.h.mako delete mode 100644 tools/interface_generator/templates/interface_mock.cpp.mako delete mode 100644 tools/interface_generator/templates/interface_mock.hpp.mako delete mode 100755 tools/interface_generator/templates_unittest.py (limited to 'tools') diff --git a/tools/interface_generator/BUILD.bazel b/tools/interface_generator/BUILD.bazel index e62d7b9..8c4bde6 100644 --- a/tools/interface_generator/BUILD.bazel +++ b/tools/interface_generator/BUILD.bazel @@ -1,7 +1,4 @@ load("@rules_python//python:pip.bzl", "compile_pip_requirements") -load("@pip//:requirements.bzl", "requirement") -load(":defs.bzl", "generate_interface") -load("//private:defs.bzl", "py_pytest") package(default_visibility = ["//visibility:public"]) @@ -12,40 +9,5 @@ compile_pip_requirements( requirements_txt = "requirements_lock.txt", ) -py_binary( - name = "interface_generator", - srcs = [ - "interface_definition.py", - "interface_generator.py", - "templates.py", - ], - data = glob(["templates/*"]) + ["LICENSE.md"], - main = "interface_generator.py", - deps = ["@pip//mako"], -) - -# interface generator python tests -py_test( - name = "templates_unittest", - srcs = [ - "templates.py", - "templates_unittest.py", - ], - data = glob(["templates/*"]), - deps = ["@pip//mako"], -) - -py_pytest( - name = "pytest", - srcs = [ - "interface_definition.py", - "interface_generator.py", - "templates.py", - ], - deps = [ - requirement("mako"), - ], -) - # make license available to test package exports_files(["LICENSE.md"]) diff --git a/tools/interface_generator/bin/BUILD.bazel b/tools/interface_generator/bin/BUILD.bazel new file mode 100644 index 0000000..c593b93 --- /dev/null +++ b/tools/interface_generator/bin/BUILD.bazel @@ -0,0 +1,57 @@ +load("@pip//:requirements.bzl", "requirement") +load("//private:defs.bzl", "py_pytest") + +""" interface definition """ + +py_library( + name = "interface_definition", + srcs = ["interface_definition.py"], +) + +""" templates """ + +py_library( + name = "templates", + srcs = ["templates.py"], + data = glob(["templates/*"]), +) + +py_test( + name = "templates_unittest", + srcs = ["templates_unittest.py"], + imports = ["."], + deps = [":templates"], +) + +""" interface_generator """ + +py_binary( + name = "interface_generator", + srcs = [ + "interface_generator.py", + "templates.py", + ], + data = ["//:LICENSE.md"], + imports = ["."], + visibility = ["//visibility:public"], + deps = [ + ":interface_definition", + ":templates", + requirement("mako"), + ], +) + +""" pytest """ + +py_pytest( + name = "pytest", + srcs = [ + "interface_definition.py", + "interface_generator.py", + "templates.py", + ], + deps = [ + ":interface_generator", + requirement("mako"), + ], +) diff --git a/tools/interface_generator/bin/interface_definition.py b/tools/interface_generator/bin/interface_definition.py new file mode 100644 index 0000000..3b9c4a3 --- /dev/null +++ b/tools/interface_generator/bin/interface_definition.py @@ -0,0 +1,47 @@ +""" +interface_definition.py +""" + + +def __read_license(path): + """read and starrify a license""" + license_text = "" + with open(path, encoding="utf-8") as license_file: + license_text = "".join( + [ + f" * { line.rstrip().ljust(72)[:72] } * \n" + for line in license_file.readlines() + ] + ).rstrip() + + return license_text + + +def parse(args): + """return a mock interface definition""" + + interface_dict = { + "name": "kstdio", + "license": __read_license(args.license), + "system_includes": ["stdarg.h"], + "types": [ + { + "name": "File", + "members": [ + "int fd", + "int (*putc)(const struct kstdio_File*, const char)", + "int (*puts)(const struct kstdio_File*, const char*)", + ], + }, + ], + "functions": [ + { + "name": "printf", + "return": "int", + "arguments": ["const char* format"], + "argument_names": ["format"], + }, + ], + } + + return interface_dict diff --git a/tools/interface_generator/bin/interface_generator.py b/tools/interface_generator/bin/interface_generator.py new file mode 100755 index 0000000..90d03ba --- /dev/null +++ b/tools/interface_generator/bin/interface_generator.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 + +""" +interface_generator.py +""" + +from argparse import ArgumentParser +from pathlib import Path +import sys +from mako.lookup import TemplateLookup +from interface_definition import parse as parse_interface +from templates import get_templates + +PROG = { + "name": "interface_generator", + "version": "0.1", +} + + +def generate_file(template: Path, templates: Path, output, kwargs): + """generate file using a tempalte and write it to the output location""" + lookup = TemplateLookup(directories=[".", templates.absolute()]) + mako_template = lookup.get_template(str(template.relative_to(templates))) + result = mako_template.render(**kwargs, PROG=PROG) + + output_name = template.stem.replace("interface", kwargs["name"]) + print(f'{kwargs["name"]} via {template.name} => {output_name}') + + if isinstance(output, Path): + # print(f"writing to {(output / output_name).absolute()}") + with open(output / output_name, "w", encoding="utf-8") as output_file: + print(result, file=output_file) + else: + print(result, file=output) + + +def main(): + """main function""" + parser = ArgumentParser( + prog="interface_generator", + description="Generate C header and mock files from an interface definition", + ) + parser.add_argument( + "-i", + "--interface", + type=Path, + # required=True, + help="path to interface file", + ) + parser.add_argument( + "-t", + "--templates", + type=Path, + default=Path(sys.argv[0]).parent / "templates", + help="templates location", + ) + parser.add_argument( + "-l", + "--license", + type=Path, + required=True, + help="path to license file", + ) + parser.add_argument( + "-o", + "--output", + type=Path, + default=sys.stdout, + help="path to output, stdout by default", + ) + + args = parser.parse_args() + # print(args) + + interface = parse_interface(args) + # print(interface) + + for template in get_templates(args.templates): + # print(template) + generate_file(template, args.templates, args.output, interface) + + +if __name__ == "__main__": + main() diff --git a/tools/interface_generator/bin/templates.py b/tools/interface_generator/bin/templates.py new file mode 100644 index 0000000..d6a6bb5 --- /dev/null +++ b/tools/interface_generator/bin/templates.py @@ -0,0 +1,12 @@ +"""template helper functions""" + +from pathlib import Path +import re + + +def get_templates(path: Path): + """list templates in given path""" + template_pattern = re.compile(r"^[^_]\S+\.mako$") + result = list(path.glob("*.mako")) + result = [item for item in result if template_pattern.match(item.name)] + return result diff --git a/tools/interface_generator/bin/templates/__c_functions.mako b/tools/interface_generator/bin/templates/__c_functions.mako new file mode 100644 index 0000000..2e40119 --- /dev/null +++ b/tools/interface_generator/bin/templates/__c_functions.mako @@ -0,0 +1,4 @@ +/* Functions */ +% for fn in functions: +${fn['return']} ${name}_${fn['name']}(${ ", ".join(fn['arguments']) }); +% endfor diff --git a/tools/interface_generator/bin/templates/__c_system_include.mako b/tools/interface_generator/bin/templates/__c_system_include.mako new file mode 100644 index 0000000..d6a9d09 --- /dev/null +++ b/tools/interface_generator/bin/templates/__c_system_include.mako @@ -0,0 +1,4 @@ +/* System includes */ +% for path in system_includes: +#include <${path}> +% endfor diff --git a/tools/interface_generator/bin/templates/__c_types.mako b/tools/interface_generator/bin/templates/__c_types.mako new file mode 100644 index 0000000..ce6b6b5 --- /dev/null +++ b/tools/interface_generator/bin/templates/__c_types.mako @@ -0,0 +1,6 @@ +/* Types */ +% for type in types: +typedef struct ${name}_${type['name']} { +${ "\n".join([ " {};".format(member) for member in type['members'] ]) } +} ${name}_${type['name']}; +% endfor diff --git a/tools/interface_generator/bin/templates/__header.mako b/tools/interface_generator/bin/templates/__header.mako new file mode 100644 index 0000000..24b0381 --- /dev/null +++ b/tools/interface_generator/bin/templates/__header.mako @@ -0,0 +1,5 @@ +/* This file is generated by ${PROG['name']} v${PROG['version']} */ + +/****************************************************************************** +${ license } + ******************************************************************************/ diff --git a/tools/interface_generator/bin/templates/interface.h.mako b/tools/interface_generator/bin/templates/interface.h.mako new file mode 100644 index 0000000..47ea940 --- /dev/null +++ b/tools/interface_generator/bin/templates/interface.h.mako @@ -0,0 +1,8 @@ +<%include file="__header.mako" /> +#ifndef ${ name.upper() } +#define ${ name.upper() } + +<%include file="__c_system_include.mako" /> +<%include file="__c_types.mako" /> +<%include file="__c_functions.mako" /> +#endif /* ${ name.upper() } */ diff --git a/tools/interface_generator/bin/templates/interface_mock.cpp.mako b/tools/interface_generator/bin/templates/interface_mock.cpp.mako new file mode 100644 index 0000000..721f2c4 --- /dev/null +++ b/tools/interface_generator/bin/templates/interface_mock.cpp.mako @@ -0,0 +1,32 @@ +<%include file="__header.mako" /> + +#include +#include "${name}_mock.hpp" + +static I${name}_mock *s_instance = nullptr; + +I${name}_mock::I${name}_mock() +{ + if(s_instance != nullptr) + { + throw std::runtime_error("Creating a second instance of mock object"); + } + s_instance = this; +} + +I${name}_mock::~I${name}_mock() +{ + // destructors shouldn't throw exceptions + s_instance = nullptr; +} + +% for fn in functions: +${fn['return']} ${name}_${fn['name']}(${ ", ".join(fn['arguments']) }) +{ + if(s_instance == nullptr) + { + throw std::runtime_error("No mock created to handle function ${name}_${fn['name']}"); + } + return s_instance->${fn['name']}(${ ", ".join(fn['argument_names']) }); +} +% endfor diff --git a/tools/interface_generator/bin/templates/interface_mock.hpp.mako b/tools/interface_generator/bin/templates/interface_mock.hpp.mako new file mode 100644 index 0000000..e33d50e --- /dev/null +++ b/tools/interface_generator/bin/templates/interface_mock.hpp.mako @@ -0,0 +1,26 @@ +<%include file="__header.mako" /> +#ifndef ${ name.upper() }_MOCK +#define ${ name.upper() }_MOCK + +extern "C" { +#include "${ name }.h" +} + +#include +#include + +class I${name}_mock +{ +public: + I${name}_mock(); + ~I${name}_mock(); + + /* Functions */ +% for fn in functions: + MOCK_METHOD(${fn['return']}, ${fn['name']}, (${ ", ".join(fn['arguments']) })); +% endfor +}; + +using ${name}_mock = ::testing::NiceMock; + +#endif /* ${ name.upper() }_MOCK */ diff --git a/tools/interface_generator/bin/templates_unittest.py b/tools/interface_generator/bin/templates_unittest.py new file mode 100755 index 0000000..feb418a --- /dev/null +++ b/tools/interface_generator/bin/templates_unittest.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 + +import sys +import unittest +import templates +from pathlib import Path + + +class Templates(unittest.TestCase): + def test_templates_dir_in_argv_0(self): + path = Path(sys.argv[0]).parent + self.assertTrue("templates" in [ item.name for item in path.iterdir() ]) + + def test_get_templates(self): + path = Path(sys.argv[0]).parent / "templates" + result = templates.get_templates(path) + + self.assertGreater(len(result), 0) + for template in result: + self.assertTrue(isinstance(template, Path)) + self.assertTrue(template.exists()) + self.assertTrue(template.is_file()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/interface_generator/defs.bzl b/tools/interface_generator/defs.bzl index d5c6f91..238588f 100644 --- a/tools/interface_generator/defs.bzl +++ b/tools/interface_generator/defs.bzl @@ -37,7 +37,7 @@ generate_interface_rule = rule( cfg = "exec", allow_files = True, providers = [DefaultInfo], - default = Label("//:interface_generator"), + default = Label("//bin:interface_generator"), ), "outs": attr.output_list(), }, diff --git a/tools/interface_generator/interface_definition.py b/tools/interface_generator/interface_definition.py deleted file mode 100644 index 3b9c4a3..0000000 --- a/tools/interface_generator/interface_definition.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -interface_definition.py -""" - - -def __read_license(path): - """read and starrify a license""" - license_text = "" - with open(path, encoding="utf-8") as license_file: - license_text = "".join( - [ - f" * { line.rstrip().ljust(72)[:72] } * \n" - for line in license_file.readlines() - ] - ).rstrip() - - return license_text - - -def parse(args): - """return a mock interface definition""" - - interface_dict = { - "name": "kstdio", - "license": __read_license(args.license), - "system_includes": ["stdarg.h"], - "types": [ - { - "name": "File", - "members": [ - "int fd", - "int (*putc)(const struct kstdio_File*, const char)", - "int (*puts)(const struct kstdio_File*, const char*)", - ], - }, - ], - "functions": [ - { - "name": "printf", - "return": "int", - "arguments": ["const char* format"], - "argument_names": ["format"], - }, - ], - } - - return interface_dict diff --git a/tools/interface_generator/interface_generator.py b/tools/interface_generator/interface_generator.py deleted file mode 100755 index 0693897..0000000 --- a/tools/interface_generator/interface_generator.py +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env python3 - -""" -interface_generator.py -""" - -from argparse import ArgumentParser -from pathlib import Path -import sys -from mako.lookup import TemplateLookup -from interface_definition import parse as parse_interface -from templates import get_templates - -PROG = { - "name": "interface_generator", - "version": "0.1", -} - - -def generate_file(template: Path, templates: Path, output, kwargs): - """generate file using a tempalte and write it to the output location""" - lookup = TemplateLookup(directories=[".", "templates"]) - mako_template = lookup.get_template(str(template.relative_to(templates))) - result = mako_template.render(**kwargs, PROG=PROG) - - output_name = template.stem.replace("interface", kwargs["name"]) - print(f'{kwargs["name"]} via {template.name} => {output_name}') - - if isinstance(output, Path): - # print(f"writing to {(output / output_name).absolute()}") - with open(output / output_name, "w", encoding="utf-8") as output_file: - print(result, file=output_file) - else: - print(result, file=output) - - -def main(): - """main function""" - parser = ArgumentParser( - prog="interface_generator", - description="Generate C header and mock files from an interface definition", - ) - parser.add_argument( - "-i", - "--interface", - type=Path, - # required=True, - help="path to interface file", - ) - parser.add_argument( - "-t", - "--templates", - type=Path, - default=Path("templates"), - help="templates location", - ) - parser.add_argument( - "-l", - "--license", - type=Path, - required=True, - help="path to license file", - ) - parser.add_argument( - "-o", - "--output", - type=Path, - default=sys.stdout, - help="path to output, stdout by default", - ) - - args = parser.parse_args() - # print(args) - - interface = parse_interface(args) - # print(interface) - - for template in get_templates(args.templates): - generate_file(template, args.templates, args.output, interface) - - -if __name__ == "__main__": - main() diff --git a/tools/interface_generator/templates.py b/tools/interface_generator/templates.py deleted file mode 100644 index d6a6bb5..0000000 --- a/tools/interface_generator/templates.py +++ /dev/null @@ -1,12 +0,0 @@ -"""template helper functions""" - -from pathlib import Path -import re - - -def get_templates(path: Path): - """list templates in given path""" - template_pattern = re.compile(r"^[^_]\S+\.mako$") - result = list(path.glob("*.mako")) - result = [item for item in result if template_pattern.match(item.name)] - return result diff --git a/tools/interface_generator/templates/__c_functions.mako b/tools/interface_generator/templates/__c_functions.mako deleted file mode 100644 index 2e40119..0000000 --- a/tools/interface_generator/templates/__c_functions.mako +++ /dev/null @@ -1,4 +0,0 @@ -/* Functions */ -% for fn in functions: -${fn['return']} ${name}_${fn['name']}(${ ", ".join(fn['arguments']) }); -% endfor diff --git a/tools/interface_generator/templates/__c_system_include.mako b/tools/interface_generator/templates/__c_system_include.mako deleted file mode 100644 index d6a9d09..0000000 --- a/tools/interface_generator/templates/__c_system_include.mako +++ /dev/null @@ -1,4 +0,0 @@ -/* System includes */ -% for path in system_includes: -#include <${path}> -% endfor diff --git a/tools/interface_generator/templates/__c_types.mako b/tools/interface_generator/templates/__c_types.mako deleted file mode 100644 index ce6b6b5..0000000 --- a/tools/interface_generator/templates/__c_types.mako +++ /dev/null @@ -1,6 +0,0 @@ -/* Types */ -% for type in types: -typedef struct ${name}_${type['name']} { -${ "\n".join([ " {};".format(member) for member in type['members'] ]) } -} ${name}_${type['name']}; -% endfor diff --git a/tools/interface_generator/templates/__header.mako b/tools/interface_generator/templates/__header.mako deleted file mode 100644 index 24b0381..0000000 --- a/tools/interface_generator/templates/__header.mako +++ /dev/null @@ -1,5 +0,0 @@ -/* This file is generated by ${PROG['name']} v${PROG['version']} */ - -/****************************************************************************** -${ license } - ******************************************************************************/ diff --git a/tools/interface_generator/templates/interface.h.mako b/tools/interface_generator/templates/interface.h.mako deleted file mode 100644 index 47ea940..0000000 --- a/tools/interface_generator/templates/interface.h.mako +++ /dev/null @@ -1,8 +0,0 @@ -<%include file="__header.mako" /> -#ifndef ${ name.upper() } -#define ${ name.upper() } - -<%include file="__c_system_include.mako" /> -<%include file="__c_types.mako" /> -<%include file="__c_functions.mako" /> -#endif /* ${ name.upper() } */ diff --git a/tools/interface_generator/templates/interface_mock.cpp.mako b/tools/interface_generator/templates/interface_mock.cpp.mako deleted file mode 100644 index 721f2c4..0000000 --- a/tools/interface_generator/templates/interface_mock.cpp.mako +++ /dev/null @@ -1,32 +0,0 @@ -<%include file="__header.mako" /> - -#include -#include "${name}_mock.hpp" - -static I${name}_mock *s_instance = nullptr; - -I${name}_mock::I${name}_mock() -{ - if(s_instance != nullptr) - { - throw std::runtime_error("Creating a second instance of mock object"); - } - s_instance = this; -} - -I${name}_mock::~I${name}_mock() -{ - // destructors shouldn't throw exceptions - s_instance = nullptr; -} - -% for fn in functions: -${fn['return']} ${name}_${fn['name']}(${ ", ".join(fn['arguments']) }) -{ - if(s_instance == nullptr) - { - throw std::runtime_error("No mock created to handle function ${name}_${fn['name']}"); - } - return s_instance->${fn['name']}(${ ", ".join(fn['argument_names']) }); -} -% endfor diff --git a/tools/interface_generator/templates/interface_mock.hpp.mako b/tools/interface_generator/templates/interface_mock.hpp.mako deleted file mode 100644 index e33d50e..0000000 --- a/tools/interface_generator/templates/interface_mock.hpp.mako +++ /dev/null @@ -1,26 +0,0 @@ -<%include file="__header.mako" /> -#ifndef ${ name.upper() }_MOCK -#define ${ name.upper() }_MOCK - -extern "C" { -#include "${ name }.h" -} - -#include -#include - -class I${name}_mock -{ -public: - I${name}_mock(); - ~I${name}_mock(); - - /* Functions */ -% for fn in functions: - MOCK_METHOD(${fn['return']}, ${fn['name']}, (${ ", ".join(fn['arguments']) })); -% endfor -}; - -using ${name}_mock = ::testing::NiceMock; - -#endif /* ${ name.upper() }_MOCK */ diff --git a/tools/interface_generator/templates_unittest.py b/tools/interface_generator/templates_unittest.py deleted file mode 100755 index 594753d..0000000 --- a/tools/interface_generator/templates_unittest.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python3 - -import unittest -import templates -from pathlib import Path - - -class Templates(unittest.TestCase): - def test_get_templates(self): - path = Path("templates") - result = templates.get_templates(path) - - self.assertGreater(len(result), 0) - for template in result: - self.assertTrue(isinstance(template, Path)) - self.assertTrue(template.exists()) - self.assertTrue(template.is_file()) - - -if __name__ == "__main__": - unittest.main() -- cgit v1.2.1