diff --git a/README b/README deleted file mode 100644 index 647dc8d..0000000 --- a/README +++ /dev/null @@ -1,50 +0,0 @@ -Doxygen filter for Google Protocol Buffers .proto files. -==================== -How to enable this filter in Doxygen: - 1. Generate Doxygen configuration file with command 'doxygen -g ' - e.g. doxygen -g doxyfile - 2. In the Doxygen configuration file, find JAVADOC_AUTOBRIEF and set it enabled - JAVADOC_AUTOBRIEF = YES - This is required since version 0.6-beta - 3. In the Doxygen configuration file, find FILE_PATTERNS and add *.proto - FILE_PATTERNS = *.proto - 4. In the Doxygen configuration file, find EXTENSION_MAPPING and add proto=C - EXTENSION_MAPPING = proto=C - 5. In the Doxygen configuration file, find INPUT_FILTER and add this script - INPUT_FILTER = "python proto2cpp.py" - 6. Run Doxygen with the modified configuration - doxygen doxyfile -==================== -Version history: --------------------- - 0.1-pre-alpha (2012-06-13) - - initial version --------------------- - 0.2-pre-alpha (2012-06-15) - - added support for enums --------------------- - 0.3-alpha (2013-01-29) - - moved .proto file parsing logic to another function - - added comments to the file --------------------- - 0.4-beta (2013-08-29) - - 'classified' proto2cpp and updated documentation to make the - script itself Doxygen compatible - - changed all print statements to print() functions - * 64-bit Python v3.3.1 running on 64-bit Windows 7 Home Premium - did not automatically convert print statements to print() - functions but instead raised a syntax error - - made a change so that .proto files are converted before printing - and other files are printed to stdout as is - * this allows using the filter with multiple file types --------------------- - 0.5-beta (2014-11-16) - - fixed enum ending to have semicolon to have proper enum syntax - in struct (thanks to m47iast for pointing this out) --------------------- - 0.6-beta (2015-07-27) - - made output to be more compact by removing extra empty lines and - not moving member comments before the member but keeping it after - the member instead - * these changes lead into need of enabling JAVADOC_AUTOBRIEF - - added steps for enabling the filter in Doxygen in this file \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..7035741 --- /dev/null +++ b/README.md @@ -0,0 +1,94 @@ +Doxygen filter for Google Protocol Buffers .proto files +======================================================= + +How to enable this filter in Doxygen: + 1. Generate Doxygen configuration file with command 'doxygen -g ': + e.g. doxygen -g doxyfile + 2. In the Doxygen configuration file, find FILE_PATTERNS and add *.proto: + FILE_PATTERNS = *.proto + 3. In the Doxygen configuration file, find EXTENSION_MAPPING and add proto=C++: + EXTENSION_MAPPING = proto=C++ + 4. In the Doxygen configuration file, find INPUT_FILTER and add this script: + INPUT_FILTER = "python proto2cpp.py" + 5. Run Doxygen with the modified configuration: + doxygen doxyfile + +Following change is recommended by Timo Marjoniemi but must not be used: + In the Doxygen configuration file, find JAVADOC_AUTOBRIEF and set it enabled: + JAVADOC_AUTOBRIEF = YES + +Version history +=============== + +0.8-beta (2018-12-09) OSI +------------------------- + - Bugfix regarding long comments, remove typo + - Bugfix and extensions have been made by Open Simulation Interface (OSI) Carsten Kuebler https://github.com/OpenSimulationInterface + +0.7-beta (2018-04-19) OSI +------------------------- + - Include changes from University of California. + - Support for all OSI *.proto files. + - Separate statement and comments to treat both parts differently (remove bugs + regarding string modifications). + - Remove "option" statements. + - Add support for "extend" statements. + - Change "repeat" from Template to standard member. --> Better collaboration + diagrams. + - Fix problems with references of nested messages (replace "." with "::"). + - Change mapping from C to C++. + - Bugfix and extensions have been made by Open Simulation Interface (OSI) Carsten Kuebler https://github.com/OpenSimulationInterface + +0.6-beta (2015-07-27) +-------------------- + - made output to be more compact by removing extra empty lines and + not moving member comments before the member but keeping it after + the member instead + * these changes lead into need of enabling JAVADOC_AUTOBRIEF + - added steps for enabling the filter in Doxygen in this file + +0.5-beta (2014-11-16) +-------------------- + - fixed enum ending to have semicolon to have proper enum syntax + in struct (thanks to m47iast for pointing this out) + +0.4-beta (2013-08-29) +-------------------- + - 'classified' proto2cpp and updated documentation to make the + script itself Doxygen compatible + - changed all print statements to print() functions + * 64-bit Python v3.3.1 running on 64-bit Windows 7 Home Premium + did not automatically convert print statements to print() + functions but instead raised a syntax error + - made a change so that .proto files are converted before printing + and other files are printed to stdout as is + * this allows using the filter with multiple file types + +0.3-alpha (2013-01-29) +-------------------- + - moved .proto file parsing logic to another function + - added comments to the file + +0.2-pre-alpha (2012-06-15) +-------------------- + - added support for enums + +0.1-pre-alpha (2012-06-13) +-------------------- + - initial version + + +Copyright +========= + +Version 0.7-beta - +------------------ +Extensions for Open Simulation Interface - License MIT + +Version 0.7-beta +---------------- +Copyright (C) 2016 Regents of the University of California https://github.com/vgteam/vg + +Version 0.1-beta - 0.6-beta +---------------------------- +Copyright (C) 2012-2015 Timo Marjoniemi https://sourceforge.net/p/proto2cpp/wiki/Home/ diff --git a/doxyfile b/doxyfile index ad14802..5a89ee2 100644 --- a/doxyfile +++ b/doxyfile @@ -32,7 +32,7 @@ PROJECT_NAME = "Protocol Buffers demo project" # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 0.6-beta +PROJECT_NUMBER = 0.7-beta # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer @@ -238,7 +238,7 @@ OPTIMIZE_OUTPUT_VHDL = NO # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. -EXTENSION_MAPPING = proto=C +EXTENSION_MAPPING = proto=C++ # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should diff --git a/proto2cpp.py b/proto2cpp.py index 96506aa..a355aaa 100644 --- a/proto2cpp.py +++ b/proto2cpp.py @@ -1,26 +1,28 @@ +#!/usr/bin/env python ## # Doxygen filter for Google Protocol Buffers .proto files. # This script converts .proto files into C++ style ones # and prints the output to standard output. # -# version 0.6-beta +# version 0.8-beta OSI # # How to enable this filter in Doxygen: # 1. Generate Doxygen configuration file with command 'doxygen -g ' # e.g. doxygen -g doxyfile -# 2. In the Doxygen configuration file, find JAVADOC_AUTOBRIEF and set it enabled -# JAVADOC_AUTOBRIEF = YES -# 3. In the Doxygen configuration file, find FILE_PATTERNS and add *.proto +# 2. In the Doxygen configuration file, find FILE_PATTERNS and add *.proto # FILE_PATTERNS = *.proto -# 4. In the Doxygen configuration file, find EXTENSION_MAPPING and add proto=C -# EXTENSION_MAPPING = proto=C -# 5. In the Doxygen configuration file, find INPUT_FILTER and add this script +# 3. In the Doxygen configuration file, find EXTENSION_MAPPING and add proto=C++ +# EXTENSION_MAPPING = proto=C++ +# 4. In the Doxygen configuration file, find INPUT_FILTER and add this script # INPUT_FILTER = "python proto2cpp.py" -# 6. Run Doxygen with the modified configuration +# 5. Run Doxygen with the modified configuration # doxygen doxyfile # # -# Copyright (C) 2012-2015 Timo Marjoniemi +# Version 0.8 2018 Bugfix regarding long comments, remove typo +# Version 0.7 2018 Bugfix and extensions have been made by Open Simulation Interface (OSI) Carsten Kuebler https://github.com/OpenSimulationInterface, +# Copyright (C) 2016 Regents of the University of California https://github.com/vgteam/vg +# Copyright (C) 2012-2015 Timo Marjoniemi https://sourceforge.net/p/proto2cpp/wiki/Home/ # All rights reserved. # # This library is free software; you can redistribute it and/or @@ -46,7 +48,7 @@ import inspect ## Class for converting Google Protocol Buffers .proto files into C++ style output to enable Doxygen usage. -## +## ## The C++ style output is printed into standard output.
## There are three different logging levels for the class: ##
  • #logNone: do not log anything
  • @@ -87,39 +89,64 @@ def handleFile(self, fileName): if fnmatch.fnmatch(filename, '*.proto'): self.log('\nXXXXXXXXXX\nXX ' + filename + '\nXXXXXXXXXX\n\n') # Open the file. Use try to detect whether or not we have an actual file. - try: - with open(filename, 'r') as inputFile: - self.parseFile(inputFile) - pass - except IOError as e: - self.logError('the file ' + filename + ' could not be opened for reading') + if (sys.version_info >= (3, 0)): + try: + with open(filename, 'r', encoding='utf8') as inputFile: + self.parseFile(inputFile) + pass + except IOError as e: + self.logError('the file ' + filename + ' could not be opened for reading') + else: + # Python 2 code in this block + try: + with open(filename, 'r') as inputFile: + self.parseFile(inputFile) + pass + except IOError as e: + self.logError('the file ' + filename + ' could not be opened for reading') elif not fnmatch.fnmatch(filename, os.path.basename(inspect.getfile(inspect.currentframe()))): self.log('\nXXXXXXXXXX\nXX ' + filename + '\nXXXXXXXXXX\n\n') - try: - with open(filename, 'r') as theFile: - output = '' - for theLine in theFile: - output += theLine - print(output) - self.log(output) - pass - except IOError as e: - self.logError('the file ' + filename + ' could not be opened for reading') + if (sys.version_info > (3, 0)): + try: + with open(filename, 'r', encoding='utf8') as theFile: + output = '' + for theLine in theFile: + output += theLine + print(output) + self.log(output) + pass + except IOError as e: + self.logError('the file ' + filename + ' could not be opened for reading') + else: + # Python 2 code in this block + try: + with open(filename, 'r') as theFile: + output = '' + for theLine in theFile: + output += theLine + print(output) + self.log(output) + pass + except IOError as e: + self.logError('the file ' + filename + ' could not be opened for reading') + else: self.log('\nXXXXXXXXXX\nXX ' + filename + ' --skipped--\nXXXXXXXXXX\n\n') ## Parser function. - ## + ## ## The function takes a .proto file object as input ## parameter and modifies the contents into C++ style. ## The modified data is printed into standard output. - ## + ## ## @param inputFile Input file object # def parseFile(self, inputFile): # Go through the input file line by line. isEnum = False + isPackage = False + isMultilineComment = False # This variable is here as a workaround for not getting extra line breaks (each line # ends with a line separator and print() method will add another one). # We will be adding lines into this var and then print the var out at the end. @@ -129,24 +156,62 @@ def parseFile(self, inputFile): # block to make Doxygen detect it. matchComment = re.search("//", line) # Search for semicolon and if one is found before comment, add a third slash character - # ("/") and a smaller than ("<") chracter to the comment to make Doxygen detect it. + # ("/") and a smaller than ("<") character to the comment to make Doxygen detect it. matchSemicolon = re.search(";", line) if matchSemicolon is not None and (matchComment is not None and matchSemicolon.start() < matchComment.start()): - line = line[:matchComment.start()] + "///<" + line[matchComment.end():] + comment = "///<" + line[matchComment.end():] + # Replace '.' in nested message references with '::' + # don't work for multi-nested references and generates problems with URLs and acronyms + #comment = re.sub(r'\s(\w+)\.(\w+)\s', r' \1::\2 ', comment) + line = line[:matchComment.start()] elif matchComment is not None: - line = line[:matchComment.start()] + "///" + line[matchComment.end():] + if isMultilineComment: + comment = " * " + line[matchComment.end():] + else: + comment = "/** " + line[matchComment.end():] + isMultilineComment = True + # replace '.' in nested message references with '::' + # don't work for multi-nested references and generates problems with URLs and acronyms + #comment = re.sub(r'\s(\w+)\.(\w+)\s', r' \1::\2 ', comment) + line = line[:matchComment.start()] + else: + comment = "" + + # End multiline comment, if there is no comment or if there are some chars before the comment. + if (matchComment is None or len(line.strip())>0) and isMultilineComment: + theOutput += " */\n" + isMultilineComment = False + + # line = line.replace(".", "::") but not in quoted strings (Necessary for import statement) + line = re.sub(r'\.(?=(?:[^"]*"[^"]*")*[^"]*$)',r'::',line) + + # Search for " option ...;", remove it + line = re.sub(r'\boption\b[^;]+;', r'', line) + + # Search for " package ", make a namespace + matchPackage = re.search(r"\bpackage\b", line) + if matchPackage is not None: + isPackage = True + # Convert to C++-style separator and block instead of statement + line = "namespace" + line[:matchPackage.start()] + line[matchPackage.end():].replace(";", " {") - # Search for "enum" and if one is found before comment, - # start changing all semicolons (";") to commas (","). - matchEnum = re.search("enum", line) - if matchEnum is not None and (matchComment is None or matchEnum.start() < matchComment.start()): + # Search for " repeated " fields and make them ... + #matchRepeated = re.search(r"\brepeated\b", line) + #if matchRepeated is not None: + # # Convert + # line = re.sub(r'\brepeated\s+(\S+)', r' repeated \1', line) + + # Search for "enum", start changing all semicolons (";") to commas (","). + matchEnum = re.search(r"\benum\b", line) + if matchEnum is not None: isEnum = True - # Search again for semicolon if we have detected an enum, and replace semicolon with comma. - if isEnum is True and re.search(";", line) is not None: - matchSemicolon = re.search(";", line) - line = line[:matchSemicolon.start()] + "," + line[matchSemicolon.end():] + + # Search semicolon if we have detected an enum, and replace semicolon with comma. + if isEnum is True and matchSemicolon is not None: + line = line.replace(";", ",") + # Search for a closing brace. - matchClosingBrace = re.search("}", line[:matchComment.start()] if matchComment else line) + matchClosingBrace = re.search("}", line) if isEnum is True and matchClosingBrace is not None: line = line[:matchClosingBrace.start()] + "};" + line[matchClosingBrace.end():] isEnum = False @@ -154,24 +219,40 @@ def parseFile(self, inputFile): # Message (to be struct) ends => add semicolon so that it'll # be a proper C(++) struct and Doxygen will handle it correctly. line = line[:matchClosingBrace.start()] + "};" + line[matchClosingBrace.end():] - # Search for 'message' and replace it with 'struct' unless 'message' is behind a comment. - matchMsg = re.search("message", line) - if matchMsg is not None and (matchComment is None or matchMsg.start() < matchComment.start()): - output = "struct" + line[:matchMsg.start()] + line[matchMsg.end():] - theOutput += output - else: - theOutput += line + + # Replacements change start of comment... + matchMsg = re.search(r"\bmessage\b", line) + if matchMsg is not None: + line = line[:matchMsg.start()] + "struct" + line[matchMsg.end():] + + # Replacements change start of comment... + matchExt = re.search(r"\bextend\b", line) + if matchExt is not None: + a_extend = line[matchExt.end():] + matchName = re.search(r"\b\w[\S:]*\b", a_extend) + if matchName is not None: + name = a_extend[matchName.start():matchName.end()] + name = re.sub(r'\w+::',r'',name) + a_extend = a_extend[:matchName.start()] + name + ": public " + a_extend[matchName.start():] + else: + a_extend = "_Dummy: public " + a_extend; + line = line[:matchExt.start()] + "struct " + a_extend + + theOutput += (line.strip() + ' ' + comment.strip()).strip() + '\n' + + if isPackage: + # Close the package namespace + theOutput += "}" + isPackage = False + # Now that we've got all lines in the string let's split the lines and print out # one by one. # This is a workaround to get rid of extra empty line at the end which print() method adds. lines = theOutput.splitlines() for line in lines: if len(line) > 0: - print(line) - # Our logger does not add extra line breaks so explicitly adding one to make the log more readable. + print(line) # Add linebreak to generate documentation correct. self.log(line + '\n') - else: - self.log('\n --- skipped empty line') ## Writes @p string to log file. ##