]> git.proxmox.com Git - mirror_edk2.git/blob - BaseTools/Scripts/PackageDocumentTools/packagedoc_cli.py
CorebootPayloadPkg: Conditionally add DebugAgentLib for DXE drivers
[mirror_edk2.git] / BaseTools / Scripts / PackageDocumentTools / packagedoc_cli.py
1 ## @file
2 # This module provide command line entry for generating package document!
3 #
4 # Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
5 #
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
10 #
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.
13 #
14
15 import os, sys, logging, traceback, subprocess
16 from optparse import OptionParser
17
18 import plugins.EdkPlugins.edk2.model.baseobject as baseobject
19 import plugins.EdkPlugins.edk2.model.doxygengen as doxygengen
20
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= '}
29
30 def parseCmdArgs():
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()
49
50 # validate the options
51 errors = []
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)
56
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)
61
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
67 else:
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)
71
72 if options.OutputPath != None:
73 if not os.path.exists(options.OutputPath):
74 # create output
75 try:
76 os.makedirs(options.OutputPath)
77 except:
78 errors.append('- Fail to create the output directory %s' % options.OutputPath)
79 else:
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):
86 try:
87 os.makedirs(default)
88 except:
89 errors.append('- Fail to create default output directory %s! Please specify document output diretory via option -o' % default)
90 else:
91 errors.append('- Please specify document output path via option -o!')
92
93 if options.Arch == None:
94 options.Arch = 'ALL'
95 print "Warning: Assume arch is \"ALL\". If not, specify via -a"
96
97 if options.DocumentMode == None:
98 options.DocumentMode = "HTML"
99 print "Warning: Assume document mode is \"HTML\". If not, specify via -m"
100
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."
104
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
111 else:
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)
115
116 if len(errors) != 0:
117 print '\n'
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)
121
122 def createPackageObject(wsPath, pkgPath):
123 try:
124 pkgObj = baseobject.Package(None, wsPath)
125 pkgObj.Load(pkgPath)
126 except:
127 logging.getLogger().error ('Fail to create package object!')
128 return None
129
130 return pkgObj
131
132 def callbackLogMessage(msg, level):
133 print msg.strip()
134
135 def callbackCreateDoxygenProcess(doxPath, configPath):
136 if sys.platform == 'win32':
137 cmd = '"%s" %s' % (doxPath, configPath)
138 else:
139 cmd = '%s %s' % (doxPath, configPath)
140 print cmd
141 subprocess.call(cmd, shell=True)
142
143
144 def DocumentFixup(outPath, arch):
145 # find BASE_LIBRARY_JUMP_BUFFER structure reference page
146
147 print '\n >>> Start fixup document \n'
148
149 for root, dirs, files in os.walk(outPath):
150 for dir in dirs:
151 if dir.lower() in ['.svn', '_svn', 'cvs']:
152 dirs.remove(dir)
153 for file in files:
154 if not file.lower().endswith('.html'): continue
155 fullpath = os.path.join(outPath, root, file)
156 try:
157 f = open(fullpath, 'r')
158 text = f.read()
159 f.close()
160 except:
161 logging.getLogger().error('\nFail to open file %s\n' % fullpath)
162 continue
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)
174
175 print ' >>> Finish all document fixing up! \n'
176
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):
183 line = lines[index]
184 if line.strip() == '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT&nbsp;&nbsp;&nbsp;4 </td>':
185 lines[index] = '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT&nbsp;&nbsp;&nbsp;4&nbsp;[IA32] </td>'
186 if line.strip() == '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT&nbsp;&nbsp;&nbsp;0x10 </td>':
187 lines[index] = '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT&nbsp;&nbsp;&nbsp;0x10&nbsp;[IPF] </td>'
188 if line.strip() == '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT&nbsp;&nbsp;&nbsp;8 </td>':
189 lines[index] = '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT&nbsp;&nbsp;&nbsp;9&nbsp;[EBC, x64] </td>'
190 if line.find('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a>&nbsp;&nbsp;&nbsp;4') != -1:
191 lines[index] = lines[index].replace('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a>&nbsp;&nbsp;&nbsp;4',
192 'BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a>&nbsp;&nbsp;&nbsp;4&nbsp;[IA32]')
193 if line.find('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a>&nbsp;&nbsp;&nbsp;0x10') != -1:
194 lines[index] = lines[index].replace('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a>&nbsp;&nbsp;&nbsp;0x10',
195 'BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a>&nbsp;&nbsp;&nbsp;0x10&nbsp;[IPF]')
196 if line.find('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a>&nbsp;&nbsp;&nbsp;8') != -1:
197 lines[index] = lines[index].replace('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a>&nbsp;&nbsp;&nbsp;8',
198 'BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a>&nbsp;&nbsp;&nbsp;8&nbsp;[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
207 try:
208 f = open(path, 'w')
209 f.write('\n'.join(lines))
210 f.close()
211 except:
212 logging.getLogger().error(" <<< Fail to fixup file %s\n" % path)
213 return
214 print " <<< Finish to fixup file %s\n" % path
215
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>')
225 try:
226 f = open(path, 'w')
227 f.write('\n'.join(lines))
228 f.close()
229 except:
230 logging.getLogger().error(" <<< Fail to fixup file %s\n" % path)
231 return
232 print " <<< Finish to fixup file %s\n" % path
233
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')
237 bInDetail = True
238 bNeedRemove = False
239 for index in range(len(lines) - 1, -1, -1):
240 line = lines[index]
241 if line.find('Detailed Description') != -1:
242 bInDetail = False
243 if line.startswith('EBC context buffer used by') and lines[index - 1].startswith('x64 context buffer'):
244 lines[index] = "IA32/IPF/X64/" + line
245 bNeedRemove = True
246 if line.startswith("x64 context buffer") or line.startswith('IPF context buffer used by') or \
247 line.startswith('IA32 context buffer used by'):
248 if bNeedRemove:
249 lines.remove(line)
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>')
262 try:
263 f = open(path, 'w')
264 f.write('\n'.join(lines))
265 f.close()
266 except:
267 logging.getLogger().error(" <<< Fail to fixup file %s" % path)
268 return
269 print " <<< Finish to fixup file %s\n" % path
270
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
275 bInEfiMain = False
276 ModuleEntryDlCount = 0
277 ModuleEntryDelStart = 0
278 ModuleEntryDelEnd = 0
279 EfiMainDlCount = 0
280 EfiMainDelStart = 0
281 EfiMainDelEnd = 0
282
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:
288 bInEfiMain = True
289 if line.startswith('<p>References <a'):
290 if bInModuleEntry:
291 ModuleEntryDelEnd = index - 1
292 bInModuleEntry = False
293 elif bInEfiMain:
294 EfiMainDelEnd = index - 1
295 bInEfiMain = False
296 if bInModuleEntry:
297 if line.startswith('</dl>'):
298 ModuleEntryDlCount = ModuleEntryDlCount + 1
299 if ModuleEntryDlCount == 1:
300 ModuleEntryDelStart = index + 1
301 if bInEfiMain:
302 if line.startswith('</dl>'):
303 EfiMainDlCount = EfiMainDlCount + 1
304 if EfiMainDlCount == 1:
305 EfiMainDelStart = index + 1
306
307 if EfiMainDelEnd > EfiMainDelStart:
308 for index in range(EfiMainDelEnd, EfiMainDelStart, -1):
309 del lines[index]
310 if ModuleEntryDelEnd > ModuleEntryDelStart:
311 for index in range(ModuleEntryDelEnd, ModuleEntryDelStart, -1):
312 del lines[index]
313
314 try:
315 f = open(path, 'w')
316 f.write('\n'.join(lines))
317 f.close()
318 except:
319 logging.getLogger().error(" <<< Fail to fixup file %s" % path)
320 return
321 print " <<< Finish to fixup file %s\n" % path
322
323
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
328 bInEfiMain = False
329 ModuleEntryDlCount = 0
330 ModuleEntryDelStart = 0
331 ModuleEntryDelEnd = 0
332 EfiMainDlCount = 0
333 EfiMainDelStart = 0
334 EfiMainDelEnd = 0
335
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:
341 bInEfiMain = True
342 if line.startswith('<p>References <a'):
343 if bInModuleEntry:
344 ModuleEntryDelEnd = index - 1
345 bInModuleEntry = False
346 elif bInEfiMain:
347 EfiMainDelEnd = index - 1
348 bInEfiMain = False
349 if bInModuleEntry:
350 if line.startswith('</dl>'):
351 ModuleEntryDlCount = ModuleEntryDlCount + 1
352 if ModuleEntryDlCount == 1:
353 ModuleEntryDelStart = index + 1
354 if bInEfiMain:
355 if line.startswith('</dl>'):
356 EfiMainDlCount = EfiMainDlCount + 1
357 if EfiMainDlCount == 1:
358 EfiMainDelStart = index + 1
359
360 if EfiMainDelEnd > EfiMainDelStart:
361 for index in range(EfiMainDelEnd, EfiMainDelStart, -1):
362 del lines[index]
363 if ModuleEntryDelEnd > ModuleEntryDelStart:
364 for index in range(ModuleEntryDelEnd, ModuleEntryDelStart, -1):
365 del lines[index]
366
367 try:
368 f = open(path, 'w')
369 f.write('\n'.join(lines))
370 f.close()
371 except:
372 logging.getLogger().error(" <<< Fail to fixup file %s" % path)
373 return
374 print " <<< Finish to fixup file %s\n" % path
375
376 if __name__ == '__main__':
377 wspath, pkgpath, doxpath, outpath, archtag, docmode, isinc, hwpath = parseCmdArgs()
378
379 # configure logging system
380 logfilepath = os.path.join(outpath, 'log.txt')
381 logging.basicConfig(format='%(levelname)-8s %(message)s', level=logging.DEBUG)
382
383 # create package model object firstly
384 pkgObj = createPackageObject(wspath, pkgpath)
385 if pkgObj == None:
386 sys.exit(-1)
387
388 # create doxygen action model
389 arch = None
390 tooltag = None
391 if archtag.lower() != 'all':
392 arch = archtag.split('_')[0]
393 tooltag = archtag.split('_')[1]
394 else:
395 arch = 'all'
396 tooltag = 'all'
397
398 # preprocess package and call doxygen
399 try:
400 action = doxygengen.PackageDocumentAction(doxpath,
401 hwpath,
402 outpath,
403 pkgObj,
404 docmode,
405 callbackLogMessage,
406 arch,
407 tooltag,
408 isinc,
409 True)
410 action.RegisterCallbackDoxygenProcess(callbackCreateDoxygenProcess)
411 action.Generate()
412 except:
413 message = traceback.format_exception(*sys.exc_info())
414 logging.getLogger().error('Fail to create doxygen action! \n%s' % ''.join(message))
415 sys.exit(-1)
416
417 DocumentFixup(outpath, arch)
418
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)
424 else:
425 cmd = '%s %s' % (hwpath, indexpath)
426 subprocess.call(cmd)
427 print '\nFinish to generate package document! Please open %s for review' % os.path.join(outpath, 'html', 'index.chm')
428 else:
429 print '\nFinish to generate package document! Please open %s for review' % os.path.join(outpath, 'html', 'index.html')