Sync EDKII BaseTools to BaseTools project r1903.
[efi/edk2/.git] / edk2 / BaseTools / 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 StringIO\r
21 import sys\r
22 import glob\r
23 import time\r
24 import platform\r
25 import traceback\r
26 \r
27 from struct import *\r
28 from threading import *\r
29 from optparse import OptionParser\r
30 from subprocess import *\r
31 from Common import Misc as Utils\r
32 \r
33 from Common.TargetTxtClassObject import *\r
34 from Common.ToolDefClassObject import *\r
35 from Common.DataType import *\r
36 from AutoGen.AutoGen import *\r
37 from Common.BuildToolError import *\r
38 from Workspace.WorkspaceDatabase import *\r
39 \r
40 from BuildReport import BuildReport\r
41 from GenPatchPcdTable.GenPatchPcdTable import *\r
42 from PatchPcdValue.PatchPcdValue import *\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 ## Launch an external program\r
221 #\r
222 # This method will call subprocess.Popen to execute an external program with\r
223 # given options in specified directory. Because of the dead-lock issue during\r
224 # redirecting output of the external program, threads are used to to do the\r
225 # redirection work.\r
226 #\r
227 # @param  Command               A list or string containing the call of the program\r
228 # @param  WorkingDir            The directory in which the program will be running\r
229 #\r
230 def LaunchCommand(Command, WorkingDir):\r
231     # if working directory doesn't exist, Popen() will raise an exception\r
232     if not os.path.isdir(WorkingDir):\r
233         EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=WorkingDir)\r
234 \r
235     Proc = None\r
236     EndOfProcedure = None\r
237     try:\r
238         # launch the command\r
239         Proc = Popen(Command, stdout=PIPE, stderr=PIPE, env=os.environ, cwd=WorkingDir, bufsize=-1)\r
240 \r
241         # launch two threads to read the STDOUT and STDERR\r
242         EndOfProcedure = Event()\r
243         EndOfProcedure.clear()\r
244         if Proc.stdout:\r
245             StdOutThread = Thread(target=ReadMessage, args=(Proc.stdout, EdkLogger.info, EndOfProcedure))\r
246             StdOutThread.setName("STDOUT-Redirector")\r
247             StdOutThread.setDaemon(False)\r
248             StdOutThread.start()\r
249 \r
250         if Proc.stderr:\r
251             StdErrThread = Thread(target=ReadMessage, args=(Proc.stderr, EdkLogger.quiet, EndOfProcedure))\r
252             StdErrThread.setName("STDERR-Redirector")\r
253             StdErrThread.setDaemon(False)\r
254             StdErrThread.start()\r
255 \r
256         # waiting for program exit\r
257         Proc.wait()\r
258     except: # in case of aborting\r
259         # terminate the threads redirecting the program output\r
260         if EndOfProcedure != None:\r
261             EndOfProcedure.set()\r
262         if Proc == None:\r
263             if type(Command) != type(""):\r
264                 Command = " ".join(Command)\r
265             EdkLogger.error("build", COMMAND_FAILURE, "Failed to start command", ExtraData="%s [%s]" % (Command, WorkingDir))\r
266 \r
267     if Proc.stdout:\r
268         StdOutThread.join()\r
269     if Proc.stderr:\r
270         StdErrThread.join()\r
271 \r
272     # check the return code of the program\r
273     if Proc.returncode != 0:\r
274         if type(Command) != type(""):\r
275             Command = " ".join(Command)\r
276         EdkLogger.error("build", COMMAND_FAILURE, ExtraData="%s [%s]" % (Command, WorkingDir))\r
277 \r
278 ## The smallest unit that can be built in multi-thread build mode\r
279 #\r
280 # This is the base class of build unit. The "Obj" parameter must provide\r
281 # __str__(), __eq__() and __hash__() methods. Otherwise there could be build units\r
282 # missing build.\r
283 #\r
284 # Currently the "Obj" should be only ModuleAutoGen or PlatformAutoGen objects.\r
285 #\r
286 class BuildUnit:\r
287     ## The constructor\r
288     #\r
289     #   @param  self        The object pointer\r
290     #   @param  Obj         The object the build is working on\r
291     #   @param  Target      The build target name, one of gSupportedTarget\r
292     #   @param  Dependency  The BuildUnit(s) which must be completed in advance\r
293     #   @param  WorkingDir  The directory build command starts in\r
294     #\r
295     def __init__(self, Obj, BuildCommand, Target, Dependency, WorkingDir="."):\r
296         self.BuildObject = Obj\r
297         self.Dependency = Dependency\r
298         self.WorkingDir = WorkingDir\r
299         self.Target = Target\r
300         self.BuildCommand = BuildCommand\r
301         if BuildCommand == None or len(BuildCommand) == 0:\r
302             EdkLogger.error("build", OPTION_MISSING, "No build command found for",\r
303                             ExtraData=str(Obj))\r
304 \r
305     ## str() method\r
306     #\r
307     #   It just returns the string representaion of self.BuildObject\r
308     #\r
309     #   @param  self        The object pointer\r
310     #\r
311     def __str__(self):\r
312         return str(self.BuildObject)\r
313 \r
314     ## "==" operator method\r
315     #\r
316     #   It just compares self.BuildObject with "Other". So self.BuildObject must\r
317     #   provide its own __eq__() method.\r
318     #\r
319     #   @param  self        The object pointer\r
320     #   @param  Other       The other BuildUnit object compared to\r
321     #\r
322     def __eq__(self, Other):\r
323         return Other != None and self.BuildObject == Other.BuildObject \\r
324                 and self.BuildObject.Arch == Other.BuildObject.Arch\r
325 \r
326     ## hash() method\r
327     #\r
328     #   It just returns the hash value of self.BuildObject which must be hashable.\r
329     #\r
330     #   @param  self        The object pointer\r
331     #\r
332     def __hash__(self):\r
333         return hash(self.BuildObject) + hash(self.BuildObject.Arch)\r
334 \r
335     def __repr__(self):\r
336         return repr(self.BuildObject)\r
337 \r
338 ## The smallest module unit that can be built by nmake/make command in multi-thread build mode\r
339 #\r
340 # This class is for module build by nmake/make build system. The "Obj" parameter\r
341 # must provide __str__(), __eq__() and __hash__() methods. Otherwise there could\r
342 # be make units missing build.\r
343 #\r
344 # Currently the "Obj" should be only ModuleAutoGen object.\r
345 #\r
346 class ModuleMakeUnit(BuildUnit):\r
347     ## The constructor\r
348     #\r
349     #   @param  self        The object pointer\r
350     #   @param  Obj         The ModuleAutoGen object the build is working on\r
351     #   @param  Target      The build target name, one of gSupportedTarget\r
352     #\r
353     def __init__(self, Obj, Target):\r
354         Dependency = [ModuleMakeUnit(La, Target) for La in Obj.LibraryAutoGenList]\r
355         BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir)\r
356         if Target in [None, "", "all"]:\r
357             self.Target = "tbuild"\r
358 \r
359 ## The smallest platform unit that can be built by nmake/make command in multi-thread build mode\r
360 #\r
361 # This class is for platform build by nmake/make build system. The "Obj" parameter\r
362 # must provide __str__(), __eq__() and __hash__() methods. Otherwise there could\r
363 # be make units missing build.\r
364 #\r
365 # Currently the "Obj" should be only PlatformAutoGen object.\r
366 #\r
367 class PlatformMakeUnit(BuildUnit):\r
368     ## The constructor\r
369     #\r
370     #   @param  self        The object pointer\r
371     #   @param  Obj         The PlatformAutoGen object the build is working on\r
372     #   @param  Target      The build target name, one of gSupportedTarget\r
373     #\r
374     def __init__(self, Obj, Target):\r
375         Dependency = [ModuleMakeUnit(Lib, Target) for Lib in self.BuildObject.LibraryAutoGenList]\r
376         Dependency.extend([ModuleMakeUnit(Mod, Target) for Mod in self.BuildObject.ModuleAutoGenList])\r
377         BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir)\r
378 \r
379 ## The class representing the task of a module build or platform build\r
380 #\r
381 # This class manages the build tasks in multi-thread build mode. Its jobs include\r
382 # scheduling thread running, catching thread error, monitor the thread status, etc.\r
383 #\r
384 class BuildTask:\r
385     # queue for tasks waiting for schedule\r
386     _PendingQueue = sdict()\r
387     _PendingQueueLock = threading.Lock()\r
388 \r
389     # queue for tasks ready for running\r
390     _ReadyQueue = sdict()\r
391     _ReadyQueueLock = threading.Lock()\r
392 \r
393     # queue for run tasks\r
394     _RunningQueue = sdict()\r
395     _RunningQueueLock = threading.Lock()\r
396 \r
397     # queue containing all build tasks, in case duplicate build\r
398     _TaskQueue = sdict()\r
399 \r
400     # flag indicating error occurs in a running thread\r
401     _ErrorFlag = threading.Event()\r
402     _ErrorFlag.clear()\r
403     _ErrorMessage = ""\r
404 \r
405     # BoundedSemaphore object used to control the number of running threads\r
406     _Thread = None\r
407 \r
408     # flag indicating if the scheduler is started or not\r
409     _SchedulerStopped = threading.Event()\r
410     _SchedulerStopped.set()\r
411 \r
412     ## Start the task scheduler thread\r
413     #\r
414     #   @param  MaxThreadNumber     The maximum thread number\r
415     #   @param  ExitFlag            Flag used to end the scheduler\r
416     #\r
417     @staticmethod\r
418     def StartScheduler(MaxThreadNumber, ExitFlag):\r
419         SchedulerThread = Thread(target=BuildTask.Scheduler, args=(MaxThreadNumber, ExitFlag))\r
420         SchedulerThread.setName("Build-Task-Scheduler")\r
421         SchedulerThread.setDaemon(False)\r
422         SchedulerThread.start()\r
423         # wait for the scheduler to be started, especially useful in Linux\r
424         while not BuildTask.IsOnGoing():\r
425             time.sleep(0.01)\r
426 \r
427     ## Scheduler method\r
428     #\r
429     #   @param  MaxThreadNumber     The maximum thread number\r
430     #   @param  ExitFlag            Flag used to end the scheduler\r
431     #\r
432     @staticmethod\r
433     def Scheduler(MaxThreadNumber, ExitFlag):\r
434         BuildTask._SchedulerStopped.clear()\r
435         try:\r
436             # use BoundedSemaphore to control the maximum running threads\r
437             BuildTask._Thread = BoundedSemaphore(MaxThreadNumber)\r
438             #\r
439             # scheduling loop, which will exits when no pending/ready task and\r
440             # indicated to do so, or there's error in running thread\r
441             #\r
442             while (len(BuildTask._PendingQueue) > 0 or len(BuildTask._ReadyQueue) > 0 \\r
443                    or not ExitFlag.isSet()) and not BuildTask._ErrorFlag.isSet():\r
444                 EdkLogger.debug(EdkLogger.DEBUG_8, "Pending Queue (%d), Ready Queue (%d)"\r
445                                 % (len(BuildTask._PendingQueue), len(BuildTask._ReadyQueue)))\r
446 \r
447                 # get all pending tasks\r
448                 BuildTask._PendingQueueLock.acquire()\r
449                 BuildObjectList = BuildTask._PendingQueue.keys()\r
450                 #\r
451                 # check if their dependency is resolved, and if true, move them\r
452                 # into ready queue\r
453                 #\r
454                 for BuildObject in BuildObjectList:\r
455                     Bt = BuildTask._PendingQueue[BuildObject]\r
456                     if Bt.IsReady():\r
457                         BuildTask._ReadyQueue[BuildObject] = BuildTask._PendingQueue.pop(BuildObject)\r
458                 BuildTask._PendingQueueLock.release()\r
459 \r
460                 # launch build thread until the maximum number of threads is reached\r
461                 while not BuildTask._ErrorFlag.isSet():\r
462                     # empty ready queue, do nothing further\r
463                     if len(BuildTask._ReadyQueue) == 0:\r
464                         break\r
465 \r
466                     # wait for active thread(s) exit\r
467                     BuildTask._Thread.acquire(True)\r
468 \r
469                     # start a new build thread\r
470                     Bo = BuildTask._ReadyQueue.keys()[0]\r
471                     Bt = BuildTask._ReadyQueue.pop(Bo)\r
472 \r
473                     # move into running queue\r
474                     BuildTask._RunningQueueLock.acquire()\r
475                     BuildTask._RunningQueue[Bo] = Bt\r
476                     BuildTask._RunningQueueLock.release()\r
477 \r
478                     Bt.Start()\r
479                     # avoid tense loop\r
480                     time.sleep(0.01)\r
481 \r
482                 # avoid tense loop\r
483                 time.sleep(0.01)\r
484 \r
485             # wait for all running threads exit\r
486             if BuildTask._ErrorFlag.isSet():\r
487                 EdkLogger.quiet("\nWaiting for all build threads exit...")\r
488             # while not BuildTask._ErrorFlag.isSet() and \\r
489             while len(BuildTask._RunningQueue) > 0:\r
490                 EdkLogger.verbose("Waiting for thread ending...(%d)" % len(BuildTask._RunningQueue))\r
491                 EdkLogger.debug(EdkLogger.DEBUG_8, "Threads [%s]" % ", ".join([Th.getName() for Th in threading.enumerate()]))\r
492                 # avoid tense loop\r
493                 time.sleep(0.1)\r
494         except BaseException, X:\r
495             #\r
496             # TRICK: hide the output of threads left runing, so that the user can\r
497             #        catch the error message easily\r
498             #\r
499             EdkLogger.SetLevel(EdkLogger.ERROR)\r
500             BuildTask._ErrorFlag.set()\r
501             BuildTask._ErrorMessage = "build thread scheduler error\n\t%s" % str(X)\r
502 \r
503         BuildTask._PendingQueue.clear()\r
504         BuildTask._ReadyQueue.clear()\r
505         BuildTask._RunningQueue.clear()\r
506         BuildTask._TaskQueue.clear()\r
507         BuildTask._SchedulerStopped.set()\r
508 \r
509     ## Wait for all running method exit\r
510     #\r
511     @staticmethod\r
512     def WaitForComplete():\r
513         BuildTask._SchedulerStopped.wait()\r
514 \r
515     ## Check if the scheduler is running or not\r
516     #\r
517     @staticmethod\r
518     def IsOnGoing():\r
519         return not BuildTask._SchedulerStopped.isSet()\r
520 \r
521     ## Abort the build\r
522     @staticmethod\r
523     def Abort():\r
524         if BuildTask.IsOnGoing():\r
525             BuildTask._ErrorFlag.set()\r
526             BuildTask.WaitForComplete()\r
527 \r
528     ## Check if there's error in running thread\r
529     #\r
530     #   Since the main thread cannot catch exceptions in other thread, we have to\r
531     #   use threading.Event to communicate this formation to main thread.\r
532     #\r
533     @staticmethod\r
534     def HasError():\r
535         return BuildTask._ErrorFlag.isSet()\r
536 \r
537     ## Get error message in running thread\r
538     #\r
539     #   Since the main thread cannot catch exceptions in other thread, we have to\r
540     #   use a static variable to communicate this message to main thread.\r
541     #\r
542     @staticmethod\r
543     def GetErrorMessage():\r
544         return BuildTask._ErrorMessage\r
545 \r
546     ## Factory method to create a BuildTask object\r
547     #\r
548     #   This method will check if a module is building or has been built. And if\r
549     #   true, just return the associated BuildTask object in the _TaskQueue. If\r
550     #   not, create and return a new BuildTask object. The new BuildTask object\r
551     #   will be appended to the _PendingQueue for scheduling later.\r
552     #\r
553     #   @param  BuildItem       A BuildUnit object representing a build object\r
554     #   @param  Dependency      The dependent build object of BuildItem\r
555     #\r
556     @staticmethod\r
557     def New(BuildItem, Dependency=None):\r
558         if BuildItem in BuildTask._TaskQueue:\r
559             Bt = BuildTask._TaskQueue[BuildItem]\r
560             return Bt\r
561 \r
562         Bt = BuildTask()\r
563         Bt._Init(BuildItem, Dependency)\r
564         BuildTask._TaskQueue[BuildItem] = Bt\r
565 \r
566         BuildTask._PendingQueueLock.acquire()\r
567         BuildTask._PendingQueue[BuildItem] = Bt\r
568         BuildTask._PendingQueueLock.release()\r
569 \r
570         return Bt\r
571 \r
572     ## The real constructor of BuildTask\r
573     #\r
574     #   @param  BuildItem       A BuildUnit object representing a build object\r
575     #   @param  Dependency      The dependent build object of BuildItem\r
576     #\r
577     def _Init(self, BuildItem, Dependency=None):\r
578         self.BuildItem = BuildItem\r
579 \r
580         self.DependencyList = []\r
581         if Dependency == None:\r
582             Dependency = BuildItem.Dependency\r
583         else:\r
584             Dependency.extend(BuildItem.Dependency)\r
585         self.AddDependency(Dependency)\r
586         # flag indicating build completes, used to avoid unnecessary re-build\r
587         self.CompleteFlag = False\r
588 \r
589     ## Check if all dependent build tasks are completed or not\r
590     #\r
591     def IsReady(self):\r
592         ReadyFlag = True\r
593         for Dep in self.DependencyList:\r
594             if Dep.CompleteFlag == True:\r
595                 continue\r
596             ReadyFlag = False\r
597             break\r
598 \r
599         return ReadyFlag\r
600 \r
601     ## Add dependent build task\r
602     #\r
603     #   @param  Dependency      The list of dependent build objects\r
604     #\r
605     def AddDependency(self, Dependency):\r
606         for Dep in Dependency:\r
607             self.DependencyList.append(BuildTask.New(Dep))    # BuildTask list\r
608 \r
609     ## The thread wrapper of LaunchCommand function\r
610     #\r
611     # @param  Command               A list or string contains the call of the command\r
612     # @param  WorkingDir            The directory in which the program will be running\r
613     #\r
614     def _CommandThread(self, Command, WorkingDir):\r
615         try:\r
616             LaunchCommand(Command, WorkingDir)\r
617             self.CompleteFlag = True\r
618         except:\r
619             #\r
620             # TRICK: hide the output of threads left runing, so that the user can\r
621             #        catch the error message easily\r
622             #\r
623             if not BuildTask._ErrorFlag.isSet():\r
624                 GlobalData.gBuildingModule = "%s [%s, %s, %s]" % (str(self.BuildItem.BuildObject),\r
625                                                                   self.BuildItem.BuildObject.Arch,\r
626                                                                   self.BuildItem.BuildObject.ToolChain,\r
627                                                                   self.BuildItem.BuildObject.BuildTarget\r
628                                                                  )\r
629             EdkLogger.SetLevel(EdkLogger.ERROR)\r
630             BuildTask._ErrorFlag.set()\r
631             BuildTask._ErrorMessage = "%s broken\n    %s [%s]" % \\r
632                                       (threading.currentThread().getName(), Command, WorkingDir)\r
633         # indicate there's a thread is available for another build task\r
634         BuildTask._RunningQueueLock.acquire()\r
635         BuildTask._RunningQueue.pop(self.BuildItem)\r
636         BuildTask._RunningQueueLock.release()\r
637         BuildTask._Thread.release()\r
638 \r
639     ## Start build task thread\r
640     #\r
641     def Start(self):\r
642         EdkLogger.quiet("Building ... %s" % repr(self.BuildItem))\r
643         Command = self.BuildItem.BuildCommand + [self.BuildItem.Target]\r
644         self.BuildTread = Thread(target=self._CommandThread, args=(Command, self.BuildItem.WorkingDir))\r
645         self.BuildTread.setName("build thread")\r
646         self.BuildTread.setDaemon(False)\r
647         self.BuildTread.start()\r
648 \r
649 ## The class contains the information related to EFI image\r
650 #\r
651 class PeImageInfo():\r
652     ## Constructor\r
653     #\r
654     # Constructor will load all required image information.\r
655     #\r
656     #   @param  BaseName          The full file path of image. \r
657     #   @param  Guid              The GUID for image.\r
658     #   @param  Arch              Arch of this image.\r
659     #   @param  OutpuDir          The output directory for image.\r
660     #   @param  ImageClass        PeImage Information\r
661     #\r
662     def __init__(self, BaseName, Guid, Arch, OutpuDir, ImageClass):\r
663         self.BaseName         = BaseName\r
664         self.Guid             = Guid\r
665         self.Arch             = Arch\r
666         self.OutpuDir         = OutpuDir\r
667         self.Image            = ImageClass\r
668         self.Image.Size       = (self.Image.Size / 0x1000 + 1) * 0x1000\r
669 \r
670 ## The class implementing the EDK2 build process\r
671 #\r
672 #   The build process includes:\r
673 #       1. Load configuration from target.txt and tools_def.txt in $(WORKSPACE)/Conf\r
674 #       2. Parse DSC file of active platform\r
675 #       3. Parse FDF file if any\r
676 #       4. Establish build database, including parse all other files (module, package)\r
677 #       5. Create AutoGen files (C code file, depex file, makefile) if necessary\r
678 #       6. Call build command\r
679 #\r
680 class Build():\r
681     ## Constructor\r
682     #\r
683     # Constructor will load all necessary configurations, parse platform, modules\r
684     # and packages and the establish a database for AutoGen.\r
685     #\r
686     #   @param  Target              The build command target, one of gSupportedTarget\r
687     #   @param  WorkspaceDir        The directory of workspace\r
688     #   @param  Platform            The DSC file of active platform\r
689     #   @param  Module              The INF file of active module, if any\r
690     #   @param  Arch                The Arch list of platform or module\r
691     #   @param  ToolChain           The name list of toolchain\r
692     #   @param  BuildTarget         The "DEBUG" or "RELEASE" build\r
693     #   @param  FlashDefinition     The FDF file of active platform\r
694     #   @param  FdList=[]           The FD names to be individually built\r
695     #   @param  FvList=[]           The FV names to be individually built\r
696     #   @param  MakefileType        The type of makefile (for MSFT make or GNU make)\r
697     #   @param  SilentMode          Indicate multi-thread build mode\r
698     #   @param  ThreadNumber        The maximum number of thread if in multi-thread build mode\r
699     #   @param  SkipAutoGen         Skip AutoGen step\r
700     #   @param  Reparse             Re-parse all meta files\r
701     #   @param  SkuId               SKU id from command line\r
702     #\r
703     def __init__(self, Target, WorkspaceDir, Platform, Module, Arch, ToolChain,\r
704                  BuildTarget, FlashDefinition, FdList=[], FvList=[],\r
705                  MakefileType="nmake", SilentMode=False, ThreadNumber=2,\r
706                  SkipAutoGen=False, Reparse=False, SkuId=None, \r
707                  ReportFile=None, ReportType=None):\r
708 \r
709         self.WorkspaceDir = WorkspaceDir\r
710         self.Target         = Target\r
711         self.PlatformFile   = Platform\r
712         self.ModuleFile     = Module\r
713         self.ArchList       = Arch\r
714         self.ToolChainList  = ToolChain\r
715         self.BuildTargetList= BuildTarget\r
716         self.Fdf            = FlashDefinition\r
717         self.FdList         = FdList\r
718         self.FvList         = FvList\r
719         self.MakefileType   = MakefileType\r
720         self.SilentMode     = SilentMode\r
721         self.ThreadNumber   = ThreadNumber\r
722         self.SkipAutoGen    = SkipAutoGen\r
723         self.Reparse        = Reparse\r
724         self.SkuId          = SkuId\r
725         self.SpawnMode      = True\r
726         self.BuildReport    = BuildReport(ReportFile, ReportType)\r
727         self.TargetTxt      = TargetTxtClassObject()\r
728         self.ToolDef        = ToolDefClassObject()\r
729         self.Db             = WorkspaceDatabase(None, GlobalData.gGlobalDefines, self.Reparse)\r
730         #self.Db             = WorkspaceDatabase(None, {}, self.Reparse)\r
731         self.BuildDatabase  = self.Db.BuildObject\r
732         self.Platform       = None\r
733         self.LoadFixAddress = 0\r
734 \r
735         # print dot charater during doing some time-consuming work\r
736         self.Progress = Utils.Progressor()\r
737 \r
738         # parse target.txt, tools_def.txt, and platform file\r
739         #self.RestoreBuildData()\r
740         self.LoadConfiguration()\r
741         self.InitBuild()\r
742 \r
743         # print current build environment and configuration\r
744         EdkLogger.quiet("%-24s = %s" % ("WORKSPACE", os.environ["WORKSPACE"]))\r
745         EdkLogger.quiet("%-24s = %s" % ("ECP_SOURCE", os.environ["ECP_SOURCE"]))\r
746         EdkLogger.quiet("%-24s = %s" % ("EDK_SOURCE", os.environ["EDK_SOURCE"]))\r
747         EdkLogger.quiet("%-24s = %s" % ("EFI_SOURCE", os.environ["EFI_SOURCE"]))\r
748         EdkLogger.quiet("%-24s = %s" % ("EDK_TOOLS_PATH", os.environ["EDK_TOOLS_PATH"]))\r
749 \r
750         EdkLogger.info('\n%-24s = %s' % ("TARGET_ARCH", ' '.join(self.ArchList)))\r
751         EdkLogger.info('%-24s = %s' % ("TARGET", ' '.join(self.BuildTargetList)))\r
752         EdkLogger.info('%-24s = %s' % ("TOOL_CHAIN_TAG", ' '.join(self.ToolChainList)))\r
753 \r
754         EdkLogger.info('\n%-24s = %s' % ("Active Platform", self.PlatformFile))\r
755 \r
756         if self.Fdf != None and self.Fdf != "":\r
757             EdkLogger.info('%-24s = %s' % ("Flash Image Definition", self.Fdf))\r
758 \r
759         if self.ModuleFile != None and self.ModuleFile != "":\r
760             EdkLogger.info('%-24s = %s' % ("Active Module", self.ModuleFile))\r
761 \r
762         os.chdir(self.WorkspaceDir)\r
763         self.Progress.Start("\nProcessing meta-data")\r
764 \r
765     ## Load configuration\r
766     #\r
767     #   This method will parse target.txt and get the build configurations.\r
768     #\r
769     def LoadConfiguration(self):\r
770         #\r
771         # Check target.txt and tools_def.txt and Init them\r
772         #\r
773         BuildConfigurationFile = os.path.normpath(os.path.join(self.WorkspaceDir, gBuildConfiguration))\r
774         if os.path.isfile(BuildConfigurationFile) == True:\r
775             StatusCode = self.TargetTxt.LoadTargetTxtFile(BuildConfigurationFile)\r
776 \r
777             ToolDefinitionFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_CONF]\r
778             if ToolDefinitionFile == '':\r
779                 ToolDefinitionFile = gToolsDefinition\r
780             ToolDefinitionFile = os.path.normpath(os.path.join(self.WorkspaceDir, ToolDefinitionFile))\r
781             if os.path.isfile(ToolDefinitionFile) == True:\r
782                 StatusCode = self.ToolDef.LoadToolDefFile(ToolDefinitionFile)\r
783             else:\r
784                 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=ToolDefinitionFile)\r
785         else:\r
786             EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=BuildConfigurationFile)\r
787 \r
788         # if no ARCH given in command line, get it from target.txt\r
789         if self.ArchList == None or len(self.ArchList) == 0:\r
790             self.ArchList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET_ARCH]\r
791 \r
792         # if no build target given in command line, get it from target.txt\r
793         if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
794             self.BuildTargetList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET]\r
795 \r
796         # if no tool chain given in command line, get it from target.txt\r
797         if self.ToolChainList == None or len(self.ToolChainList) == 0:\r
798             self.ToolChainList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_TAG]\r
799             if self.ToolChainList == None or len(self.ToolChainList) == 0:\r
800                 EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, ExtraData="No toolchain given. Don't know how to build.\n")\r
801 \r
802         # check if the tool chains are defined or not\r
803         NewToolChainList = []\r
804         for ToolChain in self.ToolChainList:\r
805             if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG]:\r
806                 EdkLogger.warn("build", "Tool chain [%s] is not defined" % ToolChain)\r
807             else:\r
808                 NewToolChainList.append(ToolChain)\r
809         # if no tool chain available, break the build\r
810         if len(NewToolChainList) == 0:\r
811             EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,\r
812                             ExtraData="[%s] not defined. No toolchain available for build!\n" % ", ".join(self.ToolChainList))\r
813         else:\r
814             self.ToolChainList = NewToolChainList\r
815 \r
816         if self.ThreadNumber == None:\r
817             self.ThreadNumber = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER]\r
818             if self.ThreadNumber == '':\r
819                 self.ThreadNumber = 0\r
820             else:\r
821                 self.ThreadNumber = int(self.ThreadNumber, 0)\r
822 \r
823         if self.ThreadNumber == 0:\r
824             self.ThreadNumber = 1\r
825 \r
826         if not self.PlatformFile:\r
827             PlatformFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_ACTIVE_PLATFORM]\r
828             if not PlatformFile:\r
829                 # Try to find one in current directory\r
830                 WorkingDirectory = os.getcwd()\r
831                 FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.dsc')))\r
832                 FileNum = len(FileList)\r
833                 if FileNum >= 2:\r
834                     EdkLogger.error("build", OPTION_MISSING,\r
835                                     ExtraData="There are %d DSC files in %s. Use '-p' to specify one.\n" % (FileNum, WorkingDirectory))\r
836                 elif FileNum == 1:\r
837                     PlatformFile = FileList[0]\r
838                 else:\r
839                     EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,\r
840                                     ExtraData="No active platform specified in target.txt or command line! Nothing can be built.\n")\r
841 \r
842             self.PlatformFile = PathClass(NormFile(PlatformFile, self.WorkspaceDir), self.WorkspaceDir)\r
843             ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False)\r
844             if ErrorCode != 0:\r
845                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
846 \r
847     ## Initialize build configuration\r
848     #\r
849     #   This method will parse DSC file and merge the configurations from\r
850     #   command line and target.txt, then get the final build configurations.\r
851     #\r
852     def InitBuild(self):\r
853         ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc")\r
854         if ErrorCode != 0:\r
855             EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
856 \r
857         # create metafile database\r
858         self.Db.InitDatabase()\r
859 \r
860         # we need information in platform description file to determine how to build\r
861         self.Platform = self.BuildDatabase[self.PlatformFile, 'COMMON']\r
862         if not self.Fdf:\r
863             self.Fdf = self.Platform.FlashDefinition\r
864         \r
865         LoadFixAddressString = None\r
866         if TAB_FIX_LOAD_TOP_MEMORY_ADDRESS in GlobalData.gGlobalDefines:\r
867             LoadFixAddressString = GlobalData.gGlobalDefines[TAB_FIX_LOAD_TOP_MEMORY_ADDRESS]\r
868         else:\r
869             LoadFixAddressString = self.Platform.LoadFixAddress\r
870 \r
871         if LoadFixAddressString != None and LoadFixAddressString != '':\r
872             try:\r
873                 if LoadFixAddressString.upper().startswith('0X'):\r
874                     self.LoadFixAddress = int (LoadFixAddressString, 16)\r
875                 else:\r
876                     self.LoadFixAddress = int (LoadFixAddressString)\r
877             except:
878                 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS %s is not valid dec or hex string" % (LoadFixAddressString))\r
879             if self.LoadFixAddress < 0:\r
880                 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS is set to the invalid negative value %s" % (LoadFixAddressString))\r
881             if self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress % 0x1000 != 0:\r
882                 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS is set to the invalid unaligned 4K value %s" % (LoadFixAddressString))\r
883 \r
884         if self.SkuId == None or self.SkuId == '':\r
885             self.SkuId = self.Platform.SkuName\r
886 \r
887         # check FD/FV build target\r
888         if self.Fdf == None or self.Fdf == "":\r
889             if self.FdList != []:\r
890                 EdkLogger.info("No flash definition file found. FD [%s] will be ignored." % " ".join(self.FdList))\r
891                 self.FdList = []\r
892             if self.FvList != []:\r
893                 EdkLogger.info("No flash definition file found. FV [%s] will be ignored." % " ".join(self.FvList))\r
894                 self.FvList = []\r
895         else:\r
896             FdfParserObj = FdfParser(str(self.Fdf))\r
897             FdfParserObj.ParseFile()\r
898             for fvname in self.FvList:\r
899                 if fvname.upper() not in FdfParserObj.Profile.FvDict.keys():\r
900                     EdkLogger.error("build", OPTION_VALUE_INVALID,\r
901                                     "No such an FV in FDF file: %s" % fvname)\r
902 \r
903         #\r
904         # Merge Arch\r
905         #\r
906         if self.ArchList == None or len(self.ArchList) == 0:\r
907             ArchList = set(self.Platform.SupArchList)\r
908         else:\r
909             ArchList = set(self.ArchList) & set(self.Platform.SupArchList)\r
910         if len(ArchList) == 0:\r
911             EdkLogger.error("build", PARAMETER_INVALID,\r
912                             ExtraData = "Active platform supports [%s] only, but [%s] is given."\r
913                                         % (" ".join(self.Platform.SupArchList), " ".join(self.ArchList)))\r
914         elif len(ArchList) != len(self.ArchList):\r
915             SkippedArchList = set(self.ArchList).symmetric_difference(set(self.Platform.SupArchList))\r
916             EdkLogger.verbose("\nArch [%s] is ignored because active platform supports [%s] but [%s] is specified !"\r
917                            % (" ".join(SkippedArchList), " ".join(self.Platform.SupArchList), " ".join(self.ArchList)))\r
918         self.ArchList = tuple(ArchList)\r
919 \r
920         # Merge build target\r
921         if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
922             BuildTargetList = self.Platform.BuildTargets\r
923         else:\r
924             BuildTargetList = list(set(self.BuildTargetList) & set(self.Platform.BuildTargets))\r
925         if BuildTargetList == []:\r
926             EdkLogger.error("build", PARAMETER_INVALID, "Active platform only supports [%s], but [%s] is given"\r
927                                 % (" ".join(self.Platform.BuildTargets), " ".join(self.BuildTargetList)))\r
928         self.BuildTargetList = BuildTargetList\r
929 \r
930     ## Build a module or platform\r
931     #\r
932     # Create autogen code and makfile for a module or platform, and the launch\r
933     # "make" command to build it\r
934     #\r
935     #   @param  Target                      The target of build command\r
936     #   @param  Platform                    The platform file\r
937     #   @param  Module                      The module file\r
938     #   @param  BuildTarget                 The name of build target, one of "DEBUG", "RELEASE"\r
939     #   @param  ToolChain                   The name of toolchain to build\r
940     #   @param  Arch                        The arch of the module/platform\r
941     #   @param  CreateDepModuleCodeFile     Flag used to indicate creating code\r
942     #                                       for dependent modules/Libraries\r
943     #   @param  CreateDepModuleMakeFile     Flag used to indicate creating makefile\r
944     #                                       for dependent modules/Libraries\r
945     #\r
946     def _Build(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True):\r
947         if AutoGenObject == None:\r
948             return False\r
949 \r
950         # skip file generation for cleanxxx targets, run and fds target\r
951         if Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
952             # for target which must generate AutoGen code and makefile\r
953             if not self.SkipAutoGen or Target == 'genc':\r
954                 self.Progress.Start("Generating code")\r
955                 AutoGenObject.CreateCodeFile(CreateDepsCodeFile)\r
956                 self.Progress.Stop("done!")\r
957             if Target == "genc":\r
958                 return True\r
959 \r
960             if not self.SkipAutoGen or Target == 'genmake':\r
961                 self.Progress.Start("Generating makefile")\r
962                 AutoGenObject.CreateMakeFile(CreateDepsMakeFile)\r
963                 self.Progress.Stop("done!")\r
964             if Target == "genmake":\r
965                 return True\r
966         else:\r
967             # always recreate top/platform makefile when clean, just in case of inconsistency\r
968             AutoGenObject.CreateCodeFile(False)\r
969             AutoGenObject.CreateMakeFile(False)\r
970 \r
971         if EdkLogger.GetLevel() == EdkLogger.QUIET:\r
972             EdkLogger.quiet("Building ... %s" % repr(AutoGenObject))\r
973 \r
974         BuildCommand = AutoGenObject.BuildCommand\r
975         if BuildCommand == None or len(BuildCommand) == 0:\r
976             EdkLogger.error("build", OPTION_MISSING, ExtraData="No MAKE command found for [%s, %s, %s]" % Key)\r
977 \r
978         BuildCommand = BuildCommand + [Target]\r
979         LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir)\r
980         if Target == 'cleanall':\r
981             try:\r
982                 #os.rmdir(AutoGenObject.BuildDir)\r
983                 RemoveDirectory(AutoGenObject.BuildDir, True)\r
984             except WindowsError, X:\r
985                 EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X))\r
986         return True\r
987 \r
988     ## Rebase module image and Get function address for the inpug module list.\r
989     #\r
990     def _RebaseModule (self, MapBuffer, BaseAddress, ModuleList, AddrIsOffset = True, ModeIsSmm = False):\r
991         if ModeIsSmm:\r
992             AddrIsOffset = False\r
993         InfFileNameList = ModuleList.keys()\r
994         #InfFileNameList.sort()\r
995         for InfFile in InfFileNameList:\r
996             sys.stdout.write (".")
997             sys.stdout.flush()
998             ModuleInfo = ModuleList[InfFile]\r
999             ModuleName = ModuleInfo.BaseName\r
1000             ## for SMM module in SMRAM, the SMRAM will be allocated from base to top.\r
1001             if not ModeIsSmm:\r
1002                 BaseAddress = BaseAddress - ModuleInfo.Image.Size\r
1003                 #\r
1004                 # Update Image to new BaseAddress by GenFw tool\r
1005                 #\r
1006                 LaunchCommand(["GenFw", "--rebase", str(BaseAddress), "-r", ModuleInfo.Image.FileName], ModuleInfo.OutpuDir)\r
1007             else:\r
1008                 #\r
1009                 # Set new address to the section header only for SMM driver.\r
1010                 #\r
1011                 LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleInfo.Image.FileName], ModuleInfo.OutpuDir)\r
1012             #\r
1013             # Collect funtion address from Map file\r
1014             #\r
1015             ImageMapTable = ModuleInfo.Image.FileName.replace('.efi', '.map')\r
1016             FunctionList = []\r
1017             if os.path.exists(ImageMapTable):\r
1018                 OrigImageBaseAddress = 0\r
1019                 ImageMap = open (ImageMapTable, 'r')\r
1020                 for LinStr in ImageMap:\r
1021                     if len (LinStr.strip()) == 0:\r
1022                         continue\r
1023                     #\r
1024                     # Get the preferred address set on link time.\r
1025                     #\r
1026                     if LinStr.find ('Preferred load address is') != -1:\r
1027                         StrList = LinStr.split()\r
1028                         OrigImageBaseAddress = int (StrList[len(StrList) - 1], 16)\r
1029 \r
1030                     StrList = LinStr.split()\r
1031                     if len (StrList) > 4:\r
1032                         if StrList[3] == 'f' or StrList[3] =='F':\r
1033                             Name = StrList[1]\r
1034                             RelativeAddress = int (StrList[2], 16) - OrigImageBaseAddress\r
1035                             FunctionList.append ((Name, RelativeAddress))\r
1036                             if ModuleInfo.Arch == 'IPF' and Name.endswith('_ModuleEntryPoint'):\r
1037                                 #\r
1038                                 # Get the real entry point address for IPF image.\r
1039                                 #\r
1040                                 ModuleInfo.Image.EntryPoint = RelativeAddress\r
1041                 ImageMap.close()\r
1042             #\r
1043             # Add general information.\r
1044             #\r
1045             if ModeIsSmm:\r
1046                 MapBuffer.write('\n\n%s (Fixed SMRAM Offset,   BaseAddress=0x%010X,  EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint))\r
1047             elif AddrIsOffset:\r
1048                 MapBuffer.write('\n\n%s (Fixed Memory Offset,  BaseAddress=-0x%010X, EntryPoint=-0x%010X)\n' % (ModuleName, 0 - BaseAddress, 0 - (BaseAddress + ModuleInfo.Image.EntryPoint)))\r
1049             else:\r
1050                 MapBuffer.write('\n\n%s (Fixed Memory Address, BaseAddress=0x%010X,  EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint))\r
1051             #\r
1052             # Add guid and general seciton section.\r
1053             #\r
1054             TextSectionAddress = 0\r
1055             DataSectionAddress = 0\r
1056             for SectionHeader in ModuleInfo.Image.SectionHeaderList:\r
1057                 if SectionHeader[0] == '.text':\r
1058                     TextSectionAddress = SectionHeader[1]\r
1059                 elif SectionHeader[0] in ['.data', '.sdata']:\r
1060                     DataSectionAddress = SectionHeader[1]\r
1061             if AddrIsOffset:\r
1062                 MapBuffer.write('(GUID=%s, .textbaseaddress=-0x%010X, .databaseaddress=-0x%010X)\n\n' % (ModuleInfo.Guid, 0 - (BaseAddress + TextSectionAddress), 0 - (BaseAddress + DataSectionAddress))) \r
1063             else:\r
1064                 MapBuffer.write('(GUID=%s, .textbaseaddress=0x%010X, .databaseaddress=0x%010X)\n\n' % (ModuleInfo.Guid, BaseAddress + TextSectionAddress, BaseAddress + DataSectionAddress)) \r
1065             #\r
1066             # Add funtion address\r
1067             #\r
1068             for Function in FunctionList:\r
1069                 if AddrIsOffset:\r
1070                     MapBuffer.write('  -0x%010X    %s\n' % (0 - (BaseAddress + Function[1]), Function[0]))\r
1071                 else:\r
1072                     MapBuffer.write('  0x%010X    %s\n' % (BaseAddress + Function[1], Function[0]))\r
1073             ImageMap.close()\r
1074 \r
1075             #\r
1076             # for SMM module in SMRAM, the SMRAM will be allocated from base to top.\r
1077             #\r
1078             if ModeIsSmm:\r
1079                 BaseAddress = BaseAddress + ModuleInfo.Image.Size\r
1080 \r
1081     ## Collect MAP information of all FVs\r
1082     #\r
1083     def _CollectFvMapBuffer (self, MapBuffer, Wa):\r
1084         if self.Fdf != '':\r
1085             # First get the XIP base address for FV map file.\r
1086             for FvName in Wa.FdfProfile.FvDict.keys():\r
1087                 FvMapBuffer = os.path.join(Wa.FvDir, FvName + '.Fv.map')\r
1088                 if not os.path.exists(FvMapBuffer):\r
1089                     continue\r
1090                 FvMap = open (FvMapBuffer, 'r')\r
1091                 #skip FV size information\r
1092                 FvMap.readline()\r
1093                 FvMap.readline()\r
1094                 FvMap.readline()\r
1095                 FvMap.readline()\r
1096                 MapBuffer.write(FvMap.read())\r
1097                 FvMap.close()\r
1098 \r
1099     ## Collect MAP information of all modules\r
1100     #\r
1101     def _CollectModuleMapBuffer (self, MapBuffer, ModuleList):\r
1102         sys.stdout.write ("Generate Load Module At Fix Address Map")
1103         sys.stdout.flush()
1104         PatchEfiImageList = []\r
1105         PeiModuleList  = {}\r
1106         BtModuleList   = {}\r
1107         RtModuleList   = {}\r
1108         SmmModuleList  = {}\r
1109         PeiSize = 0\r
1110         BtSize  = 0\r
1111         RtSize  = 0\r
1112         # reserve 4K size in SMRAM to make SMM module address not from 0.\r
1113         SmmSize = 0x1000\r
1114         IsIpfPlatform = False\r
1115         if 'IPF' in self.ArchList:\r
1116             IsIpfPlatform = True\r
1117         for Module in ModuleList:\r
1118             GlobalData.gProcessingFile = "%s [%s, %s, %s]" % (Module.MetaFile, Module.Arch, Module.ToolChain, Module.BuildTarget)\r
1119             \r
1120             OutputImageFile = ''\r
1121             for ResultFile in Module.CodaTargetList:\r
1122                 if str(ResultFile.Target).endswith('.efi'):\r
1123                     #\r
1124                     # module list for PEI, DXE, RUNTIME and SMM\r
1125                     #\r
1126                     OutputImageFile = os.path.join(Module.OutputDir, Module.Name + '.efi')\r
1127                     ImageClass = PeImageClass (OutputImageFile)\r
1128                     if not ImageClass.IsValid:\r
1129                         EdkLogger.error("build", FILE_PARSE_FAILURE, ExtraData=ImageClass.ErrorInfo)\r
1130                     ImageInfo = PeImageInfo(Module.Name, Module.Guid, Module.Arch, Module.OutputDir, ImageClass)\r
1131                     if Module.ModuleType in ['PEI_CORE', 'PEIM', 'COMBINED_PEIM_DRIVER','PIC_PEIM', 'RELOCATABLE_PEIM', 'DXE_CORE']:\r
1132                         PeiModuleList[Module.MetaFile] = ImageInfo\r
1133                         PeiSize += ImageInfo.Image.Size\r
1134                     elif Module.ModuleType in ['BS_DRIVER', 'DXE_DRIVER', 'UEFI_DRIVER']:\r
1135                         BtModuleList[Module.MetaFile] = ImageInfo\r
1136                         BtSize += ImageInfo.Image.Size\r
1137                     elif Module.ModuleType in ['DXE_RUNTIME_DRIVER', 'RT_DRIVER', 'DXE_SAL_DRIVER', 'SAL_RT_DRIVER']:\r
1138                         RtModuleList[Module.MetaFile] = ImageInfo\r
1139                         #IPF runtime driver needs to be at 2 page alignment.\r
1140                         if IsIpfPlatform and ImageInfo.Image.Size % 0x2000 != 0:\r
1141                             ImageInfo.Image.Size = (ImageInfo.Image.Size / 0x2000 + 1) * 0x2000\r
1142                         RtSize += ImageInfo.Image.Size\r
1143                     elif Module.ModuleType in ['SMM_CORE', 'DXE_SMM_DRIVER']:\r
1144                         SmmModuleList[Module.MetaFile] = ImageInfo\r
1145                         SmmSize += ImageInfo.Image.Size\r
1146                         if Module.ModuleType == 'DXE_SMM_DRIVER':\r
1147                             PiSpecVersion = 0
1148                             if 'PI_SPECIFICATION_VERSION' in Module.Module.Specification:
1149                                 PiSpecVersion = Module.Module.Specification['PI_SPECIFICATION_VERSION']
1150                             # for PI specification < PI1.1, DXE_SMM_DRIVER also runs as BOOT time driver.\r
1151                             if PiSpecVersion < 0x0001000A:\r
1152                                 BtModuleList[Module.MetaFile] = ImageInfo\r
1153                                 BtSize += ImageInfo.Image.Size\r
1154                     break\r
1155             #\r
1156             # EFI image is final target.\r
1157             # Check EFI image contains patchable FixAddress related PCDs.\r
1158             #\r
1159             if OutputImageFile != '':\r
1160                 ModuleIsPatch = False\r
1161                 for Pcd in Module.ModulePcdList:\r
1162                     if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_LIST:\r
1163                         ModuleIsPatch = True\r
1164                         break\r
1165                 if not ModuleIsPatch:\r
1166                     for Pcd in Module.LibraryPcdList:\r
1167                         if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_LIST:\r
1168                             ModuleIsPatch = True\r
1169                             break\r
1170                 \r
1171                 if not ModuleIsPatch:\r
1172                     continue\r
1173                 #\r
1174                 # Module includes the patchable load fix address PCDs.\r
1175                 # It will be fixed up later. \r
1176                 #\r
1177                 PatchEfiImageList.append (OutputImageFile)\r
1178         \r
1179         #\r
1180         # Get Top Memory address\r
1181         #\r
1182         ReservedRuntimeMemorySize = 0\r
1183         TopMemoryAddress = 0\r
1184         if self.LoadFixAddress == 0xFFFFFFFFFFFFFFFF:\r
1185             TopMemoryAddress = 0\r
1186         else:\r
1187             TopMemoryAddress = self.LoadFixAddress\r
1188             if TopMemoryAddress < RtSize + BtSize + PeiSize:\r
1189                 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS is too low to load driver")\r
1190             # Make IPF runtime driver at 2 page alignment.\r
1191             if IsIpfPlatform:\r
1192                 ReservedRuntimeMemorySize = TopMemoryAddress % 0x2000\r
1193                 RtSize = RtSize + ReservedRuntimeMemorySize\r
1194 \r
1195         #\r
1196         # Patch FixAddress related PCDs into EFI image\r
1197         #\r
1198         for EfiImage in PatchEfiImageList: \r
1199             EfiImageMap = EfiImage.replace('.efi', '.map')\r
1200             if not os.path.exists(EfiImageMap):\r
1201                 continue\r
1202             #\r
1203             # Get PCD offset in EFI image by GenPatchPcdTable function\r
1204             #\r
1205             PcdTable = parsePcdInfoFromMapFile(EfiImageMap, EfiImage) 
1206             #\r
1207             # Patch real PCD value by PatchPcdValue tool\r
1208             #\r
1209             for PcdInfo in PcdTable:\r
1210                 ReturnValue = 0\r
1211                 if PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE:\r
1212                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE_DATA_TYPE, str (PeiSize/0x1000))\r
1213                 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE:\r
1214                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE_DATA_TYPE, str (BtSize/0x1000))\r
1215                 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE:\r
1216                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE_DATA_TYPE, str (RtSize/0x1000))\r
1217                 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE and len (SmmModuleList) > 0:\r
1218                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE_DATA_TYPE, str (SmmSize/0x1000))\r
1219                 if ReturnValue != 0:\r
1220                     EdkLogger.error("build", PARAMETER_INVALID, "Patch PCD value failed", ExtraData=ErrorInfo)\r
1221         \r
1222         MapBuffer.write('PEI_CODE_PAGE_NUMBER      = 0x%x\n' % (PeiSize/0x1000))\r
1223         MapBuffer.write('BOOT_CODE_PAGE_NUMBER     = 0x%x\n' % (BtSize/0x1000))\r
1224         MapBuffer.write('RUNTIME_CODE_PAGE_NUMBER  = 0x%x\n' % (RtSize/0x1000))\r
1225         if len (SmmModuleList) > 0:\r
1226             MapBuffer.write('SMM_CODE_PAGE_NUMBER      = 0x%x\n' % (SmmSize/0x1000))\r
1227         \r
1228         PeiBaseAddr = TopMemoryAddress - RtSize - BtSize\r
1229         BtBaseAddr  = TopMemoryAddress - RtSize\r
1230         RtBaseAddr  = TopMemoryAddress - ReservedRuntimeMemorySize\r
1231 \r
1232         self._RebaseModule (MapBuffer, PeiBaseAddr, PeiModuleList, TopMemoryAddress == 0)\r
1233         self._RebaseModule (MapBuffer, BtBaseAddr, BtModuleList, TopMemoryAddress == 0)\r
1234         self._RebaseModule (MapBuffer, RtBaseAddr, RtModuleList, TopMemoryAddress == 0)\r
1235         self._RebaseModule (MapBuffer, 0x1000, SmmModuleList, AddrIsOffset = False, ModeIsSmm = True)\r
1236         MapBuffer.write('\n\n')\r
1237         sys.stdout.write ("\n")
1238         sys.stdout.flush()
1239     \r
1240     ## Save platform Map file\r
1241     #\r
1242     def _SaveMapFile (self, MapBuffer, Wa):\r
1243         #\r
1244         # Map file path is got.\r
1245         #\r
1246         MapFilePath = os.path.join(Wa.BuildDir, Wa.Name + '.map')\r
1247         #\r
1248         # Save address map into MAP file.\r
1249         #\r
1250         SaveFileOnChange(MapFilePath, MapBuffer.getvalue(), False)\r
1251         MapBuffer.close()\r
1252         sys.stdout.write ("\nLoad Module At Fix Address Map file saved to %s\n" %(MapFilePath))
1253         sys.stdout.flush()
1254 \r
1255     ## Build active platform for different build targets and different tool chains\r
1256     #\r
1257     def _BuildPlatform(self):\r
1258         for BuildTarget in self.BuildTargetList:\r
1259             for ToolChain in self.ToolChainList:\r
1260                 Wa = WorkspaceAutoGen(\r
1261                         self.WorkspaceDir,\r
1262                         self.Platform,\r
1263                         BuildTarget,\r
1264                         ToolChain,\r
1265                         self.ArchList,\r
1266                         self.BuildDatabase,\r
1267                         self.TargetTxt,\r
1268                         self.ToolDef,\r
1269                         self.Fdf,\r
1270                         self.FdList,\r
1271                         self.FvList,\r
1272                         self.SkuId\r
1273                         )\r
1274                 self.BuildReport.AddPlatformReport(Wa)\r
1275                 self.Progress.Stop("done!")\r
1276                 self._Build(self.Target, Wa)\r
1277                 \r
1278                 # Create MAP file when Load Fix Address is enabled.\r
1279                 if self.Target in ["", "all", "fds"] and self.LoadFixAddress != 0:\r
1280                     for Arch in self.ArchList:\r
1281                         #\r
1282                         # Check whether the set fix address is above 4G for 32bit image.\r
1283                         #\r
1284                         if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
1285                             EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS can't be set to larger than or equal to 4G for the platorm with IA32 or ARM arch modules")\r
1286                     #\r
1287                     # Get Module List\r
1288                     #\r
1289                     ModuleList = []\r
1290                     for Pa in Wa.AutoGenObjectList:\r
1291                         for Ma in Pa.ModuleAutoGenList:\r
1292                             if Ma == None:\r
1293                                 continue\r
1294                             if not Ma.IsLibrary:\r
1295                                 ModuleList.append (Ma)\r
1296 \r
1297                     MapBuffer = StringIO('')\r
1298                     #\r
1299                     # Rebase module to the preferred memory address before GenFds\r
1300                     #\r
1301                     self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
1302                     if self.Fdf != '':\r
1303                         #\r
1304                         # create FDS again for the updated EFI image\r
1305                         #\r
1306                         self._Build("fds", Wa)\r
1307                         #\r
1308                         # Create MAP file for all platform FVs after GenFds.\r
1309                         #\r
1310                         self._CollectFvMapBuffer(MapBuffer, Wa)\r
1311                     #\r
1312                     # Save MAP buffer into MAP file.\r
1313                     #\r
1314                     self._SaveMapFile (MapBuffer, Wa)\r
1315 \r
1316     ## Build active module for different build targets, different tool chains and different archs\r
1317     #\r
1318     def _BuildModule(self):\r
1319         for BuildTarget in self.BuildTargetList:\r
1320             for ToolChain in self.ToolChainList:\r
1321                 #\r
1322                 # module build needs platform build information, so get platform\r
1323                 # AutoGen first\r
1324                 #\r
1325                 Wa = WorkspaceAutoGen(\r
1326                         self.WorkspaceDir,\r
1327                         self.Platform,\r
1328                         BuildTarget,\r
1329                         ToolChain,\r
1330                         self.ArchList,\r
1331                         self.BuildDatabase,\r
1332                         self.TargetTxt,\r
1333                         self.ToolDef,\r
1334                         self.Fdf,\r
1335                         self.FdList,\r
1336                         self.FvList,\r
1337                         self.SkuId\r
1338                         )\r
1339                 self.BuildReport.AddPlatformReport(Wa)\r
1340                 Wa.CreateMakeFile(False)\r
1341                 self.Progress.Stop("done!")\r
1342                 MaList = []\r
1343                 for Arch in self.ArchList:\r
1344                     Ma = ModuleAutoGen(Wa, self.ModuleFile, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
1345                     if Ma == None: continue\r
1346                     MaList.append(Ma)\r
1347                     self._Build(self.Target, Ma)\r
1348                 if MaList == []:\r
1349                     EdkLogger.error(\r
1350                                 'build',\r
1351                                 BUILD_ERROR,\r
1352                                 "Module for [%s] is not a component of active platform."\\r
1353                                 " Please make sure that the ARCH and inf file path are"\\r
1354                                 " given in the same as in [%s]" %\\r
1355                                     (', '.join(self.ArchList), self.Platform),\r
1356                                 ExtraData=self.ModuleFile\r
1357                                 )\r
1358                 # Create MAP file when Load Fix Address is enabled.\r
1359                 if self.LoadFixAddress != 0 and self.Target == "fds" and self.Fdf != '':\r
1360                     for Arch in self.ArchList:\r
1361                         #\r
1362                         # Check whether the set fix address is above 4G for 32bit image.\r
1363                         #\r
1364                         if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
1365                             EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS can't be set to larger than or equal to 4G for the platorm with IA32 or ARM arch modules")\r
1366                     #\r
1367                     # Get Module List\r
1368                     #\r
1369                     ModuleList = []\r
1370                     for Pa in Wa.AutoGenObjectList:\r
1371                         for Ma in Pa.ModuleAutoGenList:\r
1372                             if Ma == None:\r
1373                                 continue\r
1374                             if not Ma.IsLibrary:\r
1375                                 ModuleList.append (Ma)\r
1376 \r
1377                     MapBuffer = StringIO('')\r
1378                     #\r
1379                     # Rebase module to the preferred memory address before GenFds\r
1380                     #\r
1381                     self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
1382                     #\r
1383                     # create FDS again for the updated EFI image\r
1384                     #\r
1385                     self._Build("fds", Wa)\r
1386                     #\r
1387                     # Create MAP file for all platform FVs after GenFds.\r
1388                     #\r
1389                     self._CollectFvMapBuffer(MapBuffer, Wa)\r
1390                     #\r
1391                     # Save MAP buffer into MAP file.\r
1392                     #\r
1393                     self._SaveMapFile (MapBuffer, Wa)\r
1394 \r
1395     ## Build a platform in multi-thread mode\r
1396     #\r
1397     def _MultiThreadBuildPlatform(self):\r
1398         for BuildTarget in self.BuildTargetList:\r
1399             for ToolChain in self.ToolChainList:\r
1400                 Wa = WorkspaceAutoGen(\r
1401                         self.WorkspaceDir,\r
1402                         self.Platform,\r
1403                         BuildTarget,\r
1404                         ToolChain,\r
1405                         self.ArchList,\r
1406                         self.BuildDatabase,\r
1407                         self.TargetTxt,\r
1408                         self.ToolDef,\r
1409                         self.Fdf,\r
1410                         self.FdList,\r
1411                         self.FvList,\r
1412                         self.SkuId\r
1413                         )\r
1414                 self.BuildReport.AddPlatformReport(Wa)\r
1415                 Wa.CreateMakeFile(False)\r
1416 \r
1417                 # multi-thread exit flag\r
1418                 ExitFlag = threading.Event()\r
1419                 ExitFlag.clear()\r
1420                 for Arch in self.ArchList:\r
1421                     Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)\r
1422                     if Pa == None:\r
1423                         continue\r
1424                     for Module in Pa.Platform.Modules:\r
1425                         # Get ModuleAutoGen object to generate C code file and makefile\r
1426                         Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
1427                         if Ma == None:\r
1428                             continue\r
1429                         # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'\r
1430                         if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
1431                             # for target which must generate AutoGen code and makefile\r
1432                             if not self.SkipAutoGen or self.Target == 'genc':\r
1433                                 Ma.CreateCodeFile(True)\r
1434                             if self.Target == "genc":\r
1435                                 continue\r
1436 \r
1437                             if not self.SkipAutoGen or self.Target == 'genmake':\r
1438                                 Ma.CreateMakeFile(True)\r
1439                             if self.Target == "genmake":\r
1440                                 continue\r
1441                         self.Progress.Stop("done!")\r
1442                         # Generate build task for the module\r
1443                         Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))\r
1444                         # Break build if any build thread has error\r
1445                         if BuildTask.HasError():\r
1446                             # we need a full version of makefile for platform\r
1447                             ExitFlag.set()\r
1448                             BuildTask.WaitForComplete()\r
1449                             Pa.CreateMakeFile(False)\r
1450                             EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1451                         # Start task scheduler\r
1452                         if not BuildTask.IsOnGoing():\r
1453                             BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)\r
1454 \r
1455                     # in case there's an interruption. we need a full version of makefile for platform\r
1456                     Pa.CreateMakeFile(False)\r
1457                     if BuildTask.HasError():\r
1458                         EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1459 \r
1460                 #\r
1461                 # All modules have been put in build tasks queue. Tell task scheduler\r
1462                 # to exit if all tasks are completed\r
1463                 #\r
1464                 ExitFlag.set()\r
1465                 BuildTask.WaitForComplete()\r
1466 \r
1467                 #\r
1468                 # Check for build error, and raise exception if one\r
1469                 # has been signaled.\r
1470                 #\r
1471                 if BuildTask.HasError():\r
1472                     EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1473 \r
1474                 # Create MAP file when Load Fix Address is enabled.\r
1475                 if self.Target in ["", "all", "fds"] and self.LoadFixAddress != 0:\r
1476                     for Arch in self.ArchList:\r
1477                         #\r
1478                         # Check whether the set fix address is above 4G for 32bit image.\r
1479                         #\r
1480                         if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
1481                             EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS can't be set to larger than or equal to 4G for the platorm with IA32 or ARM arch modules")\r
1482                     #\r
1483                     # Get Module List\r
1484                     #\r
1485                     ModuleList = []\r
1486                     for Pa in Wa.AutoGenObjectList:\r
1487                         for Ma in Pa.ModuleAutoGenList:\r
1488                             if Ma == None:\r
1489                                 continue\r
1490                             if not Ma.IsLibrary:\r
1491                                 ModuleList.append (Ma)\r
1492                     #\r
1493                     # Rebase module to the preferred memory address before GenFds\r
1494                     #\r
1495                     MapBuffer = StringIO('')\r
1496                     self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
1497 \r
1498                 # Generate FD image if there's a FDF file found\r
1499                 if self.Fdf != '' and self.Target in ["", "all", "fds"]:\r
1500                     LaunchCommand(Wa.BuildCommand + ["fds"], Wa.MakeFileDir)\r
1501 \r
1502                 # Create MAP file for all platform FV after GenFds\r
1503                 if self.Target in ["", "all", "fds"] and self.LoadFixAddress != 0:\r
1504                     if self.Fdf != '':\r
1505                         #\r
1506                         # Create MAP file for all platform FVs after GenFds.\r
1507                         #\r
1508                         self._CollectFvMapBuffer(MapBuffer, Wa)\r
1509                     #\r
1510                     # Save MAP buffer into MAP file.\r
1511                     #\r
1512                     self._SaveMapFile(MapBuffer, Wa)\r
1513 \r
1514     ## Generate GuidedSectionTools.txt in the FV directories.\r
1515     #\r
1516     def CreateGuidedSectionToolsFile(self):\r
1517         for Arch in self.ArchList:\r
1518             for BuildTarget in self.BuildTargetList:\r
1519                 for ToolChain in self.ToolChainList:\r
1520                     FvDir = os.path.join(\r
1521                                 self.WorkspaceDir,\r
1522                                 self.Platform.OutputDirectory,\r
1523                                 '_'.join((BuildTarget, ToolChain)),\r
1524                                 'FV'\r
1525                                 )\r
1526                     if not os.path.exists(FvDir):\r
1527                         continue\r
1528                     # Build up the list of supported architectures for this build\r
1529                     prefix = '%s_%s_%s_' % (BuildTarget, ToolChain, Arch)\r
1530 \r
1531                     # Look through the tool definitions for GUIDed tools\r
1532                     guidAttribs = []\r
1533                     for (attrib, value) in self.ToolDef.ToolsDefTxtDictionary.iteritems():\r
1534                         if attrib.upper().endswith('_GUID'):\r
1535                             split = attrib.split('_')\r
1536                             thisPrefix = '_'.join(split[0:3]) + '_'\r
1537                             if thisPrefix == prefix:\r
1538                                 guid = self.ToolDef.ToolsDefTxtDictionary[attrib]\r
1539                                 guid = guid.lower()\r
1540                                 toolName = split[3]\r
1541                                 path = '_'.join(split[0:4]) + '_PATH'\r
1542                                 path = self.ToolDef.ToolsDefTxtDictionary[path]\r
1543                                 path = self.GetFullPathOfTool(path)\r
1544                                 guidAttribs.append((guid, toolName, path))\r
1545 \r
1546                     # Write out GuidedSecTools.txt\r
1547                     toolsFile = os.path.join(FvDir, 'GuidedSectionTools.txt')\r
1548                     toolsFile = open(toolsFile, 'wt')\r
1549                     for guidedSectionTool in guidAttribs:\r
1550                         print >> toolsFile, ' '.join(guidedSectionTool)\r
1551                     toolsFile.close()\r
1552 \r
1553     ## Returns the full path of the tool.\r
1554     #\r
1555     def GetFullPathOfTool (self, tool):\r
1556         if os.path.exists(tool):\r
1557             return os.path.realpath(tool)\r
1558         else:\r
1559             # We need to search for the tool using the\r
1560             # PATH environment variable.\r
1561             for dirInPath in os.environ['PATH'].split(os.pathsep):\r
1562                 foundPath = os.path.join(dirInPath, tool)\r
1563                 if os.path.exists(foundPath):\r
1564                     return os.path.realpath(foundPath)\r
1565 \r
1566         # If the tool was not found in the path then we just return\r
1567         # the input tool.\r
1568         return tool\r
1569 \r
1570     ## Launch the module or platform build\r
1571     #\r
1572     def Launch(self):\r
1573         if self.ModuleFile == None or self.ModuleFile == "":\r
1574             if not self.SpawnMode or self.Target not in ["", "all"]:\r
1575                 self.SpawnMode = False\r
1576                 self._BuildPlatform()\r
1577             else:\r
1578                 self._MultiThreadBuildPlatform()\r
1579             self.CreateGuidedSectionToolsFile()\r
1580         else:\r
1581             self.SpawnMode = False\r
1582             self._BuildModule()\r
1583 \r
1584     ## Do some clean-up works when error occurred\r
1585     def Relinquish(self):\r
1586         OldLogLevel = EdkLogger.GetLevel()\r
1587         EdkLogger.SetLevel(EdkLogger.ERROR)\r
1588         #self.DumpBuildData()\r
1589         Utils.Progressor.Abort()\r
1590         if self.SpawnMode == True:\r
1591             BuildTask.Abort()\r
1592         EdkLogger.SetLevel(OldLogLevel)\r
1593 \r
1594     def DumpBuildData(self):\r
1595         CacheDirectory = os.path.join(self.WorkspaceDir, gBuildCacheDir)\r
1596         Utils.CreateDirectory(CacheDirectory)\r
1597         Utils.DataDump(Utils.gFileTimeStampCache, os.path.join(CacheDirectory, "gFileTimeStampCache"))\r
1598         Utils.DataDump(Utils.gDependencyDatabase, os.path.join(CacheDirectory, "gDependencyDatabase"))\r
1599 \r
1600     def RestoreBuildData(self):\r
1601         FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gFileTimeStampCache")\r
1602         if Utils.gFileTimeStampCache == {} and os.path.isfile(FilePath):\r
1603             Utils.gFileTimeStampCache = Utils.DataRestore(FilePath)\r
1604             if Utils.gFileTimeStampCache == None:\r
1605                 Utils.gFileTimeStampCache = {}\r
1606 \r
1607         FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gDependencyDatabase")\r
1608         if Utils.gDependencyDatabase == {} and os.path.isfile(FilePath):\r
1609             Utils.gDependencyDatabase = Utils.DataRestore(FilePath)\r
1610             if Utils.gDependencyDatabase == None:\r
1611                 Utils.gDependencyDatabase = {}\r
1612 \r
1613 def ParseDefines(DefineList=[]):\r
1614     DefineDict = {}\r
1615     if DefineList != None:\r
1616         for Define in DefineList:\r
1617             DefineTokenList = Define.split("=", 1)\r
1618             if len(DefineTokenList) == 1:\r
1619                 DefineDict[DefineTokenList[0]] = ""\r
1620             else:\r
1621                 DefineDict[DefineTokenList[0]] = DefineTokenList[1].strip()\r
1622     return DefineDict\r
1623 \r
1624 gParamCheck = []\r
1625 def SingleCheckCallback(option, opt_str, value, parser):\r
1626     if option not in gParamCheck:\r
1627         setattr(parser.values, option.dest, value)\r
1628         gParamCheck.append(option)\r
1629     else:\r
1630         parser.error("Option %s only allows one instance in command line!" % option)\r
1631 \r
1632 ## Parse command line options\r
1633 #\r
1634 # Using standard Python module optparse to parse command line option of this tool.\r
1635 #\r
1636 #   @retval Opt   A optparse.Values object containing the parsed options\r
1637 #   @retval Args  Target of build command\r
1638 #\r
1639 def MyOptionParser():\r
1640     Parser = OptionParser(description=__copyright__,version=__version__,prog="build.exe",usage="%prog [options] [all|fds|genc|genmake|clean|cleanall|cleanlib|modules|libraries|run]")\r
1641     Parser.add_option("-a", "--arch", action="append", type="choice", choices=['IA32','X64','IPF','EBC','ARM'], dest="TargetArch",\r
1642         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
1643     Parser.add_option("-p", "--platform", action="callback", type="string", dest="PlatformFile", callback=SingleCheckCallback,\r
1644         help="Build the platform specified by the DSC file name argument, overriding target.txt's ACTIVE_PLATFORM definition.")\r
1645     Parser.add_option("-m", "--module", action="callback", type="string", dest="ModuleFile", callback=SingleCheckCallback,\r
1646         help="Build the module specified by the INF file name argument.")\r
1647     Parser.add_option("-b", "--buildtarget", action="append", type="choice", choices=['DEBUG','RELEASE'], dest="BuildTarget",\r
1648         help="BuildTarget is one of list: DEBUG, RELEASE, which overrides target.txt's TARGET definition. To specify more TARGET, please repeat this option.")\r
1649     Parser.add_option("-t", "--tagname", action="append", type="string", dest="ToolChain",\r
1650         help="Using the Tool Chain Tagname to build the platform, overriding target.txt's TOOL_CHAIN_TAG definition.")\r
1651     Parser.add_option("-x", "--sku-id", action="callback", type="string", dest="SkuId", callback=SingleCheckCallback,\r
1652         help="Using this name of SKU ID to build the platform, overriding SKUID_IDENTIFIER in DSC file.")\r
1653 \r
1654     Parser.add_option("-n", action="callback", type="int", dest="ThreadNumber", callback=SingleCheckCallback,\r
1655         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
1656 \r
1657     Parser.add_option("-f", "--fdf", action="callback", type="string", dest="FdfFile", callback=SingleCheckCallback,\r
1658         help="The name of the FDF file to use, which overrides the setting in the DSC file.")\r
1659     Parser.add_option("-r", "--rom-image", action="append", type="string", dest="RomImage", default=[],\r
1660         help="The name of FD to be generated. The name must be from [FD] section in FDF file.")\r
1661     Parser.add_option("-i", "--fv-image", action="append", type="string", dest="FvImage", default=[],\r
1662         help="The name of FV to be generated. The name must be from [FV] section in FDF file.")\r
1663 \r
1664     Parser.add_option("-u", "--skip-autogen", action="store_true", dest="SkipAutoGen", help="Skip AutoGen step.")\r
1665     Parser.add_option("-e", "--re-parse", action="store_true", dest="Reparse", help="Re-parse all meta-data files.")\r
1666 \r
1667     Parser.add_option("-c", "--case-insensitive", action="store_true", dest="CaseInsensitive", help="Don't check case of file name.")\r
1668 \r
1669     # Parser.add_option("-D", "--define", action="append", dest="Defines", metavar="NAME[=[VALUE]]",\r
1670     #     help="Define global macro which can be used in DSC/DEC/INF files.")\r
1671 \r
1672     Parser.add_option("-w", "--warning-as-error", action="store_true", dest="WarningAsError", help="Treat warning in tools as error.")\r
1673     Parser.add_option("-j", "--log", action="store", dest="LogFile", help="Put log in specified file as well as on console.")\r
1674 \r
1675     Parser.add_option("-s", "--silent", action="store_true", type=None, dest="SilentMode",\r
1676         help="Make use of silent mode of (n)make.")\r
1677     Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")\r
1678     Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\\r
1679                                                                                "including library instances selected, final dependency expression, "\\r
1680                                                                                "and warning messages, etc.")\r
1681     Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")\r
1682     Parser.add_option("-D", "--define", action="append", type="string", dest="Macros", help="Macro: \"Name [= Value]\".")\r
1683 \r
1684     Parser.add_option("-y", "--report-file", action="store", dest="ReportFile", help="Create/overwrite the report to the specified filename.")\r
1685     Parser.add_option("-Y", "--report-type", action="append", type="choice", choices=['PCD','LIBRARY','FLASH','DEPEX','BUILD_FLAGS','FIXED_ADDRESS', 'EXECUTION_ORDER'], dest="ReportType", default=[],\r
1686         help="Flags that control the type of build report to generate.  Must be one of: [PCD, LIBRARY, FLASH, DEPEX, BUILD_FLAGS, FIXED_ADDRESS, EXECUTION_ORDER].  "\\r
1687              "To specify more than one flag, repeat this option on the command line and the default flag set is [PCD, LIBRARY, FLASH, DEPEX, BUILD_FLAGS, FIXED_ADDRESS]")\r
1688 \r
1689     (Opt, Args)=Parser.parse_args()\r
1690     return (Opt, Args)\r
1691 \r
1692 ## Tool entrance method\r
1693 #\r
1694 # This method mainly dispatch specific methods per the command line options.\r
1695 # If no error found, return zero value so the caller of this tool can know\r
1696 # if it's executed successfully or not.\r
1697 #\r
1698 #   @retval 0     Tool was successful\r
1699 #   @retval 1     Tool failed\r
1700 #\r
1701 def Main():\r
1702     StartTime = time.time()\r
1703 \r
1704     # Initialize log system\r
1705     EdkLogger.Initialize()\r
1706 \r
1707     #\r
1708     # Parse the options and args\r
1709     #\r
1710     (Option, Target) = MyOptionParser()\r
1711     GlobalData.gOptions = Option\r
1712     GlobalData.gCaseInsensitive = Option.CaseInsensitive\r
1713 \r
1714     # Set log level\r
1715     if Option.verbose != None:\r
1716         EdkLogger.SetLevel(EdkLogger.VERBOSE)\r
1717     elif Option.quiet != None:\r
1718         EdkLogger.SetLevel(EdkLogger.QUIET)\r
1719     elif Option.debug != None:\r
1720         EdkLogger.SetLevel(Option.debug + 1)\r
1721     else:\r
1722         EdkLogger.SetLevel(EdkLogger.INFO)\r
1723 \r
1724     if Option.LogFile != None:\r
1725         EdkLogger.SetLogFile(Option.LogFile)\r
1726 \r
1727     if Option.WarningAsError == True:\r
1728         EdkLogger.SetWarningAsError()\r
1729 \r
1730     if platform.platform().find("Windows") >= 0:\r
1731         GlobalData.gIsWindows = True\r
1732     else:\r
1733         GlobalData.gIsWindows = False\r
1734 \r
1735     EdkLogger.quiet(time.strftime("%H:%M:%S, %b.%d %Y ", time.localtime()) + "[%s]\n" % platform.platform())\r
1736     ReturnCode = 0\r
1737     MyBuild = None\r
1738     try:\r
1739         if len(Target) == 0:\r
1740             Target = "all"\r
1741         elif len(Target) >= 2:\r
1742             EdkLogger.error("build", OPTION_NOT_SUPPORTED, "More than one targets are not supported.",\r
1743                             ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))\r
1744         else:\r
1745             Target = Target[0].lower()\r
1746 \r
1747         if Target not in gSupportedTarget:\r
1748             EdkLogger.error("build", OPTION_NOT_SUPPORTED, "Not supported target [%s]." % Target,\r
1749                             ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))\r
1750 \r
1751         GlobalData.gGlobalDefines = ParseDefines(Option.Macros)\r
1752         #\r
1753         # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH\r
1754         #\r
1755         CheckEnvVariable()\r
1756         Workspace = os.getenv("WORKSPACE")\r
1757         #\r
1758         # Get files real name in workspace dir\r
1759         #\r
1760         GlobalData.gAllFiles = Utils.DirCache(Workspace)\r
1761 \r
1762         WorkingDirectory = os.getcwd()\r
1763         if not Option.ModuleFile:\r
1764             FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.inf')))\r
1765             FileNum = len(FileList)\r
1766             if FileNum >= 2:\r
1767                 EdkLogger.error("build", OPTION_NOT_SUPPORTED, "There are %d INF files in %s." % (FileNum, WorkingDirectory),\r
1768                                 ExtraData="Please use '-m <INF_FILE_PATH>' switch to choose one.")\r
1769             elif FileNum == 1:\r
1770                 Option.ModuleFile = NormFile(FileList[0], Workspace)\r
1771 \r
1772         if Option.ModuleFile:\r
1773             if os.path.isabs (Option.ModuleFile):\r
1774                 if os.path.normcase (os.path.normpath(Option.ModuleFile)).find (Workspace) == 0:\r
1775                     Option.ModuleFile = NormFile(os.path.normpath(Option.ModuleFile), Workspace)\r
1776             Option.ModuleFile = PathClass(Option.ModuleFile, Workspace)\r
1777             ErrorCode, ErrorInfo = Option.ModuleFile.Validate(".inf", False)\r
1778             if ErrorCode != 0:\r
1779                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
1780 \r
1781         if Option.PlatformFile != None:\r
1782             if os.path.isabs (Option.PlatformFile):\r
1783                 if os.path.normcase (os.path.normpath(Option.PlatformFile)).find (Workspace) == 0:\r
1784                     Option.PlatformFile = NormFile(os.path.normpath(Option.PlatformFile), Workspace)\r
1785             Option.PlatformFile = PathClass(Option.PlatformFile, Workspace)\r
1786             ErrorCode, ErrorInfo = Option.PlatformFile.Validate(".dsc", False)\r
1787             if ErrorCode != 0:\r
1788                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
1789 \r
1790         if Option.FdfFile != None:\r
1791             if os.path.isabs (Option.FdfFile):\r
1792                 if os.path.normcase (os.path.normpath(Option.FdfFile)).find (Workspace) == 0:\r
1793                     Option.FdfFile = NormFile(os.path.normpath(Option.FdfFile), Workspace)\r
1794             Option.FdfFile = PathClass(Option.FdfFile, Workspace)\r
1795             ErrorCode, ErrorInfo = Option.FdfFile.Validate(".fdf", False)\r
1796             if ErrorCode != 0:\r
1797                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
1798 \r
1799         MyBuild = Build(Target, Workspace, Option.PlatformFile, Option.ModuleFile,\r
1800                         Option.TargetArch, Option.ToolChain, Option.BuildTarget,\r
1801                         Option.FdfFile, Option.RomImage, Option.FvImage,\r
1802                         None, Option.SilentMode, Option.ThreadNumber,\r
1803                         Option.SkipAutoGen, Option.Reparse, Option.SkuId, \r
1804                         Option.ReportFile, Option.ReportType)\r
1805         MyBuild.Launch()\r
1806         #MyBuild.DumpBuildData()\r
1807     except FatalError, X:\r
1808         if MyBuild != None:\r
1809             # for multi-thread build exits safely\r
1810             MyBuild.Relinquish()\r
1811         if Option != None and Option.debug != None:\r
1812             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1813         ReturnCode = X.args[0]\r
1814     except Warning, X:\r
1815         # error from Fdf parser\r
1816         if MyBuild != None:\r
1817             # for multi-thread build exits safely\r
1818             MyBuild.Relinquish()\r
1819         if Option != None and Option.debug != None:\r
1820             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1821         else:\r
1822             EdkLogger.error(X.ToolName, FORMAT_INVALID, File=X.FileName, Line=X.LineNumber, ExtraData=X.Message, RaiseError = False)\r
1823         ReturnCode = FORMAT_INVALID\r
1824     except KeyboardInterrupt:\r
1825         ReturnCode = ABORT_ERROR\r
1826         if Option != None and Option.debug != None:\r
1827             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1828     except:\r
1829         if MyBuild != None:\r
1830             # for multi-thread build exits safely\r
1831             MyBuild.Relinquish()\r
1832 \r
1833         # try to get the meta-file from the object causing exception\r
1834         Tb = sys.exc_info()[-1]\r
1835         MetaFile = GlobalData.gProcessingFile\r
1836         while Tb != None:\r
1837             if 'self' in Tb.tb_frame.f_locals and hasattr(Tb.tb_frame.f_locals['self'], 'MetaFile'):\r
1838                 MetaFile = Tb.tb_frame.f_locals['self'].MetaFile\r
1839             Tb = Tb.tb_next\r
1840         EdkLogger.error(\r
1841                     "\nbuild",\r
1842                     CODE_ERROR,\r
1843                     "Unknown fatal error when processing [%s]" % MetaFile,\r
1844                     ExtraData="\n(Please send email to edk2-buildtools-devel@lists.sourceforge.net for help, attaching following call stack trace!)\n",\r
1845                     RaiseError=False\r
1846                     )\r
1847         EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1848         ReturnCode = CODE_ERROR\r
1849     finally:\r
1850         Utils.Progressor.Abort()\r
1851 \r
1852     if ReturnCode == 0:\r
1853         Conclusion = "Done"\r
1854     elif ReturnCode == ABORT_ERROR:\r
1855         Conclusion = "Aborted"\r
1856     else:\r
1857         Conclusion = "Failed"\r
1858     FinishTime = time.time()\r
1859     BuildDuration = time.strftime("%M:%S", time.gmtime(int(round(FinishTime - StartTime))))\r
1860     if MyBuild != None:\r
1861         MyBuild.BuildReport.GenerateReport(BuildDuration)\r
1862         MyBuild.Db.Close()\r
1863     EdkLogger.SetLevel(EdkLogger.QUIET)\r
1864     EdkLogger.quiet("\n- %s -\n%s [%s]" % (Conclusion, time.strftime("%H:%M:%S, %b.%d %Y", time.localtime()), BuildDuration))\r
1865 \r
1866     return ReturnCode\r
1867 \r
1868 if __name__ == '__main__':\r
1869     r = Main()\r
1870     ## 0-127 is a safe return range, and 1 is a standard default error\r
1871     if r < 0 or r > 127: r = 1\r
1872     sys.exit(r)\r
1873 \r