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