699be5068a107b2fe705833c3130896884060637
[efi/basetools/.git] / Source / Python / build / build.py
1 ## @file\r
2 # build a platform or a module\r
3 #\r
4 #  Copyright (c) 2007 - 2010, Intel Corporation\r
5 #\r
6 #  All rights reserved. This program and the accompanying materials\r
7 #  are licensed and made available under the terms and conditions of the BSD License\r
8 #  which accompanies this distribution.  The full text of the license may be found at\r
9 #  http://opensource.org/licenses/bsd-license.php\r
10 #\r
11 #  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12 #  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13 #\r
14 \r
15 ##\r
16 # Import Modules\r
17 #\r
18 import os\r
19 import re\r
20 import sys\r
21 import glob\r
22 import time\r
23 import platform\r
24 import traceback\r
25 import types\r
26 if sys.platform == "win32":\r
27     from _subprocess import CreateProcess\r
28 \r
29 from threading import *\r
30 from optparse import OptionParser\r
31 from subprocess import *\r
32 from Common import Misc as Utils\r
33 \r
34 from Common.TargetTxtClassObject import *\r
35 from Common.ToolDefClassObject import *\r
36 from Common.DataType import *\r
37 from AutoGen.AutoGen import *\r
38 from Common.BuildToolError import *\r
39 from Workspace.WorkspaceDatabase import *\r
40 \r
41 from BuildReport import BuildReport\r
42 \r
43 import Common.EdkLogger\r
44 import Common.GlobalData as GlobalData\r
45 \r
46 # Version and Copyright\r
47 VersionNumber = "0.5"\r
48 __version__ = "%prog Version " + VersionNumber\r
49 __copyright__ = "Copyright (c) 2007 - 2010, Intel Corporation  All rights reserved."\r
50 \r
51 ## standard targets of build command\r
52 gSupportedTarget = ['all', 'genc', 'genmake', 'modules', 'libraries', 'fds', 'clean', 'cleanall', 'cleanlib', 'run']\r
53 \r
54 ## build configuration file\r
55 gBuildConfiguration = "Conf/target.txt"\r
56 gBuildCacheDir = "Conf/.cache"\r
57 gToolsDefinition = "Conf/tools_def.txt"\r
58 \r
59 ## Check environment PATH variable to make sure the specified tool is found\r
60 #\r
61 #   If the tool is found in the PATH, then True is returned\r
62 #   Otherwise, False is returned\r
63 #\r
64 def IsToolInPath(tool):\r
65     if os.environ.has_key('PATHEXT'):\r
66         extns = os.environ['PATHEXT'].split(os.path.pathsep)\r
67     else:\r
68         extns = ('',)\r
69     for pathDir in os.environ['PATH'].split(os.path.pathsep):\r
70         for ext in extns:\r
71             if os.path.exists(os.path.join(pathDir, tool + ext)):\r
72                 return True\r
73     return False\r
74 \r
75 ## Check environment variables\r
76 #\r
77 #  Check environment variables that must be set for build. Currently they are\r
78 #\r
79 #   WORKSPACE           The directory all packages/platforms start from\r
80 #   EDK_TOOLS_PATH      The directory contains all tools needed by the build\r
81 #   PATH                $(EDK_TOOLS_PATH)/Bin/<sys> must be set in PATH\r
82 #\r
83 #   If any of above environment variable is not set or has error, the build\r
84 #   will be broken.\r
85 #\r
86 def CheckEnvVariable():\r
87     # check WORKSPACE\r
88     if "WORKSPACE" not in os.environ:\r
89         EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",\r
90                         ExtraData="WORKSPACE")\r
91 \r
92     WorkspaceDir = os.path.normcase(os.path.normpath(os.environ["WORKSPACE"]))\r
93     if not os.path.exists(WorkspaceDir):\r
94         EdkLogger.error("build", FILE_NOT_FOUND, "WORKSPACE doesn't exist", ExtraData="%s" % WorkspaceDir)\r
95     elif ' ' in WorkspaceDir:\r
96         EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in WORKSPACE path",\r
97                         ExtraData=WorkspaceDir)\r
98     os.environ["WORKSPACE"] = WorkspaceDir\r
99 \r
100     #\r
101     # Check EFI_SOURCE (R8 build convention). EDK_SOURCE will always point to ECP\r
102     #\r
103     os.environ["ECP_SOURCE"] = os.path.join(WorkspaceDir, GlobalData.gEdkCompatibilityPkg)\r
104     if "EFI_SOURCE" not in os.environ:\r
105         os.environ["EFI_SOURCE"] = os.environ["ECP_SOURCE"]\r
106     if "EDK_SOURCE" not in os.environ:\r
107         os.environ["EDK_SOURCE"] = os.environ["ECP_SOURCE"]\r
108 \r
109     #\r
110     # Unify case of characters on case-insensitive systems\r
111     #\r
112     EfiSourceDir = os.path.normcase(os.path.normpath(os.environ["EFI_SOURCE"]))\r
113     EdkSourceDir = os.path.normcase(os.path.normpath(os.environ["EDK_SOURCE"]))\r
114     EcpSourceDir = os.path.normcase(os.path.normpath(os.environ["ECP_SOURCE"]))\r
115  \r
116     os.environ["EFI_SOURCE"] = EfiSourceDir\r
117     os.environ["EDK_SOURCE"] = EdkSourceDir\r
118     os.environ["ECP_SOURCE"] = EcpSourceDir\r
119     os.environ["EDK_TOOLS_PATH"] = os.path.normcase(os.environ["EDK_TOOLS_PATH"])\r
120     \r
121     if not os.path.exists(EcpSourceDir):\r
122         EdkLogger.verbose("ECP_SOURCE = %s doesn't exist. R8 modules could not be built." % EcpSourceDir)\r
123     elif ' ' in EcpSourceDir:\r
124         EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in ECP_SOURCE path",\r
125                         ExtraData=EcpSourceDir)\r
126     if not os.path.exists(EdkSourceDir):\r
127         if EdkSourceDir == EcpSourceDir:\r
128             EdkLogger.verbose("EDK_SOURCE = %s doesn't exist. R8 modules could not be built." % EdkSourceDir)\r
129         else:\r
130             EdkLogger.error("build", PARAMETER_INVALID, "EDK_SOURCE does not exist",\r
131                             ExtraData=EdkSourceDir)\r
132     elif ' ' in EdkSourceDir:\r
133         EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in EDK_SOURCE path",\r
134                         ExtraData=EdkSourceDir)\r
135     if not os.path.exists(EfiSourceDir):\r
136         if EfiSourceDir == EcpSourceDir:\r
137             EdkLogger.verbose("EFI_SOURCE = %s doesn't exist. R8 modules could not be built." % EfiSourceDir)\r
138         else:\r
139             EdkLogger.error("build", PARAMETER_INVALID, "EFI_SOURCE does not exist",\r
140                             ExtraData=EfiSourceDir)\r
141     elif ' ' in EfiSourceDir:\r
142         EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in EFI_SOURCE path",\r
143                         ExtraData=EfiSourceDir)\r
144 \r
145     # change absolute path to relative path to WORKSPACE\r
146     if EfiSourceDir.upper().find(WorkspaceDir.upper()) != 0:\r
147         EdkLogger.error("build", PARAMETER_INVALID, "EFI_SOURCE is not under WORKSPACE",\r
148                         ExtraData="WORKSPACE = %s\n    EFI_SOURCE = %s" % (WorkspaceDir, EfiSourceDir))\r
149     if EdkSourceDir.upper().find(WorkspaceDir.upper()) != 0:\r
150         EdkLogger.error("build", PARAMETER_INVALID, "EDK_SOURCE is not under WORKSPACE",\r
151                         ExtraData="WORKSPACE = %s\n    EDK_SOURCE = %s" % (WorkspaceDir, EdkSourceDir))\r
152     if EcpSourceDir.upper().find(WorkspaceDir.upper()) != 0:\r
153         EdkLogger.error("build", PARAMETER_INVALID, "ECP_SOURCE is not under WORKSPACE",\r
154                         ExtraData="WORKSPACE = %s\n    ECP_SOURCE = %s" % (WorkspaceDir, EcpSourceDir))\r
155 \r
156     # check EDK_TOOLS_PATH\r
157     if "EDK_TOOLS_PATH" not in os.environ:\r
158         EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",\r
159                         ExtraData="EDK_TOOLS_PATH")\r
160 \r
161     # check PATH\r
162     if "PATH" not in os.environ:\r
163         EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",\r
164                         ExtraData="PATH")\r
165 \r
166     GlobalData.gWorkspace = WorkspaceDir\r
167     GlobalData.gEfiSource = EfiSourceDir\r
168     GlobalData.gEdkSource = EdkSourceDir\r
169     GlobalData.gEcpSource = EcpSourceDir\r
170 \r
171 ## Get normalized file path\r
172 #\r
173 # Convert the path to be local format, and remove the WORKSPACE path at the\r
174 # beginning if the file path is given in full path.\r
175 #\r
176 # @param  FilePath      File path to be normalized\r
177 # @param  Workspace     Workspace path which the FilePath will be checked against\r
178 #\r
179 # @retval string        The normalized file path\r
180 #\r
181 def NormFile(FilePath, Workspace):\r
182     # check if the path is absolute or relative\r
183     if os.path.isabs(FilePath):\r
184         FileFullPath = os.path.normpath(FilePath)\r
185     else:\r
186         FileFullPath = os.path.normpath(os.path.join(Workspace, FilePath))\r
187 \r
188     # check if the file path exists or not\r
189     if not os.path.isfile(FileFullPath):\r
190         EdkLogger.error("build", FILE_NOT_FOUND, ExtraData="\t%s (Please give file in absolute path or relative to WORKSPACE)"  % FileFullPath)\r
191 \r
192     # remove workspace directory from the beginning part of the file path\r
193     if Workspace[-1] in ["\\", "/"]:\r
194         return FileFullPath[len(Workspace):]\r
195     else:\r
196         return FileFullPath[(len(Workspace) + 1):]\r
197 \r
198 ## Get the output of an external program\r
199 #\r
200 # This is the entrance method of thread reading output of an external program and\r
201 # putting them in STDOUT/STDERR of current program.\r
202 #\r
203 # @param  From      The stream message read from\r
204 # @param  To        The stream message put on\r
205 # @param  ExitFlag  The flag used to indicate stopping reading\r
206 #\r
207 def ReadMessage(From, To, ExitFlag):\r
208     while True:\r
209         # read one line a time\r
210         Line = From.readline()\r
211         # empty string means "end"\r
212         if Line != None and Line != "":\r
213             To(Line.rstrip())\r
214         else:\r
215             break\r
216         if ExitFlag.isSet():\r
217             break\r
218 \r
219 class WinPopen(Popen):\r
220     def _execute_child(self, args, executable, preexec_fn, close_fds,\r
221                        cwd, env, universal_newlines,\r
222                        startupinfo, creationflags, shell,\r
223                        p2cread, p2cwrite,\r
224                        c2pread, c2pwrite,\r
225                        errread, errwrite):\r
226             """Execute program (MS Windows version)"""\r
227 \r
228         if not isinstance(args, types.StringTypes):\r
229             args = list2cmdline(args)\r
230 \r
231             # Start the process\r
232             try:\r
233                 hp, ht, pid, tid = CreateProcess(executable, args,\r
234                                          None, None,\r
235                                          0,\r
236                                          creationflags,\r
237                                          env,\r
238                                          cwd,\r
239                                          None)\r
240             except IOError, e:\r
241                 raise WindowsError(*e.args)\r
242 \r
243             # Retain the process handle, but close the thread handle\r
244             self._child_created = True\r
245             self._handle = hp\r
246             self.pid = pid\r
247             ht.Close()\r
248 \r
249             if p2cread is not None:\r
250                 p2cread.Close()\r
251             if c2pwrite is not None:\r
252                 c2pwrite.Close()\r
253             if errwrite is not None:\r
254                 errwrite.Close()\r
255 \r
256 ## Launch an external program\r
257 #\r
258 # This method will call subprocess.Popen to execute an external program with\r
259 # given options in specified directory. Because of the dead-lock issue during\r
260 # redirecting output of the external program, threads are used to to do the\r
261 # redirection work.\r
262 #\r
263 # @param  Command               A list or string containing the call of the program\r
264 # @param  WorkingDir            The directory in which the program will be running\r
265 #\r
266 def LaunchCommand(Command, WorkingDir):\r
267     # if working directory doesn't exist, Popen() will raise an exception\r
268     if not os.path.isdir(WorkingDir):\r
269         EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=WorkingDir)\r
270 \r
271     Proc = None\r
272     EndOfProcedure = None\r
273     try:\r
274         # launch the command\r
275         if sys.platform == "win32":\r
276           Proc = WinPopen(Command, stdout=PIPE, stderr=PIPE, env=os.environ, cwd=WorkingDir, bufsize=-1)\r
277         else:\r
278           Proc = Popen(Command, stdout=PIPE, stderr=PIPE, env=os.environ, cwd=WorkingDir, bufsize=-1)\r
279 \r
280         # launch two threads to read the STDOUT and STDERR\r
281         EndOfProcedure = Event()\r
282         EndOfProcedure.clear()\r
283         if Proc.stdout:\r
284             StdOutThread = Thread(target=ReadMessage, args=(Proc.stdout, EdkLogger.info, EndOfProcedure))\r
285             StdOutThread.setName("STDOUT-Redirector")\r
286             StdOutThread.setDaemon(False)\r
287             StdOutThread.start()\r
288 \r
289         if Proc.stderr:\r
290             StdErrThread = Thread(target=ReadMessage, args=(Proc.stderr, EdkLogger.quiet, EndOfProcedure))\r
291             StdErrThread.setName("STDERR-Redirector")\r
292             StdErrThread.setDaemon(False)\r
293             StdErrThread.start()\r
294 \r
295         # waiting for program exit\r
296         Proc.wait()\r
297     except: # in case of aborting\r
298         # terminate the threads redirecting the program output\r
299         if EndOfProcedure != None:\r
300             EndOfProcedure.set()\r
301         if Proc == None:\r
302             if type(Command) != type(""):\r
303                 Command = " ".join(Command)\r
304             EdkLogger.error("build", COMMAND_FAILURE, "Failed to start command", ExtraData="%s [%s]" % (Command, WorkingDir))\r
305 \r
306     if Proc.stdout:\r
307         StdOutThread.join()\r
308     if Proc.stderr:\r
309         StdErrThread.join()\r
310 \r
311     # check the return code of the program\r
312     if Proc.returncode != 0:\r
313         if type(Command) != type(""):\r
314             Command = " ".join(Command)\r
315         EdkLogger.error("build", COMMAND_FAILURE, ExtraData="%s [%s]" % (Command, WorkingDir))\r
316 \r
317 ## The smallest unit that can be built in multi-thread build mode\r
318 #\r
319 # This is the base class of build unit. The "Obj" parameter must provide\r
320 # __str__(), __eq__() and __hash__() methods. Otherwise there could be build units\r
321 # missing build.\r
322 #\r
323 # Currently the "Obj" should be only ModuleAutoGen or PlatformAutoGen objects.\r
324 #\r
325 class BuildUnit:\r
326     ## The constructor\r
327     #\r
328     #   @param  self        The object pointer\r
329     #   @param  Obj         The object the build is working on\r
330     #   @param  Target      The build target name, one of gSupportedTarget\r
331     #   @param  Dependency  The BuildUnit(s) which must be completed in advance\r
332     #   @param  WorkingDir  The directory build command starts in\r
333     #\r
334     def __init__(self, Obj, BuildCommand, Target, Dependency, WorkingDir="."):\r
335         self.BuildObject = Obj\r
336         self.Dependency = Dependency\r
337         self.WorkingDir = WorkingDir\r
338         self.Target = Target\r
339         self.BuildCommand = BuildCommand\r
340         if BuildCommand == None or len(BuildCommand) == 0:\r
341             EdkLogger.error("build", OPTION_MISSING, "No build command found for",\r
342                             ExtraData=str(Obj))\r
343 \r
344     ## str() method\r
345     #\r
346     #   It just returns the string representaion of self.BuildObject\r
347     #\r
348     #   @param  self        The object pointer\r
349     #\r
350     def __str__(self):\r
351         return str(self.BuildObject)\r
352 \r
353     ## "==" operator method\r
354     #\r
355     #   It just compares self.BuildObject with "Other". So self.BuildObject must\r
356     #   provide its own __eq__() method.\r
357     #\r
358     #   @param  self        The object pointer\r
359     #   @param  Other       The other BuildUnit object compared to\r
360     #\r
361     def __eq__(self, Other):\r
362         return Other != None and self.BuildObject == Other.BuildObject \\r
363                 and self.BuildObject.Arch == Other.BuildObject.Arch\r
364 \r
365     ## hash() method\r
366     #\r
367     #   It just returns the hash value of self.BuildObject which must be hashable.\r
368     #\r
369     #   @param  self        The object pointer\r
370     #\r
371     def __hash__(self):\r
372         return hash(self.BuildObject) + hash(self.BuildObject.Arch)\r
373 \r
374     def __repr__(self):\r
375         return repr(self.BuildObject)\r
376 \r
377 ## The smallest module unit that can be built by nmake/make command in multi-thread build mode\r
378 #\r
379 # This class is for module build by nmake/make build system. The "Obj" parameter\r
380 # must provide __str__(), __eq__() and __hash__() methods. Otherwise there could\r
381 # be make units missing build.\r
382 #\r
383 # Currently the "Obj" should be only ModuleAutoGen object.\r
384 #\r
385 class ModuleMakeUnit(BuildUnit):\r
386     ## The constructor\r
387     #\r
388     #   @param  self        The object pointer\r
389     #   @param  Obj         The ModuleAutoGen object the build is working on\r
390     #   @param  Target      The build target name, one of gSupportedTarget\r
391     #\r
392     def __init__(self, Obj, Target):\r
393         Dependency = [ModuleMakeUnit(La, Target) for La in Obj.LibraryAutoGenList]\r
394         BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir)\r
395         if Target in [None, "", "all"]:\r
396             self.Target = "tbuild"\r
397 \r
398 ## The smallest platform unit that can be built by nmake/make command in multi-thread build mode\r
399 #\r
400 # This class is for platform build by nmake/make build system. The "Obj" parameter\r
401 # must provide __str__(), __eq__() and __hash__() methods. Otherwise there could\r
402 # be make units missing build.\r
403 #\r
404 # Currently the "Obj" should be only PlatformAutoGen object.\r
405 #\r
406 class PlatformMakeUnit(BuildUnit):\r
407     ## The constructor\r
408     #\r
409     #   @param  self        The object pointer\r
410     #   @param  Obj         The PlatformAutoGen object the build is working on\r
411     #   @param  Target      The build target name, one of gSupportedTarget\r
412     #\r
413     def __init__(self, Obj, Target):\r
414         Dependency = [ModuleMakeUnit(Lib, Target) for Lib in self.BuildObject.LibraryAutoGenList]\r
415         Dependency.extend([ModuleMakeUnit(Mod, Target) for Mod in self.BuildObject.ModuleAutoGenList])\r
416         BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir)\r
417 \r
418 ## The class representing the task of a module build or platform build\r
419 #\r
420 # This class manages the build tasks in multi-thread build mode. Its jobs include\r
421 # scheduling thread running, catching thread error, monitor the thread status, etc.\r
422 #\r
423 class BuildTask:\r
424     # queue for tasks waiting for schedule\r
425     _PendingQueue = sdict()\r
426     _PendingQueueLock = threading.Lock()\r
427 \r
428     # queue for tasks ready for running\r
429     _ReadyQueue = sdict()\r
430     _ReadyQueueLock = threading.Lock()\r
431 \r
432     # queue for run tasks\r
433     _RunningQueue = sdict()\r
434     _RunningQueueLock = threading.Lock()\r
435 \r
436     # queue containing all build tasks, in case duplicate build\r
437     _TaskQueue = sdict()\r
438 \r
439     # flag indicating error occurs in a running thread\r
440     _ErrorFlag = threading.Event()\r
441     _ErrorFlag.clear()\r
442     _ErrorMessage = ""\r
443 \r
444     # BoundedSemaphore object used to control the number of running threads\r
445     _Thread = None\r
446 \r
447     # flag indicating if the scheduler is started or not\r
448     _SchedulerStopped = threading.Event()\r
449     _SchedulerStopped.set()\r
450 \r
451     ## Start the task scheduler thread\r
452     #\r
453     #   @param  MaxThreadNumber     The maximum thread number\r
454     #   @param  ExitFlag            Flag used to end the scheduler\r
455     #\r
456     @staticmethod\r
457     def StartScheduler(MaxThreadNumber, ExitFlag):\r
458         SchedulerThread = Thread(target=BuildTask.Scheduler, args=(MaxThreadNumber, ExitFlag))\r
459         SchedulerThread.setName("Build-Task-Scheduler")\r
460         SchedulerThread.setDaemon(False)\r
461         SchedulerThread.start()\r
462         # wait for the scheduler to be started, especially useful in Linux\r
463         while not BuildTask.IsOnGoing():\r
464             time.sleep(0.01)\r
465 \r
466     ## Scheduler method\r
467     #\r
468     #   @param  MaxThreadNumber     The maximum thread number\r
469     #   @param  ExitFlag            Flag used to end the scheduler\r
470     #\r
471     @staticmethod\r
472     def Scheduler(MaxThreadNumber, ExitFlag):\r
473         BuildTask._SchedulerStopped.clear()\r
474         try:\r
475             # use BoundedSemaphore to control the maximum running threads\r
476             BuildTask._Thread = BoundedSemaphore(MaxThreadNumber)\r
477             #\r
478             # scheduling loop, which will exits when no pending/ready task and\r
479             # indicated to do so, or there's error in running thread\r
480             #\r
481             while (len(BuildTask._PendingQueue) > 0 or len(BuildTask._ReadyQueue) > 0 \\r
482                    or not ExitFlag.isSet()) and not BuildTask._ErrorFlag.isSet():\r
483                 EdkLogger.debug(EdkLogger.DEBUG_8, "Pending Queue (%d), Ready Queue (%d)"\r
484                                 % (len(BuildTask._PendingQueue), len(BuildTask._ReadyQueue)))\r
485 \r
486                 # get all pending tasks\r
487                 BuildTask._PendingQueueLock.acquire()\r
488                 BuildObjectList = BuildTask._PendingQueue.keys()\r
489                 #\r
490                 # check if their dependency is resolved, and if true, move them\r
491                 # into ready queue\r
492                 #\r
493                 for BuildObject in BuildObjectList:\r
494                     Bt = BuildTask._PendingQueue[BuildObject]\r
495                     if Bt.IsReady():\r
496                         BuildTask._ReadyQueue[BuildObject] = BuildTask._PendingQueue.pop(BuildObject)\r
497                 BuildTask._PendingQueueLock.release()\r
498 \r
499                 # launch build thread until the maximum number of threads is reached\r
500                 while not BuildTask._ErrorFlag.isSet():\r
501                     # empty ready queue, do nothing further\r
502                     if len(BuildTask._ReadyQueue) == 0:\r
503                         break\r
504 \r
505                     # wait for active thread(s) exit\r
506                     BuildTask._Thread.acquire(True)\r
507 \r
508                     # start a new build thread\r
509                     Bo = BuildTask._ReadyQueue.keys()[0]\r
510                     Bt = BuildTask._ReadyQueue.pop(Bo)\r
511 \r
512                     # move into running queue\r
513                     BuildTask._RunningQueueLock.acquire()\r
514                     BuildTask._RunningQueue[Bo] = Bt\r
515                     BuildTask._RunningQueueLock.release()\r
516 \r
517                     Bt.Start()\r
518                     # avoid tense loop\r
519                     time.sleep(0.01)\r
520 \r
521                 # avoid tense loop\r
522                 time.sleep(0.01)\r
523 \r
524             # wait for all running threads exit\r
525             if BuildTask._ErrorFlag.isSet():\r
526                 EdkLogger.quiet("\nWaiting for all build threads exit...")\r
527             # while not BuildTask._ErrorFlag.isSet() and \\r
528             while len(BuildTask._RunningQueue) > 0:\r
529                 EdkLogger.verbose("Waiting for thread ending...(%d)" % len(BuildTask._RunningQueue))\r
530                 EdkLogger.debug(EdkLogger.DEBUG_8, "Threads [%s]" % ", ".join([Th.getName() for Th in threading.enumerate()]))\r
531                 # avoid tense loop\r
532                 time.sleep(0.1)\r
533         except BaseException, X:\r
534             #\r
535             # TRICK: hide the output of threads left runing, so that the user can\r
536             #        catch the error message easily\r
537             #\r
538             EdkLogger.SetLevel(EdkLogger.ERROR)\r
539             BuildTask._ErrorFlag.set()\r
540             BuildTask._ErrorMessage = "build thread scheduler error\n\t%s" % str(X)\r
541 \r
542         BuildTask._PendingQueue.clear()\r
543         BuildTask._ReadyQueue.clear()\r
544         BuildTask._RunningQueue.clear()\r
545         BuildTask._TaskQueue.clear()\r
546         BuildTask._SchedulerStopped.set()\r
547 \r
548     ## Wait for all running method exit\r
549     #\r
550     @staticmethod\r
551     def WaitForComplete():\r
552         BuildTask._SchedulerStopped.wait()\r
553 \r
554     ## Check if the scheduler is running or not\r
555     #\r
556     @staticmethod\r
557     def IsOnGoing():\r
558         return not BuildTask._SchedulerStopped.isSet()\r
559 \r
560     ## Abort the build\r
561     @staticmethod\r
562     def Abort():\r
563         if BuildTask.IsOnGoing():\r
564             BuildTask._ErrorFlag.set()\r
565             BuildTask.WaitForComplete()\r
566 \r
567     ## Check if there's error in running thread\r
568     #\r
569     #   Since the main thread cannot catch exceptions in other thread, we have to\r
570     #   use threading.Event to communicate this formation to main thread.\r
571     #\r
572     @staticmethod\r
573     def HasError():\r
574         return BuildTask._ErrorFlag.isSet()\r
575 \r
576     ## Get error message in running thread\r
577     #\r
578     #   Since the main thread cannot catch exceptions in other thread, we have to\r
579     #   use a static variable to communicate this message to main thread.\r
580     #\r
581     @staticmethod\r
582     def GetErrorMessage():\r
583         return BuildTask._ErrorMessage\r
584 \r
585     ## Factory method to create a BuildTask object\r
586     #\r
587     #   This method will check if a module is building or has been built. And if\r
588     #   true, just return the associated BuildTask object in the _TaskQueue. If\r
589     #   not, create and return a new BuildTask object. The new BuildTask object\r
590     #   will be appended to the _PendingQueue for scheduling later.\r
591     #\r
592     #   @param  BuildItem       A BuildUnit object representing a build object\r
593     #   @param  Dependency      The dependent build object of BuildItem\r
594     #\r
595     @staticmethod\r
596     def New(BuildItem, Dependency=None):\r
597         if BuildItem in BuildTask._TaskQueue:\r
598             Bt = BuildTask._TaskQueue[BuildItem]\r
599             return Bt\r
600 \r
601         Bt = BuildTask()\r
602         Bt._Init(BuildItem, Dependency)\r
603         BuildTask._TaskQueue[BuildItem] = Bt\r
604 \r
605         BuildTask._PendingQueueLock.acquire()\r
606         BuildTask._PendingQueue[BuildItem] = Bt\r
607         BuildTask._PendingQueueLock.release()\r
608 \r
609         return Bt\r
610 \r
611     ## The real constructor of BuildTask\r
612     #\r
613     #   @param  BuildItem       A BuildUnit object representing a build object\r
614     #   @param  Dependency      The dependent build object of BuildItem\r
615     #\r
616     def _Init(self, BuildItem, Dependency=None):\r
617         self.BuildItem = BuildItem\r
618 \r
619         self.DependencyList = []\r
620         if Dependency == None:\r
621             Dependency = BuildItem.Dependency\r
622         else:\r
623             Dependency.extend(BuildItem.Dependency)\r
624         self.AddDependency(Dependency)\r
625         # flag indicating build completes, used to avoid unnecessary re-build\r
626         self.CompleteFlag = False\r
627 \r
628     ## Check if all dependent build tasks are completed or not\r
629     #\r
630     def IsReady(self):\r
631         ReadyFlag = True\r
632         for Dep in self.DependencyList:\r
633             if Dep.CompleteFlag == True:\r
634                 continue\r
635             ReadyFlag = False\r
636             break\r
637 \r
638         return ReadyFlag\r
639 \r
640     ## Add dependent build task\r
641     #\r
642     #   @param  Dependency      The list of dependent build objects\r
643     #\r
644     def AddDependency(self, Dependency):\r
645         for Dep in Dependency:\r
646             self.DependencyList.append(BuildTask.New(Dep))    # BuildTask list\r
647 \r
648     ## The thread wrapper of LaunchCommand function\r
649     #\r
650     # @param  Command               A list or string contains the call of the command\r
651     # @param  WorkingDir            The directory in which the program will be running\r
652     #\r
653     def _CommandThread(self, Command, WorkingDir):\r
654         try:\r
655             LaunchCommand(Command, WorkingDir)\r
656             self.CompleteFlag = True\r
657         except:\r
658             #\r
659             # TRICK: hide the output of threads left runing, so that the user can\r
660             #        catch the error message easily\r
661             #\r
662             if not BuildTask._ErrorFlag.isSet():\r
663                 GlobalData.gBuildingModule = "%s [%s, %s, %s]" % (str(self.BuildItem.BuildObject),\r
664                                                                   self.BuildItem.BuildObject.Arch,\r
665                                                                   self.BuildItem.BuildObject.ToolChain,\r
666                                                                   self.BuildItem.BuildObject.BuildTarget\r
667                                                                  )\r
668             EdkLogger.SetLevel(EdkLogger.ERROR)\r
669             BuildTask._ErrorFlag.set()\r
670             BuildTask._ErrorMessage = "%s broken\n    %s [%s]" % \\r
671                                       (threading.currentThread().getName(), Command, WorkingDir)\r
672         # indicate there's a thread is available for another build task\r
673         BuildTask._RunningQueueLock.acquire()\r
674         BuildTask._RunningQueue.pop(self.BuildItem)\r
675         BuildTask._RunningQueueLock.release()\r
676         BuildTask._Thread.release()\r
677 \r
678     ## Start build task thread\r
679     #\r
680     def Start(self):\r
681         EdkLogger.quiet("Building ... %s" % repr(self.BuildItem))\r
682         Command = self.BuildItem.BuildCommand + [self.BuildItem.Target]\r
683         self.BuildTread = Thread(target=self._CommandThread, args=(Command, self.BuildItem.WorkingDir))\r
684         self.BuildTread.setName("build thread")\r
685         self.BuildTread.setDaemon(False)\r
686         self.BuildTread.start()\r
687 \r
688 ## The class implementing the EDK2 build process\r
689 #\r
690 #   The build process includes:\r
691 #       1. Load configuration from target.txt and tools_def.txt in $(WORKSPACE)/Conf\r
692 #       2. Parse DSC file of active platform\r
693 #       3. Parse FDF file if any\r
694 #       4. Establish build database, including parse all other files (module, package)\r
695 #       5. Create AutoGen files (C code file, depex file, makefile) if necessary\r
696 #       6. Call build command\r
697 #\r
698 class Build():\r
699     ## Constructor\r
700     #\r
701     # Constructor will load all necessary configurations, parse platform, modules\r
702     # and packages and the establish a database for AutoGen.\r
703     #\r
704     #   @param  Target              The build command target, one of gSupportedTarget\r
705     #   @param  WorkspaceDir        The directory of workspace\r
706     #   @param  Platform            The DSC file of active platform\r
707     #   @param  Module              The INF file of active module, if any\r
708     #   @param  Arch                The Arch list of platform or module\r
709     #   @param  ToolChain           The name list of toolchain\r
710     #   @param  BuildTarget         The "DEBUG" or "RELEASE" build\r
711     #   @param  FlashDefinition     The FDF file of active platform\r
712     #   @param  FdList=[]           The FD names to be individually built\r
713     #   @param  FvList=[]           The FV names to be individually built\r
714     #   @param  MakefileType        The type of makefile (for MSFT make or GNU make)\r
715     #   @param  SilentMode          Indicate multi-thread build mode\r
716     #   @param  ThreadNumber        The maximum number of thread if in multi-thread build mode\r
717     #   @param  SkipAutoGen         Skip AutoGen step\r
718     #   @param  Reparse             Re-parse all meta files\r
719     #   @param  SkuId               SKU id from command line\r
720     #\r
721     def __init__(self, Target, WorkspaceDir, Platform, Module, Arch, ToolChain,\r
722                  BuildTarget, FlashDefinition, FdList=[], FvList=[],\r
723                  MakefileType="nmake", SilentMode=False, ThreadNumber=2,\r
724                  SkipAutoGen=False, Reparse=False, SkuId=None, \r
725                  ReportFile=None, ReportType=None):\r
726 \r
727         self.WorkspaceDir = WorkspaceDir\r
728         self.Target         = Target\r
729         self.PlatformFile   = Platform\r
730         self.ModuleFile     = Module\r
731         self.ArchList       = Arch\r
732         self.ToolChainList  = ToolChain\r
733         self.BuildTargetList= BuildTarget\r
734         self.Fdf            = FlashDefinition\r
735         self.FdList         = FdList\r
736         self.FvList         = FvList\r
737         self.MakefileType   = MakefileType\r
738         self.SilentMode     = SilentMode\r
739         self.ThreadNumber   = ThreadNumber\r
740         self.SkipAutoGen    = SkipAutoGen\r
741         self.Reparse        = Reparse\r
742         self.SkuId          = SkuId\r
743         self.SpawnMode      = True\r
744         self.BuildReport    = BuildReport(ReportFile, ReportType)\r
745         self.TargetTxt      = TargetTxtClassObject()\r
746         self.ToolDef        = ToolDefClassObject()\r
747         self.Db             = WorkspaceDatabase(None, GlobalData.gGlobalDefines, self.Reparse)\r
748         #self.Db             = WorkspaceDatabase(None, {}, self.Reparse)\r
749         self.BuildDatabase  = self.Db.BuildObject\r
750         self.Platform       = None\r
751 \r
752         # print dot charater during doing some time-consuming work\r
753         self.Progress = Utils.Progressor()\r
754 \r
755         # parse target.txt, tools_def.txt, and platform file\r
756         #self.RestoreBuildData()\r
757         self.LoadConfiguration()\r
758         self.InitBuild()\r
759 \r
760         # print current build environment and configuration\r
761         EdkLogger.quiet("%-24s = %s" % ("WORKSPACE", os.environ["WORKSPACE"]))\r
762         EdkLogger.quiet("%-24s = %s" % ("ECP_SOURCE", os.environ["ECP_SOURCE"]))\r
763         EdkLogger.quiet("%-24s = %s" % ("EDK_SOURCE", os.environ["EDK_SOURCE"]))\r
764         EdkLogger.quiet("%-24s = %s" % ("EFI_SOURCE", os.environ["EFI_SOURCE"]))\r
765         EdkLogger.quiet("%-24s = %s" % ("EDK_TOOLS_PATH", os.environ["EDK_TOOLS_PATH"]))\r
766 \r
767         EdkLogger.info('\n%-24s = %s' % ("TARGET_ARCH", ' '.join(self.ArchList)))\r
768         EdkLogger.info('%-24s = %s' % ("TARGET", ' '.join(self.BuildTargetList)))\r
769         EdkLogger.info('%-24s = %s' % ("TOOL_CHAIN_TAG", ' '.join(self.ToolChainList)))\r
770 \r
771         EdkLogger.info('\n%-24s = %s' % ("Active Platform", self.PlatformFile))\r
772 \r
773         if self.Fdf != None and self.Fdf != "":\r
774             EdkLogger.info('%-24s = %s' % ("Flash Image Definition", self.Fdf))\r
775 \r
776         if self.ModuleFile != None and self.ModuleFile != "":\r
777             EdkLogger.info('%-24s = %s' % ("Active Module", self.ModuleFile))\r
778 \r
779         os.chdir(self.WorkspaceDir)\r
780         self.Progress.Start("\nProcessing meta-data")\r
781 \r
782     ## Load configuration\r
783     #\r
784     #   This method will parse target.txt and get the build configurations.\r
785     #\r
786     def LoadConfiguration(self):\r
787         #\r
788         # Check target.txt and tools_def.txt and Init them\r
789         #\r
790         BuildConfigurationFile = os.path.normpath(os.path.join(self.WorkspaceDir, gBuildConfiguration))\r
791         if os.path.isfile(BuildConfigurationFile) == True:\r
792             StatusCode = self.TargetTxt.LoadTargetTxtFile(BuildConfigurationFile)\r
793 \r
794             ToolDefinitionFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_CONF]\r
795             if ToolDefinitionFile == '':\r
796                 ToolDefinitionFile = gToolsDefinition\r
797             ToolDefinitionFile = os.path.normpath(os.path.join(self.WorkspaceDir, ToolDefinitionFile))\r
798             if os.path.isfile(ToolDefinitionFile) == True:\r
799                 StatusCode = self.ToolDef.LoadToolDefFile(ToolDefinitionFile)\r
800             else:\r
801                 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=ToolDefinitionFile)\r
802         else:\r
803             EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=BuildConfigurationFile)\r
804 \r
805         # if no ARCH given in command line, get it from target.txt\r
806         if self.ArchList == None or len(self.ArchList) == 0:\r
807             self.ArchList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET_ARCH]\r
808 \r
809         # if no build target given in command line, get it from target.txt\r
810         if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
811             self.BuildTargetList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET]\r
812 \r
813         # if no tool chain given in command line, get it from target.txt\r
814         if self.ToolChainList == None or len(self.ToolChainList) == 0:\r
815             self.ToolChainList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_TAG]\r
816             if self.ToolChainList == None or len(self.ToolChainList) == 0:\r
817                 EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, ExtraData="No toolchain given. Don't know how to build.\n")\r
818 \r
819         # check if the tool chains are defined or not\r
820         NewToolChainList = []\r
821         for ToolChain in self.ToolChainList:\r
822             if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG]:\r
823                 EdkLogger.warn("build", "Tool chain [%s] is not defined" % ToolChain)\r
824             else:\r
825                 NewToolChainList.append(ToolChain)\r
826         # if no tool chain available, break the build\r
827         if len(NewToolChainList) == 0:\r
828             EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,\r
829                             ExtraData="[%s] not defined. No toolchain available for build!\n" % ", ".join(self.ToolChainList))\r
830         else:\r
831             self.ToolChainList = NewToolChainList\r
832 \r
833         if self.ThreadNumber == None:\r
834             self.ThreadNumber = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER]\r
835             if self.ThreadNumber == '':\r
836                 self.ThreadNumber = 0\r
837             else:\r
838                 self.ThreadNumber = int(self.ThreadNumber, 0)\r
839 \r
840         if self.ThreadNumber == 0:\r
841             self.ThreadNumber = 1\r
842 \r
843         if not self.PlatformFile:\r
844             PlatformFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_ACTIVE_PLATFORM]\r
845             if not PlatformFile:\r
846                 # Try to find one in current directory\r
847                 WorkingDirectory = os.getcwd()\r
848                 FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.dsc')))\r
849                 FileNum = len(FileList)\r
850                 if FileNum >= 2:\r
851                     EdkLogger.error("build", OPTION_MISSING,\r
852                                     ExtraData="There are %d DSC files in %s. Use '-p' to specify one.\n" % (FileNum, WorkingDirectory))\r
853                 elif FileNum == 1:\r
854                     PlatformFile = FileList[0]\r
855                 else:\r
856                     EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,\r
857                                     ExtraData="No active platform specified in target.txt or command line! Nothing can be built.\n")\r
858 \r
859             self.PlatformFile = PathClass(NormFile(PlatformFile, self.WorkspaceDir), self.WorkspaceDir)\r
860             ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False)\r
861             if ErrorCode != 0:\r
862                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
863 \r
864     ## Initialize build configuration\r
865     #\r
866     #   This method will parse DSC file and merge the configurations from\r
867     #   command line and target.txt, then get the final build configurations.\r
868     #\r
869     def InitBuild(self):\r
870         ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc")\r
871         if ErrorCode != 0:\r
872             EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
873 \r
874         # create metafile database\r
875         self.Db.InitDatabase()\r
876 \r
877         # we need information in platform description file to determine how to build\r
878         self.Platform = self.BuildDatabase[self.PlatformFile, 'COMMON']\r
879         if not self.Fdf:\r
880             self.Fdf = self.Platform.FlashDefinition\r
881 \r
882         if self.SkuId == None or self.SkuId == '':\r
883             self.SkuId = self.Platform.SkuName\r
884 \r
885         # check FD/FV build target\r
886         if self.Fdf == None or self.Fdf == "":\r
887             if self.FdList != []:\r
888                 EdkLogger.info("No flash definition file found. FD [%s] will be ignored." % " ".join(self.FdList))\r
889                 self.FdList = []\r
890             if self.FvList != []:\r
891                 EdkLogger.info("No flash definition file found. FV [%s] will be ignored." % " ".join(self.FvList))\r
892                 self.FvList = []\r
893         else:\r
894             FdfParserObj = FdfParser(str(self.Fdf))\r
895             FdfParserObj.ParseFile()\r
896             for fvname in self.FvList:\r
897                 if fvname.upper() not in FdfParserObj.Profile.FvDict.keys():\r
898                     EdkLogger.error("build", OPTION_VALUE_INVALID,\r
899                                     "No such an FV in FDF file: %s" % fvname)\r
900 \r
901         #\r
902         # Merge Arch\r
903         #\r
904         if self.ArchList == None or len(self.ArchList) == 0:\r
905             ArchList = set(self.Platform.SupArchList)\r
906         else:\r
907             ArchList = set(self.ArchList) & set(self.Platform.SupArchList)\r
908         if len(ArchList) == 0:\r
909             EdkLogger.error("build", PARAMETER_INVALID,\r
910                             ExtraData = "Active platform supports [%s] only, but [%s] is given."\r
911                                         % (" ".join(self.Platform.SupArchList), " ".join(self.ArchList)))\r
912         elif len(ArchList) != len(self.ArchList):\r
913             SkippedArchList = set(self.ArchList).symmetric_difference(set(self.Platform.SupArchList))\r
914             EdkLogger.verbose("\nArch [%s] is ignored because active platform supports [%s] but [%s] is specified !"\r
915                            % (" ".join(SkippedArchList), " ".join(self.Platform.SupArchList), " ".join(self.ArchList)))\r
916         self.ArchList = tuple(ArchList)\r
917 \r
918         # Merge build target\r
919         if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
920             BuildTargetList = self.Platform.BuildTargets\r
921         else:\r
922             BuildTargetList = list(set(self.BuildTargetList) & set(self.Platform.BuildTargets))\r
923         if BuildTargetList == []:\r
924             EdkLogger.error("build", PARAMETER_INVALID, "Active platform only supports [%s], but [%s] is given"\r
925                                 % (" ".join(self.Platform.BuildTargets), " ".join(self.BuildTargetList)))\r
926         self.BuildTargetList = BuildTargetList\r
927 \r
928     ## Build a module or platform\r
929     #\r
930     # Create autogen code and makfile for a module or platform, and the launch\r
931     # "make" command to build it\r
932     #\r
933     #   @param  Target                      The target of build command\r
934     #   @param  Platform                    The platform file\r
935     #   @param  Module                      The module file\r
936     #   @param  BuildTarget                 The name of build target, one of "DEBUG", "RELEASE"\r
937     #   @param  ToolChain                   The name of toolchain to build\r
938     #   @param  Arch                        The arch of the module/platform\r
939     #   @param  CreateDepModuleCodeFile     Flag used to indicate creating code\r
940     #                                       for dependent modules/Libraries\r
941     #   @param  CreateDepModuleMakeFile     Flag used to indicate creating makefile\r
942     #                                       for dependent modules/Libraries\r
943     #\r
944     def _Build(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True):\r
945         if AutoGenObject == None:\r
946             return False\r
947 \r
948         # skip file generation for cleanxxx targets, run and fds target\r
949         if Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
950             # for target which must generate AutoGen code and makefile\r
951             if not self.SkipAutoGen or Target == 'genc':\r
952                 self.Progress.Start("Generating code")\r
953                 AutoGenObject.CreateCodeFile(CreateDepsCodeFile)\r
954                 self.Progress.Stop("done!")\r
955             if Target == "genc":\r
956                 return True\r
957 \r
958             if not self.SkipAutoGen or Target == 'genmake':\r
959                 self.Progress.Start("Generating makefile")\r
960                 AutoGenObject.CreateMakeFile(CreateDepsMakeFile)\r
961                 self.Progress.Stop("done!")\r
962             if Target == "genmake":\r
963                 return True\r
964         else:\r
965             # always recreate top/platform makefile when clean, just in case of inconsistency\r
966             AutoGenObject.CreateCodeFile(False)\r
967             AutoGenObject.CreateMakeFile(False)\r
968 \r
969         if EdkLogger.GetLevel() == EdkLogger.QUIET:\r
970             EdkLogger.quiet("Building ... %s" % repr(AutoGenObject))\r
971 \r
972         BuildCommand = AutoGenObject.BuildCommand\r
973         if BuildCommand == None or len(BuildCommand) == 0:\r
974             EdkLogger.error("build", OPTION_MISSING, ExtraData="No MAKE command found for [%s, %s, %s]" % Key)\r
975 \r
976         BuildCommand = BuildCommand + [Target]\r
977         LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir)\r
978         if Target == 'cleanall':\r
979             try:\r
980                 #os.rmdir(AutoGenObject.BuildDir)\r
981                 RemoveDirectory(AutoGenObject.BuildDir, True)\r
982             except WindowsError, X:\r
983                 EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X))\r
984         return True\r
985 \r
986     ## Build active platform for different build targets and different tool chains\r
987     #\r
988     def _BuildPlatform(self):\r
989         for BuildTarget in self.BuildTargetList:\r
990             for ToolChain in self.ToolChainList:\r
991                 Wa = WorkspaceAutoGen(\r
992                         self.WorkspaceDir,\r
993                         self.Platform,\r
994                         BuildTarget,\r
995                         ToolChain,\r
996                         self.ArchList,\r
997                         self.BuildDatabase,\r
998                         self.TargetTxt,\r
999                         self.ToolDef,\r
1000                         self.Fdf,\r
1001                         self.FdList,\r
1002                         self.FvList,\r
1003                         self.SkuId\r
1004                         )\r
1005                 self.BuildReport.AddPlatformReport(Wa)\r
1006                 self.Progress.Stop("done!")\r
1007                 self._Build(self.Target, Wa)\r
1008 \r
1009     ## Build active module for different build targets, different tool chains and different archs\r
1010     #\r
1011     def _BuildModule(self):\r
1012         for BuildTarget in self.BuildTargetList:\r
1013             for ToolChain in self.ToolChainList:\r
1014                 #\r
1015                 # module build needs platform build information, so get platform\r
1016                 # AutoGen first\r
1017                 #\r
1018                 Wa = WorkspaceAutoGen(\r
1019                         self.WorkspaceDir,\r
1020                         self.Platform,\r
1021                         BuildTarget,\r
1022                         ToolChain,\r
1023                         self.ArchList,\r
1024                         self.BuildDatabase,\r
1025                         self.TargetTxt,\r
1026                         self.ToolDef,\r
1027                         self.Fdf,\r
1028                         self.FdList,\r
1029                         self.FvList,\r
1030                         self.SkuId\r
1031                         )\r
1032                 self.BuildReport.AddPlatformReport(Wa)\r
1033                 Wa.CreateMakeFile(False)\r
1034                 self.Progress.Stop("done!")\r
1035                 MaList = []\r
1036                 for Arch in self.ArchList:\r
1037                     Ma = ModuleAutoGen(Wa, self.ModuleFile, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
1038                     if Ma == None: continue\r
1039                     MaList.append(Ma)\r
1040                     self._Build(self.Target, Ma)\r
1041                 if MaList == []:\r
1042                     EdkLogger.error(\r
1043                                 'build',\r
1044                                 BUILD_ERROR,\r
1045                                 "Module for [%s] is not a component of active platform."\\r
1046                                 " Please make sure that the ARCH and inf file path are"\\r
1047                                 " given in the same as in [%s]" %\\r
1048                                     (', '.join(self.ArchList), self.Platform),\r
1049                                 ExtraData=self.ModuleFile\r
1050                                 )\r
1051 \r
1052     ## Build a platform in multi-thread mode\r
1053     #\r
1054     def _MultiThreadBuildPlatform(self):\r
1055         for BuildTarget in self.BuildTargetList:\r
1056             for ToolChain in self.ToolChainList:\r
1057                 Wa = WorkspaceAutoGen(\r
1058                         self.WorkspaceDir,\r
1059                         self.Platform,\r
1060                         BuildTarget,\r
1061                         ToolChain,\r
1062                         self.ArchList,\r
1063                         self.BuildDatabase,\r
1064                         self.TargetTxt,\r
1065                         self.ToolDef,\r
1066                         self.Fdf,\r
1067                         self.FdList,\r
1068                         self.FvList,\r
1069                         self.SkuId\r
1070                         )\r
1071                 self.BuildReport.AddPlatformReport(Wa)\r
1072                 Wa.CreateMakeFile(False)\r
1073 \r
1074                 # multi-thread exit flag\r
1075                 ExitFlag = threading.Event()\r
1076                 ExitFlag.clear()\r
1077                 for Arch in self.ArchList:\r
1078                     Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)\r
1079                     if Pa == None:\r
1080                         continue\r
1081                     for Module in Pa.Platform.Modules:\r
1082                         # Get ModuleAutoGen object to generate C code file and makefile\r
1083                         Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
1084                         if Ma == None:\r
1085                             continue\r
1086                         # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'\r
1087                         if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
1088                             # for target which must generate AutoGen code and makefile\r
1089                             if not self.SkipAutoGen or self.Target == 'genc':\r
1090                                 Ma.CreateCodeFile(True)\r
1091                             if self.Target == "genc":\r
1092                                 continue\r
1093 \r
1094                             if not self.SkipAutoGen or self.Target == 'genmake':\r
1095                                 Ma.CreateMakeFile(True)\r
1096                             if self.Target == "genmake":\r
1097                                 continue\r
1098                         self.Progress.Stop("done!")\r
1099                         # Generate build task for the module\r
1100                         Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))\r
1101                         # Break build if any build thread has error\r
1102                         if BuildTask.HasError():\r
1103                             # we need a full version of makefile for platform\r
1104                             ExitFlag.set()\r
1105                             BuildTask.WaitForComplete()\r
1106                             Pa.CreateMakeFile(False)\r
1107                             EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1108                         # Start task scheduler\r
1109                         if not BuildTask.IsOnGoing():\r
1110                             BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)\r
1111 \r
1112                     # in case there's an interruption. we need a full version of makefile for platform\r
1113                     Pa.CreateMakeFile(False)\r
1114                     if BuildTask.HasError():\r
1115                         EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1116 \r
1117                 #\r
1118                 # All modules have been put in build tasks queue. Tell task scheduler\r
1119                 # to exit if all tasks are completed\r
1120                 #\r
1121                 ExitFlag.set()\r
1122                 BuildTask.WaitForComplete()\r
1123 \r
1124                 #\r
1125                 # Check for build error, and raise exception if one\r
1126                 # has been signaled.\r
1127                 #\r
1128                 if BuildTask.HasError():\r
1129                     EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1130 \r
1131                 # Generate FD image if there's a FDF file found\r
1132                 if self.Fdf != '' and self.Target in ["", "all", "fds"]:\r
1133                     LaunchCommand(Wa.BuildCommand + ["fds"], Wa.MakeFileDir)\r
1134 \r
1135     ## Generate GuidedSectionTools.txt in the FV directories.\r
1136     #\r
1137     def CreateGuidedSectionToolsFile(self):\r
1138         for Arch in self.ArchList:\r
1139             for BuildTarget in self.BuildTargetList:\r
1140                 for ToolChain in self.ToolChainList:\r
1141                     FvDir = os.path.join(\r
1142                                 self.WorkspaceDir,\r
1143                                 self.Platform.OutputDirectory,\r
1144                                 '_'.join((BuildTarget, ToolChain)),\r
1145                                 'FV'\r
1146                                 )\r
1147                     if not os.path.exists(FvDir):\r
1148                         continue\r
1149                     # Build up the list of supported architectures for this build\r
1150                     prefix = '%s_%s_%s_' % (BuildTarget, ToolChain, Arch)\r
1151 \r
1152                     # Look through the tool definitions for GUIDed tools\r
1153                     guidAttribs = []\r
1154                     for (attrib, value) in self.ToolDef.ToolsDefTxtDictionary.iteritems():\r
1155                         if attrib.upper().endswith('_GUID'):\r
1156                             split = attrib.split('_')\r
1157                             thisPrefix = '_'.join(split[0:3]) + '_'\r
1158                             if thisPrefix == prefix:\r
1159                                 guid = self.ToolDef.ToolsDefTxtDictionary[attrib]\r
1160                                 guid = guid.lower()\r
1161                                 toolName = split[3]\r
1162                                 path = '_'.join(split[0:4]) + '_PATH'\r
1163                                 path = self.ToolDef.ToolsDefTxtDictionary[path]\r
1164                                 path = self.GetFullPathOfTool(path)\r
1165                                 guidAttribs.append((guid, toolName, path))\r
1166 \r
1167                     # Write out GuidedSecTools.txt\r
1168                     toolsFile = os.path.join(FvDir, 'GuidedSectionTools.txt')\r
1169                     toolsFile = open(toolsFile, 'wt')\r
1170                     for guidedSectionTool in guidAttribs:\r
1171                         print >> toolsFile, ' '.join(guidedSectionTool)\r
1172                     toolsFile.close()\r
1173 \r
1174     ## Returns the full path of the tool.\r
1175     #\r
1176     def GetFullPathOfTool (self, tool):\r
1177         if os.path.exists(tool):\r
1178             return os.path.realpath(tool)\r
1179         else:\r
1180             # We need to search for the tool using the\r
1181             # PATH environment variable.\r
1182             for dirInPath in os.environ['PATH'].split(os.pathsep):\r
1183                 foundPath = os.path.join(dirInPath, tool)\r
1184                 if os.path.exists(foundPath):\r
1185                     return os.path.realpath(foundPath)\r
1186 \r
1187         # If the tool was not found in the path then we just return\r
1188         # the input tool.\r
1189         return tool\r
1190 \r
1191     ## Launch the module or platform build\r
1192     #\r
1193     def Launch(self):\r
1194         if self.ModuleFile == None or self.ModuleFile == "":\r
1195             if not self.SpawnMode or self.Target not in ["", "all"]:\r
1196                 self.SpawnMode = False\r
1197                 self._BuildPlatform()\r
1198             else:\r
1199                 self._MultiThreadBuildPlatform()\r
1200             self.CreateGuidedSectionToolsFile()\r
1201         else:\r
1202             self.SpawnMode = False\r
1203             self._BuildModule()\r
1204 \r
1205     ## Do some clean-up works when error occurred\r
1206     def Relinquish(self):\r
1207         OldLogLevel = EdkLogger.GetLevel()\r
1208         EdkLogger.SetLevel(EdkLogger.ERROR)\r
1209         #self.DumpBuildData()\r
1210         Utils.Progressor.Abort()\r
1211         if self.SpawnMode == True:\r
1212             BuildTask.Abort()\r
1213         EdkLogger.SetLevel(OldLogLevel)\r
1214 \r
1215     def DumpBuildData(self):\r
1216         CacheDirectory = os.path.join(self.WorkspaceDir, gBuildCacheDir)\r
1217         Utils.CreateDirectory(CacheDirectory)\r
1218         Utils.DataDump(Utils.gFileTimeStampCache, os.path.join(CacheDirectory, "gFileTimeStampCache"))\r
1219         Utils.DataDump(Utils.gDependencyDatabase, os.path.join(CacheDirectory, "gDependencyDatabase"))\r
1220 \r
1221     def RestoreBuildData(self):\r
1222         FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gFileTimeStampCache")\r
1223         if Utils.gFileTimeStampCache == {} and os.path.isfile(FilePath):\r
1224             Utils.gFileTimeStampCache = Utils.DataRestore(FilePath)\r
1225             if Utils.gFileTimeStampCache == None:\r
1226                 Utils.gFileTimeStampCache = {}\r
1227 \r
1228         FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gDependencyDatabase")\r
1229         if Utils.gDependencyDatabase == {} and os.path.isfile(FilePath):\r
1230             Utils.gDependencyDatabase = Utils.DataRestore(FilePath)\r
1231             if Utils.gDependencyDatabase == None:\r
1232                 Utils.gDependencyDatabase = {}\r
1233 \r
1234 def ParseDefines(DefineList=[]):\r
1235     DefineDict = {}\r
1236     if DefineList != None:\r
1237         for Define in DefineList:\r
1238             DefineTokenList = Define.split("=", 1)\r
1239             if len(DefineTokenList) == 1:\r
1240                 DefineDict[DefineTokenList[0]] = ""\r
1241             else:\r
1242                 DefineDict[DefineTokenList[0]] = DefineTokenList[1].strip()\r
1243     return DefineDict\r
1244 \r
1245 gParamCheck = []\r
1246 def SingleCheckCallback(option, opt_str, value, parser):\r
1247     if option not in gParamCheck:\r
1248         setattr(parser.values, option.dest, value)\r
1249         gParamCheck.append(option)\r
1250     else:\r
1251         parser.error("Option %s only allows one instance in command line!" % option)\r
1252 \r
1253 ## Parse command line options\r
1254 #\r
1255 # Using standard Python module optparse to parse command line option of this tool.\r
1256 #\r
1257 #   @retval Opt   A optparse.Values object containing the parsed options\r
1258 #   @retval Args  Target of build command\r
1259 #\r
1260 def MyOptionParser():\r
1261     Parser = OptionParser(description=__copyright__,version=__version__,prog="build.exe",usage="%prog [options] [all|fds|genc|genmake|clean|cleanall|cleanlib|modules|libraries|run]")\r
1262     Parser.add_option("-a", "--arch", action="append", type="choice", choices=['IA32','X64','IPF','EBC','ARM'], dest="TargetArch",\r
1263         help="ARCHS is one of list: IA32, X64, IPF, ARM or EBC, which overrides target.txt's TARGET_ARCH definition. To specify more archs, please repeat this option.")\r
1264     Parser.add_option("-p", "--platform", action="callback", type="string", dest="PlatformFile", callback=SingleCheckCallback,\r
1265         help="Build the platform specified by the DSC file name argument, overriding target.txt's ACTIVE_PLATFORM definition.")\r
1266     Parser.add_option("-m", "--module", action="callback", type="string", dest="ModuleFile", callback=SingleCheckCallback,\r
1267         help="Build the module specified by the INF file name argument.")\r
1268     Parser.add_option("-b", "--buildtarget", action="append", type="choice", choices=['DEBUG','RELEASE'], dest="BuildTarget",\r
1269         help="BuildTarget is one of list: DEBUG, RELEASE, which overrides target.txt's TARGET definition. To specify more TARGET, please repeat this option.")\r
1270     Parser.add_option("-t", "--tagname", action="append", type="string", dest="ToolChain",\r
1271         help="Using the Tool Chain Tagname to build the platform, overriding target.txt's TOOL_CHAIN_TAG definition.")\r
1272     Parser.add_option("-x", "--sku-id", action="callback", type="string", dest="SkuId", callback=SingleCheckCallback,\r
1273         help="Using this name of SKU ID to build the platform, overriding SKUID_IDENTIFIER in DSC file.")\r
1274 \r
1275     Parser.add_option("-n", action="callback", type="int", dest="ThreadNumber", callback=SingleCheckCallback,\r
1276         help="Build the platform using multi-threaded compiler. The value overrides target.txt's MAX_CONCURRENT_THREAD_NUMBER. Less than 2 will disable multi-thread builds.")\r
1277 \r
1278     Parser.add_option("-f", "--fdf", action="callback", type="string", dest="FdfFile", callback=SingleCheckCallback,\r
1279         help="The name of the FDF file to use, which overrides the setting in the DSC file.")\r
1280     Parser.add_option("-r", "--rom-image", action="append", type="string", dest="RomImage", default=[],\r
1281         help="The name of FD to be generated. The name must be from [FD] section in FDF file.")\r
1282     Parser.add_option("-i", "--fv-image", action="append", type="string", dest="FvImage", default=[],\r
1283         help="The name of FV to be generated. The name must be from [FV] section in FDF file.")\r
1284 \r
1285     Parser.add_option("-u", "--skip-autogen", action="store_true", dest="SkipAutoGen", help="Skip AutoGen step.")\r
1286     Parser.add_option("-e", "--re-parse", action="store_true", dest="Reparse", help="Re-parse all meta-data files.")\r
1287 \r
1288     Parser.add_option("-c", "--case-insensitive", action="store_true", dest="CaseInsensitive", help="Don't check case of file name.")\r
1289 \r
1290     # Parser.add_option("-D", "--define", action="append", dest="Defines", metavar="NAME[=[VALUE]]",\r
1291     #     help="Define global macro which can be used in DSC/DEC/INF files.")\r
1292 \r
1293     Parser.add_option("-w", "--warning-as-error", action="store_true", dest="WarningAsError", help="Treat warning in tools as error.")\r
1294     Parser.add_option("-j", "--log", action="store", dest="LogFile", help="Put log in specified file as well as on console.")\r
1295 \r
1296     Parser.add_option("-s", "--silent", action="store_true", type=None, dest="SilentMode",\r
1297         help="Make use of silent mode of (n)make.")\r
1298     Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")\r
1299     Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\\r
1300                                                                                "including library instances selected, final dependency expression, "\\r
1301                                                                                "and warning messages, etc.")\r
1302     Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")\r
1303     Parser.add_option("-D", "--define", action="append", type="string", dest="Macros", help="Macro: \"Name [= Value]\".")\r
1304 \r
1305     Parser.add_option("-y", "--report-file", action="store", dest="ReportFile", help="Create/overwrite the report to the specified filename.")\r
1306     Parser.add_option("-Y", "--report-type", action="append", type="choice", choices=['ALL','PCD','LIBRARY','FLASH','DEPEX','BUILD_FLAGS','PREDICTION'], dest="ReportType",\r
1307         help="Flags that control the type of build report to generate.  Must be one of: [ALL, PCD, LIBRARY, FLASH, DEPEX, BUILD_FLAGS, PREDICTION].  To specify more than one flag, repeat this option on the command line.")\r
1308 \r
1309     (Opt, Args)=Parser.parse_args()\r
1310     return (Opt, Args)\r
1311 \r
1312 ## Tool entrance method\r
1313 #\r
1314 # This method mainly dispatch specific methods per the command line options.\r
1315 # If no error found, return zero value so the caller of this tool can know\r
1316 # if it's executed successfully or not.\r
1317 #\r
1318 #   @retval 0     Tool was successful\r
1319 #   @retval 1     Tool failed\r
1320 #\r
1321 def Main():\r
1322     StartTime = time.time()\r
1323 \r
1324     # Initialize log system\r
1325     EdkLogger.Initialize()\r
1326 \r
1327     #\r
1328     # Parse the options and args\r
1329     #\r
1330     (Option, Target) = MyOptionParser()\r
1331     GlobalData.gOptions = Option\r
1332     GlobalData.gCaseInsensitive = Option.CaseInsensitive\r
1333 \r
1334     # Set log level\r
1335     if Option.verbose != None:\r
1336         EdkLogger.SetLevel(EdkLogger.VERBOSE)\r
1337     elif Option.quiet != None:\r
1338         EdkLogger.SetLevel(EdkLogger.QUIET)\r
1339     elif Option.debug != None:\r
1340         EdkLogger.SetLevel(Option.debug + 1)\r
1341     else:\r
1342         EdkLogger.SetLevel(EdkLogger.INFO)\r
1343 \r
1344     if Option.LogFile != None:\r
1345         EdkLogger.SetLogFile(Option.LogFile)\r
1346 \r
1347     if Option.WarningAsError == True:\r
1348         EdkLogger.SetWarningAsError()\r
1349 \r
1350     if platform.platform().find("Windows") >= 0:\r
1351         GlobalData.gIsWindows = True\r
1352     else:\r
1353         GlobalData.gIsWindows = False\r
1354 \r
1355     EdkLogger.quiet(time.strftime("%H:%M:%S, %b.%d %Y ", time.localtime()) + "[%s]\n" % platform.platform())\r
1356     ReturnCode = 0\r
1357     MyBuild = None\r
1358     try:\r
1359         if len(Target) == 0:\r
1360             Target = "all"\r
1361         elif len(Target) >= 2:\r
1362             EdkLogger.error("build", OPTION_NOT_SUPPORTED, "More than one targets are not supported.",\r
1363                             ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))\r
1364         else:\r
1365             Target = Target[0].lower()\r
1366 \r
1367         if Target not in gSupportedTarget:\r
1368             EdkLogger.error("build", OPTION_NOT_SUPPORTED, "Not supported target [%s]." % Target,\r
1369                             ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))\r
1370 \r
1371         GlobalData.gGlobalDefines = ParseDefines(Option.Macros)\r
1372         #\r
1373         # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH\r
1374         #\r
1375         CheckEnvVariable()\r
1376         Workspace = os.getenv("WORKSPACE")\r
1377         #\r
1378         # Get files real name in workspace dir\r
1379         #\r
1380         GlobalData.gAllFiles = Utils.DirCache(Workspace)\r
1381 \r
1382         WorkingDirectory = os.getcwd()\r
1383         if not Option.ModuleFile:\r
1384             FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.inf')))\r
1385             FileNum = len(FileList)\r
1386             if FileNum >= 2:\r
1387                 EdkLogger.error("build", OPTION_NOT_SUPPORTED, "There are %d INF files in %s." % (FileNum, WorkingDirectory),\r
1388                                 ExtraData="Please use '-m <INF_FILE_PATH>' switch to choose one.")\r
1389             elif FileNum == 1:\r
1390                 Option.ModuleFile = NormFile(FileList[0], Workspace)\r
1391 \r
1392         if Option.ModuleFile:\r
1393             Option.ModuleFile = PathClass(Option.ModuleFile, Workspace)\r
1394             ErrorCode, ErrorInfo = Option.ModuleFile.Validate(".inf", False)\r
1395             if ErrorCode != 0:\r
1396                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
1397 \r
1398         if Option.PlatformFile != None:\r
1399             Option.PlatformFile = PathClass(Option.PlatformFile, Workspace)\r
1400             ErrorCode, ErrorInfo = Option.PlatformFile.Validate(".dsc", False)\r
1401             if ErrorCode != 0:\r
1402                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
1403 \r
1404         if Option.FdfFile != None:\r
1405             Option.FdfFile = PathClass(Option.FdfFile, Workspace)\r
1406             ErrorCode, ErrorInfo = Option.FdfFile.Validate(".fdf", False)\r
1407             if ErrorCode != 0:\r
1408                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
1409 \r
1410         MyBuild = Build(Target, Workspace, Option.PlatformFile, Option.ModuleFile,\r
1411                         Option.TargetArch, Option.ToolChain, Option.BuildTarget,\r
1412                         Option.FdfFile, Option.RomImage, Option.FvImage,\r
1413                         None, Option.SilentMode, Option.ThreadNumber,\r
1414                         Option.SkipAutoGen, Option.Reparse, Option.SkuId, \r
1415                         Option.ReportFile, Option.ReportType)\r
1416         MyBuild.Launch()\r
1417         #MyBuild.DumpBuildData()\r
1418     except FatalError, X:\r
1419         if MyBuild != None:\r
1420             # for multi-thread build exits safely\r
1421             MyBuild.Relinquish()\r
1422         if Option != None and Option.debug != None:\r
1423             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1424         ReturnCode = X.args[0]\r
1425     except Warning, X:\r
1426         # error from Fdf parser\r
1427         if MyBuild != None:\r
1428             # for multi-thread build exits safely\r
1429             MyBuild.Relinquish()\r
1430         if Option != None and Option.debug != None:\r
1431             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1432         else:\r
1433             EdkLogger.error(X.ToolName, FORMAT_INVALID, File=X.FileName, Line=X.LineNumber, ExtraData=X.Message, RaiseError = False)\r
1434         ReturnCode = FORMAT_INVALID\r
1435     except KeyboardInterrupt:\r
1436         ReturnCode = ABORT_ERROR\r
1437         if Option != None and Option.debug != None:\r
1438             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1439     except:\r
1440         if MyBuild != None:\r
1441             # for multi-thread build exits safely\r
1442             MyBuild.Relinquish()\r
1443 \r
1444         # try to get the meta-file from the object causing exception\r
1445         Tb = sys.exc_info()[-1]\r
1446         MetaFile = GlobalData.gProcessingFile\r
1447         while Tb != None:\r
1448             if 'self' in Tb.tb_frame.f_locals and hasattr(Tb.tb_frame.f_locals['self'], 'MetaFile'):\r
1449                 MetaFile = Tb.tb_frame.f_locals['self'].MetaFile\r
1450             Tb = Tb.tb_next\r
1451         EdkLogger.error(\r
1452                     "\nbuild",\r
1453                     CODE_ERROR,\r
1454                     "Unknown fatal error when processing [%s]" % MetaFile,\r
1455                     ExtraData="\n(Please send email to dev@buildtools.tianocore.org for help, attaching following call stack trace!)\n",\r
1456                     RaiseError=False\r
1457                     )\r
1458         EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1459         ReturnCode = CODE_ERROR\r
1460     finally:\r
1461         Utils.Progressor.Abort()\r
1462 \r
1463     if ReturnCode == 0:\r
1464         Conclusion = "Done"\r
1465     elif ReturnCode == ABORT_ERROR:\r
1466         Conclusion = "Aborted"\r
1467     else:\r
1468         Conclusion = "Failed"\r
1469     FinishTime = time.time()\r
1470     BuildDuration = time.strftime("%M:%S", time.gmtime(int(round(FinishTime - StartTime))))\r
1471     if MyBuild != None:\r
1472         MyBuild.BuildReport.GenerateReport(BuildDuration)\r
1473         MyBuild.Db.Close()\r
1474     EdkLogger.SetLevel(EdkLogger.QUIET)\r
1475     EdkLogger.quiet("\n- %s -\n%s [%s]" % (Conclusion, time.strftime("%H:%M:%S, %b.%d %Y", time.localtime()), BuildDuration))\r
1476 \r
1477     return ReturnCode\r
1478 \r
1479 if __name__ == '__main__':\r
1480     r = Main()\r
1481     ## 0-127 is a safe return range, and 1 is a standard default error\r
1482     if r < 0 or r > 127: r = 1\r
1483     sys.exit(r)\r
1484 \r