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