2 # This file is used to define common string related functions used in parsing
5 # Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
7 # This program and the accompanying materials are licensed and made available
8 # under the terms and conditions of the BSD License which accompanies this
9 # distribution. The full text of the license may be found at
10 # http://opensource.org/licenses/bsd-license.php
12 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16 import os, sys, wx, logging
19 import wx.lib.newevent
20 import wx.lib.agw.genericmessagedialog as GMD
21 import plugins.EdkPlugins.edk2.model.baseobject as baseobject
22 import plugins.EdkPlugins.edk2.model.doxygengen as doxygengen
24 if hasattr(sys, "frozen"):
25 appPath = os.path.abspath(os.path.dirname(sys.executable))
27 appPath = os.path.abspath(os.path.dirname(__file__))
29 AppCallBackEvent, EVT_APP_CALLBACK = wx.lib.newevent.NewEvent()
30 LogEvent, EVT_LOG = wx.lib.newevent.NewEvent()
32 class PackageDocApp(wx.App):
35 logfile = os.path.join(appPath, 'log.txt')
36 logging.basicConfig(format='%(name)-8s %(levelname)-8s %(message)s',
37 filename=logfile, level=logging.ERROR)
39 self.SetAppName('Package Doxygen Generate Application')
40 frame = PackageDocMainFrame(None, "Package Document Generation Application!")
41 self.SetTopWindow(frame)
45 EVT_APP_CALLBACK( self, self.OnAppCallBack)
49 return logging.getLogger('')
51 def ForegroundProcess(self, function, args):
52 wx.PostEvent(self, AppCallBackEvent(callback=function, args=args))
54 def OnAppCallBack(self, event):
56 event.callback(*event.args)
58 self._logger.exception( 'OnAppCallBack<%s.%s>\n' %
59 (event.callback.__module__, event.callback.__name__ ))
61 class PackageDocMainFrame(wx.Frame):
62 def __init__(self, parent, title):
63 wx.Frame.__init__(self, parent, -1, title, size=(550, 290), style=wx.MINIMIZE_BOX|wx.SYSTEM_MENU|wx.CAPTION|wx.CLOSE_BOX )
65 panel = wx.Panel(self)
66 sizer = wx.BoxSizer(wx.VERTICAL)
68 subsizer = wx.GridBagSizer(5, 10)
69 subsizer.AddGrowableCol(1)
70 subsizer.Add(wx.StaticText(panel, -1, "Workspace Location : "), (0, 0), flag=wx.ALIGN_CENTER_VERTICAL)
71 self._workspacePathCtrl = wx.ComboBox(panel, -1)
72 list = self.GetConfigure("WorkspacePath")
75 self._workspacePathCtrl.Append(item)
76 self._workspacePathCtrl.SetValue(list[len(list) - 1])
78 subsizer.Add(self._workspacePathCtrl, (0, 1), flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
79 self._workspacePathBt = wx.BitmapButton(panel, -1, bitmap=wx.ArtProvider_GetBitmap(wx.ART_FILE_OPEN))
80 subsizer.Add(self._workspacePathBt, (0, 2), flag=wx.ALIGN_CENTER_VERTICAL)
81 wx.EVT_BUTTON(self._workspacePathBt, self._workspacePathBt.GetId(), self.OnBrowsePath)
83 subsizer.Add(wx.StaticText(panel, -1, "Package DEC Location : "), (1, 0), flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
84 self._packagePathCtrl = wx.ComboBox(panel, -1)
85 list = self.GetConfigure("PackagePath")
88 self._packagePathCtrl.Append(item)
89 self._packagePathCtrl.SetValue(list[len(list) - 1])
90 subsizer.Add(self._packagePathCtrl, (1, 1), flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
91 self._packagePathBt = wx.BitmapButton(panel, -1, bitmap=wx.ArtProvider_GetBitmap(wx.ART_FILE_OPEN))
92 subsizer.Add(self._packagePathBt, (1, 2), flag=wx.ALIGN_CENTER_VERTICAL)
93 wx.EVT_BUTTON(self._packagePathBt, self._packagePathBt.GetId(), self.OnBrowsePath)
95 subsizer.Add(wx.StaticText(panel, -1, "Doxygen Tool Location : "), (2, 0), flag=wx.ALIGN_CENTER_VERTICAL)
96 self._doxygenPathCtrl = wx.TextCtrl(panel, -1)
97 list = self.GetConfigure('DoxygenPath')
99 self._doxygenPathCtrl.SetValue(list[0])
101 if wx.Platform == '__WXMSW__':
102 self._doxygenPathCtrl.SetValue('C:\\Program Files\\Doxygen\\bin\\doxygen.exe')
104 self._doxygenPathCtrl.SetValue('/usr/bin/doxygen')
106 self._doxygenPathBt = wx.BitmapButton(panel, -1, bitmap=wx.ArtProvider_GetBitmap(wx.ART_FILE_OPEN))
107 subsizer.Add(self._doxygenPathCtrl, (2, 1), flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
108 subsizer.Add(self._doxygenPathBt, (2, 2), flag=wx.ALIGN_CENTER_VERTICAL)
109 wx.EVT_BUTTON(self._doxygenPathBt, self._doxygenPathBt.GetId(), self.OnBrowsePath)
111 subsizer.Add(wx.StaticText(panel, -1, "CHM Tool Location : "), (3, 0), flag=wx.ALIGN_CENTER_VERTICAL)
112 self._chmPathCtrl = wx.TextCtrl(panel, -1)
113 list = self.GetConfigure('CHMPath')
115 self._chmPathCtrl.SetValue(list[0])
117 self._chmPathCtrl.SetValue('C:\\Program Files\\HTML Help Workshop\\hhc.exe')
119 self._chmPathBt = wx.BitmapButton(panel, -1, bitmap=wx.ArtProvider_GetBitmap(wx.ART_FILE_OPEN))
120 subsizer.Add(self._chmPathCtrl, (3, 1), flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
121 subsizer.Add(self._chmPathBt, (3, 2), flag=wx.ALIGN_CENTER_VERTICAL)
122 wx.EVT_BUTTON(self._chmPathBt, self._chmPathBt.GetId(), self.OnBrowsePath)
124 subsizer.Add(wx.StaticText(panel, -1, "Output Location : "), (4, 0), flag=wx.ALIGN_CENTER_VERTICAL)
125 self._outputPathCtrl = wx.ComboBox(panel, -1)
126 list = self.GetConfigure("OutputPath")
129 self._outputPathCtrl.Append(item)
130 self._outputPathCtrl.SetValue(list[len(list) - 1])
132 subsizer.Add(self._outputPathCtrl, (4, 1), flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
133 self._outputPathBt = wx.BitmapButton(panel, -1, bitmap=wx.ArtProvider_GetBitmap(wx.ART_FILE_OPEN))
134 subsizer.Add(self._outputPathBt, (4, 2), flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
135 wx.EVT_BUTTON(self._outputPathBt, self._outputPathBt.GetId(), self.OnBrowsePath)
137 subsizer.Add(wx.StaticText(panel, -1, "Architecture Specified : "), (5, 0), flag=wx.ALIGN_CENTER_VERTICAL)
138 self._archCtrl = wx.ComboBox(panel, -1, value='ALL', choices=['ALL', 'IA32/MSFT', 'IA32/GNU', 'X64/INTEL', 'X64/GNU', 'IPF/MSFT', 'IPF/GNU', 'EBC/INTEL'],
139 style=wx.CB_READONLY)
140 self._archCtrl.Bind(wx.EVT_COMBOBOX, self.OnArchtectureSelectChanged)
141 subsizer.Add(self._archCtrl, (5, 1), (1, 2), flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
142 sizer.Add(subsizer, 0, wx.EXPAND|wx.TOP|wx.BOTTOM|wx.LEFT|wx.RIGHT, 5)
144 sizer6 = wx.BoxSizer(wx.HORIZONTAL)
145 self._modesel = wx.RadioBox(panel, -1, 'Generated Document Mode', majorDimension=2, choices=['CHM', 'HTML'], style=wx.RA_SPECIFY_COLS)
146 self._modesel.SetStringSelection('HTML')
148 self._includeonlysel = wx.CheckBox(panel, -1, 'Only document public include')
150 sizer6.Add(self._modesel, 0 , wx.EXPAND)
151 sizer6.Add(self._includeonlysel, 0, wx.EXPAND|wx.LEFT, 5)
153 sizer.Add(sizer6, 0, wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT, 5)
155 self._generateBt = wx.Button(panel, -1, "Generate Package Document!")
156 self._generateBt.Bind(wx.EVT_BUTTON, self.OnGenerate)
157 sizer.Add(self._generateBt, 0, wx.EXPAND|wx.TOP|wx.LEFT|wx.RIGHT, 5)
159 panel.SetSizer(sizer)
161 panel.SetAutoLayout(True)
162 self.CenterOnScreen()
164 def SaveConfigure(self, name, value):
165 if value ==None or len(value) == 0:
167 config = wx.ConfigBase_Get()
168 oldvalues = config.Read(name, '').split(';')
169 if len(oldvalues) >= 10:
170 oldvalues.remove(oldvalues[0])
171 if value not in oldvalues:
172 oldvalues.append(value)
174 oldvalues.remove(value)
175 oldvalues.append(value)
177 config.Write(name, ';'.join(oldvalues))
179 def GetConfigure(self, name):
180 config = wx.ConfigBase_Get()
181 values = config.Read(name, '').split(';')
188 def OnBrowsePath(self, event):
193 if id == self._packagePathBt.GetId():
194 dlgTitle = "Choose package path:"
195 editctrl = self._packagePathCtrl
197 if os.path.exists(self.GetWorkspace()):
198 startdir = self.GetWorkspace()
199 elif id == self._workspacePathBt.GetId():
200 dlgTitle = "Choose workspace path:"
201 editctrl = self._workspacePathCtrl
202 startdir = editctrl.GetValue()
203 elif id == self._doxygenPathBt.GetId():
205 dlgTitle = "Choose doxygen installation path:"
206 editctrl = self._doxygenPathCtrl
207 startdir = editctrl.GetValue()
208 elif id == self._outputPathBt.GetId():
209 dlgTitle = "Choose document output path:"
210 editctrl = self._outputPathCtrl
211 if os.path.exists(self.GetWorkspace()):
212 startdir = self.GetWorkspace()
213 startdir = editctrl.GetValue()
214 elif id == self._chmPathBt.GetId():
216 dlgTitle = "Choose installation path for Microsoft HTML workshop software"
217 editctrl = self._chmPathCtrl
218 startdir = editctrl.GetValue()
223 dlg = wx.DirDialog(self, dlgTitle, defaultPath=startdir)
225 dlg = wx.FileDialog(self, dlgTitle, defaultDir=startdir)
227 if dlg.ShowModal() == wx.ID_OK:
228 editctrl.SetValue(dlg.GetPath())
231 def OnArchtectureSelectChanged(self, event):
233 selarch = self._archCtrl.GetValue()
235 str += 'MDE_CPU_IA32 MDE_CPU_X64 MDE_CPU_EBC MDE_CPU_IPF _MSC_EXTENSIONS __GNUC__ __INTEL_COMPILER'
236 elif selarch == 'IA32/MSFT':
237 str += 'MDE_CPU_IA32 _MSC_EXTENSIONS'
238 elif selarch == 'IA32/GNU':
239 str += 'MDE_CPU_IA32 __GNUC__'
240 elif selarch == 'X64/MSFT':
241 str += 'MDE_CPU_X64 _MSC_EXTENSIONS'
242 elif selarch == 'X64/GNU':
243 str += 'MDE_CPU_X64 __GNUC__'
244 elif selarch == 'IPF/MSFT':
245 str += 'MDE_CPU_IPF _MSC_EXTENSIONS'
246 elif selarch == 'IPF/GNU':
247 str += 'MDE_CPU_IPF __GNUC__'
248 elif selarch == 'EBC/INTEL':
249 str += 'MDE_CPU_EBC __INTEL_COMPILER'
251 str += ' ASM_PFX= OPTIONAL= '
253 def OnMacroText(self, event):
255 selarch = self._archCtrl.GetValue()
257 str += 'MDE_CPU_IA32 MDE_CPU_X64 MDE_CPU_EBC MDE_CPU_IPF _MSC_EXTENSIONS __GNUC__ __INTEL_COMPILER'
258 elif selarch == 'IA32/MSFT':
259 str += 'MDE_CPU_IA32 _MSC_EXTENSIONS'
260 elif selarch == 'IA32/GNU':
261 str += 'MDE_CPU_IA32 __GNUC__'
262 elif selarch == 'X64/MSFT':
263 str += 'MDE_CPU_X64 _MSC_EXTENSIONS'
264 elif selarch == 'X64/GNU':
265 str += 'MDE_CPU_X64 __GNUC__'
266 elif selarch == 'IPF/MSFT':
267 str += 'MDE_CPU_IPF _MSC_EXTENSIONS'
268 elif selarch == 'IPF/GNU':
269 str += 'MDE_CPU_IPF __GNUC__'
270 elif selarch == 'EBC/INTEL':
271 str += 'MDE_CPU_EBC __INTEL_COMPILER'
273 str += ' ASM_PFX= OPTIONAL= '
275 def OnGenerate(self, event):
276 if not self.CheckInput(): return
278 dlg = ProgressDialog(self)
282 def CheckInput(self):
283 pPath = self.GetPackagePath()
284 wPath = self.GetWorkspace()
285 dPath = self.GetDoxygenToolPath()
286 cPath = self.GetChmToolPath()
287 oPath = self.GetOutputPath()
289 if len(wPath) == 0 or not os.path.exists(wPath):
290 self._Error("Please input existing workspace path!")
293 self.SaveConfigure('WorkspacePath', wPath)
295 if len(pPath) == 0 or not os.path.exists(pPath) or not pPath.lower().endswith('.dec'):
296 self._Error("Please input existing package file location!")
298 elif pPath.lower().find(wPath.lower()) == -1:
299 self._Error("Package patch should starts with workspace path, such as if workspace path is c:\\edk2, package patch could be c:\\edk2\MdePkg")
302 self.SaveConfigure('PackagePath', pPath)
304 if len(dPath) == 0 or not os.path.exists(dPath):
305 self._Error("Can not find doxygen tool from path %s! Please download it from www.stack.nl/~dimitri/doxygen/download.html" % dPath)
308 self.SaveConfigure('DoxygenPath', dPath)
310 if self._modesel.GetStringSelection() == 'CHM':
311 if (len(cPath) == 0 or not os.path.exists(cPath)):
312 self._Error("You select CHM mode to generate document, but can not find software of Microsoft HTML Help Workshop.\nPlease\
313 download it from http://www.microsoft.com/downloads/details.aspx?FamilyID=00535334-c8a6-452f-9aa0-d597d16580cc&displaylang=en\n\
317 self.SaveConfigure('CHMPath', cPath)
320 self._Error("You must specific document output path")
323 self.SaveConfigure('OutputPath', oPath)
325 if os.path.exists(oPath):
326 # add checking whether there is old doxygen config file here
327 files = os.listdir(oPath)
329 if os.path.isfile(os.path.join(oPath,file)):
330 basename, ext = os.path.splitext(file)
331 if ext.lower() == '.doxygen_config':
332 dlg = GMD.GenericMessageDialog(self, "Existing doxygen document in output directory will be overwritten\n, Are you sure?",
333 "Info", wx.ICON_WARNING|wx.YES_NO)
334 if dlg.ShowModal() == wx.ID_YES:
342 self._Error("Fail to create output directory, please select another output directory!")
347 def _Error(self, message):
348 dlg = GMD.GenericMessageDialog(self, message,
349 "Error", wx.ICON_ERROR|wx.OK)
353 def GetWorkspace(self):
354 return os.path.normpath(self._workspacePathCtrl.GetValue())
356 def GetPackagePath(self):
357 return os.path.normpath(self._packagePathCtrl.GetValue())
359 def GetOutputPath(self):
360 return os.path.normpath(self._outputPathCtrl.GetValue())
362 def GetDoxygenToolPath(self):
363 return os.path.normpath(self._doxygenPathCtrl.GetValue())
365 def GetChmToolPath(self):
366 return os.path.normpath(self._chmPathCtrl.GetValue())
368 def GetDocumentMode(self):
369 return self._modesel.GetStringSelection()
371 def GetArchitecture(self):
372 value = self._archCtrl.GetValue()
373 return value.split('/')[0]
375 def GetToolTag(self):
376 value = self._archCtrl.GetValue()
379 return value.split('/')[1]
381 def GetIsOnlyDocumentInclude(self):
382 return self._includeonlysel.IsChecked()
384 class ProgressDialog(wx.Dialog):
385 def __init__(self, parent, id=wx.ID_ANY):
386 title = "Generate Document for " + parent.GetPackagePath()
387 wx.Dialog.__init__(self, parent, id, title=title, style=wx.CAPTION, size=(600, 300))
389 sizer = wx.BoxSizer(wx.VERTICAL)
390 self._textCtrl = wx.StaticText(self, -1, "Start launching!")
391 self._gaugeCtrl = wx.Gauge(self, -1, 100, size=(-1, 10))
392 self._resultCtrl = wx.stc.StyledTextCtrl(self, -1)
393 self._closeBt = wx.Button(self, -1, "Close")
394 self._gotoOuputBt = wx.Button(self, -1, "Goto Output")
397 self._resultCtrl.SetMarginWidth(0, 0)
398 self._resultCtrl.SetMarginWidth(1, 0)
399 self._resultCtrl.SetMarginWidth(2, 0)
401 sizer.Add(self._textCtrl, 0, wx.EXPAND|wx.LEFT|wx.TOP|wx.RIGHT, 5)
402 sizer.Add(self._gaugeCtrl, 0, wx.EXPAND|wx.LEFT|wx.TOP|wx.RIGHT, 5)
403 sizer.Add(self._resultCtrl, 1, wx.EXPAND|wx.LEFT|wx.TOP|wx.RIGHT, 5)
404 btsizer = wx.BoxSizer(wx.HORIZONTAL)
405 btsizer.Add(self._gotoOuputBt, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.LEFT|wx.TOP|wx.LEFT|wx.BOTTOM, 5)
406 btsizer.Add(self._closeBt, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.LEFT|wx.TOP|wx.LEFT|wx.BOTTOM, 5)
407 sizer.Add(btsizer, 0, wx.ALIGN_CENTER_HORIZONTAL)
410 self.CenterOnScreen()
413 self._logger = logging.getLogger('')
414 self._loghandle = ResultHandler(self)
415 logging.getLogger('edk').addHandler(self._loghandle)
416 logging.getLogger('').addHandler(self._loghandle)
417 logging.getLogger('app').addHandler(self._loghandle)
419 wx.EVT_BUTTON(self._closeBt, self._closeBt.GetId(), self.OnButtonClose)
420 wx.EVT_UPDATE_UI(self, self._closeBt.GetId(), self.OnUpdateCloseButton)
421 wx.EVT_BUTTON(self._gotoOuputBt, self._gotoOuputBt.GetId(), self.OnGotoOutput)
422 EVT_LOG(self, self.OnPostLog)
429 self._inputThread = None
430 self._errorThread = None
434 wx.CallAfter(self.GenerateAction)
436 def OnUpdateCloseButton(self, event):
437 self._closeBt.Enable(not self._isBusy)
440 def OnButtonClose(self, event):
442 self._InfoDialog("Please don't close in progressing...")
445 if self._process != None:
446 self._process.CloseOutput()
448 if self._inputThread:
449 self._inputThread.Terminate()
450 if self._errorThread:
451 self._errorThread.Terminate()
453 if self._pid != None:
454 wx.Process.Kill(self._pid, wx.SIGKILL, wx.KILL_CHILDREN)
456 logging.getLogger('edk').removeHandler(self._loghandle)
457 logging.getLogger('').removeHandler(self._loghandle)
458 logging.getLogger('app').removeHandler(self._loghandle)
460 if self._pObj != None:
465 def OnGotoOutput(self, event):
466 output = self.GetParent().GetOutputPath()
467 if os.path.exists(output):
468 if wx.Platform == '__WXMSW__':
469 os.startfile(self.GetParent().GetOutputPath())
472 webbrowser.open(self.GetParent().GetOutputPath())
474 self._ErrorDialog("Output directory does not exist!")
476 def _ErrorDialog(self, message):
477 dlg = GMD.GenericMessageDialog(self, message,
478 "Error", wx.ICON_ERROR|wx.OK)
482 def _InfoDialog(self, message):
483 dlg = GMD.GenericMessageDialog(self, message,
484 "Info", wx.ICON_INFORMATION|wx.OK)
488 def _LogStep(self, index, message):
489 stepstr = "Step %d: %s" % (index, message)
490 self._textCtrl.SetLabel(stepstr)
491 self.LogMessage(os.linesep + stepstr + os.linesep)
492 self._gaugeCtrl.SetValue(index * 100 / 6 )
494 def OnPostLog(self, event):
495 self.LogMessage(event.message)
497 def GenerateAction(self):
498 self._LogStep(1, "Create Package Object Model")
499 wsPath = self.GetParent().GetWorkspace()
500 pkPath = self.GetParent().GetPackagePath()[len(wsPath) + 1:]
503 pObj = baseobject.Package(None, self.GetParent().GetWorkspace())
506 self._ErrorDialog("Fail to create package object model! Please check log.txt under this application folder!")
511 self.LogMessage(str(pObj.GetPcds()))
513 self._LogStep(2, "Preprocess and Generate Doxygen Config File")
515 action = doxygengen.PackageDocumentAction(self.GetParent().GetDoxygenToolPath(),
516 self.GetParent().GetChmToolPath(),
517 self.GetParent().GetOutputPath(),
519 self.GetParent().GetDocumentMode(),
521 self.GetParent().GetArchitecture(),
522 self.GetParent().GetToolTag(),
523 self.GetParent().GetIsOnlyDocumentInclude(),
526 self._ErrorDialog("Fail to preprocess! Please check log.txt under this application folder!")
530 action.RegisterCallbackDoxygenProcess(self.CreateDoxygeProcess)
533 if not action.Generate():
535 self.LogMessage("Fail to generate package document! Please check log.txt under this application folder!", 'error')
538 message = traceback.format_exception(*sys.exc_info())
539 logging.getLogger('').error(''.join(message))
541 self._ErrorDialog("Fail to generate package document! Please check log.txt under this application folder!")
543 def LogMessage(self, message, level='info'):
544 self._resultCtrl.DocumentEnd()
545 self._resultCtrl.SetReadOnly(False)
546 self._resultCtrl.AppendText(message)
547 self._resultCtrl.Home()
548 self._resultCtrl.Home()
549 self._resultCtrl.SetReadOnly(True)
551 wx.GetApp().GetLogger().error(message)
553 def CreateDoxygeProcess(self, doxPath, configFile):
554 self._LogStep(3, "Launch Doxygen Tool and Generate Package Document")
556 cmd = '"%s" %s' % (doxPath, configFile)
558 self._process = DoxygenProcess()
559 self._process.SetParent(self)
560 self._process.Redirect()
561 self._pid = wx.Execute(cmd, wx.EXEC_ASYNC, self._process)
562 self._input = self._process.GetInputStream()
563 self._output = self._process.GetOutputStream()
564 self._error = self._process.GetErrorStream()
566 self._ErrorDialog('Fail to launch doxygen cmd %s! Please check log.txt under this application folder!' % cmd)
570 self._inputThread = MonitorThread(self._input, self.LogMessage)
571 self._errorThread = MonitorThread(self._error, self.LogMessage)
572 self._inputThread.start()
573 self._errorThread.start()
576 def OnTerminateDoxygenProcess(self):
577 if self._inputThread:
578 self._inputThread.Terminate()
579 self._inputThread = None
580 if self._errorThread:
581 self._errorThread.Terminate()
582 self._errorThread = None
585 while self._error.CanRead():
586 text = self._error.read()
587 self.LogMessage(text)
590 while self._input.CanRead():
591 text = self._input.read()
592 self.LogMessage(text)
593 self._process.Detach()
595 self._process.CloseOutput()
601 if self.GetParent().GetDocumentMode().lower() == 'chm':
602 hhcfile = os.path.join(self.GetParent().GetOutputPath(), 'html', 'index.hhc')
603 hhpfile = os.path.join(self.GetParent().GetOutputPath(), 'html', 'index.hhp')
604 self.FixDecDoxygenFileLink(hhcfile, None)
605 if not self.CreateCHMProcess(self.GetParent().GetChmToolPath(), hhpfile):
606 self._ErrorDialog("Fail to Create %s process for %s" % (self.GetParent().GetChmToolPath(), hhpfile))
609 self._LogStep(6, "Finished Document Generation!")
611 indexpath = os.path.realpath(os.path.join(self.GetParent().GetOutputPath(), 'html', 'index.html'))
612 if wx.Platform == '__WXMSW__':
613 os.startfile(indexpath)
616 webbrowser.open(indexpath)
618 self._InfoDialog('Success create HTML doxgen document %s' % indexpath)
620 def CreateCHMProcess(self, chmPath, hhpfile):
621 self.LogMessage(" >>>>>> Start Microsoft HTML workshop process...Zzz...\n")
622 cmd = '"%s" %s' % (chmPath, hhpfile)
624 self._process = CHMProcess()
625 self._process.SetParent(self)
626 self._process.Redirect()
627 self._pid = wx.Execute(cmd, wx.EXEC_ASYNC, self._process)
628 self._input = self._process.GetInputStream()
629 self._output = self._process.GetOutputStream()
630 self._error = self._process.GetErrorStream()
632 self.LogMessage('\nFail to launch hhp cmd %s!\n' % cmd)
635 self._inputThread = MonitorThread(self._input, self.LogMessage)
636 self._errorThread = MonitorThread(self._error, self.LogMessage)
637 self._inputThread.start()
638 self._errorThread.start()
641 def OnTerminateCHMProcess(self):
642 if self._inputThread:
643 self._inputThread.Terminate()
644 self._inputThread = None
645 if self._errorThread:
646 self._errorThread.Terminate()
647 self._errorThread = None
650 while self._error.CanRead():
651 text = self._error.read()
652 self.LogMessage(text)
654 while self._input.CanRead():
655 text = self._input.read()
656 self.LogMessage(text)
657 self._process.Detach()
659 self._process.CloseOutput()
663 indexpath = os.path.realpath(os.path.join(self.GetParent().GetOutputPath(), 'html', 'index.chm'))
664 if os.path.exists(indexpath):
665 if wx.Platform == '__WXMSW__':
666 os.startfile(indexpath)
669 webbrowser.open(indexpath)
671 self._LogStep(6, "Finished Document Generation!")
672 self.LogMessage('\nSuccess create CHM doxgen document %s\n' % indexpath)
673 self._InfoDialog('Success create CHM doxgen document %s' % indexpath)
675 def DocumentFixup(self):
676 # find BASE_LIBRARY_JUMP_BUFFER structure reference page
677 self._LogStep(4, "Fixup Package Document!")
678 self.LogMessage('\n >>> Start fixup document \n')
680 for root, dirs, files in os.walk(os.path.join(self.GetParent().GetOutputPath(), 'html')):
682 if dir.lower() in ['.svn', '_svn', 'cvs']:
686 if not file.lower().endswith('.html'): continue
687 fullpath = os.path.join(self.GetParent().GetOutputPath(), root, file)
689 f = open(fullpath, 'r')
693 self.LogMessage('\nFail to open file %s\n' % fullpath)
695 if text.find('BASE_LIBRARY_JUMP_BUFFER Struct Reference') != -1 and self.GetParent().GetArchitecture() == 'ALL':
696 self.FixPageBASE_LIBRARY_JUMP_BUFFER(fullpath, text)
697 if text.find('MdePkg/Include/Library/BaseLib.h File Reference') != -1 and self.GetParent().GetArchitecture() == 'ALL':
698 self.FixPageBaseLib(fullpath, text)
699 if text.find('IA32_IDT_GATE_DESCRIPTOR Union Reference') != -1 and self.GetParent().GetArchitecture() == 'ALL':
700 self.FixPageIA32_IDT_GATE_DESCRIPTOR(fullpath, text)
701 if text.find('MdePkg/Include/Library/UefiDriverEntryPoint.h File Reference') != -1:
702 self.FixPageUefiDriverEntryPoint(fullpath, text)
703 if text.find('MdePkg/Include/Library/UefiApplicationEntryPoint.h File Reference') != -1:
704 self.FixPageUefiApplicationEntryPoint(fullpath, text)
705 if text.lower().find('.s.dox') != -1 or \
706 text.lower().find('.asm.dox') != -1 or \
707 text.lower().find('.uni.dox') != -1:
708 self.FixDoxFileLink(fullpath, text)
710 self.RemoveFileList()
711 self.LogMessage(' >>> Finish all document fixing up! \n')
713 def RemoveFileList(self):
714 path_html = os.path.join(self.GetParent().GetOutputPath(), "html", "tree.html")
715 path_chm = os.path.join(self.GetParent().GetOutputPath(), "html", "index.hhc")
716 if os.path.exists(path_html):
717 self.LogMessage(' >>>Remove FileList item from generated HTML document.\n');
719 f = open (path_html, "r")
720 lines = f.readlines()
723 for index in xrange(len(lines)):
724 if lines[index].find('<a class="el" href="files.html" target="basefrm">File List</a>') != -1:
725 lines[index] = "<!-- %s" % lines[index]
729 if lines[index].find('</div>') != -1:
730 lines[index] = "%s -->" % lines[index]
733 f = open(path_html, "w")
734 f.write("".join(lines))
737 self.LogMessage (' !!!Can not found FileList item in HTML document!\n')
739 if os.path.exists(path_chm):
740 self.LogMessage(" >>>Warning: Can not remove FileList for CHM files!\n");
742 self.LogMessage(' >>>Remove FileList item from generated CHM document!\n');
744 f = open (path_chm, "r")
745 lines = f.readlines()
748 for index in xrange(len(lines)):
750 if lines[index].find('<param name="Local" value="files.html">') != -1:
751 lines[index] = '<!-- %s' % lines[index]
755 if lines[index].find('</UL>') != -1:
756 lines[index] = '%s -->\n' % lines[index].rstrip()
759 f = open(path_chm, "w")
760 f.write("".join(lines))
765 self.LogMessage(' !!!Can not found the FileList item in CHM document!')
767 def FixPageBaseLib(self, path, text):
768 self.LogMessage(' >>> Fixup BaseLib file page at file %s \n' % path)
769 lines = text.split('\n')
770 lastBaseJumpIndex = -1
771 lastIdtGateDescriptor = -1
772 for index in range(len(lines) - 1, -1, -1):
774 if line.strip() == '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 4 </td>':
775 lines[index] = '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 4 [IA32] </td>'
776 if line.strip() == '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 0x10 </td>':
777 lines[index] = '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 0x10 [IPF] </td>'
778 if line.strip() == '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 8 </td>':
779 lines[index] = '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT 9 [EBC, x64] </td>'
780 if line.find('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 4') != -1:
781 lines[index] = lines[index].replace('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 4',
782 'BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 4 [IA32]')
783 if line.find('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 0x10') != -1:
784 lines[index] = lines[index].replace('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 0x10',
785 'BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 0x10 [IPF]')
786 if line.find('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 8') != -1:
787 lines[index] = lines[index].replace('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 8',
788 'BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a> 8 [x64, EBC]')
789 if line.find('>BASE_LIBRARY_JUMP_BUFFER</a>') != -1:
790 if lastBaseJumpIndex != -1:
791 del lines[lastBaseJumpIndex]
792 lastBaseJumpIndex = index
793 if line.find('>IA32_IDT_GATE_DESCRIPTOR</a></td>') != -1:
794 if lastIdtGateDescriptor != -1:
795 del lines[lastIdtGateDescriptor]
796 lastIdtGateDescriptor = index
799 f.write('\n'.join(lines))
803 self.LogMessage(" <<< Fail to fixup file %s\n" % path)
804 self.LogMessage(" <<< Finish to fixup file %s\n" % path)
806 def FixPageIA32_IDT_GATE_DESCRIPTOR(self, path, text):
807 self.LogMessage(' >>> Fixup structure reference IA32_IDT_GATE_DESCRIPTOR at file %s \n' % path)
808 lines = text.split('\n')
809 for index in range(len(lines) - 1, -1, -1):
810 line = lines[index].strip()
811 if line.find('struct {</td>') != -1 and lines[index - 2].find('>Uint64</a></td>') != -1:
812 lines.insert(index, '<tr><td colspan="2"><br><h2>Data Fields For X64</h2></td></tr>')
813 if line.find('struct {</td>') != -1 and lines[index - 1].find('Data Fields') != -1:
814 lines.insert(index, '<tr><td colspan="2"><br><h2>Data Fields For IA32</h2></td></tr>')
817 f.write('\n'.join(lines))
821 self.LogMessage(" <<< Fail to fixup file %s\n" % path)
822 self.LogMessage(" <<< Finish to fixup file %s\n" % path)
824 def FixPageBASE_LIBRARY_JUMP_BUFFER(self, path, text):
825 self.LogMessage(' >>> Fixup structure reference BASE_LIBRARY_JUMP_BUFFER at file %s \n' % path)
826 lines = text.split('\n')
829 for index in range(len(lines) - 1, -1, -1):
831 if line.find('Detailed Description') != -1:
833 if line.startswith('EBC context buffer used by') and lines[index - 1].startswith('x64 context buffer'):
834 lines[index] = "IA32/IPF/X64/" + line
836 if line.startswith("x64 context buffer") or line.startswith('IPF context buffer used by') or \
837 line.startswith('IA32 context buffer used by'):
840 if line.find('>R0</a>') != -1 and not bInDetail:
841 if lines[index - 1] != '<tr><td colspan="2"><br><h2>Data Fields For EBC</h2></td></tr>':
842 lines.insert(index, '<tr><td colspan="2"><br><h2>Data Fields For EBC</h2></td></tr>')
843 if line.find('>Rbx</a>') != -1 and not bInDetail:
844 if lines[index - 1] != '<tr><td colspan="2"><br><h2>Data Fields For X64</h2></td></tr>':
845 lines.insert(index, '<tr><td colspan="2"><br><h2>Data Fields For X64</h2></td></tr>')
846 if line.find('>F2</a>') != -1 and not bInDetail:
847 if lines[index - 1] != '<tr><td colspan="2"><br><h2>Data Fields For IPF</h2></td></tr>':
848 lines.insert(index, '<tr><td colspan="2"><br><h2>Data Fields For IPF</h2></td></tr>')
849 if line.find('>Ebx</a>') != -1 and not bInDetail:
850 if lines[index - 1] != '<tr><td colspan="2"><br><h2>Data Fields For IA32</h2></td></tr>':
851 lines.insert(index, '<tr><td colspan="2"><br><h2>Data Fields For IA32</h2></td></tr>')
854 f.write('\n'.join(lines))
858 self.LogMessage(" <<< Fail to fixup file %s" % path)
859 self.LogMessage(" <<< Finish to fixup file %s\n" % path)
861 def FixPageUefiDriverEntryPoint(self, path, text):
862 self.LogMessage(' >>> Fixup file reference MdePkg/Include/Library/UefiDriverEntryPoint.h at file %s \n' % path)
863 lines = text.split('\n')
864 bInModuleEntry = False
866 ModuleEntryDlCount = 0
867 ModuleEntryDelStart = 0
868 ModuleEntryDelEnd = 0
873 for index in range(len(lines)):
874 line = lines[index].strip()
875 if line.find('EFI_STATUS</a> EFIAPI _ModuleEntryPoint </td>') != -1:
876 bInModuleEntry = True
877 if line.find('EFI_STATUS</a> EFIAPI EfiMain </td>') != -1:
879 if line.startswith('<p>References <a'):
881 ModuleEntryDelEnd = index - 1
882 bInModuleEntry = False
884 EfiMainDelEnd = index - 1
887 if line.startswith('</dl>'):
888 ModuleEntryDlCount = ModuleEntryDlCount + 1
889 if ModuleEntryDlCount == 1:
890 ModuleEntryDelStart = index + 1
892 if line.startswith('</dl>'):
893 EfiMainDlCount = EfiMainDlCount + 1
894 if EfiMainDlCount == 1:
895 EfiMainDelStart = index + 1
897 if EfiMainDelEnd > EfiMainDelStart:
898 for index in range(EfiMainDelEnd, EfiMainDelStart, -1):
900 if ModuleEntryDelEnd > ModuleEntryDelStart:
901 for index in range(ModuleEntryDelEnd, ModuleEntryDelStart, -1):
906 f.write('\n'.join(lines))
910 self.LogMessage(" <<< Fail to fixup file %s" % path)
911 self.LogMessage(" <<< Finish to fixup file %s\n" % path)
913 def FixPageUefiApplicationEntryPoint(self, path, text):
914 self.LogMessage(' >>> Fixup file reference MdePkg/Include/Library/UefiApplicationEntryPoint.h at file %s \n' % path)
915 lines = text.split('\n')
916 bInModuleEntry = False
918 ModuleEntryDlCount = 0
919 ModuleEntryDelStart = 0
920 ModuleEntryDelEnd = 0
925 for index in range(len(lines)):
926 line = lines[index].strip()
927 if line.find('EFI_STATUS</a> EFIAPI _ModuleEntryPoint </td>') != -1:
928 bInModuleEntry = True
929 if line.find('EFI_STATUS</a> EFIAPI EfiMain </td>') != -1:
931 if line.startswith('<p>References <a'):
933 ModuleEntryDelEnd = index - 1
934 bInModuleEntry = False
936 EfiMainDelEnd = index - 1
939 if line.startswith('</dl>'):
940 ModuleEntryDlCount = ModuleEntryDlCount + 1
941 if ModuleEntryDlCount == 1:
942 ModuleEntryDelStart = index + 1
944 if line.startswith('</dl>'):
945 EfiMainDlCount = EfiMainDlCount + 1
946 if EfiMainDlCount == 1:
947 EfiMainDelStart = index + 1
949 if EfiMainDelEnd > EfiMainDelStart:
950 for index in range(EfiMainDelEnd, EfiMainDelStart, -1):
952 if ModuleEntryDelEnd > ModuleEntryDelStart:
953 for index in range(ModuleEntryDelEnd, ModuleEntryDelStart, -1):
958 f.write('\n'.join(lines))
962 self.LogMessage(" <<< Fail to fixup file %s" % path)
963 self.LogMessage(" <<< Finish to fixup file %s\n" % path)
966 def FixDoxFileLink(self, path, text):
967 self.LogMessage(' >>> Fixup .dox postfix for file %s \n' % path)
973 self.LogMessage (" <<<Fail to open file %s" % path)
975 text = text.replace ('.s.dox', '.s')
976 text = text.replace ('.S.dox', '.S')
977 text = text.replace ('.asm.dox', '.asm')
978 text = text.replace ('.Asm.dox', '.Asm')
979 text = text.replace ('.uni.dox', '.uni')
980 text = text.replace ('.Uni.dox', '.Uni')
986 self.LogMessage (" <<<Fail to fixup file %s" % path)
988 self.LogMessage(' >>> Finish to fixup .dox postfix for file %s \n' % path)
990 def FixDecDoxygenFileLink(self, path, text):
991 self.LogMessage(' >>> Fixup .decdoxygen postfix for file %s \n' % path)
994 lines = fd.readlines()
997 self.LogMessage (" <<<Fail to open file %s" % path)
1000 if line.find('.decdoxygen') != -1:
1004 fd = open(path, 'w')
1005 fd.write("".join(lines))
1007 except Exception, e:
1008 self.LogMessage (" <<<Fail to fixup file %s" % path)
1010 self.LogMessage(' >>> Finish to fixup .decdoxygen postfix for file %s \n' % path)
1013 class MonitorThread(threading.Thread):
1014 def __init__(self, pipe, callback):
1015 threading.Thread.__init__(self)
1017 self._callback = callback
1018 self._isCancel = False
1021 while (not self._isCancel):
1023 if self._pipe.LastRead() == 0:
1025 text = self._pipe.read()
1026 if len(text.strip()) != 0:
1027 wx.GetApp().ForegroundProcess(self._callback, (text,))
1029 def Terminate(self):
1031 self._isCancel = True
1033 class DoxygenProcess(wx.Process):
1034 def OnTerminate(self, id, status):
1035 self._parent.OnTerminateDoxygenProcess()
1037 def SetParent(self, parent):
1038 self._parent = parent
1040 class CHMProcess(wx.Process):
1041 def OnTerminate(self, id, status):
1042 self._parent.OnTerminateCHMProcess()
1044 def SetParent(self, parent):
1045 self._parent = parent
1047 class ResultHandler:
1048 def __init__(self, parent):
1049 self._parent = parent
1052 def emit(self, record):
1053 self._parent.LogMessage(record)
1055 def handle(self, record):
1056 wx.PostEvent(self._parent, LogEvent(message=record.getMessage()))
1064 if __name__ == '__main__':
1065 app = PackageDocApp(redirect=False)