From c2ce13d8d75df0371e65767306dd0f92c88403e0 Mon Sep 17 00:00:00 2001 From: Tim Felgentreff Date: Sun, 7 Feb 2021 21:49:28 +0100 Subject: [PATCH] add a script to generate lua binding docs --- HOWTO-NEW-RELEASE | 4 ++ doc/lua/generate_index.py | 136 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 doc/lua/generate_index.py diff --git a/HOWTO-NEW-RELEASE b/HOWTO-NEW-RELEASE index c74e01061..4f50e8c87 100644 --- a/HOWTO-NEW-RELEASE +++ b/HOWTO-NEW-RELEASE @@ -16,6 +16,10 @@ Update copyright information for Debian: It's a good idea to ensure the debian build works before taggging: gbp buildpackage +Create the doxygen documentation by building with -DENABLE_DOC=ON +Create the lua function bindings overview by running the Python script doc/lua/generate_index.py +Copy the generated doxygen and the generated lua bindings html to the website storage. + Tag the new release in https://github.com/Wargus/win32-stratagus-dependencies and wait for it to run through on the Appveyor CI. This will automatically cause a release with the tag name to be created, and the windows dependencies diff --git a/doc/lua/generate_index.py b/doc/lua/generate_index.py new file mode 100644 index 000000000..952627169 --- /dev/null +++ b/doc/lua/generate_index.py @@ -0,0 +1,136 @@ +import os +import re + +from os.path import join, dirname, basename, abspath + + +REGISTER_RE = re.compile(r'lua_register\([a-zA-Z0-9_]+, "([^"]+)", ([^\)]+)\)'); +END_OF_DOC_RE = re.compile(r'\*/\s*$') +C_COMMENT_RE = re.compile(r'/\*[^*]*\*+(?:[^/*][^*]*\*+)*/', re.MULTILINE); +BLANK_RE = re.compile(r'^\s*$') +COMMENT_RE = re.compile(r'^\s*/?\*+/?', re.MULTILINE) +PKG_EXPORT_RE = re.compile(r'(?:(?:extern )?[a-zA-Z0-9_]+ ([a-zA-Z0-9_]+\([^)]+\))|(?:extern )?([a-zA-Z0-9_]+ [a-zA-Z0-9_\[\]\*]+));') + + +def get_c_func_re(c_func): + return re.compile(r'int\s+' + c_func + '[^;]+$', re.MULTILINE) + + +def walk_files(directory, exts): + """Yield filenames recursively under directory that end with exts""" + for root, dirs, files in os.walk(directory): + for name in files: + for ext in exts: + if name.endswith(ext): + yield join(root, name) + break + + +def walk_lua_registers(filename): + """Yield (lua name, c function name) pairs""" + with open(filename, "r", errors="ignore") as f: + for line in f.readlines(): + m = REGISTER_RE.search(line) + if m: + yield m.group(1), m.group(2) + + +def walk_lua_exports(filename): + with open(filename, "r", errors="ignore") as f: + block = None + current_comment = [] + for line in f.readlines(): + if block: + block.append(line) + if line.strip() == "};": + yield "".join(current_comment), "".join(block) + current_comment = [] + block = None + elif line.startswith("//"): + current_comment.append(line) + elif line.startswith("class") or line.startswith("enum"): + block = [line] + else: + m = PKG_EXPORT_RE.search(line) + if m: + yield "".join(current_comment), m.group(1) or m.group(2) + current_comment = [] + + +def extract_docstring(filename, c_func): + """Return the docstring for function and the start of the line for that function""" + with open(filename, "r", errors="ignore") as f: + content = f.read() + m = get_c_func_re(c_func).search(content) + if m: + beginning = m.start + preamble = content[:m.start()] + lines = preamble.split("\n") + if len(lines) > 2: + if END_OF_DOC_RE.search(lines[-2]): + # line prior to declaration looks like end of comment + idx = 2 + while len(lines) > idx: + if BLANK_RE.match(lines[-idx]): + break + idx += 1 + m = C_COMMENT_RE.search("\n".join(lines[-idx:-1])) + docstring = m.group(0) + return COMMENT_RE.sub("", docstring), len(lines) + return "<documentation not found>", -1 + + +if __name__ == "__main__": + root = join(dirname(dirname(dirname(abspath(__file__)))), "src") + + with open("index.html", "w") as toc: + toc.write(""" + + TOC + + """) + for cppfile in walk_files(root, exts=[".cpp"]): + rule = False + for lua_register in walk_lua_registers(cppfile): + if not rule: + toc.write("
") + rule = True + luaname, cname = lua_register + doc, line = extract_docstring(cppfile, cname) + doc = doc.replace("\n", "
") + toc.write(""" +
+ {0} +

{1}

+

{2}:{3} {4}

+
+ """.format(luaname, doc, cppfile.replace(root, ""), line, cname)) + for pkgfile in walk_files(root, exts=[".pkg"]): + rule = False + for lua_register in walk_lua_exports(pkgfile): + if not rule: + toc.write("
") + rule = True + doc, code = lua_register + doc = doc.replace("\n", "
") + if "\n" not in code: + toc.write(""" +
+ {0} +

{1}

+
+ """.format(code, doc)) + else: + code = code.replace(" ", " ").replace("\t", "    ") + code = code.split("\n") + toc.write(""" +
+ {0} +

{1}

+

{2}

+
+ """.format(code[0], doc, "
".join(code))) + toc.write(""" + + + """)