2 # This module provide command line entry for generating package document!
4 # Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
6 # SPDX-License-Identifier: BSD-2-Clause-Patent
9 from __future__
import print_function
10 import os
, sys
, logging
, traceback
, subprocess
11 from optparse
import OptionParser
13 from plugins
.EdkPlugins
.edk2
.model
import baseobject
14 from plugins
.EdkPlugins
.edk2
.model
import doxygengen
16 gArchMarcoDict
= {'ALL' : 'MDE_CPU_IA32 MDE_CPU_X64 MDE_CPU_EBC MDE_CPU_IPF _MSC_EXTENSIONS __GNUC__ __INTEL_COMPILER',
17 'IA32_MSFT': 'MDE_CPU_IA32 _MSC_EXTENSIONS',
18 'IA32_GNU' : 'MDE_CPU_IA32 __GNUC__',
19 'X64_MSFT' : 'MDE_CPU_X64 _MSC_EXTENSIONS ASM_PFX= OPTIONAL= ',
20 'X64_GNU' : 'MDE_CPU_X64 __GNUC__ ASM_PFX= OPTIONAL= ',
21 'IPF_MSFT' : 'MDE_CPU_IPF _MSC_EXTENSIONS ASM_PFX= OPTIONAL= ',
22 'IPF_GNU' : 'MDE_CPU_IPF __GNUC__ ASM_PFX= OPTIONAL= ',
23 'EBC_INTEL': 'MDE_CPU_EBC __INTEL_COMPILER ASM_PFX= OPTIONAL= '}
26 parser
= OptionParser(version
="Package Document Generation Tools - Version 0.1")
27 parser
.add_option('-w', '--workspace', action
='store', type='string', dest
='WorkspacePath',
28 help='Specify workspace absolute path. For example: c:\\tianocore')
29 parser
.add_option('-p', '--decfile', action
='store', dest
='PackagePath',
30 help='Specify the absolute path for package DEC file. For example: c:\\tianocore\\MdePkg\\MdePkg.dec')
31 parser
.add_option('-x', '--doxygen', action
='store', dest
='DoxygenPath',
32 help='Specify the absolute path of doxygen tools installation. For example: C:\\Program Files\\doxygen\bin\doxygen.exe')
33 parser
.add_option('-o', '--output', action
='store', dest
='OutputPath',
34 help='Specify the document output path. For example: c:\\docoutput')
35 parser
.add_option('-a', '--arch', action
='store', dest
='Arch', choices
=list(gArchMarcoDict
.keys()),
36 help='Specify the architecture used in preprocess package\'s source. For example: -a IA32_MSFT')
37 parser
.add_option('-m', '--mode', action
='store', dest
='DocumentMode', choices
=['CHM', 'HTML'],
38 help='Specify the document mode from : CHM or HTML')
39 parser
.add_option('-i', '--includeonly', action
='store_true', dest
='IncludeOnly',
40 help='Only generate document for package\'s public interfaces produced by include folder. ')
41 parser
.add_option('-c', '--htmlworkshop', dest
='HtmlWorkshopPath',
42 help='Specify the absolute path for Microsoft HTML Workshop\'s hhc.exe file. For example: C:\\Program Files\\HTML Help Workshop\\hhc.exe')
43 (options
, args
) = parser
.parse_args()
45 # validate the options
47 if options
.WorkspacePath
is None:
48 errors
.append('- Please specify workspace path via option -w!')
49 elif not os
.path
.exists(options
.WorkspacePath
):
50 errors
.append("- Invalid workspace path %s! The workspace path should be exist in absolute path!" % options
.WorkspacePath
)
52 if options
.PackagePath
is None:
53 errors
.append('- Please specify package DEC file path via option -p!')
54 elif not os
.path
.exists(options
.PackagePath
):
55 errors
.append("- Invalid package's DEC file path %s! The DEC path should be exist in absolute path!" % options
.PackagePath
)
57 default
= "C:\\Program Files\\doxygen\\bin\\doxygen.exe"
58 if options
.DoxygenPath
is None:
59 if os
.path
.exists(default
):
60 print("Warning: Assume doxygen tool is installed at %s. If not, please specify via -x" % default
)
61 options
.DoxygenPath
= default
63 errors
.append('- Please specify the path of doxygen tool installation via option -x! or install it in default path %s' % default
)
64 elif not os
.path
.exists(options
.DoxygenPath
):
65 errors
.append("- Invalid doxygen tool path %s! The doxygen tool path should be exist in absolute path!" % options
.DoxygenPath
)
67 if options
.OutputPath
is not None:
68 if not os
.path
.exists(options
.OutputPath
):
71 os
.makedirs(options
.OutputPath
)
73 errors
.append('- Fail to create the output directory %s' % options
.OutputPath
)
75 if options
.PackagePath
is not None and os
.path
.exists(options
.PackagePath
):
76 dirpath
= os
.path
.dirname(options
.PackagePath
)
77 default
= os
.path
.join (dirpath
, "Document")
78 print('Warning: Assume document output at %s. If not, please specify via option -o' % default
)
79 options
.OutputPath
= default
80 if not os
.path
.exists(default
):
84 errors
.append('- Fail to create default output directory %s! Please specify document output diretory via option -o' % default
)
86 errors
.append('- Please specify document output path via option -o!')
88 if options
.Arch
is None:
90 print("Warning: Assume arch is \"ALL\". If not, specify via -a")
92 if options
.DocumentMode
is None:
93 options
.DocumentMode
= "HTML"
94 print("Warning: Assume document mode is \"HTML\". If not, specify via -m")
96 if options
.IncludeOnly
is None:
97 options
.IncludeOnly
= False
98 print("Warning: Assume generate package document for all package\'s source including publich interfaces and implementation libraries and modules.")
100 if options
.DocumentMode
.lower() == 'chm':
101 default
= "C:\\Program Files\\HTML Help Workshop\\hhc.exe"
102 if options
.HtmlWorkshopPath
is None:
103 if os
.path
.exists(default
):
104 print('Warning: Assume the installation path of Microsoft HTML Workshop is %s. If not, specify via option -c.' % default
)
105 options
.HtmlWorkshopPath
= default
107 errors
.append('- Please specify the installation path of Microsoft HTML Workshop via option -c!')
108 elif not os
.path
.exists(options
.HtmlWorkshopPath
):
109 errors
.append('- The installation path of Microsoft HTML Workshop %s does not exists. ' % options
.HtmlWorkshopPath
)
113 parser
.error('Fail to start due to following reasons: \n%s' %'\n'.join(errors
))
114 return (options
.WorkspacePath
, options
.PackagePath
, options
.DoxygenPath
, options
.OutputPath
,
115 options
.Arch
, options
.DocumentMode
, options
.IncludeOnly
, options
.HtmlWorkshopPath
)
117 def createPackageObject(wsPath
, pkgPath
):
119 pkgObj
= baseobject
.Package(None, wsPath
)
122 logging
.getLogger().error ('Fail to create package object!')
127 def callbackLogMessage(msg
, level
):
130 def callbackCreateDoxygenProcess(doxPath
, configPath
):
131 if sys
.platform
== 'win32':
132 cmd
= '"%s" %s' % (doxPath
, configPath
)
134 cmd
= '%s %s' % (doxPath
, configPath
)
136 subprocess
.call(cmd
, shell
=True)
139 def DocumentFixup(outPath
, arch
):
140 # find BASE_LIBRARY_JUMP_BUFFER structure reference page
142 print('\n >>> Start fixup document \n')
144 for root
, dirs
, files
in os
.walk(outPath
):
146 if dir.lower() in ['.svn', '_svn', 'cvs']:
149 if not file.lower().endswith('.html'): continue
150 fullpath
= os
.path
.join(outPath
, root
, file)
152 f
= open(fullpath
, 'r')
156 logging
.getLogger().error('\nFail to open file %s\n' % fullpath
)
158 if arch
.lower() == 'all':
159 if text
.find('BASE_LIBRARY_JUMP_BUFFER Struct Reference') != -1:
160 FixPageBASE_LIBRARY_JUMP_BUFFER(fullpath
, text
)
161 if text
.find('MdePkg/Include/Library/BaseLib.h File Reference') != -1:
162 FixPageBaseLib(fullpath
, text
)
163 if text
.find('IA32_IDT_GATE_DESCRIPTOR Union Reference') != -1:
164 FixPageIA32_IDT_GATE_DESCRIPTOR(fullpath
, text
)
165 if text
.find('MdePkg/Include/Library/UefiDriverEntryPoint.h File Reference') != -1:
166 FixPageUefiDriverEntryPoint(fullpath
, text
)
167 if text
.find('MdePkg/Include/Library/UefiApplicationEntryPoint.h File Reference') != -1:
168 FixPageUefiApplicationEntryPoint(fullpath
, text
)
170 print(' >>> Finish all document fixing up! \n')
172 def FixPageBaseLib(path
, text
):
173 print(' >>> Fixup BaseLib file page at file %s \n' % path
)
174 lines
= text
.split('\n')
175 lastBaseJumpIndex
= -1
176 lastIdtGateDescriptor
= -1
177 for index
in range(len(lines
) - 1, -1, -1):
179 if line
.strip() == '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 4 </td>':
180 lines
[index
] = '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 4 [IA32] </td>'
181 if line
.strip() == '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 0x10 </td>':
182 lines
[index
] = '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 0x10 [IPF] </td>'
183 if line
.strip() == '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 8 </td>':
184 lines
[index
] = '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 9 [EBC, x64] </td>'
185 if line
.find('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 4') != -1:
186 lines
[index
] = lines
[index
].replace('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 4',
187 'BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 4 [IA32]')
188 if line
.find('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 0x10') != -1:
189 lines
[index
] = lines
[index
].replace('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 0x10',
190 'BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 0x10 [IPF]')
191 if line
.find('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 8') != -1:
192 lines
[index
] = lines
[index
].replace('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 8',
193 'BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 8 [x64, EBC]')
194 if line
.find('>BASE_LIBRARY_JUMP_BUFFER</a>') != -1:
195 if lastBaseJumpIndex
!= -1:
196 del lines
[lastBaseJumpIndex
]
197 lastBaseJumpIndex
= index
198 if line
.find('>IA32_IDT_GATE_DESCRIPTOR</a></td>') != -1:
199 if lastIdtGateDescriptor
!= -1:
200 del lines
[lastIdtGateDescriptor
]
201 lastIdtGateDescriptor
= index
204 f
.write('\n'.join(lines
))
207 logging
.getLogger().error(" <<< Fail to fixup file %s\n" % path
)
209 print(" <<< Finish to fixup file %s\n" % path
)
211 def FixPageIA32_IDT_GATE_DESCRIPTOR(path
, text
):
212 print(' >>> Fixup structure reference IA32_IDT_GATE_DESCRIPTOR at file %s \n' % path
)
213 lines
= text
.split('\n')
214 for index
in range(len(lines
) - 1, -1, -1):
215 line
= lines
[index
].strip()
216 if line
.find('struct {</td>') != -1 and lines
[index
- 2].find('>Uint64</a></td>') != -1:
217 lines
.insert(index
, '<tr><td colspan="2"><br><h2>Data Fields For X64</h2></td></tr>')
218 if line
.find('struct {</td>') != -1 and lines
[index
- 1].find('Data Fields') != -1:
219 lines
.insert(index
, '<tr><td colspan="2"><br><h2>Data Fields For IA32</h2></td></tr>')
222 f
.write('\n'.join(lines
))
225 logging
.getLogger().error(" <<< Fail to fixup file %s\n" % path
)
227 print(" <<< Finish to fixup file %s\n" % path
)
229 def FixPageBASE_LIBRARY_JUMP_BUFFER(path
, text
):
230 print(' >>> Fixup structure reference BASE_LIBRARY_JUMP_BUFFER at file %s \n' % path
)
231 lines
= text
.split('\n')
234 for index
in range(len(lines
) - 1, -1, -1):
236 if line
.find('Detailed Description') != -1:
238 if line
.startswith('EBC context buffer used by') and lines
[index
- 1].startswith('x64 context buffer'):
239 lines
[index
] = "IA32/IPF/X64/" + line
241 if line
.startswith("x64 context buffer") or line
.startswith('IPF context buffer used by') or \
242 line
.startswith('IA32 context buffer used by'):
245 if line
.find('>R0</a>') != -1 and not bInDetail
:
246 if lines
[index
- 1] != '<tr><td colspan="2"><br><h2>Data Fields For EBC</h2></td></tr>':
247 lines
.insert(index
, '<tr><td colspan="2"><br><h2>Data Fields For EBC</h2></td></tr>')
248 if line
.find('>Rbx</a>') != -1 and not bInDetail
:
249 if lines
[index
- 1] != '<tr><td colspan="2"><br><h2>Data Fields For X64</h2></td></tr>':
250 lines
.insert(index
, '<tr><td colspan="2"><br><h2>Data Fields For X64</h2></td></tr>')
251 if line
.find('>F2</a>') != -1 and not bInDetail
:
252 if lines
[index
- 1] != '<tr><td colspan="2"><br><h2>Data Fields For IPF</h2></td></tr>':
253 lines
.insert(index
, '<tr><td colspan="2"><br><h2>Data Fields For IPF</h2></td></tr>')
254 if line
.find('>Ebx</a>') != -1 and not bInDetail
:
255 if lines
[index
- 1] != '<tr><td colspan="2"><br><h2>Data Fields For IA32</h2></td></tr>':
256 lines
.insert(index
, '<tr><td colspan="2"><br><h2>Data Fields For IA32</h2></td></tr>')
259 f
.write('\n'.join(lines
))
262 logging
.getLogger().error(" <<< Fail to fixup file %s" % path
)
264 print(" <<< Finish to fixup file %s\n" % path
)
266 def FixPageUefiDriverEntryPoint(path
, text
):
267 print(' >>> Fixup file reference MdePkg/Include/Library/UefiDriverEntryPoint.h at file %s \n' % path
)
268 lines
= text
.split('\n')
269 bInModuleEntry
= False
271 ModuleEntryDlCount
= 0
272 ModuleEntryDelStart
= 0
273 ModuleEntryDelEnd
= 0
278 for index
in range(len(lines
)):
279 line
= lines
[index
].strip()
280 if line
.find('EFI_STATUS</a> EFIAPI _ModuleEntryPoint </td>') != -1:
281 bInModuleEntry
= True
282 if line
.find('EFI_STATUS</a> EFIAPI EfiMain </td>') != -1:
284 if line
.startswith('<p>References <a'):
286 ModuleEntryDelEnd
= index
- 1
287 bInModuleEntry
= False
289 EfiMainDelEnd
= index
- 1
292 if line
.startswith('</dl>'):
293 ModuleEntryDlCount
= ModuleEntryDlCount
+ 1
294 if ModuleEntryDlCount
== 1:
295 ModuleEntryDelStart
= index
+ 1
297 if line
.startswith('</dl>'):
298 EfiMainDlCount
= EfiMainDlCount
+ 1
299 if EfiMainDlCount
== 1:
300 EfiMainDelStart
= index
+ 1
302 if EfiMainDelEnd
> EfiMainDelStart
:
303 for index
in range(EfiMainDelEnd
, EfiMainDelStart
, -1):
305 if ModuleEntryDelEnd
> ModuleEntryDelStart
:
306 for index
in range(ModuleEntryDelEnd
, ModuleEntryDelStart
, -1):
311 f
.write('\n'.join(lines
))
314 logging
.getLogger().error(" <<< Fail to fixup file %s" % path
)
316 print(" <<< Finish to fixup file %s\n" % path
)
319 def FixPageUefiApplicationEntryPoint(path
, text
):
320 print(' >>> Fixup file reference MdePkg/Include/Library/UefiApplicationEntryPoint.h at file %s \n' % path
)
321 lines
= text
.split('\n')
322 bInModuleEntry
= False
324 ModuleEntryDlCount
= 0
325 ModuleEntryDelStart
= 0
326 ModuleEntryDelEnd
= 0
331 for index
in range(len(lines
)):
332 line
= lines
[index
].strip()
333 if line
.find('EFI_STATUS</a> EFIAPI _ModuleEntryPoint </td>') != -1:
334 bInModuleEntry
= True
335 if line
.find('EFI_STATUS</a> EFIAPI EfiMain </td>') != -1:
337 if line
.startswith('<p>References <a'):
339 ModuleEntryDelEnd
= index
- 1
340 bInModuleEntry
= False
342 EfiMainDelEnd
= index
- 1
345 if line
.startswith('</dl>'):
346 ModuleEntryDlCount
= ModuleEntryDlCount
+ 1
347 if ModuleEntryDlCount
== 1:
348 ModuleEntryDelStart
= index
+ 1
350 if line
.startswith('</dl>'):
351 EfiMainDlCount
= EfiMainDlCount
+ 1
352 if EfiMainDlCount
== 1:
353 EfiMainDelStart
= index
+ 1
355 if EfiMainDelEnd
> EfiMainDelStart
:
356 for index
in range(EfiMainDelEnd
, EfiMainDelStart
, -1):
358 if ModuleEntryDelEnd
> ModuleEntryDelStart
:
359 for index
in range(ModuleEntryDelEnd
, ModuleEntryDelStart
, -1):
364 f
.write('\n'.join(lines
))
367 logging
.getLogger().error(" <<< Fail to fixup file %s" % path
)
369 print(" <<< Finish to fixup file %s\n" % path
)
371 if __name__
== '__main__':
372 wspath
, pkgpath
, doxpath
, outpath
, archtag
, docmode
, isinc
, hwpath
= parseCmdArgs()
374 # configure logging system
375 logfilepath
= os
.path
.join(outpath
, 'log.txt')
376 logging
.basicConfig(format
='%(levelname)-8s %(message)s', level
=logging
.DEBUG
)
378 # create package model object firstly
379 pkgObj
= createPackageObject(wspath
, pkgpath
)
383 # create doxygen action model
386 if archtag
.lower() != 'all':
387 arch
= archtag
.split('_')[0]
388 tooltag
= archtag
.split('_')[1]
393 # preprocess package and call doxygen
395 action
= doxygengen
.PackageDocumentAction(doxpath
,
405 action
.RegisterCallbackDoxygenProcess(callbackCreateDoxygenProcess
)
408 message
= traceback
.format_exception(*sys
.exc_info())
409 logging
.getLogger().error('Fail to create doxygen action! \n%s' % ''.join(message
))
412 DocumentFixup(outpath
, arch
)
414 # generate CHM is necessary
415 if docmode
.lower() == 'chm':
416 indexpath
= os
.path
.join(outpath
, 'html', 'index.hhp')
417 if sys
.platform
== 'win32':
418 cmd
= '"%s" %s' % (hwpath
, indexpath
)
420 cmd
= '%s %s' % (hwpath
, indexpath
)
422 print('\nFinish to generate package document! Please open %s for review' % os
.path
.join(outpath
, 'html', 'index.chm'))
424 print('\nFinish to generate package document! Please open %s for review' % os
.path
.join(outpath
, 'html', 'index.html'))