1. Create platform MAP file to record the fixed function address for XIP module.
[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, ModuleList):\r
1094         if self.Fdf != '':\r
1095             # First get the XIP base address for FV map file.\r
1096             GuidPattern = re.compile("[-a-fA-F0-9]+")\r
1097             for FvName in Wa.FdfProfile.FvDict.keys():\r
1098                 FvMapBuffer = os.path.join(Wa.FvDir, FvName + '.Fv.map')\r
1099                 if not os.path.exists(FvMapBuffer):\r
1100                     continue\r
1101                 FvMap = open (FvMapBuffer, 'r')\r
1102                 #skip FV size information\r
1103                 FvMap.readline()\r
1104                 FvMap.readline()\r
1105                 FvMap.readline()\r
1106                 FvMap.readline()\r
1107                 for Line in FvMap:\r
1108                     MatchGuid = GuidPattern.match(Line)\r
1109                     if MatchGuid != None:\r
1110                         #\r
1111                         # Replace GUID with module name\r
1112                         #\r
1113                         GuidString = MatchGuid.group()\r
1114                         if GuidString.upper() in ModuleList:\r
1115                             Line = Line.replace(GuidString, ModuleList[GuidString.upper()].Name)\r
1116                     MapBuffer.write('%s' % (Line))\r
1117                 FvMap.close()\r
1118 \r
1119     ## Collect MAP information of all modules\r
1120     #\r
1121     def _CollectModuleMapBuffer (self, MapBuffer, ModuleList):\r
1122         sys.stdout.write ("Generate Load Module At Fix Address Map")
1123         sys.stdout.flush()
1124         PatchEfiImageList = []\r
1125         PeiModuleList  = {}\r
1126         BtModuleList   = {}\r
1127         RtModuleList   = {}\r
1128         SmmModuleList  = {}\r
1129         PeiSize = 0\r
1130         BtSize  = 0\r
1131         RtSize  = 0\r
1132         # reserve 4K size in SMRAM to make SMM module address not from 0.\r
1133         SmmSize = 0x1000\r
1134         IsIpfPlatform = False\r
1135         if 'IPF' in self.ArchList:\r
1136             IsIpfPlatform = True\r
1137         for ModuleGuid in ModuleList:\r
1138             Module = ModuleList[ModuleGuid]\r
1139             GlobalData.gProcessingFile = "%s [%s, %s, %s]" % (Module.MetaFile, Module.Arch, Module.ToolChain, Module.BuildTarget)\r
1140             \r
1141             OutputImageFile = ''\r
1142             for ResultFile in Module.CodaTargetList:\r
1143                 if str(ResultFile.Target).endswith('.efi'):\r
1144                     #\r
1145                     # module list for PEI, DXE, RUNTIME and SMM\r
1146                     #\r
1147                     OutputImageFile = os.path.join(Module.OutputDir, Module.Name + '.efi')\r
1148                     ImageClass = PeImageClass (OutputImageFile)\r
1149                     if not ImageClass.IsValid:\r
1150                         EdkLogger.error("build", FILE_PARSE_FAILURE, ExtraData=ImageClass.ErrorInfo)\r
1151                     ImageInfo = PeImageInfo(Module.Name, Module.Guid, Module.Arch, Module.OutputDir, ImageClass)\r
1152                     if Module.ModuleType in ['PEI_CORE', 'PEIM', 'COMBINED_PEIM_DRIVER','PIC_PEIM', 'RELOCATABLE_PEIM', 'DXE_CORE']:\r
1153                         PeiModuleList[Module.MetaFile] = ImageInfo\r
1154                         PeiSize += ImageInfo.Image.Size\r
1155                     elif Module.ModuleType in ['BS_DRIVER', 'DXE_DRIVER', 'UEFI_DRIVER']:\r
1156                         BtModuleList[Module.MetaFile] = ImageInfo\r
1157                         BtSize += ImageInfo.Image.Size\r
1158                     elif Module.ModuleType in ['DXE_RUNTIME_DRIVER', 'RT_DRIVER', 'DXE_SAL_DRIVER', 'SAL_RT_DRIVER']:\r
1159                         RtModuleList[Module.MetaFile] = ImageInfo\r
1160                         #IPF runtime driver needs to be at 2 page alignment.\r
1161                         if IsIpfPlatform and ImageInfo.Image.Size % 0x2000 != 0:\r
1162                             ImageInfo.Image.Size = (ImageInfo.Image.Size / 0x2000 + 1) * 0x2000\r
1163                         RtSize += ImageInfo.Image.Size\r
1164                     elif Module.ModuleType in ['SMM_CORE', 'DXE_SMM_DRIVER']:\r
1165                         SmmModuleList[Module.MetaFile] = ImageInfo\r
1166                         SmmSize += ImageInfo.Image.Size\r
1167                         if Module.ModuleType == 'DXE_SMM_DRIVER':\r
1168                             PiSpecVersion = 0
1169                             if 'PI_SPECIFICATION_VERSION' in Module.Module.Specification:
1170                                 PiSpecVersion = Module.Module.Specification['PI_SPECIFICATION_VERSION']
1171                             # for PI specification < PI1.1, DXE_SMM_DRIVER also runs as BOOT time driver.\r
1172                             if PiSpecVersion < 0x0001000A:\r
1173                                 BtModuleList[Module.MetaFile] = ImageInfo\r
1174                                 BtSize += ImageInfo.Image.Size\r
1175                     break\r
1176             #\r
1177             # EFI image is final target.\r
1178             # Check EFI image contains patchable FixAddress related PCDs.\r
1179             #\r
1180             if OutputImageFile != '':\r
1181                 ModuleIsPatch = False\r
1182                 for Pcd in Module.ModulePcdList:\r
1183                     if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_LIST:\r
1184                         ModuleIsPatch = True\r
1185                         break\r
1186                 if not ModuleIsPatch:\r
1187                     for Pcd in Module.LibraryPcdList:\r
1188                         if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_LIST:\r
1189                             ModuleIsPatch = True\r
1190                             break\r
1191                 \r
1192                 if not ModuleIsPatch:\r
1193                     continue\r
1194                 #\r
1195                 # Module includes the patchable load fix address PCDs.\r
1196                 # It will be fixed up later. \r
1197                 #\r
1198                 PatchEfiImageList.append (OutputImageFile)\r
1199         \r
1200         #\r
1201         # Get Top Memory address\r
1202         #\r
1203         ReservedRuntimeMemorySize = 0\r
1204         TopMemoryAddress = 0\r
1205         if self.LoadFixAddress == 0xFFFFFFFFFFFFFFFF:\r
1206             TopMemoryAddress = 0\r
1207         else:\r
1208             TopMemoryAddress = self.LoadFixAddress\r
1209             if TopMemoryAddress < RtSize + BtSize + PeiSize:\r
1210                 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS is too low to load driver")\r
1211             # Make IPF runtime driver at 2 page alignment.\r
1212             if IsIpfPlatform:\r
1213                 ReservedRuntimeMemorySize = TopMemoryAddress % 0x2000\r
1214                 RtSize = RtSize + ReservedRuntimeMemorySize\r
1215 \r
1216         #\r
1217         # Patch FixAddress related PCDs into EFI image\r
1218         #\r
1219         for EfiImage in PatchEfiImageList: \r
1220             EfiImageMap = EfiImage.replace('.efi', '.map')\r
1221             if not os.path.exists(EfiImageMap):\r
1222                 continue\r
1223             #\r
1224             # Get PCD offset in EFI image by GenPatchPcdTable function\r
1225             #\r
1226             PcdTable = parsePcdInfoFromMapFile(EfiImageMap, EfiImage) 
1227             #\r
1228             # Patch real PCD value by PatchPcdValue tool\r
1229             #\r
1230             for PcdInfo in PcdTable:\r
1231                 ReturnValue = 0\r
1232                 if PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE:\r
1233                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE_DATA_TYPE, str (PeiSize/0x1000))\r
1234                 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE:\r
1235                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE_DATA_TYPE, str (BtSize/0x1000))\r
1236                 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE:\r
1237                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE_DATA_TYPE, str (RtSize/0x1000))\r
1238                 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE and len (SmmModuleList) > 0:\r
1239                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE_DATA_TYPE, str (SmmSize/0x1000))\r
1240                 if ReturnValue != 0:\r
1241                     EdkLogger.error("build", PARAMETER_INVALID, "Patch PCD value failed", ExtraData=ErrorInfo)\r
1242         \r
1243         MapBuffer.write('PEI_CODE_PAGE_NUMBER      = 0x%x\n' % (PeiSize/0x1000))\r
1244         MapBuffer.write('BOOT_CODE_PAGE_NUMBER     = 0x%x\n' % (BtSize/0x1000))\r
1245         MapBuffer.write('RUNTIME_CODE_PAGE_NUMBER  = 0x%x\n' % (RtSize/0x1000))\r
1246         if len (SmmModuleList) > 0:\r
1247             MapBuffer.write('SMM_CODE_PAGE_NUMBER      = 0x%x\n' % (SmmSize/0x1000))\r
1248         \r
1249         PeiBaseAddr = TopMemoryAddress - RtSize - BtSize\r
1250         BtBaseAddr  = TopMemoryAddress - RtSize\r
1251         RtBaseAddr  = TopMemoryAddress - ReservedRuntimeMemorySize\r
1252 \r
1253         self._RebaseModule (MapBuffer, PeiBaseAddr, PeiModuleList, TopMemoryAddress == 0)\r
1254         self._RebaseModule (MapBuffer, BtBaseAddr, BtModuleList, TopMemoryAddress == 0)\r
1255         self._RebaseModule (MapBuffer, RtBaseAddr, RtModuleList, TopMemoryAddress == 0)\r
1256         self._RebaseModule (MapBuffer, 0x1000, SmmModuleList, AddrIsOffset = False, ModeIsSmm = True)\r
1257         MapBuffer.write('\n\n')\r
1258         sys.stdout.write ("\n")
1259         sys.stdout.flush()
1260     \r
1261     ## Save platform Map file\r
1262     #\r
1263     def _SaveMapFile (self, MapBuffer, Wa):\r
1264         #\r
1265         # Map file path is got.\r
1266         #\r
1267         MapFilePath = os.path.join(Wa.BuildDir, Wa.Name + '.map')\r
1268         #\r
1269         # Save address map into MAP file.\r
1270         #\r
1271         SaveFileOnChange(MapFilePath, MapBuffer.getvalue(), False)\r
1272         MapBuffer.close()\r
1273         if self.LoadFixAddress != 0:
1274             sys.stdout.write ("\nLoad Module At Fix Address Map file saved to %s\n" %(MapFilePath))
1275         sys.stdout.flush()
1276 \r
1277     ## Build active platform for different build targets and different tool chains\r
1278     #\r
1279     def _BuildPlatform(self):\r
1280         for BuildTarget in self.BuildTargetList:\r
1281             for ToolChain in self.ToolChainList:\r
1282                 Wa = WorkspaceAutoGen(\r
1283                         self.WorkspaceDir,\r
1284                         self.Platform,\r
1285                         BuildTarget,\r
1286                         ToolChain,\r
1287                         self.ArchList,\r
1288                         self.BuildDatabase,\r
1289                         self.TargetTxt,\r
1290                         self.ToolDef,\r
1291                         self.Fdf,\r
1292                         self.FdList,\r
1293                         self.FvList,\r
1294                         self.SkuId\r
1295                         )\r
1296                 self.BuildReport.AddPlatformReport(Wa)\r
1297                 self.Progress.Stop("done!")\r
1298                 self._Build(self.Target, Wa)\r
1299                 \r
1300                 # Create MAP file when Load Fix Address is enabled.\r
1301                 if self.Target in ["", "all", "fds"]:\r
1302                     for Arch in self.ArchList:\r
1303                         #\r
1304                         # Check whether the set fix address is above 4G for 32bit image.\r
1305                         #\r
1306                         if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
1307                             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
1308                     #\r
1309                     # Get Module List\r
1310                     #\r
1311                     ModuleList = {}\r
1312                     for Pa in Wa.AutoGenObjectList:\r
1313                         for Ma in Pa.ModuleAutoGenList:\r
1314                             if Ma == None:\r
1315                                 continue\r
1316                             if not Ma.IsLibrary:\r
1317                                 ModuleList[Ma.Guid.upper()] = Ma\r
1318 \r
1319                     MapBuffer = StringIO('')\r
1320                     if self.LoadFixAddress != 0:\r
1321                         #\r
1322                         # Rebase module to the preferred memory address before GenFds\r
1323                         #\r
1324                         self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
1325                     if self.Fdf != '':\r
1326                         #\r
1327                         # create FDS again for the updated EFI image\r
1328                         #\r
1329                         self._Build("fds", Wa)\r
1330                         #\r
1331                         # Create MAP file for all platform FVs after GenFds.\r
1332                         #\r
1333                         self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)\r
1334                     #\r
1335                     # Save MAP buffer into MAP file.\r
1336                     #\r
1337                     self._SaveMapFile (MapBuffer, Wa)\r
1338 \r
1339     ## Build active module for different build targets, different tool chains and different archs\r
1340     #\r
1341     def _BuildModule(self):\r
1342         for BuildTarget in self.BuildTargetList:\r
1343             for ToolChain in self.ToolChainList:\r
1344                 #\r
1345                 # module build needs platform build information, so get platform\r
1346                 # AutoGen first\r
1347                 #\r
1348                 Wa = WorkspaceAutoGen(\r
1349                         self.WorkspaceDir,\r
1350                         self.Platform,\r
1351                         BuildTarget,\r
1352                         ToolChain,\r
1353                         self.ArchList,\r
1354                         self.BuildDatabase,\r
1355                         self.TargetTxt,\r
1356                         self.ToolDef,\r
1357                         self.Fdf,\r
1358                         self.FdList,\r
1359                         self.FvList,\r
1360                         self.SkuId\r
1361                         )\r
1362                 Wa.CreateMakeFile(False)\r
1363                 self.Progress.Stop("done!")\r
1364                 MaList = []\r
1365                 for Arch in self.ArchList:\r
1366                     Ma = ModuleAutoGen(Wa, self.ModuleFile, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
1367                     if Ma == None: continue\r
1368                     MaList.append(Ma)\r
1369                     self._Build(self.Target, Ma)\r
1370 \r
1371                 self.BuildReport.AddPlatformReport(Wa, MaList)\r
1372                 if MaList == []:\r
1373                     EdkLogger.error(\r
1374                                 'build',\r
1375                                 BUILD_ERROR,\r
1376                                 "Module for [%s] is not a component of active platform."\\r
1377                                 " Please make sure that the ARCH and inf file path are"\\r
1378                                 " given in the same as in [%s]" %\\r
1379                                     (', '.join(self.ArchList), self.Platform),\r
1380                                 ExtraData=self.ModuleFile\r
1381                                 )\r
1382                 # Create MAP file when Load Fix Address is enabled.\r
1383                 if self.Target == "fds" and self.Fdf != '':\r
1384                     for Arch in self.ArchList:\r
1385                         #\r
1386                         # Check whether the set fix address is above 4G for 32bit image.\r
1387                         #\r
1388                         if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
1389                             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
1390                     #\r
1391                     # Get Module List\r
1392                     #\r
1393                     ModuleList = {}\r
1394                     for Pa in Wa.AutoGenObjectList:\r
1395                         for Ma in Pa.ModuleAutoGenList:\r
1396                             if Ma == None:\r
1397                                 continue\r
1398                             if not Ma.IsLibrary:\r
1399                                 ModuleList[Ma.Guid.upper()] = Ma\r
1400 \r
1401                     MapBuffer = StringIO('')\r
1402                     if self.LoadFixAddress != 0:\r
1403                         #\r
1404                         # Rebase module to the preferred memory address before GenFds\r
1405                         #\r
1406                         self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
1407                         #\r
1408                         # create FDS again for the updated EFI image\r
1409                         #\r
1410                         self._Build("fds", Wa)\r
1411                     #\r
1412                     # Create MAP file for all platform FVs after GenFds.\r
1413                     #\r
1414                     self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)\r
1415                     #\r
1416                     # Save MAP buffer into MAP file.\r
1417                     #\r
1418                     self._SaveMapFile (MapBuffer, Wa)\r
1419 \r
1420     ## Build a platform in multi-thread mode\r
1421     #\r
1422     def _MultiThreadBuildPlatform(self):\r
1423         for BuildTarget in self.BuildTargetList:\r
1424             for ToolChain in self.ToolChainList:\r
1425                 Wa = WorkspaceAutoGen(\r
1426                         self.WorkspaceDir,\r
1427                         self.Platform,\r
1428                         BuildTarget,\r
1429                         ToolChain,\r
1430                         self.ArchList,\r
1431                         self.BuildDatabase,\r
1432                         self.TargetTxt,\r
1433                         self.ToolDef,\r
1434                         self.Fdf,\r
1435                         self.FdList,\r
1436                         self.FvList,\r
1437                         self.SkuId\r
1438                         )\r
1439                 self.BuildReport.AddPlatformReport(Wa)\r
1440                 Wa.CreateMakeFile(False)\r
1441 \r
1442                 # multi-thread exit flag\r
1443                 ExitFlag = threading.Event()\r
1444                 ExitFlag.clear()\r
1445                 for Arch in self.ArchList:\r
1446                     Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)\r
1447                     if Pa == None:\r
1448                         continue\r
1449                     for Module in Pa.Platform.Modules:\r
1450                         # Get ModuleAutoGen object to generate C code file and makefile\r
1451                         Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
1452                         if Ma == None:\r
1453                             continue\r
1454                         # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'\r
1455                         if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
1456                             # for target which must generate AutoGen code and makefile\r
1457                             if not self.SkipAutoGen or self.Target == 'genc':\r
1458                                 Ma.CreateCodeFile(True)\r
1459                             if self.Target == "genc":\r
1460                                 continue\r
1461 \r
1462                             if not self.SkipAutoGen or self.Target == 'genmake':\r
1463                                 Ma.CreateMakeFile(True)\r
1464                             if self.Target == "genmake":\r
1465                                 continue\r
1466                         self.Progress.Stop("done!")\r
1467                         # Generate build task for the module\r
1468                         Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))\r
1469                         # Break build if any build thread has error\r
1470                         if BuildTask.HasError():\r
1471                             # we need a full version of makefile for platform\r
1472                             ExitFlag.set()\r
1473                             BuildTask.WaitForComplete()\r
1474                             Pa.CreateMakeFile(False)\r
1475                             EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1476                         # Start task scheduler\r
1477                         if not BuildTask.IsOnGoing():\r
1478                             BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)\r
1479 \r
1480                     # in case there's an interruption. we need a full version of makefile for platform\r
1481                     Pa.CreateMakeFile(False)\r
1482                     if BuildTask.HasError():\r
1483                         EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1484 \r
1485                 #\r
1486                 # All modules have been put in build tasks queue. Tell task scheduler\r
1487                 # to exit if all tasks are completed\r
1488                 #\r
1489                 ExitFlag.set()\r
1490                 BuildTask.WaitForComplete()\r
1491 \r
1492                 #\r
1493                 # Check for build error, and raise exception if one\r
1494                 # has been signaled.\r
1495                 #\r
1496                 if BuildTask.HasError():\r
1497                     EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1498 \r
1499                 # Create MAP file when Load Fix Address is enabled.\r
1500                 if self.Target in ["", "all", "fds"]:\r
1501                     for Arch in self.ArchList:\r
1502                         #\r
1503                         # Check whether the set fix address is above 4G for 32bit image.\r
1504                         #\r
1505                         if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
1506                             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
1507                     #\r
1508                     # Get Module List\r
1509                     #\r
1510                     ModuleList = {}\r
1511                     for Pa in Wa.AutoGenObjectList:\r
1512                         for Ma in Pa.ModuleAutoGenList:\r
1513                             if Ma == None:\r
1514                                 continue\r
1515                             if not Ma.IsLibrary:\r
1516                                 ModuleList[Ma.Guid.upper()] = Ma\r
1517                     #\r
1518                     # Rebase module to the preferred memory address before GenFds\r
1519                     #\r
1520                     MapBuffer = StringIO('')\r
1521                     if self.LoadFixAddress != 0:\r
1522                         self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
1523 \r
1524                 # Generate FD image if there's a FDF file found\r
1525                 if self.Fdf != '' and self.Target in ["", "all", "fds"]:\r
1526                     LaunchCommand(Wa.BuildCommand + ["fds"], Wa.MakeFileDir)\r
1527 \r
1528                 # Create MAP file for all platform FV after GenFds\r
1529                 if self.Target in ["", "all", "fds"]:\r
1530                     if self.Fdf != '':\r
1531                         #\r
1532                         # Create MAP file for all platform FVs after GenFds.\r
1533                         #\r
1534                         self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)\r
1535                     #\r
1536                     # Save MAP buffer into MAP file.\r
1537                     #\r
1538                     self._SaveMapFile(MapBuffer, Wa)\r
1539 \r
1540     ## Generate GuidedSectionTools.txt in the FV directories.\r
1541     #\r
1542     def CreateGuidedSectionToolsFile(self):\r
1543         for Arch in self.ArchList:\r
1544             for BuildTarget in self.BuildTargetList:\r
1545                 for ToolChain in self.ToolChainList:\r
1546                     FvDir = os.path.join(\r
1547                                 self.WorkspaceDir,\r
1548                                 self.Platform.OutputDirectory,\r
1549                                 '_'.join((BuildTarget, ToolChain)),\r
1550                                 'FV'\r
1551                                 )\r
1552                     if not os.path.exists(FvDir):\r
1553                         continue\r
1554                     # Build up the list of supported architectures for this build\r
1555                     prefix = '%s_%s_%s_' % (BuildTarget, ToolChain, Arch)\r
1556 \r
1557                     # Look through the tool definitions for GUIDed tools\r
1558                     guidAttribs = []\r
1559                     for (attrib, value) in self.ToolDef.ToolsDefTxtDictionary.iteritems():\r
1560                         if attrib.upper().endswith('_GUID'):\r
1561                             split = attrib.split('_')\r
1562                             thisPrefix = '_'.join(split[0:3]) + '_'\r
1563                             if thisPrefix == prefix:\r
1564                                 guid = self.ToolDef.ToolsDefTxtDictionary[attrib]\r
1565                                 guid = guid.lower()\r
1566                                 toolName = split[3]\r
1567                                 path = '_'.join(split[0:4]) + '_PATH'\r
1568                                 path = self.ToolDef.ToolsDefTxtDictionary[path]\r
1569                                 path = self.GetFullPathOfTool(path)\r
1570                                 guidAttribs.append((guid, toolName, path))\r
1571 \r
1572                     # Write out GuidedSecTools.txt\r
1573                     toolsFile = os.path.join(FvDir, 'GuidedSectionTools.txt')\r
1574                     toolsFile = open(toolsFile, 'wt')\r
1575                     for guidedSectionTool in guidAttribs:\r
1576                         print >> toolsFile, ' '.join(guidedSectionTool)\r
1577                     toolsFile.close()\r
1578 \r
1579     ## Returns the full path of the tool.\r
1580     #\r
1581     def GetFullPathOfTool (self, tool):\r
1582         if os.path.exists(tool):\r
1583             return os.path.realpath(tool)\r
1584         else:\r
1585             # We need to search for the tool using the\r
1586             # PATH environment variable.\r
1587             for dirInPath in os.environ['PATH'].split(os.pathsep):\r
1588                 foundPath = os.path.join(dirInPath, tool)\r
1589                 if os.path.exists(foundPath):\r
1590                     return os.path.realpath(foundPath)\r
1591 \r
1592         # If the tool was not found in the path then we just return\r
1593         # the input tool.\r
1594         return tool\r
1595 \r
1596     ## Launch the module or platform build\r
1597     #\r
1598     def Launch(self):\r
1599         if self.ModuleFile == None or self.ModuleFile == "":\r
1600             if not self.SpawnMode or self.Target not in ["", "all"]:\r
1601                 self.SpawnMode = False\r
1602                 self._BuildPlatform()\r
1603             else:\r
1604                 self._MultiThreadBuildPlatform()\r
1605             self.CreateGuidedSectionToolsFile()\r
1606         else:\r
1607             self.SpawnMode = False\r
1608             self._BuildModule()\r
1609 \r
1610     ## Do some clean-up works when error occurred\r
1611     def Relinquish(self):\r
1612         OldLogLevel = EdkLogger.GetLevel()\r
1613         EdkLogger.SetLevel(EdkLogger.ERROR)\r
1614         #self.DumpBuildData()\r
1615         Utils.Progressor.Abort()\r
1616         if self.SpawnMode == True:\r
1617             BuildTask.Abort()\r
1618         EdkLogger.SetLevel(OldLogLevel)\r
1619 \r
1620     def DumpBuildData(self):\r
1621         CacheDirectory = os.path.join(self.WorkspaceDir, gBuildCacheDir)\r
1622         Utils.CreateDirectory(CacheDirectory)\r
1623         Utils.DataDump(Utils.gFileTimeStampCache, os.path.join(CacheDirectory, "gFileTimeStampCache"))\r
1624         Utils.DataDump(Utils.gDependencyDatabase, os.path.join(CacheDirectory, "gDependencyDatabase"))\r
1625 \r
1626     def RestoreBuildData(self):\r
1627         FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gFileTimeStampCache")\r
1628         if Utils.gFileTimeStampCache == {} and os.path.isfile(FilePath):\r
1629             Utils.gFileTimeStampCache = Utils.DataRestore(FilePath)\r
1630             if Utils.gFileTimeStampCache == None:\r
1631                 Utils.gFileTimeStampCache = {}\r
1632 \r
1633         FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gDependencyDatabase")\r
1634         if Utils.gDependencyDatabase == {} and os.path.isfile(FilePath):\r
1635             Utils.gDependencyDatabase = Utils.DataRestore(FilePath)\r
1636             if Utils.gDependencyDatabase == None:\r
1637                 Utils.gDependencyDatabase = {}\r
1638 \r
1639 def ParseDefines(DefineList=[]):\r
1640     DefineDict = {}\r
1641     if DefineList != None:\r
1642         for Define in DefineList:\r
1643             DefineTokenList = Define.split("=", 1)\r
1644             if len(DefineTokenList) == 1:\r
1645                 DefineDict[DefineTokenList[0]] = ""\r
1646             else:\r
1647                 DefineDict[DefineTokenList[0]] = DefineTokenList[1].strip()\r
1648     return DefineDict\r
1649 \r
1650 gParamCheck = []\r
1651 def SingleCheckCallback(option, opt_str, value, parser):\r
1652     if option not in gParamCheck:\r
1653         setattr(parser.values, option.dest, value)\r
1654         gParamCheck.append(option)\r
1655     else:\r
1656         parser.error("Option %s only allows one instance in command line!" % option)\r
1657 \r
1658 ## Parse command line options\r
1659 #\r
1660 # Using standard Python module optparse to parse command line option of this tool.\r
1661 #\r
1662 #   @retval Opt   A optparse.Values object containing the parsed options\r
1663 #   @retval Args  Target of build command\r
1664 #\r
1665 def MyOptionParser():\r
1666     Parser = OptionParser(description=__copyright__,version=__version__,prog="build.exe",usage="%prog [options] [all|fds|genc|genmake|clean|cleanall|cleanlib|modules|libraries|run]")\r
1667     Parser.add_option("-a", "--arch", action="append", type="choice", choices=['IA32','X64','IPF','EBC','ARM'], dest="TargetArch",\r
1668         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
1669     Parser.add_option("-p", "--platform", action="callback", type="string", dest="PlatformFile", callback=SingleCheckCallback,\r
1670         help="Build the platform specified by the DSC file name argument, overriding target.txt's ACTIVE_PLATFORM definition.")\r
1671     Parser.add_option("-m", "--module", action="callback", type="string", dest="ModuleFile", callback=SingleCheckCallback,\r
1672         help="Build the module specified by the INF file name argument.")\r
1673     Parser.add_option("-b", "--buildtarget", action="append", type="choice", choices=['DEBUG','RELEASE'], dest="BuildTarget",\r
1674         help="BuildTarget is one of list: DEBUG, RELEASE, which overrides target.txt's TARGET definition. To specify more TARGET, please repeat this option.")\r
1675     Parser.add_option("-t", "--tagname", action="append", type="string", dest="ToolChain",\r
1676         help="Using the Tool Chain Tagname to build the platform, overriding target.txt's TOOL_CHAIN_TAG definition.")\r
1677     Parser.add_option("-x", "--sku-id", action="callback", type="string", dest="SkuId", callback=SingleCheckCallback,\r
1678         help="Using this name of SKU ID to build the platform, overriding SKUID_IDENTIFIER in DSC file.")\r
1679 \r
1680     Parser.add_option("-n", action="callback", type="int", dest="ThreadNumber", callback=SingleCheckCallback,\r
1681         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
1682 \r
1683     Parser.add_option("-f", "--fdf", action="callback", type="string", dest="FdfFile", callback=SingleCheckCallback,\r
1684         help="The name of the FDF file to use, which overrides the setting in the DSC file.")\r
1685     Parser.add_option("-r", "--rom-image", action="append", type="string", dest="RomImage", default=[],\r
1686         help="The name of FD to be generated. The name must be from [FD] section in FDF file.")\r
1687     Parser.add_option("-i", "--fv-image", action="append", type="string", dest="FvImage", default=[],\r
1688         help="The name of FV to be generated. The name must be from [FV] section in FDF file.")\r
1689 \r
1690     Parser.add_option("-u", "--skip-autogen", action="store_true", dest="SkipAutoGen", help="Skip AutoGen step.")\r
1691     Parser.add_option("-e", "--re-parse", action="store_true", dest="Reparse", help="Re-parse all meta-data files.")\r
1692 \r
1693     Parser.add_option("-c", "--case-insensitive", action="store_true", dest="CaseInsensitive", help="Don't check case of file name.")\r
1694 \r
1695     # Parser.add_option("-D", "--define", action="append", dest="Defines", metavar="NAME[=[VALUE]]",\r
1696     #     help="Define global macro which can be used in DSC/DEC/INF files.")\r
1697 \r
1698     Parser.add_option("-w", "--warning-as-error", action="store_true", dest="WarningAsError", help="Treat warning in tools as error.")\r
1699     Parser.add_option("-j", "--log", action="store", dest="LogFile", help="Put log in specified file as well as on console.")\r
1700 \r
1701     Parser.add_option("-s", "--silent", action="store_true", type=None, dest="SilentMode",\r
1702         help="Make use of silent mode of (n)make.")\r
1703     Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")\r
1704     Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\\r
1705                                                                                "including library instances selected, final dependency expression, "\\r
1706                                                                                "and warning messages, etc.")\r
1707     Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")\r
1708     Parser.add_option("-D", "--define", action="append", type="string", dest="Macros", help="Macro: \"Name [= Value]\".")\r
1709 \r
1710     Parser.add_option("-y", "--report-file", action="store", dest="ReportFile", help="Create/overwrite the report to the specified filename.")\r
1711     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
1712         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
1713              "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
1714 \r
1715     (Opt, Args)=Parser.parse_args()\r
1716     return (Opt, Args)\r
1717 \r
1718 ## Tool entrance method\r
1719 #\r
1720 # This method mainly dispatch specific methods per the command line options.\r
1721 # If no error found, return zero value so the caller of this tool can know\r
1722 # if it's executed successfully or not.\r
1723 #\r
1724 #   @retval 0     Tool was successful\r
1725 #   @retval 1     Tool failed\r
1726 #\r
1727 def Main():\r
1728     StartTime = time.time()\r
1729 \r
1730     # Initialize log system\r
1731     EdkLogger.Initialize()\r
1732 \r
1733     #\r
1734     # Parse the options and args\r
1735     #\r
1736     (Option, Target) = MyOptionParser()\r
1737     GlobalData.gOptions = Option\r
1738     GlobalData.gCaseInsensitive = Option.CaseInsensitive\r
1739 \r
1740     # Set log level\r
1741     if Option.verbose != None:\r
1742         EdkLogger.SetLevel(EdkLogger.VERBOSE)\r
1743     elif Option.quiet != None:\r
1744         EdkLogger.SetLevel(EdkLogger.QUIET)\r
1745     elif Option.debug != None:\r
1746         EdkLogger.SetLevel(Option.debug + 1)\r
1747     else:\r
1748         EdkLogger.SetLevel(EdkLogger.INFO)\r
1749 \r
1750     if Option.LogFile != None:\r
1751         EdkLogger.SetLogFile(Option.LogFile)\r
1752 \r
1753     if Option.WarningAsError == True:\r
1754         EdkLogger.SetWarningAsError()\r
1755 \r
1756     if platform.platform().find("Windows") >= 0:\r
1757         GlobalData.gIsWindows = True\r
1758     else:\r
1759         GlobalData.gIsWindows = False\r
1760 \r
1761     EdkLogger.quiet(time.strftime("%H:%M:%S, %b.%d %Y ", time.localtime()) + "[%s]\n" % platform.platform())\r
1762     ReturnCode = 0\r
1763     MyBuild = None\r
1764     try:\r
1765         if len(Target) == 0:\r
1766             Target = "all"\r
1767         elif len(Target) >= 2:\r
1768             EdkLogger.error("build", OPTION_NOT_SUPPORTED, "More than one targets are not supported.",\r
1769                             ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))\r
1770         else:\r
1771             Target = Target[0].lower()\r
1772 \r
1773         if Target not in gSupportedTarget:\r
1774             EdkLogger.error("build", OPTION_NOT_SUPPORTED, "Not supported target [%s]." % Target,\r
1775                             ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))\r
1776 \r
1777         GlobalData.gGlobalDefines = ParseDefines(Option.Macros)\r
1778         #\r
1779         # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH\r
1780         #\r
1781         CheckEnvVariable()\r
1782         Workspace = os.getenv("WORKSPACE")\r
1783         #\r
1784         # Get files real name in workspace dir\r
1785         #\r
1786         GlobalData.gAllFiles = Utils.DirCache(Workspace)\r
1787 \r
1788         WorkingDirectory = os.getcwd()\r
1789         if not Option.ModuleFile:\r
1790             FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.inf')))\r
1791             FileNum = len(FileList)\r
1792             if FileNum >= 2:\r
1793                 EdkLogger.error("build", OPTION_NOT_SUPPORTED, "There are %d INF files in %s." % (FileNum, WorkingDirectory),\r
1794                                 ExtraData="Please use '-m <INF_FILE_PATH>' switch to choose one.")\r
1795             elif FileNum == 1:\r
1796                 Option.ModuleFile = NormFile(FileList[0], Workspace)\r
1797 \r
1798         if Option.ModuleFile:\r
1799             if os.path.isabs (Option.ModuleFile):\r
1800                 if os.path.normcase (os.path.normpath(Option.ModuleFile)).find (Workspace) == 0:\r
1801                     Option.ModuleFile = NormFile(os.path.normpath(Option.ModuleFile), Workspace)\r
1802             Option.ModuleFile = PathClass(Option.ModuleFile, Workspace)\r
1803             ErrorCode, ErrorInfo = Option.ModuleFile.Validate(".inf", False)\r
1804             if ErrorCode != 0:\r
1805                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
1806 \r
1807         if Option.PlatformFile != None:\r
1808             if os.path.isabs (Option.PlatformFile):\r
1809                 if os.path.normcase (os.path.normpath(Option.PlatformFile)).find (Workspace) == 0:\r
1810                     Option.PlatformFile = NormFile(os.path.normpath(Option.PlatformFile), Workspace)\r
1811             Option.PlatformFile = PathClass(Option.PlatformFile, Workspace)\r
1812             ErrorCode, ErrorInfo = Option.PlatformFile.Validate(".dsc", False)\r
1813             if ErrorCode != 0:\r
1814                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
1815 \r
1816         if Option.FdfFile != None:\r
1817             if os.path.isabs (Option.FdfFile):\r
1818                 if os.path.normcase (os.path.normpath(Option.FdfFile)).find (Workspace) == 0:\r
1819                     Option.FdfFile = NormFile(os.path.normpath(Option.FdfFile), Workspace)\r
1820             Option.FdfFile = PathClass(Option.FdfFile, Workspace)\r
1821             ErrorCode, ErrorInfo = Option.FdfFile.Validate(".fdf", False)\r
1822             if ErrorCode != 0:\r
1823                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
1824 \r
1825         MyBuild = Build(Target, Workspace, Option.PlatformFile, Option.ModuleFile,\r
1826                         Option.TargetArch, Option.ToolChain, Option.BuildTarget,\r
1827                         Option.FdfFile, Option.RomImage, Option.FvImage,\r
1828                         None, Option.SilentMode, Option.ThreadNumber,\r
1829                         Option.SkipAutoGen, Option.Reparse, Option.SkuId, \r
1830                         Option.ReportFile, Option.ReportType)\r
1831         MyBuild.Launch()\r
1832         #MyBuild.DumpBuildData()\r
1833     except FatalError, X:\r
1834         if MyBuild != None:\r
1835             # for multi-thread build exits safely\r
1836             MyBuild.Relinquish()\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         ReturnCode = X.args[0]\r
1840     except Warning, X:\r
1841         # error from Fdf parser\r
1842         if MyBuild != None:\r
1843             # for multi-thread build exits safely\r
1844             MyBuild.Relinquish()\r
1845         if Option != None and Option.debug != None:\r
1846             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1847         else:\r
1848             EdkLogger.error(X.ToolName, FORMAT_INVALID, File=X.FileName, Line=X.LineNumber, ExtraData=X.Message, RaiseError = False)\r
1849         ReturnCode = FORMAT_INVALID\r
1850     except KeyboardInterrupt:\r
1851         ReturnCode = ABORT_ERROR\r
1852         if Option != None and Option.debug != None:\r
1853             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1854     except:\r
1855         if MyBuild != None:\r
1856             # for multi-thread build exits safely\r
1857             MyBuild.Relinquish()\r
1858 \r
1859         # try to get the meta-file from the object causing exception\r
1860         Tb = sys.exc_info()[-1]\r
1861         MetaFile = GlobalData.gProcessingFile\r
1862         while Tb != None:\r
1863             if 'self' in Tb.tb_frame.f_locals and hasattr(Tb.tb_frame.f_locals['self'], 'MetaFile'):\r
1864                 MetaFile = Tb.tb_frame.f_locals['self'].MetaFile\r
1865             Tb = Tb.tb_next\r
1866         EdkLogger.error(\r
1867                     "\nbuild",\r
1868                     CODE_ERROR,\r
1869                     "Unknown fatal error when processing [%s]" % MetaFile,\r
1870                     ExtraData="\n(Please send email to edk2-buildtools-devel@lists.sourceforge.net for help, attaching following call stack trace!)\n",\r
1871                     RaiseError=False\r
1872                     )\r
1873         EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1874         ReturnCode = CODE_ERROR\r
1875     finally:\r
1876         Utils.Progressor.Abort()\r
1877 \r
1878     if ReturnCode == 0:\r
1879         Conclusion = "Done"\r
1880     elif ReturnCode == ABORT_ERROR:\r
1881         Conclusion = "Aborted"\r
1882     else:\r
1883         Conclusion = "Failed"\r
1884     FinishTime = time.time()\r
1885     BuildDuration = time.strftime("%M:%S", time.gmtime(int(round(FinishTime - StartTime))))\r
1886     if MyBuild != None:\r
1887         MyBuild.BuildReport.GenerateReport(BuildDuration)\r
1888         MyBuild.Db.Close()\r
1889     EdkLogger.SetLevel(EdkLogger.QUIET)\r
1890     EdkLogger.quiet("\n- %s -\n%s [%s]" % (Conclusion, time.strftime("%H:%M:%S, %b.%d %Y", time.localtime()), BuildDuration))\r
1891 \r
1892     return ReturnCode\r
1893 \r
1894 if __name__ == '__main__':\r
1895     r = Main()\r
1896     ## 0-127 is a safe return range, and 1 is a standard default error\r
1897     if r < 0 or r > 127: r = 1\r
1898     sys.exit(r)\r
1899 \r