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