From c876fe00443179643848a7abf29e59d8c38746bf Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 12 Aug 2013 22:34:57 +0100 Subject: [PATCH] Add support for @INCLUDE tags in doxyfiles Doxygen configs can include other doxygen configs. Add support for @INCLUDE tag. --- doxygen.py | 48 ++++++++++++++++++-------- test.py | 18 ++++++++-- test_config/include_test.cfg | 1 + test_config/recursive_include_test.cfg | 1 + 4 files changed, 52 insertions(+), 16 deletions(-) create mode 100644 test_config/include_test.cfg create mode 100644 test_config/recursive_include_test.cfg diff --git a/doxygen.py b/doxygen.py index 28ce3a9d5..13a0f5bf5 100644 --- a/doxygen.py +++ b/doxygen.py @@ -43,16 +43,17 @@ output_formats = { "XML": ("NO", "xml", "index", ".xml", ""), } -def DoxyfileParse(file_contents): +def DoxyfileParse(file_contents, conf_dir, data=None): """ Parse a Doxygen source file and return a dictionary of all the values. Values will be strings and lists of strings. """ - data = {} + if data is None: + data = {} import shlex lex = shlex.shlex(instream = file_contents, posix = True) - lex.wordchars += "*+./-:" + lex.wordchars += "*+./-:@" lex.whitespace = lex.whitespace.replace("\n", "") lex.escape = "" @@ -87,12 +88,28 @@ def DoxyfileParse(file_contents): if key == "TAGFILES" and data.has_key(key): append_data( data, key, False, "=" ) new_data=False + elif key == "@INCLUDE" and data.has_key(key): + # don't reset the @INCLUDE list when we see a new @INCLUDE line. + pass else: data[key] = list() + elif key == "@INCLUDE": + # special case for @INCLUDE key: read the referenced + # file as a doxyfile too. + nextfile = token + if not os.path.isabs(nextfile): + nextfile = os.path.join(conf_dir, nextfile) + if nextfile in data[key]: + raise Exception("recursive @INCLUDE in Doxygen config: "+nextfile) + data[key].append(nextfile) + fh = open(nextfile,'r') + DoxyfileParse(fh.read(), conf_dir, data) + fh.close() else: append_data( data, key, new_data, token ) new_data = True + last_token = token token = lex.get_token() @@ -106,7 +123,7 @@ def DoxyfileParse(file_contents): data.pop(k) # items in the following list will be kept as lists and not converted to strings - if k in ["INPUT", "FILE_PATTERNS", "EXCLUDE_PATTERNS", "TAGFILES"]: + if k in ["INPUT", "FILE_PATTERNS", "EXCLUDE_PATTERNS", "TAGFILES", "@INCLUDE"]: continue if len(v) == 1: @@ -132,7 +149,13 @@ def DoxySourceFiles(node, env): sources = [] - data = DoxyfileParse(node.get_contents()) + # We're running in the top-level directory, but the doxygen + # configuration file is in the same directory as node; this means + # that relative pathnames in node must be adjusted before they can + # go onto the sources list + conf_dir = os.path.dirname(str(node)) + + data = DoxyfileParse(node.get_contents(), conf_dir) if data.get("RECURSIVE", "NO") == "YES": recursive = True @@ -142,12 +165,6 @@ def DoxySourceFiles(node, env): file_patterns = data.get("FILE_PATTERNS", default_file_patterns) exclude_patterns = data.get("EXCLUDE_PATTERNS", default_exclude_patterns) - # We're running in the top-level directory, but the doxygen - # configuration file is in the same directory as node; this means - # that relative pathnames in node must be adjusted before they can - # go onto the sources list - conf_dir = os.path.dirname(str(node)) - input = data.get("INPUT") if input: for node in data.get("INPUT", []): @@ -185,6 +202,10 @@ def DoxySourceFiles(node, env): for pattern in file_patterns: sources.extend(glob.glob(pattern)) + # Add @INCLUDEd files to the list of source files: + for node in data.get("@INCLUDE", []): + sources.append(node) + # Add tagfiles to the list of source files: for node in data.get("TAGFILES", []): file = node.split("=")[0] @@ -226,12 +247,12 @@ def DoxySourceScanCheck(node, env): def DoxyEmitter(target, source, env): """Doxygen Doxyfile emitter""" doxy_fpath = str(source[0]) - data = DoxyfileParse(source[0].get_contents()) + conf_dir = os.path.dirname(doxy_fpath) + data = DoxyfileParse(source[0].get_contents(), conf_dir) targets = [] out_dir = data.get("OUTPUT_DIRECTORY", ".") if not os.path.isabs(out_dir): - conf_dir = os.path.dirname(doxy_fpath) out_dir = os.path.join(conf_dir, out_dir) # add our output locations @@ -291,7 +312,6 @@ def DoxyEmitter(target, source, env): tagfile = data.get("GENERATE_TAGFILE", "") if tagfile != "": if not os.path.isabs(tagfile): - conf_dir = os.path.dirname(str(source[0])) tagfile = os.path.join(conf_dir, tagfile) targets.append(env.File(tagfile)) diff --git a/test.py b/test.py index d6c313a6b..9f372de91 100755 --- a/test.py +++ b/test.py @@ -9,19 +9,33 @@ import sys from doxygen import DoxyfileParse class TestParser(unittest.TestCase): + test_config_dir = os.path.join(os.path.dirname(__file__),'test_config') + def test_simple_parse(self): text=""" # comment INPUT = test.h """ - result = DoxyfileParse(text) + result = DoxyfileParse(text, self.test_config_dir) self.assertEqual(["test.h"], result["INPUT"]) def test_parse_tag_on_first_line(self): text="""INPUT=.""" - result = DoxyfileParse(text) + result = DoxyfileParse(text, self.test_config_dir) self.assertEqual(["."], result["INPUT"]) + def test_include_tag(self): + text="""@INCLUDE=include_test.cfg""" + result = DoxyfileParse(text, self.test_config_dir) + self.assertEqual(["abc"], result["INPUT"]) + self.assertEqual([os.path.join(self.test_config_dir, + "include_test.cfg")], + result["@INCLUDE"]) + + def test_recursive_include_tag(self): + text="""@INCLUDE=recursive_include_test.cfg""" + self.assertRaises(Exception, DoxyfileParse, text, self.test_config_dir) + if __name__ == '__main__': unittest.main() diff --git a/test_config/include_test.cfg b/test_config/include_test.cfg new file mode 100644 index 000000000..1b6b7ba1a --- /dev/null +++ b/test_config/include_test.cfg @@ -0,0 +1 @@ +INPUT="abc" diff --git a/test_config/recursive_include_test.cfg b/test_config/recursive_include_test.cfg new file mode 100644 index 000000000..0ed7b9ae8 --- /dev/null +++ b/test_config/recursive_include_test.cfg @@ -0,0 +1 @@ +@INCLUDE=recursive_include_test.cfg