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 from __future__
import print_function
16 import os
, sys
, logging
, traceback
, subprocess
17 from optparse
import OptionParser
19 from plugins
.EdkPlugins
.edk2
.model
import baseobject
20 from plugins
.EdkPlugins
.edk2
.model
import doxygengen
22 gArchMarcoDict
= {'ALL' : 'MDE_CPU_IA32 MDE_CPU_X64 MDE_CPU_EBC MDE_CPU_IPF _MSC_EXTENSIONS __GNUC__ __INTEL_COMPILER',
23 'IA32_MSFT': 'MDE_CPU_IA32 _MSC_EXTENSIONS',
24 'IA32_GNU' : 'MDE_CPU_IA32 __GNUC__',
25 'X64_MSFT' : 'MDE_CPU_X64 _MSC_EXTENSIONS ASM_PFX= OPTIONAL= ',
26 'X64_GNU' : 'MDE_CPU_X64 __GNUC__ ASM_PFX= OPTIONAL= ',
27 'IPF_MSFT' : 'MDE_CPU_IPF _MSC_EXTENSIONS ASM_PFX= OPTIONAL= ',
28 'IPF_GNU' : 'MDE_CPU_IPF __GNUC__ ASM_PFX= OPTIONAL= ',
29 'EBC_INTEL': 'MDE_CPU_EBC __INTEL_COMPILER ASM_PFX= OPTIONAL= '}
32 parser
= OptionParser(version
="Package Document Generation Tools - Version 0.1")
33 parser
.add_option('-w', '--workspace', action
='store', type='string', dest
='WorkspacePath',
34 help='Specify workspace absolute path. For example: c:\\tianocore')
35 parser
.add_option('-p', '--decfile', action
='store', dest
='PackagePath',
36 help='Specify the absolute path for package DEC file. For example: c:\\tianocore\\MdePkg\\MdePkg.dec')
37 parser
.add_option('-x', '--doxygen', action
='store', dest
='DoxygenPath',
38 help='Specify the absolute path of doxygen tools installation. For example: C:\\Program Files\\doxygen\bin\doxygen.exe')
39 parser
.add_option('-o', '--output', action
='store', dest
='OutputPath',
40 help='Specify the document output path. For example: c:\\docoutput')
41 parser
.add_option('-a', '--arch', action
='store', dest
='Arch', choices
=list(gArchMarcoDict
.keys()),
42 help='Specify the architecture used in preprocess package\'s source. For example: -a IA32_MSFT')
43 parser
.add_option('-m', '--mode', action
='store', dest
='DocumentMode', choices
=['CHM', 'HTML'],
44 help='Specify the document mode from : CHM or HTML')
45 parser
.add_option('-i', '--includeonly', action
='store_true', dest
='IncludeOnly',
46 help='Only generate document for package\'s public interfaces produced by include folder. ')
47 parser
.add_option('-c', '--htmlworkshop', dest
='HtmlWorkshopPath',
48 help='Specify the absolute path for Microsoft HTML Workshop\'s hhc.exe file. For example: C:\\Program Files\\HTML Help Workshop\\hhc.exe')
49 (options
, args
) = parser
.parse_args()
51 # validate the options
53 if options
.WorkspacePath
is None:
54 errors
.append('- Please specify workspace path via option -w!')
55 elif not os
.path
.exists(options
.WorkspacePath
):
56 errors
.append("- Invalid workspace path %s! The workspace path should be exist in absolute path!" % options
.WorkspacePath
)
58 if options
.PackagePath
is None:
59 errors
.append('- Please specify package DEC file path via option -p!')
60 elif not os
.path
.exists(options
.PackagePath
):
61 errors
.append("- Invalid package's DEC file path %s! The DEC path should be exist in absolute path!" % options
.PackagePath
)
63 default
= "C:\\Program Files\\doxygen\\bin\\doxygen.exe"
64 if options
.DoxygenPath
is None:
65 if os
.path
.exists(default
):
66 print("Warning: Assume doxygen tool is installed at %s. If not, please specify via -x" % default
)
67 options
.DoxygenPath
= default
69 errors
.append('- Please specify the path of doxygen tool installation via option -x! or install it in default path %s' % default
)
70 elif not os
.path
.exists(options
.DoxygenPath
):
71 errors
.append("- Invalid doxygen tool path %s! The doxygen tool path should be exist in absolute path!" % options
.DoxygenPath
)
73 if options
.OutputPath
is not None:
74 if not os
.path
.exists(options
.OutputPath
):
77 os
.makedirs(options
.OutputPath
)
79 errors
.append('- Fail to create the output directory %s' % options
.OutputPath
)
81 if options
.PackagePath
is not None and os
.path
.exists(options
.PackagePath
):
82 dirpath
= os
.path
.dirname(options
.PackagePath
)
83 default
= os
.path
.join (dirpath
, "Document")
84 print('Warning: Assume document output at %s. If not, please specify via option -o' % default
)
85 options
.OutputPath
= default
86 if not os
.path
.exists(default
):
90 errors
.append('- Fail to create default output directory %s! Please specify document output diretory via option -o' % default
)
92 errors
.append('- Please specify document output path via option -o!')
94 if options
.Arch
is None:
96 print("Warning: Assume arch is \"ALL\". If not, specify via -a")
98 if options
.DocumentMode
is None:
99 options
.DocumentMode
= "HTML"
100 print("Warning: Assume document mode is \"HTML\". If not, specify via -m")
102 if options
.IncludeOnly
is None:
103 options
.IncludeOnly
= False
104 print("Warning: Assume generate package document for all package\'s source including publich interfaces and implementation libraries and modules.")
106 if options
.DocumentMode
.lower() == 'chm':
107 default
= "C:\\Program Files\\HTML Help Workshop\\hhc.exe"
108 if options
.HtmlWorkshopPath
is None:
109 if os
.path
.exists(default
):
110 print('Warning: Assume the installation path of Microsoft HTML Workshop is %s. If not, specify via option -c.' % default
)
111 options
.HtmlWorkshopPath
= default
113 errors
.append('- Please specify the installation path of Microsoft HTML Workshop via option -c!')
114 elif not os
.path
.exists(options
.HtmlWorkshopPath
):
115 errors
.append('- The installation path of Microsoft HTML Workshop %s does not exists. ' % options
.HtmlWorkshopPath
)
119 parser
.error('Fail to start due to following reasons: \n%s' %'\n'.join(errors
))
120 return (options
.WorkspacePath
, options
.PackagePath
, options
.DoxygenPath
, options
.OutputPath
,
121 options
.Arch
, options
.DocumentMode
, options
.IncludeOnly
, options
.HtmlWorkshopPath
)
123 def createPackageObject(wsPath
, pkgPath
):
125 pkgObj
= baseobject
.Package(None, wsPath
)
128 logging
.getLogger().error ('Fail to create package object!')
133 def callbackLogMessage(msg
, level
):
136 def callbackCreateDoxygenProcess(doxPath
, configPath
):
137 if sys
.platform
== 'win32':
138 cmd
= '"%s" %s' % (doxPath
, configPath
)
140 cmd
= '%s %s' % (doxPath
, configPath
)
142 subprocess
.call(cmd
, shell
=True)
145 def DocumentFixup(outPath
, arch
):
146 # find BASE_LIBRARY_JUMP_BUFFER structure reference page
148 print('\n >>> Start fixup document \n')
150 for root
, dirs
, files
in os
.walk(outPath
):
152 if dir.lower() in ['.svn', '_svn', 'cvs']:
155 if not file.lower().endswith('.html'): continue
156 fullpath
= os
.path
.join(outPath
, root
, file)
158 f
= open(fullpath
, 'r')
162 logging
.getLogger().error('\nFail to open file %s\n' % fullpath
)
164 if arch
.lower() == 'all':
165 if text
.find('BASE_LIBRARY_JUMP_BUFFER Struct Reference') != -1:
166 FixPageBASE_LIBRARY_JUMP_BUFFER(fullpath
, text
)
167 if text
.find('MdePkg/Include/Library/BaseLib.h File Reference') != -1:
168 FixPageBaseLib(fullpath
, text
)
169 if text
.find('IA32_IDT_GATE_DESCRIPTOR Union Reference') != -1:
170 FixPageIA32_IDT_GATE_DESCRIPTOR(fullpath
, text
)
171 if text
.find('MdePkg/Include/Library/UefiDriverEntryPoint.h File Reference') != -1:
172 FixPageUefiDriverEntryPoint(fullpath
, text
)
173 if text
.find('MdePkg/Include/Library/UefiApplicationEntryPoint.h File Reference') != -1:
174 FixPageUefiApplicationEntryPoint(fullpath
, text
)
176 print(' >>> Finish all document fixing up! \n')
178 def FixPageBaseLib(path
, text
):
179 print(' >>> Fixup BaseLib file page at file %s \n' % path
)
180 lines
= text
.split('\n')
181 lastBaseJumpIndex
= -1
182 lastIdtGateDescriptor
= -1
183 for index
in range(len(lines
) - 1, -1, -1):
185 if line
.strip() == '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 4 </td>':
186 lines
[index
] = '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 4 [IA32] </td>'
187 if line
.strip() == '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 0x10 </td>':
188 lines
[index
] = '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 0x10 [IPF] </td>'
189 if line
.strip() == '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 8 </td>':
190 lines
[index
] = '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 9 [EBC, x64] </td>'
191 if line
.find('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 4') != -1:
192 lines
[index
] = lines
[index
].replace('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 4',
193 'BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 4 [IA32]')
194 if line
.find('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 0x10') != -1:
195 lines
[index
] = lines
[index
].replace('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 0x10',
196 'BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 0x10 [IPF]')
197 if line
.find('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 8') != -1:
198 lines
[index
] = lines
[index
].replace('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 8',
199 'BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 8 [x64, EBC]')
200 if line
.find('>BASE_LIBRARY_JUMP_BUFFER</a>') != -1:
201 if lastBaseJumpIndex
!= -1:
202 del lines
[lastBaseJumpIndex
]
203 lastBaseJumpIndex
= index
204 if line
.find('>IA32_IDT_GATE_DESCRIPTOR</a></td>') != -1:
205 if lastIdtGateDescriptor
!= -1:
206 del lines
[lastIdtGateDescriptor
]
207 lastIdtGateDescriptor
= index
210 f
.write('\n'.join(lines
))
213 logging
.getLogger().error(" <<< Fail to fixup file %s\n" % path
)
215 print(" <<< Finish to fixup file %s\n" % path
)
217 def FixPageIA32_IDT_GATE_DESCRIPTOR(path
, text
):
218 print(' >>> Fixup structure reference IA32_IDT_GATE_DESCRIPTOR at file %s \n' % path
)
219 lines
= text
.split('\n')
220 for index
in range(len(lines
) - 1, -1, -1):
221 line
= lines
[index
].strip()
222 if line
.find('struct {</td>') != -1 and lines
[index
- 2].find('>Uint64</a></td>') != -1:
223 lines
.insert(index
, '<tr><td colspan="2"><br><h2>Data Fields For X64</h2></td></tr>')
224 if line
.find('struct {</td>') != -1 and lines
[index
- 1].find('Data Fields') != -1:
225 lines
.insert(index
, '<tr><td colspan="2"><br><h2>Data Fields For IA32</h2></td></tr>')
228 f
.write('\n'.join(lines
))
231 logging
.getLogger().error(" <<< Fail to fixup file %s\n" % path
)
233 print(" <<< Finish to fixup file %s\n" % path
)
235 def FixPageBASE_LIBRARY_JUMP_BUFFER(path
, text
):
236 print(' >>> Fixup structure reference BASE_LIBRARY_JUMP_BUFFER at file %s \n' % path
)
237 lines
= text
.split('\n')
240 for index
in range(len(lines
) - 1, -1, -1):
242 if line
.find('Detailed Description') != -1:
244 if line
.startswith('EBC context buffer used by') and lines
[index
- 1].startswith('x64 context buffer'):
245 lines
[index
] = "IA32/IPF/X64/" + line
247 if line
.startswith("x64 context buffer") or line
.startswith('IPF context buffer used by') or \
248 line
.startswith('IA32 context buffer used by'):
251 if line
.find('>R0</a>') != -1 and not bInDetail
:
252 if lines
[index
- 1] != '<tr><td colspan="2"><br><h2>Data Fields For EBC</h2></td></tr>':
253 lines
.insert(index
, '<tr><td colspan="2"><br><h2>Data Fields For EBC</h2></td></tr>')
254 if line
.find('>Rbx</a>') != -1 and not bInDetail
:
255 if lines
[index
- 1] != '<tr><td colspan="2"><br><h2>Data Fields For X64</h2></td></tr>':
256 lines
.insert(index
, '<tr><td colspan="2"><br><h2>Data Fields For X64</h2></td></tr>')
257 if line
.find('>F2</a>') != -1 and not bInDetail
:
258 if lines
[index
- 1] != '<tr><td colspan="2"><br><h2>Data Fields For IPF</h2></td></tr>':
259 lines
.insert(index
, '<tr><td colspan="2"><br><h2>Data Fields For IPF</h2></td></tr>')
260 if line
.find('>Ebx</a>') != -1 and not bInDetail
:
261 if lines
[index
- 1] != '<tr><td colspan="2"><br><h2>Data Fields For IA32</h2></td></tr>':
262 lines
.insert(index
, '<tr><td colspan="2"><br><h2>Data Fields For IA32</h2></td></tr>')
265 f
.write('\n'.join(lines
))
268 logging
.getLogger().error(" <<< Fail to fixup file %s" % path
)
270 print(" <<< Finish to fixup file %s\n" % path
)
272 def FixPageUefiDriverEntryPoint(path
, text
):
273 print(' >>> Fixup file reference MdePkg/Include/Library/UefiDriverEntryPoint.h at file %s \n' % path
)
274 lines
= text
.split('\n')
275 bInModuleEntry
= False
277 ModuleEntryDlCount
= 0
278 ModuleEntryDelStart
= 0
279 ModuleEntryDelEnd
= 0
284 for index
in range(len(lines
)):
285 line
= lines
[index
].strip()
286 if line
.find('EFI_STATUS</a> EFIAPI _ModuleEntryPoint </td>') != -1:
287 bInModuleEntry
= True
288 if line
.find('EFI_STATUS</a> EFIAPI EfiMain </td>') != -1:
290 if line
.startswith('<p>References <a'):
292 ModuleEntryDelEnd
= index
- 1
293 bInModuleEntry
= False
295 EfiMainDelEnd
= index
- 1
298 if line
.startswith('</dl>'):
299 ModuleEntryDlCount
= ModuleEntryDlCount
+ 1
300 if ModuleEntryDlCount
== 1:
301 ModuleEntryDelStart
= index
+ 1
303 if line
.startswith('</dl>'):
304 EfiMainDlCount
= EfiMainDlCount
+ 1
305 if EfiMainDlCount
== 1:
306 EfiMainDelStart
= index
+ 1
308 if EfiMainDelEnd
> EfiMainDelStart
:
309 for index
in range(EfiMainDelEnd
, EfiMainDelStart
, -1):
311 if ModuleEntryDelEnd
> ModuleEntryDelStart
:
312 for index
in range(ModuleEntryDelEnd
, ModuleEntryDelStart
, -1):
317 f
.write('\n'.join(lines
))
320 logging
.getLogger().error(" <<< Fail to fixup file %s" % path
)
322 print(" <<< Finish to fixup file %s\n" % path
)
325 def FixPageUefiApplicationEntryPoint(path
, text
):
326 print(' >>> Fixup file reference MdePkg/Include/Library/UefiApplicationEntryPoint.h at file %s \n' % path
)
327 lines
= text
.split('\n')
328 bInModuleEntry
= False
330 ModuleEntryDlCount
= 0
331 ModuleEntryDelStart
= 0
332 ModuleEntryDelEnd
= 0
337 for index
in range(len(lines
)):
338 line
= lines
[index
].strip()
339 if line
.find('EFI_STATUS</a> EFIAPI _ModuleEntryPoint </td>') != -1:
340 bInModuleEntry
= True
341 if line
.find('EFI_STATUS</a> EFIAPI EfiMain </td>') != -1:
343 if line
.startswith('<p>References <a'):
345 ModuleEntryDelEnd
= index
- 1
346 bInModuleEntry
= False
348 EfiMainDelEnd
= index
- 1
351 if line
.startswith('</dl>'):
352 ModuleEntryDlCount
= ModuleEntryDlCount
+ 1
353 if ModuleEntryDlCount
== 1:
354 ModuleEntryDelStart
= index
+ 1
356 if line
.startswith('</dl>'):
357 EfiMainDlCount
= EfiMainDlCount
+ 1
358 if EfiMainDlCount
== 1:
359 EfiMainDelStart
= index
+ 1
361 if EfiMainDelEnd
> EfiMainDelStart
:
362 for index
in range(EfiMainDelEnd
, EfiMainDelStart
, -1):
364 if ModuleEntryDelEnd
> ModuleEntryDelStart
:
365 for index
in range(ModuleEntryDelEnd
, ModuleEntryDelStart
, -1):
370 f
.write('\n'.join(lines
))
373 logging
.getLogger().error(" <<< Fail to fixup file %s" % path
)
375 print(" <<< Finish to fixup file %s\n" % path
)
377 if __name__
== '__main__':
378 wspath
, pkgpath
, doxpath
, outpath
, archtag
, docmode
, isinc
, hwpath
= parseCmdArgs()
380 # configure logging system
381 logfilepath
= os
.path
.join(outpath
, 'log.txt')
382 logging
.basicConfig(format
='%(levelname)-8s %(message)s', level
=logging
.DEBUG
)
384 # create package model object firstly
385 pkgObj
= createPackageObject(wspath
, pkgpath
)
389 # create doxygen action model
392 if archtag
.lower() != 'all':
393 arch
= archtag
.split('_')[0]
394 tooltag
= archtag
.split('_')[1]
399 # preprocess package and call doxygen
401 action
= doxygengen
.PackageDocumentAction(doxpath
,
411 action
.RegisterCallbackDoxygenProcess(callbackCreateDoxygenProcess
)
414 message
= traceback
.format_exception(*sys
.exc_info())
415 logging
.getLogger().error('Fail to create doxygen action! \n%s' % ''.join(message
))
418 DocumentFixup(outpath
, arch
)
420 # generate CHM is necessary
421 if docmode
.lower() == 'chm':
422 indexpath
= os
.path
.join(outpath
, 'html', 'index.hhp')
423 if sys
.platform
== 'win32':
424 cmd
= '"%s" %s' % (hwpath
, indexpath
)
426 cmd
= '%s %s' % (hwpath
, indexpath
)
428 print('\nFinish to generate package document! Please open %s for review' % os
.path
.join(outpath
, 'html', 'index.chm'))
430 print('\nFinish to generate package document! Please open %s for review' % os
.path
.join(outpath
, 'html', 'index.html'))