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