CloverBootloader/BaseTools/Scripts/PackageDocumentTools/plugins/EdkPlugins/edk2/model/doxygengen.py

1085 lines
48 KiB
Python
Raw Normal View History

## @file
#
# This file produce action class to generate doxygen document for edk2 codebase.
# The action classes are shared by GUI and command line tools.
#
# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
#
# SPDX-License-Identifier: BSD-2-Clause-Patent
"""This file produce action class to generate doxygen document for edk2 codebase.
The action classes are shared by GUI and command line tools.
"""
from plugins.EdkPlugins.basemodel import doxygen
import os
try:
import wx
gInGui = True
except:
gInGui = False
import re
from plugins.EdkPlugins.edk2.model import inf
from plugins.EdkPlugins.edk2.model import dec
from plugins.EdkPlugins.basemodel.message import *
_ignore_dir = ['.svn', '_svn', 'cvs']
_inf_key_description_mapping_table = {
'INF_VERSION':'Version of INF file specification',
#'BASE_NAME':'Module Name',
'FILE_GUID':'Module Guid',
'MODULE_TYPE': 'Module Type',
'VERSION_STRING': 'Module Version',
'LIBRARY_CLASS': 'Produced Library Class',
'EFI_SPECIFICATION_VERSION': 'UEFI Specification Version',
'PI_SPECIFICATION_VERSION': 'PI Specification Version',
'ENTRY_POINT': 'Module Entry Point Function',
'CONSTRUCTOR': 'Library Constructor Function'
}
_dec_key_description_mapping_table = {
'DEC_SPECIFICATION': 'Version of DEC file specification',
'PACKAGE_GUID': 'Package Guid'
}
class DoxygenAction:
"""This is base class for all doxygen action.
"""
def __init__(self, doxPath, chmPath, outputPath, projname, mode='html', log=None, verbose=False):
"""Constructor function.
@param doxPath the obosolution path of doxygen execute file.
@param outputPath the obosolution output path.
@param log log function for output message
"""
self._doxPath = doxPath
self._chmPath = chmPath
self._outputPath = outputPath
self._projname = projname
self._configFile = None # doxygen config file is used by doxygen exe file
self._indexPageFile = None # doxygen page file for index page.
self._log = log
self._mode = mode
self._verbose = verbose
self._doxygenCallback = None
self._chmCallback = None
def Log(self, message, level='info'):
if self._log is not None:
self._log(message, level)
def IsVerbose(self):
return self._verbose
def Generate(self):
"""Generate interface called by outer directly"""
self.Log(">>>>>> Start generate doxygen document for %s... Zzz....\n" % self._projname)
# create doxygen config file at first
self._configFile = doxygen.DoxygenConfigFile()
self._configFile.SetOutputDir(self._outputPath)
self._configFile.SetWarningFilePath(os.path.join(self._outputPath, 'warning.txt'))
if self._mode.lower() == 'html':
self._configFile.SetHtmlMode()
else:
self._configFile.SetChmMode()
self.Log(" >>>>>> Initialize doxygen config file...Zzz...\n")
self.InitializeConfigFile()
self.Log(" >>>>>> Generate doxygen index page file...Zzz...\n")
indexPagePath = self.GenerateIndexPage()
if indexPagePath is None:
self.Log("Fail to generate index page!\n", 'error')
return False
else:
self.Log("Success to create doxygen index page file %s \n" % indexPagePath)
# Add index page doxygen file to file list.
self._configFile.AddFile(indexPagePath)
# save config file to output path
configFilePath = os.path.join(self._outputPath, self._projname + '.doxygen_config')
self._configFile.Generate(configFilePath)
self.Log(" <<<<<< Success Save doxygen config file to %s...\n" % configFilePath)
# launch doxygen tool to generate document
if self._doxygenCallback is not None:
self.Log(" >>>>>> Start doxygen process...Zzz...\n")
if not self._doxygenCallback(self._doxPath, configFilePath):
return False
else:
self.Log("Fail to create doxygen process!", 'error')
return False
return True
def InitializeConfigFile(self):
"""Initialize config setting for doxygen project. It will be invoked after config file
object is created. Inherited class should implement it.
"""
def GenerateIndexPage(self):
"""Generate doxygen index page. Inherited class should implement it."""
return None
def RegisterCallbackDoxygenProcess(self, callback):
self._doxygenCallback = callback
def RegisterCallbackCHMProcess(self, callback):
self._chmCallback = callback
class PlatformDocumentAction(DoxygenAction):
"""Generate platform doxygen document, will be implement at future."""
class PackageDocumentAction(DoxygenAction):
"""Generate package reference document"""
def __init__(self, doxPath, chmPath, outputPath, pObj, mode='html', log=None, arch=None, tooltag=None,
onlyInclude=False, verbose=False):
DoxygenAction.__init__(self, doxPath, chmPath, outputPath, pObj.GetName(), mode, log, verbose)
self._pObj = pObj
self._arch = arch
self._tooltag = tooltag
self._onlyIncludeDocument = onlyInclude
def InitializeConfigFile(self):
if self._arch == 'IA32':
self._configFile.AddPreDefined('MDE_CPU_IA32')
elif self._arch == 'X64':
self._configFile.AddPreDefined('MDE_CPU_X64')
elif self._arch == 'IPF':
self._configFile.AddPreDefined('MDE_CPU_IPF')
elif self._arch == 'EBC':
self._configFile.AddPreDefined('MDE_CPU_EBC')
else:
self._arch = None
self._configFile.AddPreDefined('MDE_CPU_IA32')
self._configFile.AddPreDefined('MDE_CPU_X64')
self._configFile.AddPreDefined('MDE_CPU_IPF')
self._configFile.AddPreDefined('MDE_CPU_EBC')
self._configFile.AddPreDefined('MDE_CPU_ARM')
namestr = self._pObj.GetName()
if self._arch is not None:
namestr += '[%s]' % self._arch
if self._tooltag is not None:
namestr += '[%s]' % self._tooltag
self._configFile.SetProjectName(namestr)
self._configFile.SetStripPath(self._pObj.GetWorkspace())
self._configFile.SetProjectVersion(self._pObj.GetFileObj().GetVersion())
self._configFile.AddPattern('*.decdoxygen')
if self._tooltag.lower() == 'msft':
self._configFile.AddPreDefined('_MSC_EXTENSIONS')
elif self._tooltag.lower() == 'gnu':
self._configFile.AddPreDefined('__GNUC__')
elif self._tooltag.lower() == 'intel':
self._configFile.AddPreDefined('__INTEL_COMPILER')
else:
self._tooltag = None
self._configFile.AddPreDefined('_MSC_EXTENSIONS')
self._configFile.AddPreDefined('__GNUC__')
self._configFile.AddPreDefined('__INTEL_COMPILER')
self._configFile.AddPreDefined('ASM_PFX= ')
self._configFile.AddPreDefined('OPTIONAL= ')
def GenerateIndexPage(self):
"""Generate doxygen index page. Inherited class should implement it."""
fObj = self._pObj.GetFileObj()
pdObj = doxygen.DoxygenFile('%s Package Document' % self._pObj.GetName(),
'%s.decdoxygen' % self._pObj.GetFilename())
self._configFile.AddFile(pdObj.GetFilename())
pdObj.AddDescription(fObj.GetFileHeader())
defSection = fObj.GetSectionByName('defines')[0]
baseSection = doxygen.Section('PackageBasicInformation', 'Package Basic Information')
descr = '<TABLE>'
for obj in defSection.GetObjects():
if obj.GetKey() in _dec_key_description_mapping_table.keys():
descr += '<TR>'
descr += '<TD><B>%s</B></TD>' % _dec_key_description_mapping_table[obj.GetKey()]
descr += '<TD>%s</TD>' % obj.GetValue()
descr += '</TR>'
descr += '</TABLE><br>'
baseSection.AddDescription(descr)
pdObj.AddSection(baseSection)
knownIssueSection = doxygen.Section('Known_Issue_section', 'Known Issue')
knownIssueSection.AddDescription('<ul>')
knownIssueSection.AddDescription('<li> OPTIONAL macro for function parameter can not be dealed with doxygen, so it disapear in this document! </li>')
knownIssueSection.AddDescription('</ul>')
pdObj.AddSection(knownIssueSection)
self.AddAllIncludeFiles(self._pObj, self._configFile)
pages = self.GenerateIncludesSubPage(self._pObj, self._configFile)
if len(pages) != 0:
pdObj.AddPages(pages)
pages = self.GenerateLibraryClassesSubPage(self._pObj, self._configFile)
if len(pages) != 0:
pdObj.AddPages(pages)
pages = self.GeneratePcdSubPages(self._pObj, self._configFile)
if len(pages) != 0:
pdObj.AddPages(pages)
pages = self.GenerateGuidSubPages(self._pObj, self._configFile)
if len(pages) != 0:
pdObj.AddPages(pages)
pages = self.GeneratePpiSubPages(self._pObj, self._configFile)
if len(pages) != 0:
pdObj.AddPages(pages)
pages = self.GenerateProtocolSubPages(self._pObj, self._configFile)
if len(pages) != 0:
pdObj.AddPages(pages)
if not self._onlyIncludeDocument:
pdObj.AddPages(self.GenerateModulePages(self._pObj, self._configFile))
pdObj.Save()
return pdObj.GetFilename()
def GenerateIncludesSubPage(self, pObj, configFile):
# by default add following path as include path to config file
pkpath = pObj.GetFileObj().GetPackageRootPath()
configFile.AddIncludePath(os.path.join(pkpath, 'Include'))
configFile.AddIncludePath(os.path.join(pkpath, 'Include', 'Library'))
configFile.AddIncludePath(os.path.join(pkpath, 'Include', 'Protocol'))
configFile.AddIncludePath(os.path.join(pkpath, 'Include', 'Ppi'))
configFile.AddIncludePath(os.path.join(pkpath, 'Include', 'Guid'))
configFile.AddIncludePath(os.path.join(pkpath, 'Include', 'IndustryStandard'))
rootArray = []
pageRoot = doxygen.Page("Public Includes", "%s_public_includes" % pObj.GetName())
objs = pObj.GetFileObj().GetSectionObjectsByName('includes')
if len(objs) == 0: return []
for obj in objs:
# Add path to include path
path = os.path.join(pObj.GetFileObj().GetPackageRootPath(), obj.GetPath())
configFile.AddIncludePath(path)
# only list common folder's include file
if obj.GetArch().lower() != 'common':
continue
bNeedAddIncludePage = False
topPage = doxygen.Page(self._ConvertPathToDoxygen(path, pObj), 'public_include_top')
topPage.AddDescription('<ul>\n')
for file in os.listdir(path):
if file.lower() in _ignore_dir: continue
fullpath = os.path.join(path, file)
if os.path.isfile(fullpath):
self.ProcessSourceFileForInclude(fullpath, pObj, configFile)
topPage.AddDescription('<li> \link %s\endlink </li>\n' % self._ConvertPathToDoxygen(fullpath, pObj))
else:
if file.lower() in ['library', 'protocol', 'guid', 'ppi', 'ia32', 'x64', 'ipf', 'ebc', 'arm', 'pi', 'uefi', 'aarch64']:
continue
bNeedAddSubPage = False
subpage = doxygen.Page(self._ConvertPathToDoxygen(fullpath, pObj), 'public_include_%s' % file)
subpage.AddDescription('<ul>\n')
for subfile in os.listdir(fullpath):
if subfile.lower() in _ignore_dir: continue
bNeedAddSubPage = True
subfullpath = os.path.join(fullpath, subfile)
self.ProcessSourceFileForInclude(subfullpath, pObj, configFile)
subpage.AddDescription('<li> \link %s \endlink </li>\n' % self._ConvertPathToDoxygen(subfullpath, pObj))
subpage.AddDescription('</ul>\n')
if bNeedAddSubPage:
bNeedAddIncludePage = True
pageRoot.AddPage(subpage)
topPage.AddDescription('</ul>\n')
if bNeedAddIncludePage:
pageRoot.AddPage(topPage)
if pageRoot.GetSubpageCount() != 0:
return [pageRoot]
else:
return []
def GenerateLibraryClassesSubPage(self, pObj, configFile):
"""
Generate sub page for library class for package.
One DEC file maybe contains many library class sections
for different architecture.
@param fObj DEC file object.
"""
rootArray = []
pageRoot = doxygen.Page("Library Class", "%s_libraryclass" % pObj.GetName())
objs = pObj.GetFileObj().GetSectionObjectsByName('libraryclass', self._arch)
if len(objs) == 0: return []
if self._arch is not None:
for obj in objs:
classPage = doxygen.Page(obj.GetClassName(),
"lc_%s" % obj.GetClassName())
comments = obj.GetComment()
if len(comments) != 0:
classPage.AddDescription('<br>\n'.join(comments) + '<br>\n')
pageRoot.AddPage(classPage)
path = os.path.join(pObj.GetFileObj().GetPackageRootPath(), obj.GetHeaderFile())
path = path[len(pObj.GetWorkspace()) + 1:]
if len(comments) == 0:
classPage.AddDescription('\copydoc %s<p>' % obj.GetHeaderFile())
section = doxygen.Section('ref', 'Refer to Header File')
section.AddDescription('\link %s\n' % obj.GetHeaderFile())
section.AddDescription(' \endlink<p>\n')
classPage.AddSection(section)
fullPath = os.path.join(pObj.GetFileObj().GetPackageRootPath(), obj.GetHeaderFile())
self.ProcessSourceFileForInclude(fullPath, pObj, configFile)
else:
archPageDict = {}
for obj in objs:
if obj.GetArch() not in archPageDict.keys():
archPageDict[obj.GetArch()] = doxygen.Page(obj.GetArch(),
'lc_%s' % obj.GetArch())
pageRoot.AddPage(archPageDict[obj.GetArch()])
subArchRoot = archPageDict[obj.GetArch()]
classPage = doxygen.Page(obj.GetClassName(),
"lc_%s" % obj.GetClassName())
comments = obj.GetComment()
if len(comments) != 0:
classPage.AddDescription('<br>\n'.join(comments) + '<br>\n')
subArchRoot.AddPage(classPage)
path = os.path.join(pObj.GetFileObj().GetPackageRootPath(), obj.GetHeaderFile())
path = path[len(pObj.GetWorkspace()) + 1:]
if len(comments) == 0:
classPage.AddDescription('\copydoc %s<p>' % obj.GetHeaderFile())
section = doxygen.Section('ref', 'Refer to Header File')
section.AddDescription('\link %s\n' % obj.GetHeaderFile())
section.AddDescription(' \endlink<p>\n')
classPage.AddSection(section)
fullPath = os.path.join(pObj.GetFileObj().GetPackageRootPath(), obj.GetHeaderFile())
self.ProcessSourceFileForInclude(fullPath, pObj, configFile)
rootArray.append(pageRoot)
return rootArray
def ProcessSourceFileForInclude(self, path, pObj, configFile, infObj=None):
"""
@param path the analysising file full path
@param pObj package object
@param configFile doxygen config file.
"""
if gInGui:
wx.Yield()
if not os.path.exists(path):
ErrorMsg('Source file path %s does not exist!' % path)
return
if configFile.FileExists(path):
return
try:
with open(path, 'r') as f:
lines = f.readlines()
except UnicodeDecodeError:
return
except IOError:
ErrorMsg('Fail to open file %s' % path)
return
configFile.AddFile(path)
no = 0
for no in range(len(lines)):
if len(lines[no].strip()) == 0:
continue
if lines[no].strip()[:2] in ['##', '//', '/*', '*/']:
continue
index = lines[no].lower().find('include')
#mo = IncludePattern.finditer(lines[no].lower())
mo = re.match(r"^#\s*include\s+[<\"]([\\/\w.]+)[>\"]$", lines[no].strip().lower())
if not mo:
continue
mo = re.match(r"^[#\w\s]+[<\"]([\\/\w.]+)[>\"]$", lines[no].strip())
filePath = mo.groups()[0]
if filePath is None or len(filePath) == 0:
continue
# find header file in module's path firstly.
fullPath = None
if os.path.exists(os.path.join(os.path.dirname(path), filePath)):
# Find the file in current directory
fullPath = os.path.join(os.path.dirname(path), filePath).replace('\\', '/')
else:
# find in depedent package's include path
incObjs = pObj.GetFileObj().GetSectionObjectsByName('includes')
for incObj in incObjs:
incPath = os.path.join(pObj.GetFileObj().GetPackageRootPath(), incObj.GetPath()).strip()
incPath = os.path.realpath(os.path.join(incPath, filePath))
if os.path.exists(incPath):
fullPath = incPath
break
if infObj is not None:
pkgInfObjs = infObj.GetSectionObjectsByName('packages')
for obj in pkgInfObjs:
decObj = dec.DECFile(os.path.join(pObj.GetWorkspace(), obj.GetPath()))
if not decObj:
ErrorMsg ('Fail to create pacakge object for %s' % obj.GetPackageName())
continue
if not decObj.Parse():
ErrorMsg ('Fail to load package object for %s' % obj.GetPackageName())
continue
incObjs = decObj.GetSectionObjectsByName('includes')
for incObj in incObjs:
incPath = os.path.join(decObj.GetPackageRootPath(), incObj.GetPath()).replace('\\', '/')
if os.path.exists(os.path.join(incPath, filePath)):
fullPath = os.path.join(os.path.join(incPath, filePath))
break
if fullPath is not None:
break
if fullPath is None and self.IsVerbose():
self.Log('Can not resolve header file %s for file %s in package %s\n' % (filePath, path, pObj.GetFileObj().GetFilename()), 'error')
return
else:
fullPath = fullPath.replace('\\', '/')
if self.IsVerbose():
self.Log('Preprocessing: Add include file %s for file %s\n' % (fullPath, path))
#LogMsg ('Preprocessing: Add include file %s for file %s' % (fullPath, path))
self.ProcessSourceFileForInclude(fullPath, pObj, configFile, infObj)
def AddAllIncludeFiles(self, pObj, configFile):
objs = pObj.GetFileObj().GetSectionObjectsByName('includes')
for obj in objs:
incPath = os.path.join(pObj.GetFileObj().GetPackageRootPath(), obj.GetPath())
for root, dirs, files in os.walk(incPath):
for dir in dirs:
if dir.lower() in _ignore_dir:
dirs.remove(dir)
for file in files:
path = os.path.normpath(os.path.join(root, file))
configFile.AddFile(path.replace('/', '\\'))
def GeneratePcdSubPages(self, pObj, configFile):
"""
Generate sub pages for package's PCD definition.
@param pObj package object
@param configFile config file object
"""
rootArray = []
objs = pObj.GetFileObj().GetSectionObjectsByName('pcd')
if len(objs) == 0:
return []
pcdRootPage = doxygen.Page('PCD', 'pcd_root_page')
typeRootPageDict = {}
typeArchRootPageDict = {}
for obj in objs:
if obj.GetPcdType() not in typeRootPageDict.keys():
typeRootPageDict[obj.GetPcdType()] = doxygen.Page(obj.GetPcdType(), 'pcd_%s_root_page' % obj.GetPcdType())
pcdRootPage.AddPage(typeRootPageDict[obj.GetPcdType()])
typeRoot = typeRootPageDict[obj.GetPcdType()]
if self._arch is not None:
pcdPage = doxygen.Page('%s' % obj.GetPcdName(),
'pcd_%s_%s_%s' % (obj.GetPcdType(), obj.GetArch(), obj.GetPcdName().split('.')[1]))
pcdPage.AddDescription('<br>\n'.join(obj.GetComment()) + '<br>\n')
section = doxygen.Section('PCDinformation', 'PCD Information')
desc = '<TABLE>'
desc += '<TR>'
desc += '<TD><CAPTION>Name</CAPTION></TD>'
desc += '<TD><CAPTION>Token Space</CAPTION></TD>'
desc += '<TD><CAPTION>Token number</CAPTION></TD>'
desc += '<TD><CAPTION>Data Type</CAPTION></TD>'
desc += '<TD><CAPTION>Default Value</CAPTION></TD>'
desc += '</TR>'
desc += '<TR>'
desc += '<TD><CAPTION>%s</CAPTION></TD>' % obj.GetPcdName().split('.')[1]
desc += '<TD><CAPTION>%s</CAPTION></TD>' % obj.GetPcdName().split('.')[0]
desc += '<TD><CAPTION>%s</CAPTION></TD>' % obj.GetPcdToken()
desc += '<TD><CAPTION>%s</CAPTION></TD>' % obj.GetPcdDataType()
desc += '<TD><CAPTION>%s</CAPTION></TD>' % obj.GetPcdValue()
desc += '</TR>'
desc += '</TABLE>'
section.AddDescription(desc)
pcdPage.AddSection(section)
typeRoot.AddPage(pcdPage)
else:
keystr = obj.GetPcdType() + obj.GetArch()
if keystr not in typeArchRootPageDict.keys():
typeArchRootPage = doxygen.Page(obj.GetArch(), 'pcd_%s_%s_root_page' % (obj.GetPcdType(), obj.GetArch()))
typeArchRootPageDict[keystr] = typeArchRootPage
typeRoot.AddPage(typeArchRootPage)
typeArchRoot = typeArchRootPageDict[keystr]
pcdPage = doxygen.Page('%s' % obj.GetPcdName(),
'pcd_%s_%s_%s' % (obj.GetPcdType(), obj.GetArch(), obj.GetPcdName().split('.')[1]))
pcdPage.AddDescription('<br>\n'.join(obj.GetComment()) + '<br>\n')
section = doxygen.Section('PCDinformation', 'PCD Information')
desc = '<TABLE>'
desc += '<TR>'
desc += '<TD><CAPTION>Name</CAPTION></TD>'
desc += '<TD><CAPTION>Token Space</CAPTION></TD>'
desc += '<TD><CAPTION>Token number</CAPTION></TD>'
desc += '<TD><CAPTION>Data Type</CAPTION></TD>'
desc += '<TD><CAPTION>Default Value</CAPTION></TD>'
desc += '</TR>'
desc += '<TR>'
desc += '<TD><CAPTION>%s</CAPTION></TD>' % obj.GetPcdName().split('.')[1]
desc += '<TD><CAPTION>%s</CAPTION></TD>' % obj.GetPcdName().split('.')[0]
desc += '<TD><CAPTION>%s</CAPTION></TD>' % obj.GetPcdToken()
desc += '<TD><CAPTION>%s</CAPTION></TD>' % obj.GetPcdDataType()
desc += '<TD><CAPTION>%s</CAPTION></TD>' % obj.GetPcdValue()
desc += '</TR>'
desc += '</TABLE>'
section.AddDescription(desc)
pcdPage.AddSection(section)
typeArchRoot.AddPage(pcdPage)
return [pcdRootPage]
def _GenerateGuidSubPage(self, pObj, obj, configFile):
guidPage = doxygen.Page('%s' % obj.GetName(),
'guid_%s_%s' % (obj.GetArch(), obj.GetName()))
comments = obj.GetComment()
if len(comments) != 0:
guidPage.AddDescription('<br>'.join(obj.GetComment()) + '<br>')
section = doxygen.Section('BasicGuidInfo', 'GUID Information')
desc = '<TABLE>'
desc += '<TR>'
desc += '<TD><CAPTION>GUID\'s Guid Name</CAPTION></TD><TD><CAPTION>GUID\'s Guid</CAPTION></TD>'
desc += '</TR>'
desc += '<TR>'
desc += '<TD>%s</TD>' % obj.GetName()
desc += '<TD>%s</TD>' % obj.GetGuid()
desc += '</TR>'
desc += '</TABLE>'
section.AddDescription(desc)
guidPage.AddSection(section)
refFile = self.FindHeaderFileForGuid(pObj, obj.GetName(), configFile)
if refFile:
relPath = refFile[len(pObj.GetWorkspace()) + 1:]
if len(comments) == 0:
guidPage.AddDescription(' \\copydoc %s <br>' % relPath)
section = doxygen.Section('ref', 'Refer to Header File')
section.AddDescription('\link %s\n' % relPath)
section.AddDescription('\endlink\n')
self.ProcessSourceFileForInclude(refFile, pObj, configFile)
guidPage.AddSection(section)
return guidPage
def GenerateGuidSubPages(self, pObj, configFile):
"""
Generate sub pages for package's GUID definition.
@param pObj package object
@param configFilf doxygen config file object
"""
pageRoot = doxygen.Page('GUID', 'guid_root_page')
objs = pObj.GetFileObj().GetSectionObjectsByName('guids', self._arch)
if len(objs) == 0: return []
if self._arch is not None:
for obj in objs:
pageRoot.AddPage(self._GenerateGuidSubPage(pObj, obj, configFile))
else:
guidArchRootPageDict = {}
for obj in objs:
if obj.GetArch() not in guidArchRootPageDict.keys():
guidArchRoot = doxygen.Page(obj.GetArch(), 'guid_arch_root_%s' % obj.GetArch())
pageRoot.AddPage(guidArchRoot)
guidArchRootPageDict[obj.GetArch()] = guidArchRoot
guidArchRoot = guidArchRootPageDict[obj.GetArch()]
guidArchRoot.AddPage(self._GenerateGuidSubPage(pObj, obj, configFile))
return [pageRoot]
def _GeneratePpiSubPage(self, pObj, obj, configFile):
guidPage = doxygen.Page(obj.GetName(), 'ppi_page_%s' % obj.GetName())
comments = obj.GetComment()
if len(comments) != 0:
guidPage.AddDescription('<br>'.join(obj.GetComment()) + '<br>')
section = doxygen.Section('BasicPpiInfo', 'PPI Information')
desc = '<TABLE>'
desc += '<TR>'
desc += '<TD><CAPTION>PPI\'s Guid Name</CAPTION></TD><TD><CAPTION>PPI\'s Guid</CAPTION></TD>'
desc += '</TR>'
desc += '<TR>'
desc += '<TD>%s</TD>' % obj.GetName()
desc += '<TD>%s</TD>' % obj.GetGuid()
desc += '</TR>'
desc += '</TABLE>'
section.AddDescription(desc)
guidPage.AddSection(section)
refFile = self.FindHeaderFileForGuid(pObj, obj.GetName(), configFile)
if refFile:
relPath = refFile[len(pObj.GetWorkspace()) + 1:]
if len(comments) == 0:
guidPage.AddDescription(' \\copydoc %s <br>' % relPath)
section = doxygen.Section('ref', 'Refer to Header File')
section.AddDescription('\link %s\n' % relPath)
section.AddDescription('\endlink\n')
self.ProcessSourceFileForInclude(refFile, pObj, configFile)
guidPage.AddSection(section)
return guidPage
def GeneratePpiSubPages(self, pObj, configFile):
"""
Generate sub pages for package's GUID definition.
@param pObj package object
@param configFilf doxygen config file object
"""
pageRoot = doxygen.Page('PPI', 'ppi_root_page')
objs = pObj.GetFileObj().GetSectionObjectsByName('ppis', self._arch)
if len(objs) == 0: return []
if self._arch is not None:
for obj in objs:
pageRoot.AddPage(self._GeneratePpiSubPage(pObj, obj, configFile))
else:
guidArchRootPageDict = {}
for obj in objs:
if obj.GetArch() not in guidArchRootPageDict.keys():
guidArchRoot = doxygen.Page(obj.GetArch(), 'ppi_arch_root_%s' % obj.GetArch())
pageRoot.AddPage(guidArchRoot)
guidArchRootPageDict[obj.GetArch()] = guidArchRoot
guidArchRoot = guidArchRootPageDict[obj.GetArch()]
guidArchRoot.AddPage(self._GeneratePpiSubPage(pObj, obj, configFile))
return [pageRoot]
def _GenerateProtocolSubPage(self, pObj, obj, configFile):
guidPage = doxygen.Page(obj.GetName(), 'protocol_page_%s' % obj.GetName())
comments = obj.GetComment()
if len(comments) != 0:
guidPage.AddDescription('<br>'.join(obj.GetComment()) + '<br>')
section = doxygen.Section('BasicProtocolInfo', 'PROTOCOL Information')
desc = '<TABLE>'
desc += '<TR>'
desc += '<TD><CAPTION>PROTOCOL\'s Guid Name</CAPTION></TD><TD><CAPTION>PROTOCOL\'s Guid</CAPTION></TD>'
desc += '</TR>'
desc += '<TR>'
desc += '<TD>%s</TD>' % obj.GetName()
desc += '<TD>%s</TD>' % obj.GetGuid()
desc += '</TR>'
desc += '</TABLE>'
section.AddDescription(desc)
guidPage.AddSection(section)
refFile = self.FindHeaderFileForGuid(pObj, obj.GetName(), configFile)
if refFile:
relPath = refFile[len(pObj.GetWorkspace()) + 1:]
if len(comments) == 0:
guidPage.AddDescription(' \\copydoc %s <br>' % relPath)
section = doxygen.Section('ref', 'Refer to Header File')
section.AddDescription('\link %s\n' % relPath)
section.AddDescription('\endlink\n')
self.ProcessSourceFileForInclude(refFile, pObj, configFile)
guidPage.AddSection(section)
return guidPage
def GenerateProtocolSubPages(self, pObj, configFile):
"""
Generate sub pages for package's GUID definition.
@param pObj package object
@param configFilf doxygen config file object
"""
pageRoot = doxygen.Page('PROTOCOL', 'protocol_root_page')
objs = pObj.GetFileObj().GetSectionObjectsByName('protocols', self._arch)
if len(objs) == 0: return []
if self._arch is not None:
for obj in objs:
pageRoot.AddPage(self._GenerateProtocolSubPage(pObj, obj, configFile))
else:
guidArchRootPageDict = {}
for obj in objs:
if obj.GetArch() not in guidArchRootPageDict.keys():
guidArchRoot = doxygen.Page(obj.GetArch(), 'protocol_arch_root_%s' % obj.GetArch())
pageRoot.AddPage(guidArchRoot)
guidArchRootPageDict[obj.GetArch()] = guidArchRoot
guidArchRoot = guidArchRootPageDict[obj.GetArch()]
guidArchRoot.AddPage(self._GenerateProtocolSubPage(pObj, obj, configFile))
return [pageRoot]
def FindHeaderFileForGuid(self, pObj, name, configFile):
"""
For declaration header file for GUID/PPI/Protocol.
@param pObj package object
@param name guid/ppi/protocol's name
@param configFile config file object
@return full path of header file and None if not found.
"""
startPath = pObj.GetFileObj().GetPackageRootPath()
incPath = os.path.join(startPath, 'Include').replace('\\', '/')
# if <PackagePath>/include exist, then search header under it.
if os.path.exists(incPath):
startPath = incPath
for root, dirs, files in os.walk(startPath):
for dir in dirs:
if dir.lower() in _ignore_dir:
dirs.remove(dir)
for file in files:
fPath = os.path.join(root, file)
if not IsCHeaderFile(fPath):
continue
try:
f = open(fPath, 'r')
lines = f.readlines()
f.close()
except IOError:
self.Log('Fail to open file %s\n' % fPath)
continue
for line in lines:
if line.find(name) != -1 and \
line.find('extern') != -1:
return fPath.replace('\\', '/')
return None
def GetPackageModuleList(self, pObj):
"""
Get all module's INF path under package's root path
@param pObj package object
@return arrary of INF full path
"""
mArray = []
packPath = pObj.GetFileObj().GetPackageRootPath()
if not os.path.exists:
return None
for root, dirs, files in os.walk(packPath):
for dir in dirs:
if dir.lower() in _ignore_dir:
dirs.remove(dir)
for file in files:
if CheckPathPostfix(file, 'inf'):
fPath = os.path.join(root, file).replace('\\', '/')
mArray.append(fPath)
return mArray
def GenerateModulePages(self, pObj, configFile):
"""
Generate sub pages for package's module which is under the package
root directory.
@param pObj package object
@param configFilf doxygen config file object
"""
infList = self.GetPackageModuleList(pObj)
rootPages = []
libObjs = []
modObjs = []
for infpath in infList:
infObj = inf.INFFile(infpath)
#infObj = INFFileObject.INFFile (pObj.GetWorkspacePath(),
# inf)
if not infObj:
self.Log('Fail create INF object for %s' % inf)
continue
if not infObj.Parse():
self.Log('Fail to load INF file %s' % inf)
continue
if infObj.GetProduceLibraryClass() is not None:
libObjs.append(infObj)
else:
modObjs.append(infObj)
if len(libObjs) != 0:
libRootPage = doxygen.Page('Libraries', 'lib_root_page')
rootPages.append(libRootPage)
for libInf in libObjs:
libRootPage.AddPage(self.GenerateModulePage(pObj, libInf, configFile, True))
if len(modObjs) != 0:
modRootPage = doxygen.Page('Modules', 'module_root_page')
rootPages.append(modRootPage)
for modInf in modObjs:
modRootPage.AddPage(self.GenerateModulePage(pObj, modInf, configFile, False))
return rootPages
def GenerateModulePage(self, pObj, infObj, configFile, isLib):
"""
Generate page for a module/library.
@param infObj INF file object for module/library
@param configFile doxygen config file object
@param isLib Whether this module is library
@param module doxygen page object
"""
workspace = pObj.GetWorkspace()
refDecObjs = []
for obj in infObj.GetSectionObjectsByName('packages'):
decObj = dec.DECFile(os.path.join(workspace, obj.GetPath()))
if not decObj:
ErrorMsg ('Fail to create pacakge object for %s' % obj.GetPackageName())
continue
if not decObj.Parse():
ErrorMsg ('Fail to load package object for %s' % obj.GetPackageName())
continue
refDecObjs.append(decObj)
modPage = doxygen.Page('%s' % infObj.GetBaseName(),
'module_%s' % infObj.GetBaseName())
modPage.AddDescription(infObj.GetFileHeader())
basicInfSection = doxygen.Section('BasicModuleInformation', 'Basic Module Information')
desc = "<TABLE>"
for obj in infObj.GetSectionObjectsByName('defines'):
key = obj.GetKey()
value = obj.GetValue()
if key not in _inf_key_description_mapping_table.keys(): continue
if key == 'LIBRARY_CLASS' and value.find('|') != -1:
clsname, types = value.split('|')
desc += '<TR>'
desc += '<TD><B>%s</B></TD>' % _inf_key_description_mapping_table[key]
desc += '<TD>%s</TD>' % clsname
desc += '</TR>'
desc += '<TR>'
desc += '<TD><B>Supported Module Types</B></TD>'
desc += '<TD>%s</TD>' % types
desc += '</TR>'
else:
desc += '<TR>'
desc += '<TD><B>%s</B></TD>' % _inf_key_description_mapping_table[key]
if key == 'EFI_SPECIFICATION_VERSION' and value == '0x00020000':
value = '2.0'
desc += '<TD>%s</TD>' % value
desc += '</TR>'
desc += '</TABLE>'
basicInfSection.AddDescription(desc)
modPage.AddSection(basicInfSection)
# Add protocol section
data = []
for obj in infObj.GetSectionObjectsByName('pcd', self._arch):
data.append(obj.GetPcdName().strip())
if len(data) != 0:
s = doxygen.Section('Pcds', 'Pcds')
desc = "<TABLE>"
desc += '<TR><TD><B>PCD Name</B></TD><TD><B>TokenSpace</B></TD><TD><B>Package</B></TD></TR>'
for item in data:
desc += '<TR>'
desc += '<TD>%s</TD>' % item.split('.')[1]
desc += '<TD>%s</TD>' % item.split('.')[0]
pkgbasename = self.SearchPcdPackage(item, workspace, refDecObjs)
desc += '<TD>%s</TD>' % pkgbasename
desc += '</TR>'
desc += "</TABLE>"
s.AddDescription(desc)
modPage.AddSection(s)
# Add protocol section
#sects = infObj.GetSectionByString('protocol')
data = []
#for sect in sects:
for obj in infObj.GetSectionObjectsByName('protocol', self._arch):
data.append(obj.GetName().strip())
if len(data) != 0:
s = doxygen.Section('Protocols', 'Protocols')
desc = "<TABLE>"
desc += '<TR><TD><B>Name</B></TD><TD><B>Package</B></TD></TR>'
for item in data:
desc += '<TR>'
desc += '<TD>%s</TD>' % item
pkgbasename = self.SearchProtocolPackage(item, workspace, refDecObjs)
desc += '<TD>%s</TD>' % pkgbasename
desc += '</TR>'
desc += "</TABLE>"
s.AddDescription(desc)
modPage.AddSection(s)
# Add ppi section
#sects = infObj.GetSectionByString('ppi')
data = []
#for sect in sects:
for obj in infObj.GetSectionObjectsByName('ppi', self._arch):
data.append(obj.GetName().strip())
if len(data) != 0:
s = doxygen.Section('Ppis', 'Ppis')
desc = "<TABLE>"
desc += '<TR><TD><B>Name</B></TD><TD><B>Package</B></TD></TR>'
for item in data:
desc += '<TR>'
desc += '<TD>%s</TD>' % item
pkgbasename = self.SearchPpiPackage(item, workspace, refDecObjs)
desc += '<TD>%s</TD>' % pkgbasename
desc += '</TR>'
desc += "</TABLE>"
s.AddDescription(desc)
modPage.AddSection(s)
# Add guid section
#sects = infObj.GetSectionByString('guid')
data = []
#for sect in sects:
for obj in infObj.GetSectionObjectsByName('guid', self._arch):
data.append(obj.GetName().strip())
if len(data) != 0:
s = doxygen.Section('Guids', 'Guids')
desc = "<TABLE>"
desc += '<TR><TD><B>Name</B></TD><TD><B>Package</B></TD></TR>'
for item in data:
desc += '<TR>'
desc += '<TD>%s</TD>' % item
pkgbasename = self.SearchGuidPackage(item, workspace, refDecObjs)
desc += '<TD>%s</TD>' % pkgbasename
desc += '</TR>'
desc += "</TABLE>"
s.AddDescription(desc)
modPage.AddSection(s)
section = doxygen.Section('LibraryClasses', 'Library Classes')
desc = "<TABLE>"
desc += '<TR><TD><B>Name</B></TD><TD><B>Type</B></TD><TD><B>Package</B></TD><TD><B>Header File</B></TD></TR>'
if isLib:
desc += '<TR>'
desc += '<TD>%s</TD>' % infObj.GetProduceLibraryClass()
desc += '<TD>Produce</TD>'
try:
pkgname, hPath = self.SearchLibraryClassHeaderFile(infObj.GetProduceLibraryClass(),
workspace,
refDecObjs)
except:
self.Log ('fail to get package header file for lib class %s' % infObj.GetProduceLibraryClass())
pkgname = 'NULL'
hPath = 'NULL'
desc += '<TD>%s</TD>' % pkgname
if hPath != "NULL":
desc += '<TD>\link %s \endlink</TD>' % hPath
else:
desc += '<TD>%s</TD>' % hPath
desc += '</TR>'
for lcObj in infObj.GetSectionObjectsByName('libraryclasses', self._arch):
desc += '<TR>'
desc += '<TD>%s</TD>' % lcObj.GetClass()
retarr = self.SearchLibraryClassHeaderFile(lcObj.GetClass(),
workspace,
refDecObjs)
if retarr is not None:
pkgname, hPath = retarr
else:
self.Log('Fail find the library class %s definition from module %s dependent package!' % (lcObj.GetClass(), infObj.GetFilename()), 'error')
pkgname = 'NULL'
hPath = 'NULL'
desc += '<TD>Consume</TD>'
desc += '<TD>%s</TD>' % pkgname
desc += '<TD>\link %s \endlink</TD>' % hPath
desc += '</TR>'
desc += "</TABLE>"
section.AddDescription(desc)
modPage.AddSection(section)
section = doxygen.Section('SourceFiles', 'Source Files')
section.AddDescription('<ul>\n')
for obj in infObj.GetSourceObjects(self._arch, self._tooltag):
sPath = infObj.GetModuleRootPath()
sPath = os.path.join(sPath, obj.GetSourcePath()).replace('\\', '/').strip()
if sPath.lower().endswith('.uni') or sPath.lower().endswith('.s') or sPath.lower().endswith('.asm') or sPath.lower().endswith('.nasm'):
newPath = self.TranslateUniFile(sPath)
configFile.AddFile(newPath)
newPath = newPath[len(pObj.GetWorkspace()) + 1:]
section.AddDescription('<li> \link %s \endlink </li>' % newPath)
else:
self.ProcessSourceFileForInclude(sPath, pObj, configFile, infObj)
sPath = sPath[len(pObj.GetWorkspace()) + 1:]
section.AddDescription('<li>\link %s \endlink </li>' % sPath)
section.AddDescription('</ul>\n')
modPage.AddSection(section)
#sects = infObj.GetSectionByString('depex')
data = []
#for sect in sects:
for obj in infObj.GetSectionObjectsByName('depex'):
data.append(str(obj))
if len(data) != 0:
s = doxygen.Section('DependentSection', 'Module Dependencies')
s.AddDescription('<br>'.join(data))
modPage.AddSection(s)
return modPage
def TranslateUniFile(self, path):
newpath = path + '.dox'
#import core.textfile as textfile
#file = textfile.TextFile(path)
try:
file = open(path, 'r')
except (IOError, OSError) as msg:
return None
t = file.read()
file.close()
output = '/** @file \n'
#output = '<html><body>'
arr = t.split('\r\n')
for line in arr:
if line.find('@file') != -1:
continue
if line.find('*/') != -1:
continue
line = line.strip()
if line.strip().startswith('/'):
arr = line.split(' ')
if len(arr) > 1:
line = ' '.join(arr[1:])
else:
continue
output += '%s<br>\n' % line
output += '**/'
if os.path.exists(newpath):
os.remove(newpath)
file = open(newpath, "w")
file.write(output)
file.close()
return newpath
def SearchPcdPackage(self, pcdname, workspace, decObjs):
for decObj in decObjs:
for pcd in decObj.GetSectionObjectsByName('pcd'):
if pcdname == pcd.GetPcdName():
return decObj.GetBaseName()
return None
def SearchProtocolPackage(self, protname, workspace, decObjs):
for decObj in decObjs:
for proto in decObj.GetSectionObjectsByName('protocol'):
if protname == proto.GetName():
return decObj.GetBaseName()
return None
def SearchPpiPackage(self, ppiname, workspace, decObjs):
for decObj in decObjs:
for ppi in decObj.GetSectionObjectsByName('ppi'):
if ppiname == ppi.GetName():
return decObj.GetBaseName()
return None
def SearchGuidPackage(self, guidname, workspace, decObjs):
for decObj in decObjs:
for guid in decObj.GetSectionObjectsByName('guid'):
if guidname == guid.GetName():
return decObj.GetBaseName()
return None
def SearchLibraryClassHeaderFile(self, className, workspace, decObjs):
for decObj in decObjs:
for cls in decObj.GetSectionObjectsByName('libraryclasses'):
if cls.GetClassName().strip() == className:
path = cls.GetHeaderFile().strip()
path = os.path.join(decObj.GetPackageRootPath(), path)
path = path[len(workspace) + 1:]
return decObj.GetBaseName(), path.replace('\\', '/')
return None
def _ConvertPathToDoxygen(self, path, pObj):
pRootPath = pObj.GetWorkspace()
path = path[len(pRootPath) + 1:]
return path.replace('\\', '/')
def IsCHeaderFile(path):
return CheckPathPostfix(path, 'h')
def CheckPathPostfix(path, str):
index = path.rfind('.')
if index == -1:
return False
if path[index + 1:].lower() == str.lower():
return True
return False