2 # This module provide command line entry for generating package document!
4 # Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
6 # This program and the accompanying materials are licensed and made available
7 # under the terms and conditions of the BSD License which accompanies this
8 # distribution. The full text of the license may be found at
9 # http://opensource.org/licenses/bsd-license.php
11 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 import os
, sys
, logging
, traceback
, subprocess
16 from optparse
import OptionParser
18 import plugins
.EdkPlugins
.edk2
.model
.baseobject
as baseobject
19 import plugins
.EdkPlugins
.edk2
.model
.doxygengen
as doxygengen
21 gArchMarcoDict
= {'ALL' : 'MDE_CPU_IA32 MDE_CPU_X64 MDE_CPU_EBC MDE_CPU_IPF _MSC_EXTENSIONS __GNUC__ __INTEL_COMPILER',
22 'IA32_MSFT': 'MDE_CPU_IA32 _MSC_EXTENSIONS',
23 'IA32_GNU' : 'MDE_CPU_IA32 __GNUC__',
24 'X64_MSFT' : 'MDE_CPU_X64 _MSC_EXTENSIONS ASM_PFX= OPTIONAL= ',
25 'X64_GNU' : 'MDE_CPU_X64 __GNUC__ ASM_PFX= OPTIONAL= ',
26 'IPF_MSFT' : 'MDE_CPU_IPF _MSC_EXTENSIONS ASM_PFX= OPTIONAL= ',
27 'IPF_GNU' : 'MDE_CPU_IPF __GNUC__ ASM_PFX= OPTIONAL= ',
28 'EBC_INTEL': 'MDE_CPU_EBC __INTEL_COMPILER ASM_PFX= OPTIONAL= '}
31 parser
= OptionParser(version
="Package Document Generation Tools - Version 0.1")
32 parser
.add_option('-w', '--workspace', action
='store', type='string', dest
='WorkspacePath',
33 help='Specify workspace absolute path. For example: c:\\tianocore')
34 parser
.add_option('-p', '--decfile', action
='store', dest
='PackagePath',
35 help='Specify the absolute path for package DEC file. For example: c:\\tianocore\\MdePkg\\MdePkg.dec')
36 parser
.add_option('-x', '--doxygen', action
='store', dest
='DoxygenPath',
37 help='Specify the absolute path of doxygen tools installation. For example: C:\\Program Files\\doxygen\bin\doxygen.exe')
38 parser
.add_option('-o', '--output', action
='store', dest
='OutputPath',
39 help='Specify the document output path. For example: c:\\docoutput')
40 parser
.add_option('-a', '--arch', action
='store', dest
='Arch', choices
=gArchMarcoDict
.keys(),
41 help='Specify the architecture used in preprocess package\'s source. For example: -a IA32_MSFT')
42 parser
.add_option('-m', '--mode', action
='store', dest
='DocumentMode', choices
=['CHM', 'HTML'],
43 help='Specify the document mode from : CHM or HTML')
44 parser
.add_option('-i', '--includeonly', action
='store_true', dest
='IncludeOnly',
45 help='Only generate document for package\'s public interfaces produced by include folder. ')
46 parser
.add_option('-c', '--htmlworkshop', dest
='HtmlWorkshopPath',
47 help='Specify the absolute path for Microsoft HTML Workshop\'s hhc.exe file. For example: C:\\Program Files\\HTML Help Workshop\\hhc.exe')
48 (options
, args
) = parser
.parse_args()
50 # validate the options
52 if options
.WorkspacePath
== None:
53 errors
.append('- Please specify workspace path via option -w!')
54 elif not os
.path
.exists(options
.WorkspacePath
):
55 errors
.append("- Invalid workspace path %s! The workspace path should be exist in absolute path!" % options
.WorkspacePath
)
57 if options
.PackagePath
== None:
58 errors
.append('- Please specify package DEC file path via option -p!')
59 elif not os
.path
.exists(options
.PackagePath
):
60 errors
.append("- Invalid package's DEC file path %s! The DEC path should be exist in absolute path!" % options
.PackagePath
)
62 default
= "C:\\Program Files\\doxygen\\bin\\doxygen.exe"
63 if options
.DoxygenPath
== None:
64 if os
.path
.exists(default
):
65 print "Warning: Assume doxygen tool is installed at %s. If not, please specify via -x" % default
66 options
.DoxygenPath
= default
68 errors
.append('- Please specify the path of doxygen tool installation via option -x! or install it in default path %s' % default
)
69 elif not os
.path
.exists(options
.DoxygenPath
):
70 errors
.append("- Invalid doxygen tool path %s! The doxygen tool path should be exist in absolute path!" % options
.DoxygenPath
)
72 if options
.OutputPath
!= None:
73 if not os
.path
.exists(options
.OutputPath
):
76 os
.makedirs(options
.OutputPath
)
78 errors
.append('- Fail to create the output directory %s' % options
.OutputPath
)
80 if options
.PackagePath
!= None and os
.path
.exists(options
.PackagePath
):
81 dirpath
= os
.path
.dirname(options
.PackagePath
)
82 default
= os
.path
.join (dirpath
, "Document")
83 print 'Warning: Assume document output at %s. If not, please specify via option -o' % default
84 options
.OutputPath
= default
85 if not os
.path
.exists(default
):
89 errors
.append('- Fail to create default output directory %s! Please specify document output diretory via option -o' % default
)
91 errors
.append('- Please specify document output path via option -o!')
93 if options
.Arch
== None:
95 print "Warning: Assume arch is \"ALL\". If not, specify via -a"
97 if options
.DocumentMode
== None:
98 options
.DocumentMode
= "HTML"
99 print "Warning: Assume document mode is \"HTML\". If not, specify via -m"
101 if options
.IncludeOnly
== None:
102 options
.IncludeOnly
= False
103 print "Warning: Assume generate package document for all package\'s source including publich interfaces and implementation libraries and modules."
105 if options
.DocumentMode
.lower() == 'chm':
106 default
= "C:\\Program Files\\HTML Help Workshop\\hhc.exe"
107 if options
.HtmlWorkshopPath
== None:
108 if os
.path
.exists(default
):
109 print 'Warning: Assume the installation path of Microsoft HTML Workshop is %s. If not, specify via option -c.' % default
110 options
.HtmlWorkshopPath
= default
112 errors
.append('- Please specify the installation path of Microsoft HTML Workshop via option -c!')
113 elif not os
.path
.exists(options
.HtmlWorkshopPath
):
114 errors
.append('- The installation path of Microsoft HTML Workshop %s does not exists. ' % options
.HtmlWorkshopPath
)
118 parser
.error('Fail to start due to following reasons: \n%s' %'\n'.join(errors
))
119 return (options
.WorkspacePath
, options
.PackagePath
, options
.DoxygenPath
, options
.OutputPath
,
120 options
.Arch
, options
.DocumentMode
, options
.IncludeOnly
, options
.HtmlWorkshopPath
)
122 def createPackageObject(wsPath
, pkgPath
):
124 pkgObj
= baseobject
.Package(None, wsPath
)
127 logging
.getLogger().error ('Fail to create package object!')
132 def callbackLogMessage(msg
, level
):
135 def callbackCreateDoxygenProcess(doxPath
, configPath
):
136 if sys
.platform
== 'win32':
137 cmd
= '"%s" %s' % (doxPath
, configPath
)
139 cmd
= '%s %s' % (doxPath
, configPath
)
141 subprocess
.call(cmd
, shell
=True)
144 def DocumentFixup(outPath
, arch
):
145 # find BASE_LIBRARY_JUMP_BUFFER structure reference page
147 print '\n >>> Start fixup document \n'
149 for root
, dirs
, files
in os
.walk(outPath
):
151 if dir.lower() in ['.svn', '_svn', 'cvs']:
154 if not file.lower().endswith('.html'): continue
155 fullpath
= os
.path
.join(outPath
, root
, file)
157 f
= open(fullpath
, 'r')
161 logging
.getLogger().error('\nFail to open file %s\n' % fullpath
)
163 if arch
.lower() == 'all':
164 if text
.find('BASE_LIBRARY_JUMP_BUFFER Struct Reference') != -1:
165 FixPageBASE_LIBRARY_JUMP_BUFFER(fullpath
, text
)
166 if text
.find('MdePkg/Include/Library/BaseLib.h File Reference') != -1:
167 FixPageBaseLib(fullpath
, text
)
168 if text
.find('IA32_IDT_GATE_DESCRIPTOR Union Reference') != -1:
169 FixPageIA32_IDT_GATE_DESCRIPTOR(fullpath
, text
)
170 if text
.find('MdePkg/Include/Library/UefiDriverEntryPoint.h File Reference') != -1:
171 FixPageUefiDriverEntryPoint(fullpath
, text
)
172 if text
.find('MdePkg/Include/Library/UefiApplicationEntryPoint.h File Reference') != -1:
173 FixPageUefiApplicationEntryPoint(fullpath
, text
)
175 print ' >>> Finish all document fixing up! \n'
177 def FixPageBaseLib(path
, text
):
178 print ' >>> Fixup BaseLib file page at file %s \n' % path
179 lines
= text
.split('\n')
180 lastBaseJumpIndex
= -1
181 lastIdtGateDescriptor
= -1
182 for index
in range(len(lines
) - 1, -1, -1):
184 if line
.strip() == '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 4 </td>':
185 lines
[index
] = '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 4 [IA32] </td>'
186 if line
.strip() == '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 0x10 </td>':
187 lines
[index
] = '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 0x10 [IPF] </td>'
188 if line
.strip() == '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 8 </td>':
189 lines
[index
] = '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 9 [EBC, x64] </td>'
190 if line
.find('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 4') != -1:
191 lines
[index
] = lines
[index
].replace('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 4',
192 'BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 4 [IA32]')
193 if line
.find('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 0x10') != -1:
194 lines
[index
] = lines
[index
].replace('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 0x10',
195 'BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 0x10 [IPF]')
196 if line
.find('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 8') != -1:
197 lines
[index
] = lines
[index
].replace('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 8',
198 'BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 8 [x64, EBC]')
199 if line
.find('>BASE_LIBRARY_JUMP_BUFFER</a>') != -1:
200 if lastBaseJumpIndex
!= -1:
201 del lines
[lastBaseJumpIndex
]
202 lastBaseJumpIndex
= index
203 if line
.find('>IA32_IDT_GATE_DESCRIPTOR</a></td>') != -1:
204 if lastIdtGateDescriptor
!= -1:
205 del lines
[lastIdtGateDescriptor
]
206 lastIdtGateDescriptor
= index
209 f
.write('\n'.join(lines
))
212 logging
.getLogger().error(" <<< Fail to fixup file %s\n" % path
)
214 print " <<< Finish to fixup file %s\n" % path
216 def FixPageIA32_IDT_GATE_DESCRIPTOR(path
, text
):
217 print ' >>> Fixup structure reference IA32_IDT_GATE_DESCRIPTOR at file %s \n' % path
218 lines
= text
.split('\n')
219 for index
in range(len(lines
) - 1, -1, -1):
220 line
= lines
[index
].strip()
221 if line
.find('struct {</td>') != -1 and lines
[index
- 2].find('>Uint64</a></td>') != -1:
222 lines
.insert(index
, '<tr><td colspan="2"><br><h2>Data Fields For X64</h2></td></tr>')
223 if line
.find('struct {</td>') != -1 and lines
[index
- 1].find('Data Fields') != -1:
224 lines
.insert(index
, '<tr><td colspan="2"><br><h2>Data Fields For IA32</h2></td></tr>')
227 f
.write('\n'.join(lines
))
230 logging
.getLogger().error(" <<< Fail to fixup file %s\n" % path
)
232 print " <<< Finish to fixup file %s\n" % path
234 def FixPageBASE_LIBRARY_JUMP_BUFFER(path
, text
):
235 print ' >>> Fixup structure reference BASE_LIBRARY_JUMP_BUFFER at file %s \n' % path
236 lines
= text
.split('\n')
239 for index
in range(len(lines
) - 1, -1, -1):
241 if line
.find('Detailed Description') != -1:
243 if line
.startswith('EBC context buffer used by') and lines
[index
- 1].startswith('x64 context buffer'):
244 lines
[index
] = "IA32/IPF/X64/" + line
246 if line
.startswith("x64 context buffer") or line
.startswith('IPF context buffer used by') or \
247 line
.startswith('IA32 context buffer used by'):
250 if line
.find('>R0</a>') != -1 and not bInDetail
:
251 if lines
[index
- 1] != '<tr><td colspan="2"><br><h2>Data Fields For EBC</h2></td></tr>':
252 lines
.insert(index
, '<tr><td colspan="2"><br><h2>Data Fields For EBC</h2></td></tr>')
253 if line
.find('>Rbx</a>') != -1 and not bInDetail
:
254 if lines
[index
- 1] != '<tr><td colspan="2"><br><h2>Data Fields For X64</h2></td></tr>':
255 lines
.insert(index
, '<tr><td colspan="2"><br><h2>Data Fields For X64</h2></td></tr>')
256 if line
.find('>F2</a>') != -1 and not bInDetail
:
257 if lines
[index
- 1] != '<tr><td colspan="2"><br><h2>Data Fields For IPF</h2></td></tr>':
258 lines
.insert(index
, '<tr><td colspan="2"><br><h2>Data Fields For IPF</h2></td></tr>')
259 if line
.find('>Ebx</a>') != -1 and not bInDetail
:
260 if lines
[index
- 1] != '<tr><td colspan="2"><br><h2>Data Fields For IA32</h2></td></tr>':
261 lines
.insert(index
, '<tr><td colspan="2"><br><h2>Data Fields For IA32</h2></td></tr>')
264 f
.write('\n'.join(lines
))
267 logging
.getLogger().error(" <<< Fail to fixup file %s" % path
)
269 print " <<< Finish to fixup file %s\n" % path
271 def FixPageUefiDriverEntryPoint(path
, text
):
272 print ' >>> Fixup file reference MdePkg/Include/Library/UefiDriverEntryPoint.h at file %s \n' % path
273 lines
= text
.split('\n')
274 bInModuleEntry
= False
276 ModuleEntryDlCount
= 0
277 ModuleEntryDelStart
= 0
278 ModuleEntryDelEnd
= 0
283 for index
in range(len(lines
)):
284 line
= lines
[index
].strip()
285 if line
.find('EFI_STATUS</a> EFIAPI _ModuleEntryPoint </td>') != -1:
286 bInModuleEntry
= True
287 if line
.find('EFI_STATUS</a> EFIAPI EfiMain </td>') != -1:
289 if line
.startswith('<p>References <a'):
291 ModuleEntryDelEnd
= index
- 1
292 bInModuleEntry
= False
294 EfiMainDelEnd
= index
- 1
297 if line
.startswith('</dl>'):
298 ModuleEntryDlCount
= ModuleEntryDlCount
+ 1
299 if ModuleEntryDlCount
== 1:
300 ModuleEntryDelStart
= index
+ 1
302 if line
.startswith('</dl>'):
303 EfiMainDlCount
= EfiMainDlCount
+ 1
304 if EfiMainDlCount
== 1:
305 EfiMainDelStart
= index
+ 1
307 if EfiMainDelEnd
> EfiMainDelStart
:
308 for index
in range(EfiMainDelEnd
, EfiMainDelStart
, -1):
310 if ModuleEntryDelEnd
> ModuleEntryDelStart
:
311 for index
in range(ModuleEntryDelEnd
, ModuleEntryDelStart
, -1):
316 f
.write('\n'.join(lines
))
319 logging
.getLogger().error(" <<< Fail to fixup file %s" % path
)
321 print " <<< Finish to fixup file %s\n" % path
324 def FixPageUefiApplicationEntryPoint(path
, text
):
325 print ' >>> Fixup file reference MdePkg/Include/Library/UefiApplicationEntryPoint.h at file %s \n' % path
326 lines
= text
.split('\n')
327 bInModuleEntry
= False
329 ModuleEntryDlCount
= 0
330 ModuleEntryDelStart
= 0
331 ModuleEntryDelEnd
= 0
336 for index
in range(len(lines
)):
337 line
= lines
[index
].strip()
338 if line
.find('EFI_STATUS</a> EFIAPI _ModuleEntryPoint </td>') != -1:
339 bInModuleEntry
= True
340 if line
.find('EFI_STATUS</a> EFIAPI EfiMain </td>') != -1:
342 if line
.startswith('<p>References <a'):
344 ModuleEntryDelEnd
= index
- 1
345 bInModuleEntry
= False
347 EfiMainDelEnd
= index
- 1
350 if line
.startswith('</dl>'):
351 ModuleEntryDlCount
= ModuleEntryDlCount
+ 1
352 if ModuleEntryDlCount
== 1:
353 ModuleEntryDelStart
= index
+ 1
355 if line
.startswith('</dl>'):
356 EfiMainDlCount
= EfiMainDlCount
+ 1
357 if EfiMainDlCount
== 1:
358 EfiMainDelStart
= index
+ 1
360 if EfiMainDelEnd
> EfiMainDelStart
:
361 for index
in range(EfiMainDelEnd
, EfiMainDelStart
, -1):
363 if ModuleEntryDelEnd
> ModuleEntryDelStart
:
364 for index
in range(ModuleEntryDelEnd
, ModuleEntryDelStart
, -1):
369 f
.write('\n'.join(lines
))
372 logging
.getLogger().error(" <<< Fail to fixup file %s" % path
)
374 print " <<< Finish to fixup file %s\n" % path
376 if __name__
== '__main__':
377 wspath
, pkgpath
, doxpath
, outpath
, archtag
, docmode
, isinc
, hwpath
= parseCmdArgs()
379 # configure logging system
380 logfilepath
= os
.path
.join(outpath
, 'log.txt')
381 logging
.basicConfig(format
='%(levelname)-8s %(message)s', level
=logging
.DEBUG
)
383 # create package model object firstly
384 pkgObj
= createPackageObject(wspath
, pkgpath
)
388 # create doxygen action model
391 if archtag
.lower() != 'all':
392 arch
= archtag
.split('_')[0]
393 tooltag
= archtag
.split('_')[1]
398 # preprocess package and call doxygen
400 action
= doxygengen
.PackageDocumentAction(doxpath
,
410 action
.RegisterCallbackDoxygenProcess(callbackCreateDoxygenProcess
)
413 message
= traceback
.format_exception(*sys
.exc_info())
414 logging
.getLogger().error('Fail to create doxygen action! \n%s' % ''.join(message
))
417 DocumentFixup(outpath
, arch
)
419 # generate CHM is necessary
420 if docmode
.lower() == 'chm':
421 indexpath
= os
.path
.join(outpath
, 'html', 'index.hhp')
422 if sys
.platform
== 'win32':
423 cmd
= '"%s" %s' % (hwpath
, indexpath
)
425 cmd
= '%s %s' % (hwpath
, indexpath
)
427 print '\nFinish to generate package document! Please open %s for review' % os
.path
.join(outpath
, 'html', 'index.chm')
429 print '\nFinish to generate package document! Please open %s for review' % os
.path
.join(outpath
, 'html', 'index.html')