mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-07-06 13:33:55 -07:00
[Networking] Doxygen
This commit is contained in:
parent
05ae6f47a6
commit
98cb71ab29
17 changed files with 2179 additions and 122 deletions
285
proto2cpp.py
Normal file
285
proto2cpp.py
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
#!/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.8-beta OSI
|
||||
#
|
||||
# How to enable this filter in Doxygen:
|
||||
# 1. Generate Doxygen configuration file with command 'doxygen -g <filename>'
|
||||
# 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
|
||||
#
|
||||
#
|
||||
# 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
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
##
|
||||
|
||||
import fnmatch
|
||||
import inspect
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
## 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.<br />
|
||||
## There are three different logging levels for the class:
|
||||
## <ul><li>#logNone: do not log anything</li>
|
||||
## <li>#logErrors: log errors only</li>
|
||||
## <li>#logAll: log everything</li></ul>
|
||||
## Logging level is determined by \c #logLevel.<br />
|
||||
## Error logs are written to file determined by \c #errorLogFile.<br />
|
||||
## Debug logs are written to file determined by \c #logFile.
|
||||
#
|
||||
class proto2cpp:
|
||||
|
||||
## Logging level: do not log anything.
|
||||
logNone = 0
|
||||
## Logging level: log errors only.
|
||||
logErrors = 1
|
||||
## Logging level: log everything.
|
||||
logAll = 2
|
||||
|
||||
## Constructor
|
||||
#
|
||||
def __init__(self):
|
||||
## Debug log file name.
|
||||
self.logFile = "proto2cpp.log"
|
||||
## Error log file name.
|
||||
self.errorLogFile = "proto2cpp.error.log"
|
||||
## Logging level.
|
||||
self.logLevel = self.logNone
|
||||
|
||||
## Handles a file.
|
||||
##
|
||||
## If @p fileName has .proto suffix, it is processed through parseFile().
|
||||
## Otherwise it is printed to stdout as is except for file \c proto2cpp.py without
|
||||
## path since it's the script given to python for processing.
|
||||
##
|
||||
## @param fileName Name of the file to be handled.
|
||||
#
|
||||
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.
|
||||
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')
|
||||
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.
|
||||
theOutput = ''
|
||||
for line in inputFile:
|
||||
# Search for comment ("//") and add one more slash character ("/") to the comment
|
||||
# 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 ("<") 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()):
|
||||
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:
|
||||
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 " 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 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)
|
||||
if isEnum is True and matchClosingBrace is not None:
|
||||
line = line[:matchClosingBrace.start()] + "};" + line[matchClosingBrace.end():]
|
||||
isEnum = False
|
||||
elif isEnum is False and matchClosingBrace is not None:
|
||||
# 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():]
|
||||
|
||||
# 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) # Add linebreak to generate documentation correct.
|
||||
self.log(line + '\n')
|
||||
|
||||
## Writes @p string to log file.
|
||||
##
|
||||
## #logLevel must be #logAll or otherwise the logging is skipped.
|
||||
##
|
||||
## @param string String to be written to log file.
|
||||
#
|
||||
def log(self, string):
|
||||
if self.logLevel >= self.logAll:
|
||||
with open(self.logFile, 'a') as theFile:
|
||||
theFile.write(string)
|
||||
|
||||
## Writes @p string to error log file.
|
||||
##
|
||||
## #logLevel must be #logError or #logAll or otherwise the logging is skipped.
|
||||
##
|
||||
## @param string String to be written to error log file.
|
||||
#
|
||||
def logError(self, string):
|
||||
if self.logLevel >= self.logError:
|
||||
with open(self.errorLogFile, 'a') as theFile:
|
||||
theFile.write(string)
|
||||
|
||||
converter = proto2cpp()
|
||||
# Doxygen will give us the file names
|
||||
for filename in sys.argv[1:]:
|
||||
converter.handleFile(filename)
|
||||
|
||||
# end of file
|
||||
Loading…
Add table
Add a link
Reference in a new issue