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