1## @file 2# 3# This file produce action class to generate doxygen document for edk2 codebase. 4# The action classes are shared by GUI and command line tools. 5# 6# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR> 7# 8# SPDX-License-Identifier: BSD-2-Clause-Patent 9 10from plugins.EdkPlugins.basemodel import doxygen 11import os 12try: 13 import wx 14 gInGui = True 15except: 16 gInGui = False 17import re 18from plugins.EdkPlugins.edk2.model import inf 19from plugins.EdkPlugins.edk2.model import dec 20from plugins.EdkPlugins.basemodel.message import * 21 22_ignore_dir = ['.svn', '_svn', 'cvs'] 23_inf_key_description_mapping_table = { 24 'INF_VERSION':'Version of INF file specification', 25 #'BASE_NAME':'Module Name', 26 'FILE_GUID':'Module Guid', 27 'MODULE_TYPE': 'Module Type', 28 'VERSION_STRING': 'Module Version', 29 'LIBRARY_CLASS': 'Produced Library Class', 30 'EFI_SPECIFICATION_VERSION': 'UEFI Specification Version', 31 'PI_SPECIFICATION_VERSION': 'PI Specification Version', 32 'ENTRY_POINT': 'Module Entry Point Function', 33 'CONSTRUCTOR': 'Library Constructor Function' 34} 35 36_dec_key_description_mapping_table = { 37 'DEC_SPECIFICATION': 'Version of DEC file specification', 38 'PACKAGE_GUID': 'Package Guid' 39} 40class DoxygenAction: 41 """This is base class for all doxygen action. 42 """ 43 44 def __init__(self, doxPath, chmPath, outputPath, projname, mode='html', log=None, verbose=False): 45 """Constructor function. 46 @param doxPath the obosolution path of doxygen execute file. 47 @param outputPath the obosolution output path. 48 @param log log function for output message 49 """ 50 self._doxPath = doxPath 51 self._chmPath = chmPath 52 self._outputPath = outputPath 53 self._projname = projname 54 self._configFile = None # doxygen config file is used by doxygen exe file 55 self._indexPageFile = None # doxygen page file for index page. 56 self._log = log 57 self._mode = mode 58 self._verbose = verbose 59 self._doxygenCallback = None 60 self._chmCallback = None 61 62 def Log(self, message, level='info'): 63 if self._log is not None: 64 self._log(message, level) 65 66 def IsVerbose(self): 67 return self._verbose 68 69 def Generate(self): 70 """Generate interface called by outer directly""" 71 self.Log(">>>>>> Start generate doxygen document for %s... Zzz....\n" % self._projname) 72 73 # create doxygen config file at first 74 self._configFile = doxygen.DoxygenConfigFile() 75 self._configFile.SetOutputDir(self._outputPath) 76 77 self._configFile.SetWarningFilePath(os.path.join(self._outputPath, 'warning.txt')) 78 if self._mode.lower() == 'html': 79 self._configFile.SetHtmlMode() 80 else: 81 self._configFile.SetChmMode() 82 83 self.Log(" >>>>>> Initialize doxygen config file...Zzz...\n") 84 self.InitializeConfigFile() 85 86 self.Log(" >>>>>> Generate doxygen index page file...Zzz...\n") 87 indexPagePath = self.GenerateIndexPage() 88 if indexPagePath is None: 89 self.Log("Fail to generate index page!\n", 'error') 90 return False 91 else: 92 self.Log("Success to create doxygen index page file %s \n" % indexPagePath) 93 94 # Add index page doxygen file to file list. 95 self._configFile.AddFile(indexPagePath) 96 97 # save config file to output path 98 configFilePath = os.path.join(self._outputPath, self._projname + '.doxygen_config') 99 self._configFile.Generate(configFilePath) 100 self.Log(" <<<<<< Success Save doxygen config file to %s...\n" % configFilePath) 101 102 # launch doxygen tool to generate document 103 if self._doxygenCallback is not None: 104 self.Log(" >>>>>> Start doxygen process...Zzz...\n") 105 if not self._doxygenCallback(self._doxPath, configFilePath): 106 return False 107 else: 108 self.Log("Fail to create doxygen process!", 'error') 109 return False 110 111 return True 112 113 def InitializeConfigFile(self): 114 """Initialize config setting for doxygen project. It will be invoked after config file 115 object is created. Inherited class should implement it. 116 """ 117 118 def GenerateIndexPage(self): 119 """Generate doxygen index page. Inherited class should implement it.""" 120 return None 121 122 def RegisterCallbackDoxygenProcess(self, callback): 123 self._doxygenCallback = callback 124 125 def RegisterCallbackCHMProcess(self, callback): 126 self._chmCallback = callback 127 128class PlatformDocumentAction(DoxygenAction): 129 """Generate platform doxygen document, will be implement at future.""" 130 131class PackageDocumentAction(DoxygenAction): 132 """Generate package reference document""" 133 134 def __init__(self, doxPath, chmPath, outputPath, pObj, mode='html', log=None, arch=None, tooltag=None, 135 macros=[], onlyInclude=False, verbose=False): 136 DoxygenAction.__init__(self, doxPath, chmPath, outputPath, pObj.GetName(), mode, log, verbose) 137 self._pObj = pObj 138 self._arch = arch 139 self._tooltag = tooltag 140 self._macros = macros 141 self._onlyIncludeDocument = onlyInclude 142 143 def InitializeConfigFile(self): 144 if self._arch == 'IA32': 145 self._configFile.AddPreDefined('MDE_CPU_IA32') 146 elif self._arch == 'X64': 147 self._configFile.AddPreDefined('MDE_CPU_X64') 148 elif self._arch == 'IPF': 149 self._configFile.AddPreDefined('MDE_CPU_IPF') 150 elif self._arch == 'EBC': 151 self._configFile.AddPreDefined('MDE_CPU_EBC') 152 else: 153 self._arch = None 154 self._configFile.AddPreDefined('MDE_CPU_IA32') 155 self._configFile.AddPreDefined('MDE_CPU_X64') 156 self._configFile.AddPreDefined('MDE_CPU_IPF') 157 self._configFile.AddPreDefined('MDE_CPU_EBC') 158 self._configFile.AddPreDefined('MDE_CPU_ARM') 159 160 for macro in self._macros: 161 self._configFile.AddPreDefined(macro) 162 163 namestr = self._pObj.GetName() 164 if self._arch is not None: 165 namestr += '[%s]' % self._arch 166 if self._tooltag is not None: 167 namestr += '[%s]' % self._tooltag 168 self._configFile.SetProjectName(namestr) 169 self._configFile.SetStripPath(self._pObj.GetWorkspace()) 170 self._configFile.SetProjectVersion(self._pObj.GetFileObj().GetVersion()) 171 self._configFile.AddPattern('*.decdoxygen') 172 173 if self._tooltag.lower() == 'msft': 174 self._configFile.AddPreDefined('_MSC_EXTENSIONS') 175 elif self._tooltag.lower() == 'gnu': 176 self._configFile.AddPreDefined('__GNUC__') 177 elif self._tooltag.lower() == 'intel': 178 self._configFile.AddPreDefined('__INTEL_COMPILER') 179 else: 180 self._tooltag = None 181 self._configFile.AddPreDefined('_MSC_EXTENSIONS') 182 self._configFile.AddPreDefined('__GNUC__') 183 self._configFile.AddPreDefined('__INTEL_COMPILER') 184 185 self._configFile.AddPreDefined('ASM_PFX= ') 186 self._configFile.AddPreDefined('OPTIONAL= ') 187 188 def GenerateIndexPage(self): 189 """Generate doxygen index page. Inherited class should implement it.""" 190 fObj = self._pObj.GetFileObj() 191 pdObj = doxygen.DoxygenFile('%s Package Document' % self._pObj.GetName(), 192 '%s.decdoxygen' % self._pObj.GetFilename()) 193 self._configFile.AddFile(pdObj.GetFilename()) 194 pdObj.AddDescription(fObj.GetFileHeader()) 195 196 defSection = fObj.GetSectionByName('defines')[0] 197 baseSection = doxygen.Section('PackageBasicInformation', 'Package Basic Information') 198 descr = '<TABLE>' 199 for obj in defSection.GetObjects(): 200 if obj.GetKey() in _dec_key_description_mapping_table.keys(): 201 descr += '<TR>' 202 descr += '<TD><B>%s</B></TD>' % _dec_key_description_mapping_table[obj.GetKey()] 203 descr += '<TD>%s</TD>' % obj.GetValue() 204 descr += '</TR>' 205 descr += '</TABLE><br>' 206 baseSection.AddDescription(descr) 207 pdObj.AddSection(baseSection) 208 209 knownIssueSection = doxygen.Section('Known_Issue_section', 'Known Issue') 210 knownIssueSection.AddDescription('<ul>') 211 knownIssueSection.AddDescription('<li> OPTIONAL macro for function parameter can not be dealed with doxygen, so it disapear in this document! </li>') 212 knownIssueSection.AddDescription('</ul>') 213 pdObj.AddSection(knownIssueSection) 214 215 self.AddAllIncludeFiles(self._pObj, self._configFile) 216 pages = self.GenerateIncludesSubPage(self._pObj, self._configFile) 217 if len(pages) != 0: 218 pdObj.AddPages(pages) 219 pages = self.GenerateLibraryClassesSubPage(self._pObj, self._configFile) 220 if len(pages) != 0: 221 pdObj.AddPages(pages) 222 pages = self.GeneratePcdSubPages(self._pObj, self._configFile) 223 if len(pages) != 0: 224 pdObj.AddPages(pages) 225 pages = self.GenerateGuidSubPages(self._pObj, self._configFile) 226 if len(pages) != 0: 227 pdObj.AddPages(pages) 228 pages = self.GeneratePpiSubPages(self._pObj, self._configFile) 229 if len(pages) != 0: 230 pdObj.AddPages(pages) 231 pages = self.GenerateProtocolSubPages(self._pObj, self._configFile) 232 if len(pages) != 0: 233 pdObj.AddPages(pages) 234 if not self._onlyIncludeDocument: 235 pdObj.AddPages(self.GenerateModulePages(self._pObj, self._configFile)) 236 237 pdObj.Save() 238 return pdObj.GetFilename() 239 240 def GenerateIncludesSubPage(self, pObj, configFile): 241 # by default add following path as include path to config file 242 pkpath = pObj.GetFileObj().GetPackageRootPath() 243 configFile.AddIncludePath(os.path.join(pkpath, 'Include')) 244 configFile.AddIncludePath(os.path.join(pkpath, 'Include', 'Library')) 245 configFile.AddIncludePath(os.path.join(pkpath, 'Include', 'Protocol')) 246 configFile.AddIncludePath(os.path.join(pkpath, 'Include', 'Ppi')) 247 configFile.AddIncludePath(os.path.join(pkpath, 'Include', 'Guid')) 248 configFile.AddIncludePath(os.path.join(pkpath, 'Include', 'IndustryStandard')) 249 250 rootArray = [] 251 pageRoot = doxygen.Page("Public Includes", "%s_public_includes" % pObj.GetName()) 252 objs = pObj.GetFileObj().GetSectionObjectsByName('includes') 253 if len(objs) == 0: return [] 254 255 for obj in objs: 256 # Add path to include path 257 path = os.path.join(pObj.GetFileObj().GetPackageRootPath(), obj.GetPath()) 258 configFile.AddIncludePath(path) 259 260 # only list common folder's include file 261 if obj.GetArch().lower() != 'common': 262 continue 263 264 bNeedAddIncludePage = False 265 topPage = doxygen.Page(self._ConvertPathToDoxygen(path, pObj), 'public_include_top') 266 267 topPage.AddDescription('<ul>\n') 268 for file in os.listdir(path): 269 if file.lower() in _ignore_dir: continue 270 fullpath = os.path.join(path, file) 271 if os.path.isfile(fullpath): 272 self.ProcessSourceFileForInclude(fullpath, pObj, configFile) 273 topPage.AddDescription('<li> \link %s\endlink </li>\n' % self._ConvertPathToDoxygen(fullpath, pObj)) 274 else: 275 if file.lower() in ['library', 'protocol', 'guid', 'ppi', 'ia32', 'x64', 'ipf', 'ebc', 'arm', 'pi', 'uefi', 'aarch64']: 276 continue 277 bNeedAddSubPage = False 278 subpage = doxygen.Page(self._ConvertPathToDoxygen(fullpath, pObj), 'public_include_%s' % file) 279 subpage.AddDescription('<ul>\n') 280 for subfile in os.listdir(fullpath): 281 if subfile.lower() in _ignore_dir: continue 282 bNeedAddSubPage = True 283 subfullpath = os.path.join(fullpath, subfile) 284 self.ProcessSourceFileForInclude(subfullpath, pObj, configFile) 285 subpage.AddDescription('<li> \link %s \endlink </li>\n' % self._ConvertPathToDoxygen(subfullpath, pObj)) 286 subpage.AddDescription('</ul>\n') 287 if bNeedAddSubPage: 288 bNeedAddIncludePage = True 289 pageRoot.AddPage(subpage) 290 topPage.AddDescription('</ul>\n') 291 if bNeedAddIncludePage: 292 pageRoot.AddPage(topPage) 293 294 if pageRoot.GetSubpageCount() != 0: 295 return [pageRoot] 296 else: 297 return [] 298 299 def GenerateLibraryClassesSubPage(self, pObj, configFile): 300 """ 301 Generate sub page for library class for package. 302 One DEC file maybe contains many library class sections 303 for different architecture. 304 305 @param fObj DEC file object. 306 """ 307 rootArray = [] 308 pageRoot = doxygen.Page("Library Class", "%s_libraryclass" % pObj.GetName()) 309 objs = pObj.GetFileObj().GetSectionObjectsByName('libraryclass', self._arch) 310 if len(objs) == 0: return [] 311 312 if self._arch is not None: 313 for obj in objs: 314 classPage = doxygen.Page(obj.GetClassName(), 315 "lc_%s" % obj.GetClassName()) 316 comments = obj.GetComment() 317 if len(comments) != 0: 318 classPage.AddDescription('<br>\n'.join(comments) + '<br>\n') 319 pageRoot.AddPage(classPage) 320 path = os.path.join(pObj.GetFileObj().GetPackageRootPath(), obj.GetHeaderFile()) 321 path = path[len(pObj.GetWorkspace()) + 1:] 322 if len(comments) == 0: 323 classPage.AddDescription('\copydoc %s<p>' % obj.GetHeaderFile()) 324 section = doxygen.Section('ref', 'Refer to Header File') 325 section.AddDescription('\link %s\n' % obj.GetHeaderFile()) 326 section.AddDescription(' \endlink<p>\n') 327 classPage.AddSection(section) 328 fullPath = os.path.join(pObj.GetFileObj().GetPackageRootPath(), obj.GetHeaderFile()) 329 self.ProcessSourceFileForInclude(fullPath, pObj, configFile) 330 else: 331 archPageDict = {} 332 for obj in objs: 333 if obj.GetArch() not in archPageDict.keys(): 334 archPageDict[obj.GetArch()] = doxygen.Page(obj.GetArch(), 335 'lc_%s' % obj.GetArch()) 336 pageRoot.AddPage(archPageDict[obj.GetArch()]) 337 subArchRoot = archPageDict[obj.GetArch()] 338 classPage = doxygen.Page(obj.GetClassName(), 339 "lc_%s" % obj.GetClassName()) 340 comments = obj.GetComment() 341 if len(comments) != 0: 342 classPage.AddDescription('<br>\n'.join(comments) + '<br>\n') 343 subArchRoot.AddPage(classPage) 344 path = os.path.join(pObj.GetFileObj().GetPackageRootPath(), obj.GetHeaderFile()) 345 path = path[len(pObj.GetWorkspace()) + 1:] 346 if len(comments) == 0: 347 classPage.AddDescription('\copydoc %s<p>' % obj.GetHeaderFile()) 348 section = doxygen.Section('ref', 'Refer to Header File') 349 section.AddDescription('\link %s\n' % obj.GetHeaderFile()) 350 section.AddDescription(' \endlink<p>\n') 351 classPage.AddSection(section) 352 fullPath = os.path.join(pObj.GetFileObj().GetPackageRootPath(), obj.GetHeaderFile()) 353 354 self.ProcessSourceFileForInclude(fullPath, pObj, configFile) 355 rootArray.append(pageRoot) 356 return rootArray 357 358 def ProcessSourceFileForInclude(self, path, pObj, configFile, infObj=None): 359 """ 360 @param path the analysising file full path 361 @param pObj package object 362 @param configFile doxygen config file. 363 """ 364 365 if gInGui: 366 wx.Yield() 367 if not os.path.exists(path): 368 ErrorMsg('Source file path %s does not exist!' % path) 369 return 370 371 if configFile.FileExists(path): 372 return 373 374 try: 375 f = open(path, 'r') 376 lines = f.readlines() 377 f.close() 378 except IOError: 379 ErrorMsg('Fail to open file %s' % path) 380 return 381 382 configFile.AddFile(path) 383 return 384 no = 0 385 for no in range(len(lines)): 386 if len(lines[no].strip()) == 0: 387 continue 388 if lines[no].strip()[:2] in ['##', '//', '/*', '*/']: 389 continue 390 index = lines[no].lower().find('include') 391 #mo = IncludePattern.finditer(lines[no].lower()) 392 mo = re.match(r"^#\s*include\s+[<\"]([\\/\w.]+)[>\"]$", lines[no].strip().lower()) 393 if not mo: 394 continue 395 mo = re.match(r"^[#\w\s]+[<\"]([\\/\w.]+)[>\"]$", lines[no].strip()) 396 filePath = mo.groups()[0] 397 398 if filePath is None or len(filePath) == 0: 399 continue 400 401 # find header file in module's path firstly. 402 fullPath = None 403 404 if os.path.exists(os.path.join(os.path.dirname(path), filePath)): 405 # Find the file in current directory 406 fullPath = os.path.join(os.path.dirname(path), filePath).replace('\\', '/') 407 else: 408 # find in depedent package's include path 409 incObjs = pObj.GetFileObj().GetSectionObjectsByName('includes') 410 for incObj in incObjs: 411 incPath = os.path.join(pObj.GetFileObj().GetPackageRootPath(), incObj.GetPath()).strip() 412 incPath = os.path.realpath(os.path.join(incPath, filePath)) 413 if os.path.exists(incPath): 414 fullPath = incPath 415 break 416 if infObj is not None: 417 pkgInfObjs = infObj.GetSectionObjectsByName('packages') 418 for obj in pkgInfObjs: 419 decObj = dec.DECFile(os.path.join(pObj.GetWorkspace(), obj.GetPath())) 420 if not decObj: 421 ErrorMsg ('Fail to create pacakge object for %s' % obj.GetPackageName()) 422 continue 423 if not decObj.Parse(): 424 ErrorMsg ('Fail to load package object for %s' % obj.GetPackageName()) 425 continue 426 incObjs = decObj.GetSectionObjectsByName('includes') 427 for incObj in incObjs: 428 incPath = os.path.join(decObj.GetPackageRootPath(), incObj.GetPath()).replace('\\', '/') 429 if os.path.exists(os.path.join(incPath, filePath)): 430 fullPath = os.path.join(os.path.join(incPath, filePath)) 431 break 432 if fullPath is not None: 433 break 434 435 if fullPath is None and self.IsVerbose(): 436 self.Log('Can not resolve header file %s for file %s in package %s\n' % (filePath, path, pObj.GetFileObj().GetFilename()), 'error') 437 return 438 else: 439 fullPath = fullPath.replace('\\', '/') 440 if self.IsVerbose(): 441 self.Log('Preprocessing: Add include file %s for file %s\n' % (fullPath, path)) 442 #LogMsg ('Preprocessing: Add include file %s for file %s' % (fullPath, path)) 443 self.ProcessSourceFileForInclude(fullPath, pObj, configFile, infObj) 444 445 def AddAllIncludeFiles(self, pObj, configFile): 446 objs = pObj.GetFileObj().GetSectionObjectsByName('includes') 447 for obj in objs: 448 incPath = os.path.join(pObj.GetFileObj().GetPackageRootPath(), obj.GetPath()) 449 for root, dirs, files in os.walk(incPath): 450 for dir in dirs: 451 if dir.lower() in _ignore_dir: 452 dirs.remove(dir) 453 for file in files: 454 path = os.path.normpath(os.path.join(root, file)) 455 configFile.AddFile(path.replace('/', '\\')) 456 457 def GeneratePcdSubPages(self, pObj, configFile): 458 """ 459 Generate sub pages for package's PCD definition. 460 @param pObj package object 461 @param configFile config file object 462 """ 463 rootArray = [] 464 objs = pObj.GetFileObj().GetSectionObjectsByName('pcd') 465 if len(objs) == 0: 466 return [] 467 468 pcdRootPage = doxygen.Page('PCD', 'pcd_root_page') 469 typeRootPageDict = {} 470 typeArchRootPageDict = {} 471 for obj in objs: 472 if obj.GetPcdType() not in typeRootPageDict.keys(): 473 typeRootPageDict[obj.GetPcdType()] = doxygen.Page(obj.GetPcdType(), 'pcd_%s_root_page' % obj.GetPcdType()) 474 pcdRootPage.AddPage(typeRootPageDict[obj.GetPcdType()]) 475 typeRoot = typeRootPageDict[obj.GetPcdType()] 476 if self._arch is not None: 477 pcdPage = doxygen.Page('%s' % obj.GetPcdName(), 478 'pcd_%s_%s_%s' % (obj.GetPcdType(), obj.GetArch(), obj.GetPcdName().split('.')[1])) 479 pcdPage.AddDescription('<br>\n'.join(obj.GetComment()) + '<br>\n') 480 section = doxygen.Section('PCDinformation', 'PCD Information') 481 desc = '<TABLE>' 482 desc += '<TR>' 483 desc += '<TD><CAPTION>Name</CAPTION></TD>' 484 desc += '<TD><CAPTION>Token Space</CAPTION></TD>' 485 desc += '<TD><CAPTION>Token number</CAPTION></TD>' 486 desc += '<TD><CAPTION>Data Type</CAPTION></TD>' 487 desc += '<TD><CAPTION>Default Value</CAPTION></TD>' 488 desc += '</TR>' 489 desc += '<TR>' 490 desc += '<TD><CAPTION>%s</CAPTION></TD>' % obj.GetPcdName().split('.')[1] 491 desc += '<TD><CAPTION>%s</CAPTION></TD>' % obj.GetPcdName().split('.')[0] 492 desc += '<TD><CAPTION>%s</CAPTION></TD>' % obj.GetPcdToken() 493 desc += '<TD><CAPTION>%s</CAPTION></TD>' % obj.GetPcdDataType() 494 desc += '<TD><CAPTION>%s</CAPTION></TD>' % obj.GetPcdValue() 495 desc += '</TR>' 496 desc += '</TABLE>' 497 section.AddDescription(desc) 498 pcdPage.AddSection(section) 499 typeRoot.AddPage(pcdPage) 500 else: 501 keystr = obj.GetPcdType() + obj.GetArch() 502 if keystr not in typeArchRootPageDict.keys(): 503 typeArchRootPage = doxygen.Page(obj.GetArch(), 'pcd_%s_%s_root_page' % (obj.GetPcdType(), obj.GetArch())) 504 typeArchRootPageDict[keystr] = typeArchRootPage 505 typeRoot.AddPage(typeArchRootPage) 506 typeArchRoot = typeArchRootPageDict[keystr] 507 pcdPage = doxygen.Page('%s' % obj.GetPcdName(), 508 'pcd_%s_%s_%s' % (obj.GetPcdType(), obj.GetArch(), obj.GetPcdName().split('.')[1])) 509 pcdPage.AddDescription('<br>\n'.join(obj.GetComment()) + '<br>\n') 510 section = doxygen.Section('PCDinformation', 'PCD Information') 511 desc = '<TABLE>' 512 desc += '<TR>' 513 desc += '<TD><CAPTION>Name</CAPTION></TD>' 514 desc += '<TD><CAPTION>Token Space</CAPTION></TD>' 515 desc += '<TD><CAPTION>Token number</CAPTION></TD>' 516 desc += '<TD><CAPTION>Data Type</CAPTION></TD>' 517 desc += '<TD><CAPTION>Default Value</CAPTION></TD>' 518 desc += '</TR>' 519 desc += '<TR>' 520 desc += '<TD><CAPTION>%s</CAPTION></TD>' % obj.GetPcdName().split('.')[1] 521 desc += '<TD><CAPTION>%s</CAPTION></TD>' % obj.GetPcdName().split('.')[0] 522 desc += '<TD><CAPTION>%s</CAPTION></TD>' % obj.GetPcdToken() 523 desc += '<TD><CAPTION>%s</CAPTION></TD>' % obj.GetPcdDataType() 524 desc += '<TD><CAPTION>%s</CAPTION></TD>' % obj.GetPcdValue() 525 desc += '</TR>' 526 desc += '</TABLE>' 527 section.AddDescription(desc) 528 pcdPage.AddSection(section) 529 typeArchRoot.AddPage(pcdPage) 530 return [pcdRootPage] 531 532 def _GenerateGuidSubPage(self, pObj, obj, configFile): 533 guidPage = doxygen.Page('%s' % obj.GetName(), 534 'guid_%s_%s' % (obj.GetArch(), obj.GetName())) 535 comments = obj.GetComment() 536 if len(comments) != 0: 537 guidPage.AddDescription('<br>'.join(obj.GetComment()) + '<br>') 538 section = doxygen.Section('BasicGuidInfo', 'GUID Information') 539 desc = '<TABLE>' 540 desc += '<TR>' 541 desc += '<TD><CAPTION>GUID\'s Guid Name</CAPTION></TD><TD><CAPTION>GUID\'s Guid</CAPTION></TD>' 542 desc += '</TR>' 543 desc += '<TR>' 544 desc += '<TD>%s</TD>' % obj.GetName() 545 desc += '<TD>%s</TD>' % obj.GetGuid() 546 desc += '</TR>' 547 desc += '</TABLE>' 548 section.AddDescription(desc) 549 guidPage.AddSection(section) 550 refFile = self.FindHeaderFileForGuid(pObj, obj.GetName(), configFile) 551 if refFile: 552 relPath = refFile[len(pObj.GetWorkspace()) + 1:] 553 if len(comments) == 0: 554 guidPage.AddDescription(' \\copydoc %s <br>' % relPath) 555 556 section = doxygen.Section('ref', 'Refer to Header File') 557 section.AddDescription('\link %s\n' % relPath) 558 section.AddDescription('\endlink\n') 559 self.ProcessSourceFileForInclude(refFile, pObj, configFile) 560 guidPage.AddSection(section) 561 return guidPage 562 563 def GenerateGuidSubPages(self, pObj, configFile): 564 """ 565 Generate sub pages for package's GUID definition. 566 @param pObj package object 567 @param configFilf doxygen config file object 568 """ 569 pageRoot = doxygen.Page('GUID', 'guid_root_page') 570 objs = pObj.GetFileObj().GetSectionObjectsByName('guids', self._arch) 571 if len(objs) == 0: return [] 572 if self._arch is not None: 573 for obj in objs: 574 pageRoot.AddPage(self._GenerateGuidSubPage(pObj, obj, configFile)) 575 else: 576 guidArchRootPageDict = {} 577 for obj in objs: 578 if obj.GetArch() not in guidArchRootPageDict.keys(): 579 guidArchRoot = doxygen.Page(obj.GetArch(), 'guid_arch_root_%s' % obj.GetArch()) 580 pageRoot.AddPage(guidArchRoot) 581 guidArchRootPageDict[obj.GetArch()] = guidArchRoot 582 guidArchRoot = guidArchRootPageDict[obj.GetArch()] 583 guidArchRoot.AddPage(self._GenerateGuidSubPage(pObj, obj, configFile)) 584 return [pageRoot] 585 586 def _GeneratePpiSubPage(self, pObj, obj, configFile): 587 guidPage = doxygen.Page(obj.GetName(), 'ppi_page_%s' % obj.GetName()) 588 comments = obj.GetComment() 589 if len(comments) != 0: 590 guidPage.AddDescription('<br>'.join(obj.GetComment()) + '<br>') 591 section = doxygen.Section('BasicPpiInfo', 'PPI Information') 592 desc = '<TABLE>' 593 desc += '<TR>' 594 desc += '<TD><CAPTION>PPI\'s Guid Name</CAPTION></TD><TD><CAPTION>PPI\'s Guid</CAPTION></TD>' 595 desc += '</TR>' 596 desc += '<TR>' 597 desc += '<TD>%s</TD>' % obj.GetName() 598 desc += '<TD>%s</TD>' % obj.GetGuid() 599 desc += '</TR>' 600 desc += '</TABLE>' 601 section.AddDescription(desc) 602 guidPage.AddSection(section) 603 refFile = self.FindHeaderFileForGuid(pObj, obj.GetName(), configFile) 604 if refFile: 605 relPath = refFile[len(pObj.GetWorkspace()) + 1:] 606 if len(comments) == 0: 607 guidPage.AddDescription(' \\copydoc %s <br>' % relPath) 608 section = doxygen.Section('ref', 'Refer to Header File') 609 section.AddDescription('\link %s\n' % relPath) 610 section.AddDescription('\endlink\n') 611 self.ProcessSourceFileForInclude(refFile, pObj, configFile) 612 guidPage.AddSection(section) 613 614 return guidPage 615 616 def GeneratePpiSubPages(self, pObj, configFile): 617 """ 618 Generate sub pages for package's GUID definition. 619 @param pObj package object 620 @param configFilf doxygen config file object 621 """ 622 pageRoot = doxygen.Page('PPI', 'ppi_root_page') 623 objs = pObj.GetFileObj().GetSectionObjectsByName('ppis', self._arch) 624 if len(objs) == 0: return [] 625 if self._arch is not None: 626 for obj in objs: 627 pageRoot.AddPage(self._GeneratePpiSubPage(pObj, obj, configFile)) 628 else: 629 guidArchRootPageDict = {} 630 for obj in objs: 631 if obj.GetArch() not in guidArchRootPageDict.keys(): 632 guidArchRoot = doxygen.Page(obj.GetArch(), 'ppi_arch_root_%s' % obj.GetArch()) 633 pageRoot.AddPage(guidArchRoot) 634 guidArchRootPageDict[obj.GetArch()] = guidArchRoot 635 guidArchRoot = guidArchRootPageDict[obj.GetArch()] 636 guidArchRoot.AddPage(self._GeneratePpiSubPage(pObj, obj, configFile)) 637 return [pageRoot] 638 639 def _GenerateProtocolSubPage(self, pObj, obj, configFile): 640 guidPage = doxygen.Page(obj.GetName(), 'protocol_page_%s' % obj.GetName()) 641 comments = obj.GetComment() 642 if len(comments) != 0: 643 guidPage.AddDescription('<br>'.join(obj.GetComment()) + '<br>') 644 section = doxygen.Section('BasicProtocolInfo', 'PROTOCOL Information') 645 desc = '<TABLE>' 646 desc += '<TR>' 647 desc += '<TD><CAPTION>PROTOCOL\'s Guid Name</CAPTION></TD><TD><CAPTION>PROTOCOL\'s Guid</CAPTION></TD>' 648 desc += '</TR>' 649 desc += '<TR>' 650 desc += '<TD>%s</TD>' % obj.GetName() 651 desc += '<TD>%s</TD>' % obj.GetGuid() 652 desc += '</TR>' 653 desc += '</TABLE>' 654 section.AddDescription(desc) 655 guidPage.AddSection(section) 656 657 refFile = self.FindHeaderFileForGuid(pObj, obj.GetName(), configFile) 658 if refFile: 659 relPath = refFile[len(pObj.GetWorkspace()) + 1:] 660 if len(comments) == 0: 661 guidPage.AddDescription(' \\copydoc %s <br>' % relPath) 662 section = doxygen.Section('ref', 'Refer to Header File') 663 section.AddDescription('\link %s\n' % relPath) 664 section.AddDescription('\endlink\n') 665 self.ProcessSourceFileForInclude(refFile, pObj, configFile) 666 guidPage.AddSection(section) 667 668 return guidPage 669 670 def GenerateProtocolSubPages(self, pObj, configFile): 671 """ 672 Generate sub pages for package's GUID definition. 673 @param pObj package object 674 @param configFilf doxygen config file object 675 """ 676 pageRoot = doxygen.Page('PROTOCOL', 'protocol_root_page') 677 objs = pObj.GetFileObj().GetSectionObjectsByName('protocols', self._arch) 678 if len(objs) == 0: return [] 679 if self._arch is not None: 680 for obj in objs: 681 pageRoot.AddPage(self._GenerateProtocolSubPage(pObj, obj, configFile)) 682 else: 683 guidArchRootPageDict = {} 684 for obj in objs: 685 if obj.GetArch() not in guidArchRootPageDict.keys(): 686 guidArchRoot = doxygen.Page(obj.GetArch(), 'protocol_arch_root_%s' % obj.GetArch()) 687 pageRoot.AddPage(guidArchRoot) 688 guidArchRootPageDict[obj.GetArch()] = guidArchRoot 689 guidArchRoot = guidArchRootPageDict[obj.GetArch()] 690 guidArchRoot.AddPage(self._GenerateProtocolSubPage(pObj, obj, configFile)) 691 return [pageRoot] 692 693 def FindHeaderFileForGuid(self, pObj, name, configFile): 694 """ 695 For declaration header file for GUID/PPI/Protocol. 696 697 @param pObj package object 698 @param name guid/ppi/protocol's name 699 @param configFile config file object 700 701 @return full path of header file and None if not found. 702 """ 703 startPath = pObj.GetFileObj().GetPackageRootPath() 704 incPath = os.path.join(startPath, 'Include').replace('\\', '/') 705 # if <PackagePath>/include exist, then search header under it. 706 if os.path.exists(incPath): 707 startPath = incPath 708 709 for root, dirs, files in os.walk(startPath): 710 for dir in dirs: 711 if dir.lower() in _ignore_dir: 712 dirs.remove(dir) 713 for file in files: 714 fPath = os.path.join(root, file) 715 if not IsCHeaderFile(fPath): 716 continue 717 try: 718 f = open(fPath, 'r') 719 lines = f.readlines() 720 f.close() 721 except IOError: 722 self.Log('Fail to open file %s\n' % fPath) 723 continue 724 for line in lines: 725 if line.find(name) != -1 and \ 726 line.find('extern') != -1: 727 return fPath.replace('\\', '/') 728 return None 729 730 def GetPackageModuleList(self, pObj): 731 """ 732 Get all module's INF path under package's root path 733 @param pObj package object 734 @return arrary of INF full path 735 """ 736 mArray = [] 737 packPath = pObj.GetFileObj().GetPackageRootPath() 738 if not os.path.exists: 739 return None 740 for root, dirs, files in os.walk(packPath): 741 for dir in dirs: 742 if dir.lower() in _ignore_dir: 743 dirs.remove(dir) 744 for file in files: 745 if CheckPathPostfix(file, 'inf'): 746 fPath = os.path.join(root, file).replace('\\', '/') 747 mArray.append(fPath) 748 return mArray 749 750 def GenerateModulePages(self, pObj, configFile): 751 """ 752 Generate sub pages for package's module which is under the package 753 root directory. 754 755 @param pObj package object 756 @param configFilf doxygen config file object 757 """ 758 infList = self.GetPackageModuleList(pObj) 759 rootPages = [] 760 libObjs = [] 761 modObjs = [] 762 for infpath in infList: 763 infObj = inf.INFFile(infpath) 764 #infObj = INFFileObject.INFFile (pObj.GetWorkspacePath(), 765 # inf) 766 if not infObj: 767 self.Log('Fail create INF object for %s' % inf) 768 continue 769 if not infObj.Parse(): 770 self.Log('Fail to load INF file %s' % inf) 771 continue 772 if infObj.GetProduceLibraryClass() is not None: 773 libObjs.append(infObj) 774 else: 775 modObjs.append(infObj) 776 777 if len(libObjs) != 0: 778 libRootPage = doxygen.Page('Libraries', 'lib_root_page') 779 rootPages.append(libRootPage) 780 for libInf in libObjs: 781 libRootPage.AddPage(self.GenerateModulePage(pObj, libInf, configFile, True)) 782 783 if len(modObjs) != 0: 784 modRootPage = doxygen.Page('Modules', 'module_root_page') 785 rootPages.append(modRootPage) 786 for modInf in modObjs: 787 modRootPage.AddPage(self.GenerateModulePage(pObj, modInf, configFile, False)) 788 789 return rootPages 790 791 def GenerateModulePage(self, pObj, infObj, configFile, isLib): 792 """ 793 Generate page for a module/library. 794 @param infObj INF file object for module/library 795 @param configFile doxygen config file object 796 @param isLib Whether this module is library 797 798 @param module doxygen page object 799 """ 800 workspace = pObj.GetWorkspace() 801 refDecObjs = [] 802 for obj in infObj.GetSectionObjectsByName('packages'): 803 decObj = dec.DECFile(os.path.join(workspace, obj.GetPath())) 804 if not decObj: 805 ErrorMsg ('Fail to create pacakge object for %s' % obj.GetPackageName()) 806 continue 807 if not decObj.Parse(): 808 ErrorMsg ('Fail to load package object for %s' % obj.GetPackageName()) 809 continue 810 refDecObjs.append(decObj) 811 812 modPage = doxygen.Page('%s' % infObj.GetBaseName(), 813 'module_%s' % infObj.GetBaseName()) 814 modPage.AddDescription(infObj.GetFileHeader()) 815 816 basicInfSection = doxygen.Section('BasicModuleInformation', 'Basic Module Information') 817 desc = "<TABLE>" 818 for obj in infObj.GetSectionObjectsByName('defines'): 819 key = obj.GetKey() 820 value = obj.GetValue() 821 if key not in _inf_key_description_mapping_table.keys(): continue 822 if key == 'LIBRARY_CLASS' and value.find('|') != -1: 823 clsname, types = value.split('|') 824 desc += '<TR>' 825 desc += '<TD><B>%s</B></TD>' % _inf_key_description_mapping_table[key] 826 desc += '<TD>%s</TD>' % clsname 827 desc += '</TR>' 828 829 desc += '<TR>' 830 desc += '<TD><B>Supported Module Types</B></TD>' 831 desc += '<TD>%s</TD>' % types 832 desc += '</TR>' 833 else: 834 desc += '<TR>' 835 desc += '<TD><B>%s</B></TD>' % _inf_key_description_mapping_table[key] 836 if key == 'EFI_SPECIFICATION_VERSION' and value == '0x00020000': 837 value = '2.0' 838 desc += '<TD>%s</TD>' % value 839 desc += '</TR>' 840 desc += '</TABLE>' 841 basicInfSection.AddDescription(desc) 842 modPage.AddSection(basicInfSection) 843 844 # Add protocol section 845 data = [] 846 for obj in infObj.GetSectionObjectsByName('pcd', self._arch): 847 data.append(obj.GetPcdName().strip()) 848 if len(data) != 0: 849 s = doxygen.Section('Pcds', 'Pcds') 850 desc = "<TABLE>" 851 desc += '<TR><TD><B>PCD Name</B></TD><TD><B>TokenSpace</B></TD><TD><B>Package</B></TD></TR>' 852 for item in data: 853 desc += '<TR>' 854 desc += '<TD>%s</TD>' % item.split('.')[1] 855 desc += '<TD>%s</TD>' % item.split('.')[0] 856 pkgbasename = self.SearchPcdPackage(item, workspace, refDecObjs) 857 desc += '<TD>%s</TD>' % pkgbasename 858 desc += '</TR>' 859 desc += "</TABLE>" 860 s.AddDescription(desc) 861 modPage.AddSection(s) 862 863 # Add protocol section 864 #sects = infObj.GetSectionByString('protocol') 865 data = [] 866 #for sect in sects: 867 for obj in infObj.GetSectionObjectsByName('protocol', self._arch): 868 data.append(obj.GetName().strip()) 869 if len(data) != 0: 870 s = doxygen.Section('Protocols', 'Protocols') 871 desc = "<TABLE>" 872 desc += '<TR><TD><B>Name</B></TD><TD><B>Package</B></TD></TR>' 873 for item in data: 874 desc += '<TR>' 875 desc += '<TD>%s</TD>' % item 876 pkgbasename = self.SearchProtocolPackage(item, workspace, refDecObjs) 877 desc += '<TD>%s</TD>' % pkgbasename 878 desc += '</TR>' 879 desc += "</TABLE>" 880 s.AddDescription(desc) 881 modPage.AddSection(s) 882 883 # Add ppi section 884 #sects = infObj.GetSectionByString('ppi') 885 data = [] 886 #for sect in sects: 887 for obj in infObj.GetSectionObjectsByName('ppi', self._arch): 888 data.append(obj.GetName().strip()) 889 if len(data) != 0: 890 s = doxygen.Section('Ppis', 'Ppis') 891 desc = "<TABLE>" 892 desc += '<TR><TD><B>Name</B></TD><TD><B>Package</B></TD></TR>' 893 for item in data: 894 desc += '<TR>' 895 desc += '<TD>%s</TD>' % item 896 pkgbasename = self.SearchPpiPackage(item, workspace, refDecObjs) 897 desc += '<TD>%s</TD>' % pkgbasename 898 desc += '</TR>' 899 desc += "</TABLE>" 900 s.AddDescription(desc) 901 modPage.AddSection(s) 902 903 # Add guid section 904 #sects = infObj.GetSectionByString('guid') 905 data = [] 906 #for sect in sects: 907 for obj in infObj.GetSectionObjectsByName('guid', self._arch): 908 data.append(obj.GetName().strip()) 909 if len(data) != 0: 910 s = doxygen.Section('Guids', 'Guids') 911 desc = "<TABLE>" 912 desc += '<TR><TD><B>Name</B></TD><TD><B>Package</B></TD></TR>' 913 for item in data: 914 desc += '<TR>' 915 desc += '<TD>%s</TD>' % item 916 pkgbasename = self.SearchGuidPackage(item, workspace, refDecObjs) 917 desc += '<TD>%s</TD>' % pkgbasename 918 desc += '</TR>' 919 desc += "</TABLE>" 920 s.AddDescription(desc) 921 modPage.AddSection(s) 922 923 section = doxygen.Section('LibraryClasses', 'Library Classes') 924 desc = "<TABLE>" 925 desc += '<TR><TD><B>Name</B></TD><TD><B>Type</B></TD><TD><B>Package</B></TD><TD><B>Header File</B></TD></TR>' 926 if isLib: 927 desc += '<TR>' 928 desc += '<TD>%s</TD>' % infObj.GetProduceLibraryClass() 929 desc += '<TD>Produce</TD>' 930 try: 931 pkgname, hPath = self.SearchLibraryClassHeaderFile(infObj.GetProduceLibraryClass(), 932 workspace, 933 refDecObjs) 934 except: 935 self.Log ('fail to get package header file for lib class %s' % infObj.GetProduceLibraryClass()) 936 pkgname = 'NULL' 937 hPath = 'NULL' 938 desc += '<TD>%s</TD>' % pkgname 939 if hPath != "NULL": 940 #desc += '<TD>\link %s \endlink</TD>' % hPath 941 desc += '<TD>%s</TD>' % hPath 942 else: 943 desc += '<TD>%s</TD>' % hPath 944 desc += '</TR>' 945 for lcObj in infObj.GetSectionObjectsByName('libraryclasses', self._arch): 946 desc += '<TR>' 947 desc += '<TD>%s</TD>' % lcObj.GetClass() 948 retarr = self.SearchLibraryClassHeaderFile(lcObj.GetClass(), 949 workspace, 950 refDecObjs) 951 if retarr is not None: 952 pkgname, hPath = retarr 953 else: 954 self.Log('Fail find the library class %s definition from module %s dependent package!' % (lcObj.GetClass(), infObj.GetFilename()), 'error') 955 pkgname = 'NULL' 956 hPath = 'NULL' 957 desc += '<TD>Consume</TD>' 958 desc += '<TD>%s</TD>' % pkgname 959 desc += '<TD>%s</TD>' % hPath 960 desc += '</TR>' 961 desc += "</TABLE>" 962 section.AddDescription(desc) 963 modPage.AddSection(section) 964 965 section = doxygen.Section('SourceFiles', 'Source Files') 966 section.AddDescription('<ul>\n') 967 for obj in infObj.GetSourceObjects(self._arch, self._tooltag): 968 sPath = infObj.GetModuleRootPath() 969 sPath = os.path.join(sPath, obj.GetSourcePath()).replace('\\', '/').strip() 970 if sPath.lower().endswith('.uni') or sPath.lower().endswith('.s') or sPath.lower().endswith('.asm') or sPath.lower().endswith('.nasm'): 971 newPath = self.TranslateUniFile(sPath) 972 configFile.AddFile(newPath) 973 newPath = newPath[len(pObj.GetWorkspace()) + 1:] 974 section.AddDescription('<li> \link %s \endlink </li>' % newPath) 975 else: 976 self.ProcessSourceFileForInclude(sPath, pObj, configFile, infObj) 977 sPath = sPath[len(pObj.GetWorkspace()) + 1:] 978 section.AddDescription('<li>\link %s \endlink </li>' % sPath) 979 section.AddDescription('</ul>\n') 980 modPage.AddSection(section) 981 982 #sects = infObj.GetSectionByString('depex') 983 data = [] 984 #for sect in sects: 985 for obj in infObj.GetSectionObjectsByName('depex'): 986 data.append(str(obj)) 987 if len(data) != 0: 988 s = doxygen.Section('DependentSection', 'Module Dependencies') 989 s.AddDescription('<br>'.join(data)) 990 modPage.AddSection(s) 991 992 return modPage 993 994 def TranslateUniFile(self, path): 995 newpath = path + '.dox' 996 #import core.textfile as textfile 997 #file = textfile.TextFile(path) 998 999 try: 1000 file = open(path, 'r') 1001 except (IOError, OSError) as msg: 1002 return None 1003 1004 t = file.read() 1005 file.close() 1006 1007 output = '/** @file \n' 1008 #output = '<html><body>' 1009 arr = t.split('\r\n') 1010 for line in arr: 1011 if line.find('@file') != -1: 1012 continue 1013 if line.find('*/') != -1: 1014 continue 1015 line = line.strip() 1016 if line.strip().startswith('/'): 1017 arr = line.split(' ') 1018 if len(arr) > 1: 1019 line = ' '.join(arr[1:]) 1020 else: 1021 continue 1022 output += '%s<br>\n' % line 1023 output += '**/' 1024 1025 if os.path.exists(newpath): 1026 os.remove(newpath) 1027 1028 file = open(newpath, "w") 1029 file.write(output) 1030 file.close() 1031 return newpath 1032 1033 def SearchPcdPackage(self, pcdname, workspace, decObjs): 1034 for decObj in decObjs: 1035 for pcd in decObj.GetSectionObjectsByName('pcd'): 1036 if pcdname == pcd.GetPcdName(): 1037 return decObj.GetBaseName() 1038 return None 1039 1040 def SearchProtocolPackage(self, protname, workspace, decObjs): 1041 for decObj in decObjs: 1042 for proto in decObj.GetSectionObjectsByName('protocol'): 1043 if protname == proto.GetName(): 1044 return decObj.GetBaseName() 1045 return None 1046 1047 def SearchPpiPackage(self, ppiname, workspace, decObjs): 1048 for decObj in decObjs: 1049 for ppi in decObj.GetSectionObjectsByName('ppi'): 1050 if ppiname == ppi.GetName(): 1051 return decObj.GetBaseName() 1052 return None 1053 1054 def SearchGuidPackage(self, guidname, workspace, decObjs): 1055 for decObj in decObjs: 1056 for guid in decObj.GetSectionObjectsByName('guid'): 1057 if guidname == guid.GetName(): 1058 return decObj.GetBaseName() 1059 return None 1060 1061 def SearchLibraryClassHeaderFile(self, className, workspace, decObjs): 1062 for decObj in decObjs: 1063 for cls in decObj.GetSectionObjectsByName('libraryclasses'): 1064 if cls.GetClassName().strip() == className: 1065 path = cls.GetHeaderFile().strip() 1066 path = os.path.join(decObj.GetPackageRootPath(), path) 1067 path = path[len(workspace) + 1:] 1068 return decObj.GetBaseName(), path.replace('\\', '/') 1069 1070 return None 1071 1072 def _ConvertPathToDoxygen(self, path, pObj): 1073 pRootPath = pObj.GetWorkspace() 1074 path = path[len(pRootPath) + 1:] 1075 return path.replace('\\', '/') 1076 1077def IsCHeaderFile(path): 1078 return CheckPathPostfix(path, 'h') 1079 1080def CheckPathPostfix(path, str): 1081 index = path.rfind('.') 1082 if index == -1: 1083 return False 1084 if path[index + 1:].lower() == str.lower(): 1085 return True 1086 return False 1087