]> git.proxmox.com Git - mirror_edk2.git/blob - BaseTools/Scripts/PackageDocumentTools/plugins/EdkPlugins/edk2/model/doxygengen_spec.py
BaseTools: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / BaseTools / Scripts / PackageDocumentTools / plugins / EdkPlugins / edk2 / model / doxygengen_spec.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 from plugins.EdkPlugins.basemodel import doxygen
11 import os
12 try:
13 import wx
14 gInGui = True
15 except:
16 gInGui = False
17 import re
18 from plugins.EdkPlugins.edk2.model import inf
19 from plugins.EdkPlugins.edk2.model import dec
20 from 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 }
40 class 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
128 class PlatformDocumentAction(DoxygenAction):
129 """Generate platform doxygen document, will be implement at future."""
130
131 class 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 libary
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
1077 def IsCHeaderFile(path):
1078 return CheckPathPostfix(path, 'h')
1079
1080 def 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