[DEV] better documentation generated

This commit is contained in:
Edouard DUPIN 2013-12-04 21:11:44 +01:00
parent dc6fcf7415
commit 02525c7843
5 changed files with 312 additions and 111 deletions

View File

@ -5,14 +5,109 @@ import lutinTools
# TODO : Add try of generic input ... # TODO : Add try of generic input ...
sys.path.append(lutinTools.GetCurrentPath(__file__) + "/ply/ply/") sys.path.append(lutinTools.GetCurrentPath(__file__) + "/ply/ply/")
sys.path.append(lutinTools.GetCurrentPath(__file__) + "/cppParser/CppHeaderParser/") sys.path.append(lutinTools.GetCurrentPath(__file__) + "/cppParser/CppHeaderParser/")
import CppHeaderParser
import lutinDocHtml import lutinDocHtml
import lutinDocMd import lutinDocMd
##
## @brief Main Documantion class
## @param[in] moduleName Name of the module of this element
##
class doc:
def __init__(self, moduleName):
self.moduleName = moduleName
self.listClass = dict()
self.listVariable = dict()
self.listFunction = dict()
self.listNamepsaces = dict()
##
## @brief Add a File at the parsing system
## @param[in] filename File To add at the parsing element system.
## @return True if no error occured, False otherwise
##
def add_file(self, filename):
debug.debug("adding file in documantation : '" + filename + "'");
try:
metaData = CppHeaderParser.CppHeader(filename)
except CppHeaderParser.CppParseError, e:
debug.error(" can not parse the file: '" + filename + "' error : " + e)
return False
def GenerateDocFile(filename, outFolder, isHtml=True) : # add all classes :
if isHtml == True: for element in metaData.classes:
lutinDocHtml.GenerateDocFile(filename, outFolder) localClass = metaData.classes[element]
if localClass['namespace'] == '':
className = localClass['name']
else: else:
lutinDocDot.GenerateDocFile(filename, outFolder) className = localClass['namespace'] + "::" + localClass['name']
if className in self.listClass.keys():
debug.warning("Might merge class : '" + className + "' file : " + filename)
else:
self.listClass[className] = localClass
# add all namespaces:
# add all global vars:
# add all global function:
return True
##
## @brief Generate Documentation at the folder ...
## @param[in] destFolder Destination folder.
## @param[in] mode (optinnal) generation output mode {html, markdown ...}
##
def generate_documantation(self, destFolder, mode="html"):
if mode == "html":
if lutinDocHtml.generate(self, destFolder) == False:
debug.warning("Generation Documentation :'" + mode + "' ==> return an error for " + self.moduleName)
elif mode == "markdown":
# todo ...
None
else:
debug.error("Unknow Documantation mode generation :'" + mode + "'")
return False
return True
##
## @brief Get the heritage list (parent) of one element.
## @param[in] element Element name.
## @return List of all element herited
##
def get_heritage_list(self, element):
list = []
# get element class :
if element in self.listClass.keys():
localClass = self.listClass[element]
if len(localClass['inherits']) != 0:
# TODO : Support multiple heritage ...
isFirst = True
for heritedClass in localClass['inherits']:
if isFirst == True:
list = self.get_heritage_list(heritedClass['class'])
break;
debug.verbose("find parent : " + element)
list.append(element);
return list
##
## @brief Get the heritage list (child) of this element.
## @param[in] curentClassName Element name.
## @return List of all childs
##
def get_down_heritage_list(self, curentClassName):
list = []
# get element class :
for element in self.listClass:
localClass = self.listClass[element]
if len(localClass['inherits']) != 0:
for heritedClass in localClass['inherits']:
if curentClassName == heritedClass['class']:
list.append(element)
break;
debug.verbose("find childs : " + str(list))
return list

View File

@ -3,6 +3,62 @@ import lutinDebug as debug
import sys import sys
import lutinTools import lutinTools
import CppHeaderParser import CppHeaderParser
import re
global_class_link = {
"std::string" : "http://www.cplusplus.com/reference/string/string/",
"std::u16string" : "http://www.cplusplus.com/reference/string/u16string/",
"std::u32string" : "http://www.cplusplus.com/reference/string/u32string/",
"std::wstring" : "http://www.cplusplus.com/reference/string/wstring/",
"std::vector" : "http://www.cplusplus.com/reference/vector/vector/"
}
def replace_type(match):
value = "<span class=\"code-type\">" + match.group() + "</span>"
return value
def replace_storage_keyword(match):
value = "<span class=\"code-storage-keyword\">" + match.group() + "</span>"
return value
def display_color(valBase):
# storage keyword :
p = re.compile("(inline|const|class|virtual|private|public|protected|friend|const|extern|auto|register|static|unsigned|signed|volatile|char|double|float|int|long|short|void|typedef|struct|union|enum)")
val = p.sub(replace_storage_keyword, valBase)
# type :
p = re.compile("(bool|BOOL|char(16_t|32_t)?|double|float|u?int(8|16|32|64|128)?(_t)?|long|short|signed|size_t|unsigned|void|(I|U)(8|16|32|64|128))")
val = p.sub(replace_type, val)
return val, len(valBase)
def display_type(type, localClassName):
type = type.replace("inline ", "")
lenght = 0;
isFirst = True
out = ''
# we split all the element in list sepa=rated with space to keep class... and standard c+ class
for element in type.split(' '):
if isFirst == False:
out += " "
lenght += 1
isFirst = False
# check if the element in internal at the current lib
if element[:len(localClassName)] == localClassName:
out += "<a href=\"" + element.replace(":","_") + ".html\" class=\"code-type\">" + element + "</a>"
lenght += len(element)
# check if the element is in local Lib:
elif False:
None
# Ckeck if the variable in a standard class:
elif element in global_class_link.keys():
out += "<a href=\"" + global_class_link[element] + "\" class=\"code-type\">" + element + "</a>"
lenght += len(element)
else:
data, lenghtTmp = display_color(element)
out += data
lenght += lenghtTmp
# get every subelement class :
return [out,lenght]
def display_doxygen_param(comment, input, output): def display_doxygen_param(comment, input, output):
data = "<b>Parameter" data = "<b>Parameter"
@ -15,6 +71,7 @@ def display_doxygen_param(comment, input, output):
val = comment.find(" ") val = comment.find(" ")
var = comment[:val] var = comment[:val]
endComment = comment[val:] endComment = comment[val:]
# TODO : Check if it exist in the parameter list ...
data += "<span class=\"code-argument\">" + var + "</span> " + endComment data += "<span class=\"code-argument\">" + var + "</span> " + endComment
data += "<br/>" data += "<br/>"
@ -38,9 +95,9 @@ def parse_doxygen(data) :
data = data.replace("\n **", "\n") data = data.replace("\n **", "\n")
data = data.replace("\n * ", "\n") data = data.replace("\n * ", "\n")
data = data.replace("\n *", "\n") data = data.replace("\n *", "\n")
data = data.replace("\r", "") data = data.replace("\r", '')
streams = data.split("@") streams = data.split("@")
data2 = "" data2 = ''
for element in streams: for element in streams:
if element[:1] == "\n" \ if element[:1] == "\n" \
or element[:2] == "\n\n": or element[:2] == "\n\n":
@ -60,7 +117,7 @@ def parse_doxygen(data) :
data2 += element[5:] data2 += element[5:]
data2 += "<br/> " data2 += "<br/> "
data3 = "" data3 = ''
for element in streams: for element in streams:
if element[:1] == "\n" \ if element[:1] == "\n" \
or element[:2] == "\n\n": or element[:2] == "\n\n":
@ -79,41 +136,44 @@ def parse_doxygen(data) :
data3 += "<b>Return:</b> " data3 += "<b>Return:</b> "
data3 += element[7:] data3 += element[7:]
data3 += "<br/>" data3 += "<br/>"
if data3 != "": if data3 != '':
data2 += "<ul>\n" data2 += "<ul>\n"
data2 += data3 data2 += data3
data2 += "</ul>\n" data2 += "</ul>\n"
return data2 return data2
def white_space(size) :
ret = ''
def writeExpendSize(data, size) :
ret = data
for iii in range(len(ret), size): for iii in range(len(ret), size):
ret += " " ret += " "
return ret return ret
def displayReductFunction(function, file, classement, sizeReturn, sizefunction) : def displayReductFunction(function, file, classement, sizeReturn, sizefunction, localClassName) :
lineData = classement + " " file.write(classement + " ")
lenght = len(classement)+1;
if function['destructor'] : if function['destructor'] :
lineData += writeExpendSize("", sizeReturn) file.write(white_space(sizeReturn) + "~")
lineData += "~" lenght += sizeReturn+1;
elif function['constructor'] : elif function['constructor'] :
lineData += writeExpendSize("", sizeReturn+1) file.write(white_space(sizeReturn+1))
lenght += sizeReturn+1;
else : else :
lineData += writeExpendSize(function["rtnType"], sizeReturn+1) typeData, typeLen = display_type(function["rtnType"], localClassName);
parameterPos = len(lineData) + sizefunction+2; file.write(typeData)
lineData +="<a class=\"code-function\" href=\"#"+ function["name"] + "\">" + function["name"] + "</a>" file.write(white_space(sizeReturn+1 - typeLen))
lineData += writeExpendSize("", sizefunction+1 - len(function["name"])) lenght += sizeReturn+1;
lineData += "(" parameterPos = lenght + sizefunction+2;
file.write(lineData); file.write("<a class=\"code-function\" href=\"#"+ function["name"] + "\">" + function["name"] + "</a>")
file.write(white_space(sizefunction+1 - len(function["name"])))
file.write("(")
isFirst = True isFirst = True
for param in function["parameters"]: for param in function["parameters"]:
if isFirst == False: if isFirst == False:
file.write(",<br/>") file.write(",<br/>")
file.write(writeExpendSize("",parameterPos)) file.write(white_space(parameterPos))
file.write(param['type'])
typeData, typeLen = display_type(param["type"], localClassName);
file.write(typeData)
if param['name'] != "": if param['name'] != "":
file.write(" ") file.write(" ")
file.write("<span class=\"code-argument\">" + param['name'] + "</span>") file.write("<span class=\"code-argument\">" + param['name'] + "</span>")
@ -122,7 +182,7 @@ def displayReductFunction(function, file, classement, sizeReturn, sizefunction)
file.write("<br>") file.write("<br>")
def displayFunction(namespace, function, file, classement, sizeReturn, sizefunction) : def displayFunction(namespace, function, file, classement, sizeReturn, sizefunction, localClassName) :
lineData = "" lineData = ""
if ( function['constructor'] == True \ if ( function['constructor'] == True \
or function['destructor'] == True \ or function['destructor'] == True \
@ -136,22 +196,24 @@ def displayFunction(namespace, function, file, classement, sizeReturn, sizefunct
file.write("<pre>\n"); file.write("<pre>\n");
if function['destructor'] : if function['destructor'] :
lineData = "~" file.write("~")
lenght = 1;
elif function['constructor'] : elif function['constructor'] :
lineData = "" lenght = 0;
else : else :
lineData = function["rtnType"] + " " typeData, typeLen = display_type(function["rtnType"], localClassName);
file.write(typeData + " ")
lenght = typeLen+1;
parameterPos = len(lineData) + len(function["name"]) + 1; parameterPos = lenght + len(function["name"]) + 1;
lineData += "<span class=\"code-function\">" + function["name"] + "</span>" file.write("<span class=\"code-function\">" + function["name"] + "</span>(")
lineData += "("
file.write(lineData);
isFirst = True isFirst = True
for param in function["parameters"]: for param in function["parameters"]:
if isFirst == False: if isFirst == False:
file.write(",\n") file.write(",\n")
file.write(writeExpendSize("", parameterPos)) file.write(white_space(parameterPos))
file.write(param['type']) typeData, typeLen = display_type(param["type"], localClassName);
file.write(typeData)
if param['name'] != "": if param['name'] != "":
file.write(" ") file.write(" ")
file.write("<span class=\"code-argument\">" + param['name'] + "</span>") file.write("<span class=\"code-argument\">" + param['name'] + "</span>")
@ -178,53 +240,68 @@ def calsulateSizeReturn(function, size) :
return len(function["rtnType"])+1 return len(function["rtnType"])+1
return size return size
def add_header(file): def class_name_to_file_name(className):
file.write("<!DOCTYPE html>\n") className = className.replace(":", "_")
file.write("<html>\n") className = className.replace(" ", "")
file.write("<head>\n") className += ".html"
file.write(" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0\">\n") return className
file.write(" <title>Ewol Library</title>\n")
file.write(" <link rel=\"stylesheet\" href=\"base.css\">\n")
file.write("</head>\n")
file.write("<body>\n")
file.write(" <div class=\"navbar navbar-fixed-top\">\n");
file.write(" <div class=\"container\">\n")
file.write(" Ewol Library\n")
file.write(" </div>\n")
file.write(" </div>\n")
file.write(" <div class=\"container\" id=\"content\">\n")
def add_buttom(file): def generate(myDoc, outFolder) :
file.write(" </div>\n")
file.write("</body>\n")
file.write("</html>\n")
def GenerateDocFile(filename, outFolder) :
try:
metaData = CppHeaderParser.CppHeader(filename)
except CppHeaderParser.CppParseError, e:
debug.error(" can not parse the file: '" + filename + "' error : " + e)
return False
lutinTools.CreateDirectoryOfFile(outFolder+"/");
lutinTools.CopyFile(lutinTools.GetCurrentPath(__file__)+"/theme/base.css", outFolder+"/base.css") lutinTools.CopyFile(lutinTools.GetCurrentPath(__file__)+"/theme/base.css", outFolder+"/base.css")
# create common header
for element in metaData.classes: genericHeader = "<!DOCTYPE html>\n"
classFileName = outFolder + "/" genericHeader += "<html>\n"
localClass = metaData.classes[element] genericHeader += "<head>\n"
if localClass['namespace'] == "": genericHeader += " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0\">\n"
className = localClass['name'] genericHeader += " <title>Ewol Library</title>\n"
genericHeader += " <link rel=\"stylesheet\" href=\"base.css\">\n"
genericHeader += "</head>\n"
genericHeader += "<body>\n"
genericHeader += " <div class=\"navbar navbar-fixed-top\">\n"
genericHeader += " <div class=\"container\">\n"
genericHeader += " <h1>Ewol Library</h1>\n"
#genericHeader += " <ul>\n"
baseNamespace = ""
for className in sorted(myDoc.listClass.iterkeys()) :
pos = className.find("::")
if pos >= 0:
namespace = className[:pos]
rest = className[pos+2:]
else: else:
className = localClass['namespace'] + "::" + localClass['name'] namespace = ""
rest = className
if baseNamespace != namespace:
if baseNamespace != "":
genericHeader += " </ul>\n"
genericHeader += " <li>" + namespace + "</li>\n"
genericHeader += " <ul>\n"
baseNamespace = namespace
genericHeader += " <li><a href=\"" + class_name_to_file_name(className) + "\">" + rest + "</a></li>\n"
if baseNamespace != "":
genericHeader += " </ul>\n"
#genericHeader += " </ul>\n"
genericHeader += " </div>\n"
genericHeader += " </div>\n"
genericHeader += " <div class=\"container\" id=\"content\">\n"
genericFooter = " </div>\n"
genericFooter += "</body>\n"
genericFooter += "</html>\n"
for className in sorted(myDoc.listClass.iterkeys()) :
localClass = myDoc.listClass[className]
debug.debug(" class: " + className) debug.debug(" class: " + className)
classFileName += className classFileName = outFolder + "/" + class_name_to_file_name(className)
# Replace all :: with __ # create directory (in case)
classFileName = classFileName.replace(":", "_") lutinTools.CreateDirectoryOfFile(classFileName);
classFileName = classFileName.replace(" ", "") debug.printElement("doc", myDoc.moduleName, "<==", className)
classFileName += ".html" # open the file :
file = open(classFileName, "w") file = open(classFileName, "w")
add_header(file) file.write(genericHeader)
file.write("<h1>" + className + "</h1>\n") file.write("<h1>" + className + "</h1>\n")
file.write("\n") file.write("\n")
@ -247,27 +324,34 @@ def GenerateDocFile(filename, outFolder) :
# TODO: ... # TODO: ...
file.write("<pre>\n"); file.write("<pre>\n");
for function in localClass["methods"]["public"]: for function in localClass["methods"]["public"]:
displayReductFunction(function, file, "public: ", sizeReturn, sizefunction) displayReductFunction(function, file, "+ ", sizeReturn, sizefunction, className)
for function in localClass["methods"]["protected"]: for function in localClass["methods"]["protected"]:
displayReductFunction(function, file, "protected:", sizeReturn, sizefunction) displayReductFunction(function, file, "# ", sizeReturn, sizefunction, className)
for function in localClass["methods"]["private"]: for function in localClass["methods"]["private"]:
displayReductFunction(function, file, "private: ", sizeReturn, sizefunction) displayReductFunction(function, file, "- ", sizeReturn, sizefunction, className)
file.write("</pre>\n"); file.write("</pre>\n");
file.write("\n") file.write("\n")
file.write("\n") file.write("\n")
heritage = myDoc.get_heritage_list(className)
heritageDown = myDoc.get_down_heritage_list(className)
if len(localClass['inherits']) != 0: if len(heritage) > 1 \
or len(heritageDown) > 0:
file.write("<h2>Object Hierarchy:</h2>\n") file.write("<h2>Object Hierarchy:</h2>\n")
file.write("<pre>\n") file.write("<pre>\n")
for heritedClass in localClass['inherits']: level = 0;
file.write("" + heritedClass['class'] + "\n") for heritedClass in heritage:
file.write(" |\n") if level != 0:
file.write(" +--> " + localClass['name'] + "\n") file.write(white_space(level*4) + "+--> ")
if heritedClass != className:
file.write("<a href=\"" + class_name_to_file_name(heritedClass) + "\">" + heritedClass + "</a>\n")
else:
file.write("<b>" + heritedClass + "</b>\n")
level += 1;
for heritedClass in heritageDown:
file.write(white_space(level*4) + "+--> ")
file.write("<a href=\"" + class_name_to_file_name(heritedClass) + "\">" + heritedClass + "</a>\n")
file.write("</pre>\n") file.write("</pre>\n")
file.write("<br/>\n") file.write("<br/>\n")
""" """
file.write("<h2>Signals:</h2>\n") file.write("<h2>Signals:</h2>\n")
# display all signals : # display all signals :
@ -287,21 +371,18 @@ def GenerateDocFile(filename, outFolder) :
file.write("<h2>Detail:<h2>\n") file.write("<h2>Detail:<h2>\n")
# display all the class internal functions : # display all the class internal functions :
for function in localClass["methods"]["public"]: for function in localClass["methods"]["public"]:
displayFunction(localClass['namespace'] , function, file, "public: ", sizeReturn, sizefunction) displayFunction(localClass['namespace'] , function, file, "+ ", sizeReturn, sizefunction, className)
file.write("\n<hr/>\n") file.write("\n<hr/>\n")
for function in localClass["methods"]["protected"]: for function in localClass["methods"]["protected"]:
displayFunction(localClass['namespace'] , function, file, "protected:", sizeReturn, sizefunction) displayFunction(localClass['namespace'] , function, file, "# ", sizeReturn, sizefunction, className)
file.write("\n<hr/>\n") file.write("\n<hr/>\n")
for function in localClass["methods"]["private"]: for function in localClass["methods"]["private"]:
displayFunction(localClass['namespace'] , function, file, "private: ", sizeReturn, sizefunction) displayFunction(localClass['namespace'] , function, file, "- ", sizeReturn, sizefunction, className)
file.write("\n<hr/>\n") file.write("\n<hr/>\n")
add_buttom(file) file.write(genericFooter)
file.close() file.close()
"""
"""

View File

@ -33,11 +33,12 @@ class module:
# type of the module: # type of the module:
self.type='LIBRARY' self.type='LIBRARY'
# Name of the module # Name of the module
self.name='' self.name=moduleName
# Dependency list: # Dependency list:
self.depends=[] self.depends=[]
# Dependency list: # Dependency list:
self.docPath=lutinTools.GetCurrentPath(file) self.docPath=lutinTools.GetCurrentPath(file)
self.documentation = lutinDoc.doc(self.name)
# export PATH # export PATH
self.export_path=[] self.export_path=[]
self.local_path=[] self.local_path=[]
@ -72,7 +73,6 @@ class module:
raise 'Input value error' raise 'Input value error'
self.originFile = file; self.originFile = file;
self.originFolder = lutinTools.GetCurrentPath(self.originFile) self.originFolder = lutinTools.GetCurrentPath(self.originFile)
self.name=moduleName
self.localHeritage = heritage.heritage(self) self.localHeritage = heritage.heritage(self)
self.packageProp = { "COMPAGNY_TYPE" : set(""), self.packageProp = { "COMPAGNY_TYPE" : set(""),
@ -384,14 +384,16 @@ class module:
def CreateDoc(self, target) : def CreateDoc(self, target) :
if self.docPath == "": if self.docPath == "":
return False return False
lutinTools.RemoveFolderAndSubFolder(target.GetDocFolder(self.name));
for root, dirnames, filenames in os.walk(self.docPath): for root, dirnames, filenames in os.walk(self.docPath):
tmpList = fnmatch.filter(filenames, "*.h") tmpList = fnmatch.filter(filenames, "*.h")
# Import the module : # Import the module :
for filename in tmpList: for filename in tmpList:
fileCompleteName = os.path.join(root, filename) fileCompleteName = os.path.join(root, filename)
debug.debug(" Find a file : '" + fileCompleteName + "'") debug.debug(" Find a file : '" + fileCompleteName + "'")
lutinDoc.GenerateDocFile(fileCompleteName, target.GetDocFolder(self.name)); self.documentation.add_file(fileCompleteName)
# Real creation of the documentation :
lutinTools.RemoveFolderAndSubFolder(target.GetDocFolder(self.name));
self.documentation.generate_documantation(target.GetDocFolder(self.name))
# call here to build the module # call here to build the module

View File

@ -5,7 +5,7 @@ html {
body { body {
font-family: 'Ubuntu',Tahoma,sans-serif; font-family: 'Ubuntu',Tahoma,sans-serif;
padding-top: 60px; padding-top: 40px;
padding-bottom: 40px; padding-bottom: 40px;
font-size: 15px; font-size: 15px;
line-height: 150%; line-height: 150%;
@ -13,7 +13,7 @@ body {
color: #333333; color: #333333;
background-color: #ffffff; background-color: #ffffff;
display: block; display: block;
margin-left: 50px; margin-left: 250px;
margin-right: 50px; margin-right: 50px;
}; };
@ -38,24 +38,31 @@ body {
} }
.navbar-fixed-top { .navbar-fixed-top {
width:210px;
display: block; display: block;
position: fixed; position: fixed;
padding-top: 0px; padding-top: 0px;
top: 0; top: 0;
min-height: 40px; height: 100%;
right: 0; right: 0;
left: 0; left: 0;
margin-bottom: 0; margin-bottom: 0;
background-color: #d44413; background-color: #d44413;
border: 1px solid #c64012; border: 1px solid #c64012;
font-size: 20px; font-size: 15px;
font-weight: 200; font-weight: 200;
color: #ffffff; color: #ffffff;
text-shadow: 0 1px 0 #ce4213; text-shadow: 0 1px 0 #ce4213;
padding: 10px 20px 10px; padding: 10px 20px 10px;
margin-left: -20px; margin-left: -20px;
overflow:scroll;
overflow-x:hidden;
} }
/*
.navbar ul {
font-size: 15px;
};
*/
h1, h2, h3, h4, h5, h6 { h1, h2, h3, h4, h5, h6 {
display: block; display: block;
margin: 10px 0; margin: 10px 0;
@ -88,6 +95,8 @@ pre {
border-radius: 4px; border-radius: 4px;
} }
.code-function { .code-function {
text-decoration:none; text-decoration:none;
color:#09857e; color:#09857e;
@ -106,3 +115,17 @@ pre {
font-weight:bold; font-weight:bold;
} }
.code-number {
text-decoration:none;
color:#007b00;
}
.code-keyword {
text-decoration:none;
color:#215eb8;
font-weight:bold;
}
.code-storage-keyword {
text-decoration:none;
color:#466cb4;
}