e3a3dd9f3fc3368f3e5d726f51fe588b3111f27a
[mirror/efi/basetools/.git] / Source / Python / build / build.py
1 ## @file\r
2 # build a platform or a module\r
3 #\r
4 #  Copyright (c) 2007 - 2010, Intel Corporation\r
5 #\r
6 #  All rights reserved. This program and the accompanying materials\r
7 #  are licensed and made available under the terms and conditions of the BSD License\r
8 #  which accompanies this distribution.  The full text of the license may be found at\r
9 #  http://opensource.org/licenses/bsd-license.php\r
10 #\r
11 #  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12 #  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13 #\r
14 \r
15 ##\r
16 # Import Modules\r
17 #\r
18 import os\r
19 import re\r
20 import 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         \r
742         #\r
743         # @attention Treat $(TARGET) in meta data files as special macro when it has only one build target.\r
744         # This is not a complete support for $(TARGET) macro as it can only support one build target in ONE\r
745         # invocation of build command. However, it should cover the frequent usage model that $(TARGET) macro\r
746         # is used in DSC files to specify different libraries & PCD setting for debug/release build.\r
747         #\r
748         if len(self.BuildTargetList) == 1:\r
749             self.Db._GlobalMacros.setdefault("TARGET", self.BuildTargetList[0])\r
750         \r
751         self.InitBuild()\r
752 \r
753         # print current build environment and configuration\r
754         EdkLogger.quiet("%-24s = %s" % ("WORKSPACE", os.environ["WORKSPACE"]))\r
755         EdkLogger.quiet("%-24s = %s" % ("ECP_SOURCE", os.environ["ECP_SOURCE"]))\r
756         EdkLogger.quiet("%-24s = %s" % ("EDK_SOURCE", os.environ["EDK_SOURCE"]))\r
757         EdkLogger.quiet("%-24s = %s" % ("EFI_SOURCE", os.environ["EFI_SOURCE"]))\r
758         EdkLogger.quiet("%-24s = %s" % ("EDK_TOOLS_PATH", os.environ["EDK_TOOLS_PATH"]))\r
759 \r
760         EdkLogger.info('\n%-24s = %s' % ("TARGET_ARCH", ' '.join(self.ArchList)))\r
761         EdkLogger.info('%-24s = %s' % ("TARGET", ' '.join(self.BuildTargetList)))\r
762         EdkLogger.info('%-24s = %s' % ("TOOL_CHAIN_TAG", ' '.join(self.ToolChainList)))\r
763 \r
764         EdkLogger.info('\n%-24s = %s' % ("Active Platform", self.PlatformFile))\r
765 \r
766         if self.Fdf != None and self.Fdf != "":\r
767             EdkLogger.info('%-24s = %s' % ("Flash Image Definition", self.Fdf))\r
768 \r
769         if self.ModuleFile != None and self.ModuleFile != "":\r
770             EdkLogger.info('%-24s = %s' % ("Active Module", self.ModuleFile))\r
771 \r
772         os.chdir(self.WorkspaceDir)\r
773         self.Progress.Start("\nProcessing meta-data")\r
774 \r
775     ## Load configuration\r
776     #\r
777     #   This method will parse target.txt and get the build configurations.\r
778     #\r
779     def LoadConfiguration(self):\r
780         #\r
781         # Check target.txt and tools_def.txt and Init them\r
782         #\r
783         BuildConfigurationFile = os.path.normpath(os.path.join(self.WorkspaceDir, gBuildConfiguration))\r
784         if os.path.isfile(BuildConfigurationFile) == True:\r
785             StatusCode = self.TargetTxt.LoadTargetTxtFile(BuildConfigurationFile)\r
786 \r
787             ToolDefinitionFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_CONF]\r
788             if ToolDefinitionFile == '':\r
789                 ToolDefinitionFile = gToolsDefinition\r
790             ToolDefinitionFile = os.path.normpath(os.path.join(self.WorkspaceDir, ToolDefinitionFile))\r
791             if os.path.isfile(ToolDefinitionFile) == True:\r
792                 StatusCode = self.ToolDef.LoadToolDefFile(ToolDefinitionFile)\r
793             else:\r
794                 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=ToolDefinitionFile)\r
795         else:\r
796             EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=BuildConfigurationFile)\r
797 \r
798         # if no ARCH given in command line, get it from target.txt\r
799         if self.ArchList == None or len(self.ArchList) == 0:\r
800             self.ArchList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET_ARCH]\r
801 \r
802         # if no build target given in command line, get it from target.txt\r
803         if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
804             self.BuildTargetList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET]\r
805 \r
806         # if no tool chain given in command line, get it from target.txt\r
807         if self.ToolChainList == None or len(self.ToolChainList) == 0:\r
808             self.ToolChainList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_TAG]\r
809             if self.ToolChainList == None or len(self.ToolChainList) == 0:\r
810                 EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, ExtraData="No toolchain given. Don't know how to build.\n")\r
811 \r
812         # check if the tool chains are defined or not\r
813         NewToolChainList = []\r
814         for ToolChain in self.ToolChainList:\r
815             if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG]:\r
816                 EdkLogger.warn("build", "Tool chain [%s] is not defined" % ToolChain)\r
817             else:\r
818                 NewToolChainList.append(ToolChain)\r
819         # if no tool chain available, break the build\r
820         if len(NewToolChainList) == 0:\r
821             EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,\r
822                             ExtraData="[%s] not defined. No toolchain available for build!\n" % ", ".join(self.ToolChainList))\r
823         else:\r
824             self.ToolChainList = NewToolChainList\r
825 \r
826         if self.ThreadNumber == None:\r
827             self.ThreadNumber = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER]\r
828             if self.ThreadNumber == '':\r
829                 self.ThreadNumber = 0\r
830             else:\r
831                 self.ThreadNumber = int(self.ThreadNumber, 0)\r
832 \r
833         if self.ThreadNumber == 0:\r
834             self.ThreadNumber = 1\r
835 \r
836         if not self.PlatformFile:\r
837             PlatformFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_ACTIVE_PLATFORM]\r
838             if not PlatformFile:\r
839                 # Try to find one in current directory\r
840                 WorkingDirectory = os.getcwd()\r
841                 FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.dsc')))\r
842                 FileNum = len(FileList)\r
843                 if FileNum >= 2:\r
844                     EdkLogger.error("build", OPTION_MISSING,\r
845                                     ExtraData="There are %d DSC files in %s. Use '-p' to specify one.\n" % (FileNum, WorkingDirectory))\r
846                 elif FileNum == 1:\r
847                     PlatformFile = FileList[0]\r
848                 else:\r
849                     EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,\r
850                                     ExtraData="No active platform specified in target.txt or command line! Nothing can be built.\n")\r
851 \r
852             self.PlatformFile = PathClass(NormFile(PlatformFile, self.WorkspaceDir), self.WorkspaceDir)\r
853             ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False)\r
854             if ErrorCode != 0:\r
855                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
856 \r
857     ## Initialize build configuration\r
858     #\r
859     #   This method will parse DSC file and merge the configurations from\r
860     #   command line and target.txt, then get the final build configurations.\r
861     #\r
862     def InitBuild(self):\r
863         ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc")\r
864         if ErrorCode != 0:\r
865             EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
866 \r
867         # create metafile database\r
868         self.Db.InitDatabase()\r
869 \r
870         # we need information in platform description file to determine how to build\r
871         self.Platform = self.BuildDatabase[self.PlatformFile, 'COMMON']\r
872         if not self.Fdf:\r
873             self.Fdf = self.Platform.FlashDefinition\r
874         \r
875         LoadFixAddressString = None\r
876         if TAB_FIX_LOAD_TOP_MEMORY_ADDRESS in GlobalData.gGlobalDefines:\r
877             LoadFixAddressString = GlobalData.gGlobalDefines[TAB_FIX_LOAD_TOP_MEMORY_ADDRESS]\r
878         else:\r
879             LoadFixAddressString = self.Platform.LoadFixAddress\r
880 \r
881         if LoadFixAddressString != None and LoadFixAddressString != '':\r
882             try:\r
883                 if LoadFixAddressString.upper().startswith('0X'):\r
884                     self.LoadFixAddress = int (LoadFixAddressString, 16)\r
885                 else:\r
886                     self.LoadFixAddress = int (LoadFixAddressString)\r
887             except:
888                 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS %s is not valid dec or hex string" % (LoadFixAddressString))\r
889             if self.LoadFixAddress < 0:\r
890                 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS is set to the invalid negative value %s" % (LoadFixAddressString))\r
891             if self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress % 0x1000 != 0:\r
892                 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS is set to the invalid unaligned 4K value %s" % (LoadFixAddressString))\r
893 \r
894         if self.SkuId == None or self.SkuId == '':\r
895             self.SkuId = self.Platform.SkuName\r
896 \r
897         # check FD/FV build target\r
898         if self.Fdf == None or self.Fdf == "":\r
899             if self.FdList != []:\r
900                 EdkLogger.info("No flash definition file found. FD [%s] will be ignored." % " ".join(self.FdList))\r
901                 self.FdList = []\r
902             if self.FvList != []:\r
903                 EdkLogger.info("No flash definition file found. FV [%s] will be ignored." % " ".join(self.FvList))\r
904                 self.FvList = []\r
905         else:\r
906             FdfParserObj = FdfParser(str(self.Fdf))\r
907             FdfParserObj.ParseFile()\r
908             for fvname in self.FvList:\r
909                 if fvname.upper() not in FdfParserObj.Profile.FvDict.keys():\r
910                     EdkLogger.error("build", OPTION_VALUE_INVALID,\r
911                                     "No such an FV in FDF file: %s" % fvname)\r
912 \r
913         #\r
914         # Merge Arch\r
915         #\r
916         if self.ArchList == None or len(self.ArchList) == 0:\r
917             ArchList = set(self.Platform.SupArchList)\r
918         else:\r
919             ArchList = set(self.ArchList) & set(self.Platform.SupArchList)\r
920         if len(ArchList) == 0:\r
921             EdkLogger.error("build", PARAMETER_INVALID,\r
922                             ExtraData = "Active platform supports [%s] only, but [%s] is given."\r
923                                         % (" ".join(self.Platform.SupArchList), " ".join(self.ArchList)))\r
924         elif len(ArchList) != len(self.ArchList):\r
925             SkippedArchList = set(self.ArchList).symmetric_difference(set(self.Platform.SupArchList))\r
926             EdkLogger.verbose("\nArch [%s] is ignored because active platform supports [%s] but [%s] is specified !"\r
927                            % (" ".join(SkippedArchList), " ".join(self.Platform.SupArchList), " ".join(self.ArchList)))\r
928         self.ArchList = tuple(ArchList)\r
929 \r
930         # Merge build target\r
931         if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
932             BuildTargetList = self.Platform.BuildTargets\r
933         else:\r
934             BuildTargetList = list(set(self.BuildTargetList) & set(self.Platform.BuildTargets))\r
935         if BuildTargetList == []:\r
936             EdkLogger.error("build", PARAMETER_INVALID, "Active platform only supports [%s], but [%s] is given"\r
937                                 % (" ".join(self.Platform.BuildTargets), " ".join(self.BuildTargetList)))\r
938         self.BuildTargetList = BuildTargetList\r
939 \r
940     ## Build a module or platform\r
941     #\r
942     # Create autogen code and makfile for a module or platform, and the launch\r
943     # "make" command to build it\r
944     #\r
945     #   @param  Target                      The target of build command\r
946     #   @param  Platform                    The platform file\r
947     #   @param  Module                      The module file\r
948     #   @param  BuildTarget                 The name of build target, one of "DEBUG", "RELEASE"\r
949     #   @param  ToolChain                   The name of toolchain to build\r
950     #   @param  Arch                        The arch of the module/platform\r
951     #   @param  CreateDepModuleCodeFile     Flag used to indicate creating code\r
952     #                                       for dependent modules/Libraries\r
953     #   @param  CreateDepModuleMakeFile     Flag used to indicate creating makefile\r
954     #                                       for dependent modules/Libraries\r
955     #\r
956     def _Build(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True):\r
957         if AutoGenObject == None:\r
958             return False\r
959 \r
960         # skip file generation for cleanxxx targets, run and fds target\r
961         if Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
962             # for target which must generate AutoGen code and makefile\r
963             if not self.SkipAutoGen or Target == 'genc':\r
964                 self.Progress.Start("Generating code")\r
965                 AutoGenObject.CreateCodeFile(CreateDepsCodeFile)\r
966                 self.Progress.Stop("done!")\r
967             if Target == "genc":\r
968                 return True\r
969 \r
970             if not self.SkipAutoGen or Target == 'genmake':\r
971                 self.Progress.Start("Generating makefile")\r
972                 AutoGenObject.CreateMakeFile(CreateDepsMakeFile)\r
973                 self.Progress.Stop("done!")\r
974             if Target == "genmake":\r
975                 return True\r
976         else:\r
977             # always recreate top/platform makefile when clean, just in case of inconsistency\r
978             AutoGenObject.CreateCodeFile(False)\r
979             AutoGenObject.CreateMakeFile(False)\r
980 \r
981         if EdkLogger.GetLevel() == EdkLogger.QUIET:\r
982             EdkLogger.quiet("Building ... %s" % repr(AutoGenObject))\r
983 \r
984         BuildCommand = AutoGenObject.BuildCommand\r
985         if BuildCommand == None or len(BuildCommand) == 0:\r
986             EdkLogger.error("build", OPTION_MISSING, ExtraData="No MAKE command found for [%s, %s, %s]" % Key)\r
987 \r
988         BuildCommand = BuildCommand + [Target]\r
989         LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir)\r
990         if Target == 'cleanall':\r
991             try:\r
992                 #os.rmdir(AutoGenObject.BuildDir)\r
993                 RemoveDirectory(AutoGenObject.BuildDir, True)\r
994             except WindowsError, X:\r
995                 EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X))\r
996         return True\r
997 \r
998     ## Rebase module image and Get function address for the inpug module list.\r
999     #\r
1000     def _RebaseModule (self, MapBuffer, BaseAddress, ModuleList, AddrIsOffset = True, ModeIsSmm = False):\r
1001         if ModeIsSmm:\r
1002             AddrIsOffset = False\r
1003         InfFileNameList = ModuleList.keys()\r
1004         #InfFileNameList.sort()\r
1005         for InfFile in InfFileNameList:\r
1006             sys.stdout.write (".")
1007             sys.stdout.flush()
1008             ModuleInfo = ModuleList[InfFile]\r
1009             ModuleName = ModuleInfo.BaseName\r
1010             ## for SMM module in SMRAM, the SMRAM will be allocated from base to top.\r
1011             if not ModeIsSmm:\r
1012                 BaseAddress = BaseAddress - ModuleInfo.Image.Size\r
1013                 #\r
1014                 # Update Image to new BaseAddress by GenFw tool\r
1015                 #\r
1016                 LaunchCommand(["GenFw", "--rebase", str(BaseAddress), "-r", ModuleInfo.Image.FileName], ModuleInfo.OutpuDir)\r
1017             else:\r
1018                 #\r
1019                 # Set new address to the section header only for SMM driver.\r
1020                 #\r
1021                 LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleInfo.Image.FileName], ModuleInfo.OutpuDir)\r
1022             #\r
1023             # Collect funtion address from Map file\r
1024             #\r
1025             ImageMapTable = ModuleInfo.Image.FileName.replace('.efi', '.map')\r
1026             FunctionList = []\r
1027             if os.path.exists(ImageMapTable):\r
1028                 OrigImageBaseAddress = 0\r
1029                 ImageMap = open (ImageMapTable, 'r')\r
1030                 for LinStr in ImageMap:\r
1031                     if len (LinStr.strip()) == 0:\r
1032                         continue\r
1033                     #\r
1034                     # Get the preferred address set on link time.\r
1035                     #\r
1036                     if LinStr.find ('Preferred load address is') != -1:\r
1037                         StrList = LinStr.split()\r
1038                         OrigImageBaseAddress = int (StrList[len(StrList) - 1], 16)\r
1039 \r
1040                     StrList = LinStr.split()\r
1041                     if len (StrList) > 4:\r
1042                         if StrList[3] == 'f' or StrList[3] =='F':\r
1043                             Name = StrList[1]\r
1044                             RelativeAddress = int (StrList[2], 16) - OrigImageBaseAddress\r
1045                             FunctionList.append ((Name, RelativeAddress))\r
1046                             if ModuleInfo.Arch == 'IPF' and Name.endswith('_ModuleEntryPoint'):\r
1047                                 #\r
1048                                 # Get the real entry point address for IPF image.\r
1049                                 #\r
1050                                 ModuleInfo.Image.EntryPoint = RelativeAddress\r
1051                 ImageMap.close()\r
1052             #\r
1053             # Add general information.\r
1054             #\r
1055             if ModeIsSmm:\r
1056                 MapBuffer.write('\n\n%s (Fixed SMRAM Offset,   BaseAddress=0x%010X,  EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint))\r
1057             elif AddrIsOffset:\r
1058                 MapBuffer.write('\n\n%s (Fixed Memory Offset,  BaseAddress=-0x%010X, EntryPoint=-0x%010X)\n' % (ModuleName, 0 - BaseAddress, 0 - (BaseAddress + ModuleInfo.Image.EntryPoint)))\r
1059             else:\r
1060                 MapBuffer.write('\n\n%s (Fixed Memory Address, BaseAddress=0x%010X,  EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint))\r
1061             #\r
1062             # Add guid and general seciton section.\r
1063             #\r
1064             TextSectionAddress = 0\r
1065             DataSectionAddress = 0\r
1066             for SectionHeader in ModuleInfo.Image.SectionHeaderList:\r
1067                 if SectionHeader[0] == '.text':\r
1068                     TextSectionAddress = SectionHeader[1]\r
1069                 elif SectionHeader[0] in ['.data', '.sdata']:\r
1070                     DataSectionAddress = SectionHeader[1]\r
1071             if AddrIsOffset:\r
1072                 MapBuffer.write('(GUID=%s, .textbaseaddress=-0x%010X, .databaseaddress=-0x%010X)\n\n' % (ModuleInfo.Guid, 0 - (BaseAddress + TextSectionAddress), 0 - (BaseAddress + DataSectionAddress))) \r
1073             else:\r
1074                 MapBuffer.write('(GUID=%s, .textbaseaddress=0x%010X, .databaseaddress=0x%010X)\n\n' % (ModuleInfo.Guid, BaseAddress + TextSectionAddress, BaseAddress + DataSectionAddress)) \r
1075             #\r
1076             # Add funtion address\r
1077             #\r
1078             for Function in FunctionList:\r
1079                 if AddrIsOffset:\r
1080                     MapBuffer.write('  -0x%010X    %s\n' % (0 - (BaseAddress + Function[1]), Function[0]))\r
1081                 else:\r
1082                     MapBuffer.write('  0x%010X    %s\n' % (BaseAddress + Function[1], Function[0]))\r
1083             ImageMap.close()\r
1084 \r
1085             #\r
1086             # for SMM module in SMRAM, the SMRAM will be allocated from base to top.\r
1087             #\r
1088             if ModeIsSmm:\r
1089                 BaseAddress = BaseAddress + ModuleInfo.Image.Size\r
1090 \r
1091     ## Collect MAP information of all FVs\r
1092     #\r
1093     def _CollectFvMapBuffer (self, MapBuffer, Wa):\r
1094         if self.Fdf != '':\r
1095             # First get the XIP base address for FV map file.\r
1096             for FvName in Wa.FdfProfile.FvDict.keys():\r
1097                 FvMapBuffer = os.path.join(Wa.FvDir, FvName + '.Fv.map')\r
1098                 if not os.path.exists(FvMapBuffer):\r
1099                     continue\r
1100                 FvMap = open (FvMapBuffer, 'r')\r
1101                 #skip FV size information\r
1102                 FvMap.readline()\r
1103                 FvMap.readline()\r
1104                 FvMap.readline()\r
1105                 FvMap.readline()\r
1106                 MapBuffer.write(FvMap.read())\r
1107                 FvMap.close()\r
1108 \r
1109     ## Collect MAP information of all modules\r
1110     #\r
1111     def _CollectModuleMapBuffer (self, MapBuffer, ModuleList):\r
1112         sys.stdout.write ("Generate Load Module At Fix Address Map")
1113         sys.stdout.flush()
1114         PatchEfiImageList = []\r
1115         PeiModuleList  = {}\r
1116         BtModuleList   = {}\r
1117         RtModuleList   = {}\r
1118         SmmModuleList  = {}\r
1119         PeiSize = 0\r
1120         BtSize  = 0\r
1121         RtSize  = 0\r
1122         # reserve 4K size in SMRAM to make SMM module address not from 0.\r
1123         SmmSize = 0x1000\r
1124         IsIpfPlatform = False\r
1125         if 'IPF' in self.ArchList:\r
1126             IsIpfPlatform = True\r
1127         for Module in ModuleList:\r
1128             GlobalData.gProcessingFile = "%s [%s, %s, %s]" % (Module.MetaFile, Module.Arch, Module.ToolChain, Module.BuildTarget)\r
1129             \r
1130             OutputImageFile = ''\r
1131             for ResultFile in Module.CodaTargetList:\r
1132                 if str(ResultFile.Target).endswith('.efi'):\r
1133                     #\r
1134                     # module list for PEI, DXE, RUNTIME and SMM\r
1135                     #\r
1136                     OutputImageFile = os.path.join(Module.OutputDir, Module.Name + '.efi')\r
1137                     ImageClass = PeImageClass (OutputImageFile)\r
1138                     if not ImageClass.IsValid:\r
1139                         EdkLogger.error("build", FILE_PARSE_FAILURE, ExtraData=ImageClass.ErrorInfo)\r
1140                     ImageInfo = PeImageInfo(Module.Name, Module.Guid, Module.Arch, Module.OutputDir, ImageClass)\r
1141                     if Module.ModuleType in ['PEI_CORE', 'PEIM', 'COMBINED_PEIM_DRIVER','PIC_PEIM', 'RELOCATABLE_PEIM', 'DXE_CORE']:\r
1142                         PeiModuleList[Module.MetaFile] = ImageInfo\r
1143                         PeiSize += ImageInfo.Image.Size\r
1144                     elif Module.ModuleType in ['BS_DRIVER', 'DXE_DRIVER', 'UEFI_DRIVER']:\r
1145                         BtModuleList[Module.MetaFile] = ImageInfo\r
1146                         BtSize += ImageInfo.Image.Size\r
1147                     elif Module.ModuleType in ['DXE_RUNTIME_DRIVER', 'RT_DRIVER', 'DXE_SAL_DRIVER', 'SAL_RT_DRIVER']:\r
1148                         RtModuleList[Module.MetaFile] = ImageInfo\r
1149                         #IPF runtime driver needs to be at 2 page alignment.\r
1150                         if IsIpfPlatform and ImageInfo.Image.Size % 0x2000 != 0:\r
1151                             ImageInfo.Image.Size = (ImageInfo.Image.Size / 0x2000 + 1) * 0x2000\r
1152                         RtSize += ImageInfo.Image.Size\r
1153                     elif Module.ModuleType in ['SMM_CORE', 'DXE_SMM_DRIVER']:\r
1154                         SmmModuleList[Module.MetaFile] = ImageInfo\r
1155                         SmmSize += ImageInfo.Image.Size\r
1156                         if Module.ModuleType == 'DXE_SMM_DRIVER':\r
1157                             PiSpecVersion = 0
1158                             if 'PI_SPECIFICATION_VERSION' in Module.Module.Specification:
1159                                 PiSpecVersion = Module.Module.Specification['PI_SPECIFICATION_VERSION']
1160                             # for PI specification < PI1.1, DXE_SMM_DRIVER also runs as BOOT time driver.\r
1161                             if PiSpecVersion < 0x0001000A:\r
1162                                 BtModuleList[Module.MetaFile] = ImageInfo\r
1163                                 BtSize += ImageInfo.Image.Size\r
1164                     break\r
1165             #\r
1166             # EFI image is final target.\r
1167             # Check EFI image contains patchable FixAddress related PCDs.\r
1168             #\r
1169             if OutputImageFile != '':\r
1170                 ModuleIsPatch = False\r
1171                 for Pcd in Module.ModulePcdList:\r
1172                     if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_LIST:\r
1173                         ModuleIsPatch = True\r
1174                         break\r
1175                 if not ModuleIsPatch:\r
1176                     for Pcd in Module.LibraryPcdList:\r
1177                         if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_LIST:\r
1178                             ModuleIsPatch = True\r
1179                             break\r
1180                 \r
1181                 if not ModuleIsPatch:\r
1182                     continue\r
1183                 #\r
1184                 # Module includes the patchable load fix address PCDs.\r
1185                 # It will be fixed up later. \r
1186                 #\r
1187                 PatchEfiImageList.append (OutputImageFile)\r
1188         \r
1189         #\r
1190         # Get Top Memory address\r
1191         #\r
1192         ReservedRuntimeMemorySize = 0\r
1193         TopMemoryAddress = 0\r
1194         if self.LoadFixAddress == 0xFFFFFFFFFFFFFFFF:\r
1195             TopMemoryAddress = 0\r
1196         else:\r
1197             TopMemoryAddress = self.LoadFixAddress\r
1198             if TopMemoryAddress < RtSize + BtSize + PeiSize:\r
1199                 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS is too low to load driver")\r
1200             # Make IPF runtime driver at 2 page alignment.\r
1201             if IsIpfPlatform:\r
1202                 ReservedRuntimeMemorySize = TopMemoryAddress % 0x2000\r
1203                 RtSize = RtSize + ReservedRuntimeMemorySize\r
1204 \r
1205         #\r
1206         # Patch FixAddress related PCDs into EFI image\r
1207         #\r
1208         for EfiImage in PatchEfiImageList: \r
1209             EfiImageMap = EfiImage.replace('.efi', '.map')\r
1210             if not os.path.exists(EfiImageMap):\r
1211                 continue\r
1212             #\r
1213             # Get PCD offset in EFI image by GenPatchPcdTable function\r
1214             #\r
1215             PcdTable = parsePcdInfoFromMapFile(EfiImageMap, EfiImage) 
1216             #\r
1217             # Patch real PCD value by PatchPcdValue tool\r
1218             #\r
1219             for PcdInfo in PcdTable:\r
1220                 ReturnValue = 0\r
1221                 if PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE:\r
1222                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE_DATA_TYPE, str (PeiSize/0x1000))\r
1223                 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE:\r
1224                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE_DATA_TYPE, str (BtSize/0x1000))\r
1225                 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE:\r
1226                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE_DATA_TYPE, str (RtSize/0x1000))\r
1227                 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE and len (SmmModuleList) > 0:\r
1228                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE_DATA_TYPE, str (SmmSize/0x1000))\r
1229                 if ReturnValue != 0:\r
1230                     EdkLogger.error("build", PARAMETER_INVALID, "Patch PCD value failed", ExtraData=ErrorInfo)\r
1231         \r
1232         MapBuffer.write('PEI_CODE_PAGE_NUMBER      = 0x%x\n' % (PeiSize/0x1000))\r
1233         MapBuffer.write('BOOT_CODE_PAGE_NUMBER     = 0x%x\n' % (BtSize/0x1000))\r
1234         MapBuffer.write('RUNTIME_CODE_PAGE_NUMBER  = 0x%x\n' % (RtSize/0x1000))\r
1235         if len (SmmModuleList) > 0:\r
1236             MapBuffer.write('SMM_CODE_PAGE_NUMBER      = 0x%x\n' % (SmmSize/0x1000))\r
1237         \r
1238         PeiBaseAddr = TopMemoryAddress - RtSize - BtSize\r
1239         BtBaseAddr  = TopMemoryAddress - RtSize\r
1240         RtBaseAddr  = TopMemoryAddress - ReservedRuntimeMemorySize\r
1241 \r
1242         self._RebaseModule (MapBuffer, PeiBaseAddr, PeiModuleList, TopMemoryAddress == 0)\r
1243         self._RebaseModule (MapBuffer, BtBaseAddr, BtModuleList, TopMemoryAddress == 0)\r
1244         self._RebaseModule (MapBuffer, RtBaseAddr, RtModuleList, TopMemoryAddress == 0)\r
1245         self._RebaseModule (MapBuffer, 0x1000, SmmModuleList, AddrIsOffset = False, ModeIsSmm = True)\r
1246         MapBuffer.write('\n\n')\r
1247         sys.stdout.write ("\n")
1248         sys.stdout.flush()
1249     \r
1250     ## Save platform Map file\r
1251     #\r
1252     def _SaveMapFile (self, MapBuffer, Wa):\r
1253         #\r
1254         # Map file path is got.\r
1255         #\r
1256         MapFilePath = os.path.join(Wa.BuildDir, Wa.Name + '.map')\r
1257         #\r
1258         # Save address map into MAP file.\r
1259         #\r
1260         SaveFileOnChange(MapFilePath, MapBuffer.getvalue(), False)\r
1261         MapBuffer.close()\r
1262         sys.stdout.write ("\nLoad Module At Fix Address Map file saved to %s\n" %(MapFilePath))
1263         sys.stdout.flush()
1264 \r
1265     ## Build active platform for different build targets and different tool chains\r
1266     #\r
1267     def _BuildPlatform(self):\r
1268         for BuildTarget in self.BuildTargetList:\r
1269             for ToolChain in self.ToolChainList:\r
1270                 Wa = WorkspaceAutoGen(\r
1271                         self.WorkspaceDir,\r
1272                         self.Platform,\r
1273                         BuildTarget,\r
1274                         ToolChain,\r
1275                         self.ArchList,\r
1276                         self.BuildDatabase,\r
1277                         self.TargetTxt,\r
1278                         self.ToolDef,\r
1279                         self.Fdf,\r
1280                         self.FdList,\r
1281                         self.FvList,\r
1282                         self.SkuId\r
1283                         )\r
1284                 self.BuildReport.AddPlatformReport(Wa)\r
1285                 self.Progress.Stop("done!")\r
1286                 self._Build(self.Target, Wa)\r
1287                 \r
1288                 # Create MAP file when Load Fix Address is enabled.\r
1289                 if self.Target in ["", "all", "fds"] and self.LoadFixAddress != 0:\r
1290                     for Arch in self.ArchList:\r
1291                         #\r
1292                         # Check whether the set fix address is above 4G for 32bit image.\r
1293                         #\r
1294                         if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
1295                             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
1296                     #\r
1297                     # Get Module List\r
1298                     #\r
1299                     ModuleList = []\r
1300                     for Pa in Wa.AutoGenObjectList:\r
1301                         for Ma in Pa.ModuleAutoGenList:\r
1302                             if Ma == None:\r
1303                                 continue\r
1304                             if not Ma.IsLibrary:\r
1305                                 ModuleList.append (Ma)\r
1306 \r
1307                     MapBuffer = StringIO('')\r
1308                     #\r
1309                     # Rebase module to the preferred memory address before GenFds\r
1310                     #\r
1311                     self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
1312                     if self.Fdf != '':\r
1313                         #\r
1314                         # create FDS again for the updated EFI image\r
1315                         #\r
1316                         self._Build("fds", Wa)\r
1317                         #\r
1318                         # Create MAP file for all platform FVs after GenFds.\r
1319                         #\r
1320                         self._CollectFvMapBuffer(MapBuffer, Wa)\r
1321                     #\r
1322                     # Save MAP buffer into MAP file.\r
1323                     #\r
1324                     self._SaveMapFile (MapBuffer, Wa)\r
1325 \r
1326     ## Build active module for different build targets, different tool chains and different archs\r
1327     #\r
1328     def _BuildModule(self):\r
1329         for BuildTarget in self.BuildTargetList:\r
1330             for ToolChain in self.ToolChainList:\r
1331                 #\r
1332                 # module build needs platform build information, so get platform\r
1333                 # AutoGen first\r
1334                 #\r
1335                 Wa = WorkspaceAutoGen(\r
1336                         self.WorkspaceDir,\r
1337                         self.Platform,\r
1338                         BuildTarget,\r
1339                         ToolChain,\r
1340                         self.ArchList,\r
1341                         self.BuildDatabase,\r
1342                         self.TargetTxt,\r
1343                         self.ToolDef,\r
1344                         self.Fdf,\r
1345                         self.FdList,\r
1346                         self.FvList,\r
1347                         self.SkuId\r
1348                         )\r
1349                 Wa.CreateMakeFile(False)\r
1350                 self.Progress.Stop("done!")\r
1351                 MaList = []\r
1352                 for Arch in self.ArchList:\r
1353                     Ma = ModuleAutoGen(Wa, self.ModuleFile, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
1354                     if Ma == None: continue\r
1355                     MaList.append(Ma)\r
1356                     self._Build(self.Target, Ma)\r
1357 \r
1358                 self.BuildReport.AddPlatformReport(Wa, MaList)\r
1359                 if MaList == []:\r
1360                     EdkLogger.error(\r
1361                                 'build',\r
1362                                 BUILD_ERROR,\r
1363                                 "Module for [%s] is not a component of active platform."\\r
1364                                 " Please make sure that the ARCH and inf file path are"\\r
1365                                 " given in the same as in [%s]" %\\r
1366                                     (', '.join(self.ArchList), self.Platform),\r
1367                                 ExtraData=self.ModuleFile\r
1368                                 )\r
1369                 # Create MAP file when Load Fix Address is enabled.\r
1370                 if self.LoadFixAddress != 0 and self.Target == "fds" and self.Fdf != '':\r
1371                     for Arch in self.ArchList:\r
1372                         #\r
1373                         # Check whether the set fix address is above 4G for 32bit image.\r
1374                         #\r
1375                         if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
1376                             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
1377                     #\r
1378                     # Get Module List\r
1379                     #\r
1380                     ModuleList = []\r
1381                     for Pa in Wa.AutoGenObjectList:\r
1382                         for Ma in Pa.ModuleAutoGenList:\r
1383                             if Ma == None:\r
1384                                 continue\r
1385                             if not Ma.IsLibrary:\r
1386                                 ModuleList.append (Ma)\r
1387 \r
1388                     MapBuffer = StringIO('')\r
1389                     #\r
1390                     # Rebase module to the preferred memory address before GenFds\r
1391                     #\r
1392                     self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
1393                     #\r
1394                     # create FDS again for the updated EFI image\r
1395                     #\r
1396                     self._Build("fds", Wa)\r
1397                     #\r
1398                     # Create MAP file for all platform FVs after GenFds.\r
1399                     #\r
1400                     self._CollectFvMapBuffer(MapBuffer, Wa)\r
1401                     #\r
1402                     # Save MAP buffer into MAP file.\r
1403                     #\r
1404                     self._SaveMapFile (MapBuffer, Wa)\r
1405 \r
1406     ## Build a platform in multi-thread mode\r
1407     #\r
1408     def _MultiThreadBuildPlatform(self):\r
1409         for BuildTarget in self.BuildTargetList:\r
1410             for ToolChain in self.ToolChainList:\r
1411                 Wa = WorkspaceAutoGen(\r
1412                         self.WorkspaceDir,\r
1413                         self.Platform,\r
1414                         BuildTarget,\r
1415                         ToolChain,\r
1416                         self.ArchList,\r
1417                         self.BuildDatabase,\r
1418                         self.TargetTxt,\r
1419                         self.ToolDef,\r
1420                         self.Fdf,\r
1421                         self.FdList,\r
1422                         self.FvList,\r
1423                         self.SkuId\r
1424                         )\r
1425                 self.BuildReport.AddPlatformReport(Wa)\r
1426                 Wa.CreateMakeFile(False)\r
1427 \r
1428                 # multi-thread exit flag\r
1429                 ExitFlag = threading.Event()\r
1430                 ExitFlag.clear()\r
1431                 for Arch in self.ArchList:\r
1432                     Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)\r
1433                     if Pa == None:\r
1434                         continue\r
1435                     for Module in Pa.Platform.Modules:\r
1436                         # Get ModuleAutoGen object to generate C code file and makefile\r
1437                         Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
1438                         if Ma == None:\r
1439                             continue\r
1440                         # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'\r
1441                         if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
1442                             # for target which must generate AutoGen code and makefile\r
1443                             if not self.SkipAutoGen or self.Target == 'genc':\r
1444                                 Ma.CreateCodeFile(True)\r
1445                             if self.Target == "genc":\r
1446                                 continue\r
1447 \r
1448                             if not self.SkipAutoGen or self.Target == 'genmake':\r
1449                                 Ma.CreateMakeFile(True)\r
1450                             if self.Target == "genmake":\r
1451                                 continue\r
1452                         self.Progress.Stop("done!")\r
1453                         # Generate build task for the module\r
1454                         Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))\r
1455                         # Break build if any build thread has error\r
1456                         if BuildTask.HasError():\r
1457                             # we need a full version of makefile for platform\r
1458                             ExitFlag.set()\r
1459                             BuildTask.WaitForComplete()\r
1460                             Pa.CreateMakeFile(False)\r
1461                             EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1462                         # Start task scheduler\r
1463                         if not BuildTask.IsOnGoing():\r
1464                             BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)\r
1465 \r
1466                     # in case there's an interruption. we need a full version of makefile for platform\r
1467                     Pa.CreateMakeFile(False)\r
1468                     if BuildTask.HasError():\r
1469                         EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1470 \r
1471                 #\r
1472                 # All modules have been put in build tasks queue. Tell task scheduler\r
1473                 # to exit if all tasks are completed\r
1474                 #\r
1475                 ExitFlag.set()\r
1476                 BuildTask.WaitForComplete()\r
1477 \r
1478                 #\r
1479                 # Check for build error, and raise exception if one\r
1480                 # has been signaled.\r
1481                 #\r
1482                 if BuildTask.HasError():\r
1483                     EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1484 \r
1485                 # Create MAP file when Load Fix Address is enabled.\r
1486                 if self.Target in ["", "all", "fds"] and self.LoadFixAddress != 0:\r
1487                     for Arch in self.ArchList:\r
1488                         #\r
1489                         # Check whether the set fix address is above 4G for 32bit image.\r
1490                         #\r
1491                         if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
1492                             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
1493                     #\r
1494                     # Get Module List\r
1495                     #\r
1496                     ModuleList = []\r
1497                     for Pa in Wa.AutoGenObjectList:\r
1498                         for Ma in Pa.ModuleAutoGenList:\r
1499                             if Ma == None:\r
1500                                 continue\r
1501                             if not Ma.IsLibrary:\r
1502                                 ModuleList.append (Ma)\r
1503                     #\r
1504                     # Rebase module to the preferred memory address before GenFds\r
1505                     #\r
1506                     MapBuffer = StringIO('')\r
1507                     self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
1508 \r
1509                 # Generate FD image if there's a FDF file found\r
1510                 if self.Fdf != '' and self.Target in ["", "all", "fds"]:\r
1511                     LaunchCommand(Wa.BuildCommand + ["fds"], Wa.MakeFileDir)\r
1512 \r
1513                 # Create MAP file for all platform FV after GenFds\r
1514                 if self.Target in ["", "all", "fds"] and self.LoadFixAddress != 0:\r
1515                     if self.Fdf != '':\r
1516                         #\r
1517                         # Create MAP file for all platform FVs after GenFds.\r
1518                         #\r
1519                         self._CollectFvMapBuffer(MapBuffer, Wa)\r
1520                     #\r
1521                     # Save MAP buffer into MAP file.\r
1522                     #\r
1523                     self._SaveMapFile(MapBuffer, Wa)\r
1524 \r
1525     ## Generate GuidedSectionTools.txt in the FV directories.\r
1526     #\r
1527     def CreateGuidedSectionToolsFile(self):\r
1528         for Arch in self.ArchList:\r
1529             for BuildTarget in self.BuildTargetList:\r
1530                 for ToolChain in self.ToolChainList:\r
1531                     FvDir = os.path.join(\r
1532                                 self.WorkspaceDir,\r
1533                                 self.Platform.OutputDirectory,\r
1534                                 '_'.join((BuildTarget, ToolChain)),\r
1535                                 'FV'\r
1536                                 )\r
1537                     if not os.path.exists(FvDir):\r
1538                         continue\r
1539                     # Build up the list of supported architectures for this build\r
1540                     prefix = '%s_%s_%s_' % (BuildTarget, ToolChain, Arch)\r
1541 \r
1542                     # Look through the tool definitions for GUIDed tools\r
1543                     guidAttribs = []\r
1544                     for (attrib, value) in self.ToolDef.ToolsDefTxtDictionary.iteritems():\r
1545                         if attrib.upper().endswith('_GUID'):\r
1546                             split = attrib.split('_')\r
1547                             thisPrefix = '_'.join(split[0:3]) + '_'\r
1548                             if thisPrefix == prefix:\r
1549                                 guid = self.ToolDef.ToolsDefTxtDictionary[attrib]\r
1550                                 guid = guid.lower()\r
1551                                 toolName = split[3]\r
1552                                 path = '_'.join(split[0:4]) + '_PATH'\r
1553                                 path = self.ToolDef.ToolsDefTxtDictionary[path]\r
1554                                 path = self.GetFullPathOfTool(path)\r
1555                                 guidAttribs.append((guid, toolName, path))\r
1556 \r
1557                     # Write out GuidedSecTools.txt\r
1558                     toolsFile = os.path.join(FvDir, 'GuidedSectionTools.txt')\r
1559                     toolsFile = open(toolsFile, 'wt')\r
1560                     for guidedSectionTool in guidAttribs:\r
1561                         print >> toolsFile, ' '.join(guidedSectionTool)\r
1562                     toolsFile.close()\r
1563 \r
1564     ## Returns the full path of the tool.\r
1565     #\r
1566     def GetFullPathOfTool (self, tool):\r
1567         if os.path.exists(tool):\r
1568             return os.path.realpath(tool)\r
1569         else:\r
1570             # We need to search for the tool using the\r
1571             # PATH environment variable.\r
1572             for dirInPath in os.environ['PATH'].split(os.pathsep):\r
1573                 foundPath = os.path.join(dirInPath, tool)\r
1574                 if os.path.exists(foundPath):\r
1575                     return os.path.realpath(foundPath)\r
1576 \r
1577         # If the tool was not found in the path then we just return\r
1578         # the input tool.\r
1579         return tool\r
1580 \r
1581     ## Launch the module or platform build\r
1582     #\r
1583     def Launch(self):\r
1584         if self.ModuleFile == None or self.ModuleFile == "":\r
1585             if not self.SpawnMode or self.Target not in ["", "all"]:\r
1586                 self.SpawnMode = False\r
1587                 self._BuildPlatform()\r
1588             else:\r
1589                 self._MultiThreadBuildPlatform()\r
1590             self.CreateGuidedSectionToolsFile()\r
1591         else:\r
1592             self.SpawnMode = False\r
1593             self._BuildModule()\r
1594 \r
1595     ## Do some clean-up works when error occurred\r
1596     def Relinquish(self):\r
1597         OldLogLevel = EdkLogger.GetLevel()\r
1598         EdkLogger.SetLevel(EdkLogger.ERROR)\r
1599         #self.DumpBuildData()\r
1600         Utils.Progressor.Abort()\r
1601         if self.SpawnMode == True:\r
1602             BuildTask.Abort()\r
1603         EdkLogger.SetLevel(OldLogLevel)\r
1604 \r
1605     def DumpBuildData(self):\r
1606         CacheDirectory = os.path.join(self.WorkspaceDir, gBuildCacheDir)\r
1607         Utils.CreateDirectory(CacheDirectory)\r
1608         Utils.DataDump(Utils.gFileTimeStampCache, os.path.join(CacheDirectory, "gFileTimeStampCache"))\r
1609         Utils.DataDump(Utils.gDependencyDatabase, os.path.join(CacheDirectory, "gDependencyDatabase"))\r
1610 \r
1611     def RestoreBuildData(self):\r
1612         FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gFileTimeStampCache")\r
1613         if Utils.gFileTimeStampCache == {} and os.path.isfile(FilePath):\r
1614             Utils.gFileTimeStampCache = Utils.DataRestore(FilePath)\r
1615             if Utils.gFileTimeStampCache == None:\r
1616                 Utils.gFileTimeStampCache = {}\r
1617 \r
1618         FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gDependencyDatabase")\r
1619         if Utils.gDependencyDatabase == {} and os.path.isfile(FilePath):\r
1620             Utils.gDependencyDatabase = Utils.DataRestore(FilePath)\r
1621             if Utils.gDependencyDatabase == None:\r
1622                 Utils.gDependencyDatabase = {}\r
1623 \r
1624 def ParseDefines(DefineList=[]):\r
1625     DefineDict = {}\r
1626     if DefineList != None:\r
1627         for Define in DefineList:\r
1628             DefineTokenList = Define.split("=", 1)\r
1629             if len(DefineTokenList) == 1:\r
1630                 DefineDict[DefineTokenList[0]] = ""\r
1631             else:\r
1632                 DefineDict[DefineTokenList[0]] = DefineTokenList[1].strip()\r
1633     return DefineDict\r
1634 \r
1635 gParamCheck = []\r
1636 def SingleCheckCallback(option, opt_str, value, parser):\r
1637     if option not in gParamCheck:\r
1638         setattr(parser.values, option.dest, value)\r
1639         gParamCheck.append(option)\r
1640     else:\r
1641         parser.error("Option %s only allows one instance in command line!" % option)\r
1642 \r
1643 ## Parse command line options\r
1644 #\r
1645 # Using standard Python module optparse to parse command line option of this tool.\r
1646 #\r
1647 #   @retval Opt   A optparse.Values object containing the parsed options\r
1648 #   @retval Args  Target of build command\r
1649 #\r
1650 def MyOptionParser():\r
1651     Parser = OptionParser(description=__copyright__,version=__version__,prog="build.exe",usage="%prog [options] [all|fds|genc|genmake|clean|cleanall|cleanlib|modules|libraries|run]")\r
1652     Parser.add_option("-a", "--arch", action="append", type="choice", choices=['IA32','X64','IPF','EBC','ARM'], dest="TargetArch",\r
1653         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
1654     Parser.add_option("-p", "--platform", action="callback", type="string", dest="PlatformFile", callback=SingleCheckCallback,\r
1655         help="Build the platform specified by the DSC file name argument, overriding target.txt's ACTIVE_PLATFORM definition.")\r
1656     Parser.add_option("-m", "--module", action="callback", type="string", dest="ModuleFile", callback=SingleCheckCallback,\r
1657         help="Build the module specified by the INF file name argument.")\r
1658     Parser.add_option("-b", "--buildtarget", action="append", type="choice", choices=['DEBUG','RELEASE'], dest="BuildTarget",\r
1659         help="BuildTarget is one of list: DEBUG, RELEASE, which overrides target.txt's TARGET definition. To specify more TARGET, please repeat this option.")\r
1660     Parser.add_option("-t", "--tagname", action="append", type="string", dest="ToolChain",\r
1661         help="Using the Tool Chain Tagname to build the platform, overriding target.txt's TOOL_CHAIN_TAG definition.")\r
1662     Parser.add_option("-x", "--sku-id", action="callback", type="string", dest="SkuId", callback=SingleCheckCallback,\r
1663         help="Using this name of SKU ID to build the platform, overriding SKUID_IDENTIFIER in DSC file.")\r
1664 \r
1665     Parser.add_option("-n", action="callback", type="int", dest="ThreadNumber", callback=SingleCheckCallback,\r
1666         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
1667 \r
1668     Parser.add_option("-f", "--fdf", action="callback", type="string", dest="FdfFile", callback=SingleCheckCallback,\r
1669         help="The name of the FDF file to use, which overrides the setting in the DSC file.")\r
1670     Parser.add_option("-r", "--rom-image", action="append", type="string", dest="RomImage", default=[],\r
1671         help="The name of FD to be generated. The name must be from [FD] section in FDF file.")\r
1672     Parser.add_option("-i", "--fv-image", action="append", type="string", dest="FvImage", default=[],\r
1673         help="The name of FV to be generated. The name must be from [FV] section in FDF file.")\r
1674 \r
1675     Parser.add_option("-u", "--skip-autogen", action="store_true", dest="SkipAutoGen", help="Skip AutoGen step.")\r
1676     Parser.add_option("-e", "--re-parse", action="store_true", dest="Reparse", help="Re-parse all meta-data files.")\r
1677 \r
1678     Parser.add_option("-c", "--case-insensitive", action="store_true", dest="CaseInsensitive", help="Don't check case of file name.")\r
1679 \r
1680     # Parser.add_option("-D", "--define", action="append", dest="Defines", metavar="NAME[=[VALUE]]",\r
1681     #     help="Define global macro which can be used in DSC/DEC/INF files.")\r
1682 \r
1683     Parser.add_option("-w", "--warning-as-error", action="store_true", dest="WarningAsError", help="Treat warning in tools as error.")\r
1684     Parser.add_option("-j", "--log", action="store", dest="LogFile", help="Put log in specified file as well as on console.")\r
1685 \r
1686     Parser.add_option("-s", "--silent", action="store_true", type=None, dest="SilentMode",\r
1687         help="Make use of silent mode of (n)make.")\r
1688     Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")\r
1689     Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\\r
1690                                                                                "including library instances selected, final dependency expression, "\\r
1691                                                                                "and warning messages, etc.")\r
1692     Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")\r
1693     Parser.add_option("-D", "--define", action="append", type="string", dest="Macros", help="Macro: \"Name [= Value]\".")\r
1694 \r
1695     Parser.add_option("-y", "--report-file", action="store", dest="ReportFile", help="Create/overwrite the report to the specified filename.")\r
1696     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
1697         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
1698              "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
1699 \r
1700     (Opt, Args)=Parser.parse_args()\r
1701     return (Opt, Args)\r
1702 \r
1703 ## Tool entrance method\r
1704 #\r
1705 # This method mainly dispatch specific methods per the command line options.\r
1706 # If no error found, return zero value so the caller of this tool can know\r
1707 # if it's executed successfully or not.\r
1708 #\r
1709 #   @retval 0     Tool was successful\r
1710 #   @retval 1     Tool failed\r
1711 #\r
1712 def Main():\r
1713     StartTime = time.time()\r
1714 \r
1715     # Initialize log system\r
1716     EdkLogger.Initialize()\r
1717 \r
1718     #\r
1719     # Parse the options and args\r
1720     #\r
1721     (Option, Target) = MyOptionParser()\r
1722     GlobalData.gOptions = Option\r
1723     GlobalData.gCaseInsensitive = Option.CaseInsensitive\r
1724 \r
1725     # Set log level\r
1726     if Option.verbose != None:\r
1727         EdkLogger.SetLevel(EdkLogger.VERBOSE)\r
1728     elif Option.quiet != None:\r
1729         EdkLogger.SetLevel(EdkLogger.QUIET)\r
1730     elif Option.debug != None:\r
1731         EdkLogger.SetLevel(Option.debug + 1)\r
1732     else:\r
1733         EdkLogger.SetLevel(EdkLogger.INFO)\r
1734 \r
1735     if Option.LogFile != None:\r
1736         EdkLogger.SetLogFile(Option.LogFile)\r
1737 \r
1738     if Option.WarningAsError == True:\r
1739         EdkLogger.SetWarningAsError()\r
1740 \r
1741     if platform.platform().find("Windows") >= 0:\r
1742         GlobalData.gIsWindows = True\r
1743     else:\r
1744         GlobalData.gIsWindows = False\r
1745 \r
1746     EdkLogger.quiet(time.strftime("%H:%M:%S, %b.%d %Y ", time.localtime()) + "[%s]\n" % platform.platform())\r
1747     ReturnCode = 0\r
1748     MyBuild = None\r
1749     try:\r
1750         if len(Target) == 0:\r
1751             Target = "all"\r
1752         elif len(Target) >= 2:\r
1753             EdkLogger.error("build", OPTION_NOT_SUPPORTED, "More than one targets are not supported.",\r
1754                             ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))\r
1755         else:\r
1756             Target = Target[0].lower()\r
1757 \r
1758         if Target not in gSupportedTarget:\r
1759             EdkLogger.error("build", OPTION_NOT_SUPPORTED, "Not supported target [%s]." % Target,\r
1760                             ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))\r
1761 \r
1762         GlobalData.gGlobalDefines = ParseDefines(Option.Macros)\r
1763         #\r
1764         # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH\r
1765         #\r
1766         CheckEnvVariable()\r
1767         Workspace = os.getenv("WORKSPACE")\r
1768         #\r
1769         # Get files real name in workspace dir\r
1770         #\r
1771         GlobalData.gAllFiles = Utils.DirCache(Workspace)\r
1772 \r
1773         WorkingDirectory = os.getcwd()\r
1774         if not Option.ModuleFile:\r
1775             FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.inf')))\r
1776             FileNum = len(FileList)\r
1777             if FileNum >= 2:\r
1778                 EdkLogger.error("build", OPTION_NOT_SUPPORTED, "There are %d INF files in %s." % (FileNum, WorkingDirectory),\r
1779                                 ExtraData="Please use '-m <INF_FILE_PATH>' switch to choose one.")\r
1780             elif FileNum == 1:\r
1781                 Option.ModuleFile = NormFile(FileList[0], Workspace)\r
1782 \r
1783         if Option.ModuleFile:\r
1784             if os.path.isabs (Option.ModuleFile):\r
1785                 if os.path.normcase (os.path.normpath(Option.ModuleFile)).find (Workspace) == 0:\r
1786                     Option.ModuleFile = NormFile(os.path.normpath(Option.ModuleFile), Workspace)\r
1787             Option.ModuleFile = PathClass(Option.ModuleFile, Workspace)\r
1788             ErrorCode, ErrorInfo = Option.ModuleFile.Validate(".inf", False)\r
1789             if ErrorCode != 0:\r
1790                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
1791 \r
1792         if Option.PlatformFile != None:\r
1793             if os.path.isabs (Option.PlatformFile):\r
1794                 if os.path.normcase (os.path.normpath(Option.PlatformFile)).find (Workspace) == 0:\r
1795                     Option.PlatformFile = NormFile(os.path.normpath(Option.PlatformFile), Workspace)\r
1796             Option.PlatformFile = PathClass(Option.PlatformFile, Workspace)\r
1797             ErrorCode, ErrorInfo = Option.PlatformFile.Validate(".dsc", False)\r
1798             if ErrorCode != 0:\r
1799                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
1800 \r
1801         if Option.FdfFile != None:\r
1802             if os.path.isabs (Option.FdfFile):\r
1803                 if os.path.normcase (os.path.normpath(Option.FdfFile)).find (Workspace) == 0:\r
1804                     Option.FdfFile = NormFile(os.path.normpath(Option.FdfFile), Workspace)\r
1805             Option.FdfFile = PathClass(Option.FdfFile, Workspace)\r
1806             ErrorCode, ErrorInfo = Option.FdfFile.Validate(".fdf", False)\r
1807             if ErrorCode != 0:\r
1808                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
1809 \r
1810         MyBuild = Build(Target, Workspace, Option.PlatformFile, Option.ModuleFile,\r
1811                         Option.TargetArch, Option.ToolChain, Option.BuildTarget,\r
1812                         Option.FdfFile, Option.RomImage, Option.FvImage,\r
1813                         None, Option.SilentMode, Option.ThreadNumber,\r
1814                         Option.SkipAutoGen, Option.Reparse, Option.SkuId, \r
1815                         Option.ReportFile, Option.ReportType)\r
1816         MyBuild.Launch()\r
1817         #MyBuild.DumpBuildData()\r
1818     except FatalError, X:\r
1819         if MyBuild != None:\r
1820             # for multi-thread build exits safely\r
1821             MyBuild.Relinquish()\r
1822         if Option != None and Option.debug != None:\r
1823             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1824         ReturnCode = X.args[0]\r
1825     except Warning, X:\r
1826         # error from Fdf parser\r
1827         if MyBuild != None:\r
1828             # for multi-thread build exits safely\r
1829             MyBuild.Relinquish()\r
1830         if Option != None and Option.debug != None:\r
1831             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1832         else:\r
1833             EdkLogger.error(X.ToolName, FORMAT_INVALID, File=X.FileName, Line=X.LineNumber, ExtraData=X.Message, RaiseError = False)\r
1834         ReturnCode = FORMAT_INVALID\r
1835     except KeyboardInterrupt:\r
1836         ReturnCode = ABORT_ERROR\r
1837         if Option != None and Option.debug != None:\r
1838             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1839     except:\r
1840         if MyBuild != None:\r
1841             # for multi-thread build exits safely\r
1842             MyBuild.Relinquish()\r
1843 \r
1844         # try to get the meta-file from the object causing exception\r
1845         Tb = sys.exc_info()[-1]\r
1846         MetaFile = GlobalData.gProcessingFile\r
1847         while Tb != None:\r
1848             if 'self' in Tb.tb_frame.f_locals and hasattr(Tb.tb_frame.f_locals['self'], 'MetaFile'):\r
1849                 MetaFile = Tb.tb_frame.f_locals['self'].MetaFile\r
1850             Tb = Tb.tb_next\r
1851         EdkLogger.error(\r
1852                     "\nbuild",\r
1853                     CODE_ERROR,\r
1854                     "Unknown fatal error when processing [%s]" % MetaFile,\r
1855                     ExtraData="\n(Please send email to edk2-buildtools-devel@lists.sourceforge.net for help, attaching following call stack trace!)\n",\r
1856                     RaiseError=False\r
1857                     )\r
1858         EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1859         ReturnCode = CODE_ERROR\r
1860     finally:\r
1861         Utils.Progressor.Abort()\r
1862 \r
1863     if ReturnCode == 0:\r
1864         Conclusion = "Done"\r
1865     elif ReturnCode == ABORT_ERROR:\r
1866         Conclusion = "Aborted"\r
1867     else:\r
1868         Conclusion = "Failed"\r
1869     FinishTime = time.time()\r
1870     BuildDuration = time.strftime("%M:%S", time.gmtime(int(round(FinishTime - StartTime))))\r
1871     if MyBuild != None:\r
1872         MyBuild.BuildReport.GenerateReport(BuildDuration)\r
1873         MyBuild.Db.Close()\r
1874     EdkLogger.SetLevel(EdkLogger.QUIET)\r
1875     EdkLogger.quiet("\n- %s -\n%s [%s]" % (Conclusion, time.strftime("%H:%M:%S, %b.%d %Y", time.localtime()), BuildDuration))\r
1876 \r
1877     return ReturnCode\r
1878 \r
1879 if __name__ == '__main__':\r
1880     r = Main()\r
1881     ## 0-127 is a safe return range, and 1 is a standard default error\r
1882     if r < 0 or r > 127: r = 1\r
1883     sys.exit(r)\r
1884 \r