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