From 3483bf14b9012b59325537b9e1c72686c16b2a2c Mon Sep 17 00:00:00 2001 From: hbristow Date: Wed, 7 Aug 2013 16:34:30 +1000 Subject: [PATCH] Added auto-generated docs to all OpenCV functions --- modules/matlab/CMakeLists.txt | 16 ++--- modules/matlab/generator/filters.py | 9 +++ modules/matlab/generator/gen_matlab.py | 58 ++++++++++++------- .../generator/templates/template_doc_base.m | 37 +++++++++--- 4 files changed, 84 insertions(+), 36 deletions(-) diff --git a/modules/matlab/CMakeLists.txt b/modules/matlab/CMakeLists.txt index d97d3dd1af..5a31c45a16 100644 --- a/modules/matlab/CMakeLists.txt +++ b/modules/matlab/CMakeLists.txt @@ -95,7 +95,7 @@ set(MATLAB_DEPS ${OPENCV_MODULE_${the_module}_REQ_DEPS} ${OPENCV_MODULE_${the_mo foreach(opencv_module ${MATLAB_DEPS}) if (HAVE_${opencv_module}) string(REPLACE "opencv_" "" module ${opencv_module}) - list(APPEND opencv_hdrs "${OPENCV_MODULE_${opencv_module}_LOCATION}/include/opencv2/${module}.hpp") + list(APPEND opencv_modules ${module}) list(APPEND ${the_module}_ACTUAL_DEPS ${opencv_module}) prepend("-I" MEX_INCLUDE_DIRS "${OPENCV_MODULE_${opencv_module}_LOCATION}/include") prepend("-l" MEX_LIBS ${opencv_module}${OPENCV_DLLVERSION}) @@ -104,7 +104,7 @@ foreach(opencv_module ${MATLAB_DEPS}) endforeach() # add extra headers by hand -list(APPEND opencv_hdrs "${OPENCV_MODULE_opencv_core_LOCATION}/include/opencv2/core/base.hpp") +list(APPEND opencv_extra_hdrs "core:${OPENCV_MODULE_opencv_core_LOCATION}/include/opencv2/core/base.hpp") # pass the OPENCV_CXX_EXTRA_FLAGS through to the mex compiler # remove the visibility modifiers, so the mex gateway is visible @@ -125,7 +125,7 @@ if (NOT MEX_WORKS) ${CMAKE_CURRENT_SOURCE_DIR}/generator/gen_matlab.py --hdrparser ${HDR_PARSER_PATH} --rstparser ${RST_PARSER_PATH} - --headers ${CMAKE_CURRENT_SOURCE_DIR}/test/test_generator.hpp + --extra "test:${CMAKE_CURRENT_SOURCE_DIR}/test/test_generator.hpp" --outdir ${CMAKE_BINARY_DIR}/junk ERROR_VARIABLE GEN_ERROR OUTPUT_QUIET @@ -183,10 +183,12 @@ add_custom_command( OUTPUT ${GENERATE_PROXY} COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/generator/gen_matlab.py - --hdrparser ${HDR_PARSER_PATH} - --rstparser ${RST_PARSER_PATH} - --headers ${opencv_hdrs} - --outdir ${CMAKE_CURRENT_BINARY_DIR} + --hdrparser ${HDR_PARSER_PATH} + --rstparser ${RST_PARSER_PATH} + --moduleroot ${CMAKE_SOURCE_DIR}/modules + --modules ${opencv_modules} + --extra ${opencv_extra_hdrs} + --outdir ${CMAKE_CURRENT_BINARY_DIR} COMMAND ${CMAKE_COMMAND} -E touch ${GENERATE_PROXY} COMMENT "Generating Matlab source files" ) diff --git a/modules/matlab/generator/filters.py b/modules/matlab/generator/filters.py index dcb8a891d3..9ef7dcc6bc 100644 --- a/modules/matlab/generator/filters.py +++ b/modules/matlab/generator/filters.py @@ -83,6 +83,15 @@ def toLowerCamelCase(text): def toUnderCase(text): s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', text) return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower() + +def stripTags(text): + upper = lambda pattern: pattern.group(1).upper() + text = re.sub('(.*?)', upper, text) + text = re.sub('<(.*?)>', '', text) + return text + +def qualify(text, name): + return re.sub(name.upper(), 'CV.'+name.upper(), text) def comment(text, wrap=80, escape='% ', escape_first='', escape_last=''): '''comment filter diff --git a/modules/matlab/generator/gen_matlab.py b/modules/matlab/generator/gen_matlab.py index fee4673a87..6abaa5d620 100644 --- a/modules/matlab/generator/gen_matlab.py +++ b/modules/matlab/generator/gen_matlab.py @@ -2,29 +2,39 @@ class MatlabWrapperGenerator(object): - def gen(self, input_files, output_dir): + def gen(self, module_root, modules, extras, output_dir): # parse each of the files and store in a dictionary # as a separate "namespace" parser = CppHeaderParser() - ns = {} - for file in input_files: - # get the file name - # TODO: Is there a cleaner way to do this? - try: - name = re.findall('include/opencv2/([^./]+)', file)[0] - except: - name = os.path.splitext(os.path.basename(file))[0] - - # add the file to the namespace - try: - ns[name] = ns[name] + parser.parse(file) - except KeyError: - ns[name] = parser.parse(file) + rst = rst_parser.RstParser(parser) + rst_parser.verbose = False + rst_parser.show_warnings = False + rst_parser.show_errors = False + rst_parser.show_critical_errors = False + + ns = dict((key, []) for key in modules) + doc = dict((key, []) for key in modules) + path_template = Template('${module}/include/opencv2/${module}.hpp') + + for module in modules: + # construct a header path from the module root and a path template + header = os.path.join(module_root, path_template.substitute(module=module)) + # parse the definitions + ns[module] = parser.parse(header) + # parse the documentation + rst.parse(module, os.path.join(module_root, module)) + doc[module] = rst.definitions + rst.definitions = {} + + for extra in extras: + module = extra.split(":")[0] + header = extra.split(":")[1] + ns[module] = ns[module] + parser.parse(header) if module in ns else parser.parse(header) # cleanify the parser output parse_tree = ParseTree() parse_tree.build(ns) - + # setup the template engine template_dir = os.path.join(os.path.dirname(__file__), 'templates') jtemplate = Environment(loader=FileSystemLoader(template_dir), trim_blocks=True, lstrip_blocks=True) @@ -35,11 +45,13 @@ class MatlabWrapperGenerator(object): jtemplate.filters['toUpperCamelCase'] = toUpperCamelCase jtemplate.filters['toLowerCamelCase'] = toLowerCamelCase jtemplate.filters['toUnderCase'] = toUnderCase + jtemplate.filters['stripTags'] = stripTags jtemplate.filters['comment'] = comment jtemplate.filters['inputs'] = inputs jtemplate.filters['ninputs'] = ninputs jtemplate.filters['outputs'] = outputs jtemplate.filters['noutputs'] = noutputs + jtemplate.filters['qualify'] = qualify jtemplate.filters['only'] = only jtemplate.filters['void'] = void jtemplate.filters['not'] = flip @@ -72,9 +84,10 @@ class MatlabWrapperGenerator(object): populated = tfunction.render(fun=method, time=time, includes=namespace.name) with open(output_source_dir+'/'+method.name+'.cpp', 'wb') as f: f.write(populated) - populated = tdoc.render(fun=method, time=time) - with open(output_class_dir+'/'+method.name+'.m', 'wb') as f: - f.write(populated) + if namespace.name in doc and method.name in doc[namespace.name]: + populated = tdoc.render(fun=method, doc=doc[namespace.name][method.name], time=time) + with open(output_class_dir+'/'+method.name+'.m', 'wb') as f: + f.write(populated) # classes for clss in namespace.classes: # cpp converter @@ -102,7 +115,9 @@ if __name__ == "__main__": parser = ArgumentParser() parser.add_argument('--hdrparser') parser.add_argument('--rstparser') - parser.add_argument('--headers', nargs='+') + parser.add_argument('--moduleroot', default='', required=False) + parser.add_argument('--modules', nargs='*', default=[], required=False) + parser.add_argument('--extra', nargs='*', default=[], required=False) parser.add_argument('--outdir') args = parser.parse_args() @@ -112,10 +127,11 @@ if __name__ == "__main__": from string import Template from hdr_parser import CppHeaderParser + import rst_parser from parse_tree import ParseTree, todict, constants from filters import * from jinja2 import Environment, FileSystemLoader # create the generator mwg = MatlabWrapperGenerator() - mwg.gen(args.headers, args.outdir) + mwg.gen(args.moduleroot, args.modules, args.extra, args.outdir) diff --git a/modules/matlab/generator/templates/template_doc_base.m b/modules/matlab/generator/templates/template_doc_base.m index d9e5922e95..6b25c67320 100644 --- a/modules/matlab/generator/templates/template_doc_base.m +++ b/modules/matlab/generator/templates/template_doc_base.m @@ -1,6 +1,10 @@ {% import 'functional.cpp' as functional %} -%CV.{{ fun.name | upper }} +{{ ('CV.' + fun.name | upper + ' ' + doc.brief | stripTags) | comment(75, '%') }} +% % {{ functional.composeMatlab(fun) | upper }} +{% if doc.long %} +{{ doc.long | stripTags | qualify(fun.name) | comment(75, '% ') }} +{% endif %} % {% if fun.rtp|void|not or fun.req|outputs|length or fun.opt|outputs|length %} % Returns: @@ -9,28 +13,45 @@ % LVALUE {% endif %} {% for arg in fun.req|outputs + fun.opt|outputs %} -% {{arg.name | upper}}{%- if arg.I -%}_OUT{% endif %} - +{% set uname = arg.name | upper + ('_OUT' if arg.I else '') %} +{% if arg.name in doc.params %} +{{ (uname + ' ' + doc.params[arg.name]) | stripTags | comment(75, '% ') }} +{% else %} +{{ uname }} +{% endif %} {% endfor %} % {% if fun.req|inputs|length %} % Required Inputs: {% endif %} {% for arg in fun.req|inputs %} -% {{arg.name | upper}}{%- if arg.O -%}_OUT{% endif %} - +{% set uname = arg.name | upper + ('_IN' if arg.O else '') %} +{% if arg.name in doc.params %} +{{ (uname + ' ' + doc.params[arg.name]) | stripTags | comment(75, '% ') }} +{% else %} +{{ uname }} +{% endif %} {% endfor %} % {% if fun.opt|inputs|length %} % Optional Inputs: {% endif %} {% for arg in fun.opt|inputs %} -% {{arg.name | upper}}{%- if arg.O -%}_OUT{% endif %} - +{% set uname = arg.name | upper + ('_IN' if arg.O else '') + ' (default: ' + arg.default + ')' %} +{% if arg.name in doc.params %} +{{ (uname + ' ' + doc.params[arg.name]) | stripTags | comment(75, '% ') }} +{% else %} +{{ uname }} +{% endif %} {% endfor %} % -% See also: +{% if 'seealso' in doc %} +% See also: {% for item in doc['seealso'] %} +cv.{{ item }}{% if not loop.last %}, {% endif %} +{% endfor %} + % +{% endif %} % Official documentation: http://docs.opencv.org % Copyright {{ time.strftime("%Y", time.localtime()) }} The OpenCV Foundation %