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