Rollback to BaseTools revision 1799. The patch tries to use Win32 platform specific...
[efi/basetools/.git] / Source / Python / build / build.py
1 ## @file\r
2 # build a platform or a module\r
3 #\r
4 #  Copyright (c) 2007 - 2010, Intel Corporation\r
5 #\r
6 #  All rights reserved. This program and the accompanying materials\r
7 #  are licensed and made available under the terms and conditions of the BSD License\r
8 #  which accompanies this distribution.  The full text of the license may be found at\r
9 #  http://opensource.org/licenses/bsd-license.php\r
10 #\r
11 #  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12 #  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13 #\r
14 \r
15 ##\r
16 # Import Modules\r
17 #\r
18 import os\r
19 import re\r
20 import sys\r
21 import glob\r
22 import time\r
23 import platform\r
24 import traceback\r
25 \r
26 from threading import *\r
27 from optparse import OptionParser\r
28 from subprocess import *\r
29 from Common import Misc as Utils\r
30 \r
31 from Common.TargetTxtClassObject import *\r
32 from Common.ToolDefClassObject import *\r
33 from Common.DataType import *\r
34 from AutoGen.AutoGen import *\r
35 from Common.BuildToolError import *\r
36 from Workspace.WorkspaceDatabase import *\r
37 \r
38 from BuildReport import BuildReport\r
39 \r
40 import Common.EdkLogger\r
41 import Common.GlobalData as GlobalData\r
42 \r
43 # Version and Copyright\r
44 VersionNumber = "0.5"\r
45 __version__ = "%prog Version " + VersionNumber\r
46 __copyright__ = "Copyright (c) 2007 - 2010, Intel Corporation  All rights reserved."\r
47 \r
48 ## standard targets of build command\r
49 gSupportedTarget = ['all', 'genc', 'genmake', 'modules', 'libraries', 'fds', 'clean', 'cleanall', 'cleanlib', 'run']\r
50 \r
51 ## build configuration file\r
52 gBuildConfiguration = "Conf/target.txt"\r
53 gBuildCacheDir = "Conf/.cache"\r
54 gToolsDefinition = "Conf/tools_def.txt"\r
55 \r
56 ## Check environment PATH variable to make sure the specified tool is found\r
57 #\r
58 #   If the tool is found in the PATH, then True is returned\r
59 #   Otherwise, False is returned\r
60 #\r
61 def IsToolInPath(tool):\r
62     if os.environ.has_key('PATHEXT'):\r
63         extns = os.environ['PATHEXT'].split(os.path.pathsep)\r
64     else:\r
65         extns = ('',)\r
66     for pathDir in os.environ['PATH'].split(os.path.pathsep):\r
67         for ext in extns:\r
68             if os.path.exists(os.path.join(pathDir, tool + ext)):\r
69                 return True\r
70     return False\r
71 \r
72 ## Check environment variables\r
73 #\r
74 #  Check environment variables that must be set for build. Currently they are\r
75 #\r
76 #   WORKSPACE           The directory all packages/platforms start from\r
77 #   EDK_TOOLS_PATH      The directory contains all tools needed by the build\r
78 #   PATH                $(EDK_TOOLS_PATH)/Bin/<sys> must be set in PATH\r
79 #\r
80 #   If any of above environment variable is not set or has error, the build\r
81 #   will be broken.\r
82 #\r
83 def CheckEnvVariable():\r
84     # check WORKSPACE\r
85     if "WORKSPACE" not in os.environ:\r
86         EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",\r
87                         ExtraData="WORKSPACE")\r
88 \r
89     WorkspaceDir = os.path.normcase(os.path.normpath(os.environ["WORKSPACE"]))\r
90     if not os.path.exists(WorkspaceDir):\r
91         EdkLogger.error("build", FILE_NOT_FOUND, "WORKSPACE doesn't exist", ExtraData="%s" % WorkspaceDir)\r
92     elif ' ' in WorkspaceDir:\r
93         EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in WORKSPACE path",\r
94                         ExtraData=WorkspaceDir)\r
95     os.environ["WORKSPACE"] = WorkspaceDir\r
96 \r
97     #\r
98     # Check EFI_SOURCE (R8 build convention). EDK_SOURCE will always point to ECP\r
99     #\r
100     os.environ["ECP_SOURCE"] = os.path.join(WorkspaceDir, GlobalData.gEdkCompatibilityPkg)\r
101     if "EFI_SOURCE" not in os.environ:\r
102         os.environ["EFI_SOURCE"] = os.environ["ECP_SOURCE"]\r
103     if "EDK_SOURCE" not in os.environ:\r
104         os.environ["EDK_SOURCE"] = os.environ["ECP_SOURCE"]\r
105 \r
106     #\r
107     # Unify case of characters on case-insensitive systems\r
108     #\r
109     EfiSourceDir = os.path.normcase(os.path.normpath(os.environ["EFI_SOURCE"]))\r
110     EdkSourceDir = os.path.normcase(os.path.normpath(os.environ["EDK_SOURCE"]))\r
111     EcpSourceDir = os.path.normcase(os.path.normpath(os.environ["ECP_SOURCE"]))\r
112  \r
113     os.environ["EFI_SOURCE"] = EfiSourceDir\r
114     os.environ["EDK_SOURCE"] = EdkSourceDir\r
115     os.environ["ECP_SOURCE"] = EcpSourceDir\r
116     os.environ["EDK_TOOLS_PATH"] = os.path.normcase(os.environ["EDK_TOOLS_PATH"])\r
117     \r
118     if not os.path.exists(EcpSourceDir):\r
119         EdkLogger.verbose("ECP_SOURCE = %s doesn't exist. R8 modules could not be built." % EcpSourceDir)\r
120     elif ' ' in EcpSourceDir:\r
121         EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in ECP_SOURCE path",\r
122                         ExtraData=EcpSourceDir)\r
123     if not os.path.exists(EdkSourceDir):\r
124         if EdkSourceDir == EcpSourceDir:\r
125             EdkLogger.verbose("EDK_SOURCE = %s doesn't exist. R8 modules could not be built." % EdkSourceDir)\r
126         else:\r
127             EdkLogger.error("build", PARAMETER_INVALID, "EDK_SOURCE does not exist",\r
128                             ExtraData=EdkSourceDir)\r
129     elif ' ' in EdkSourceDir:\r
130         EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in EDK_SOURCE path",\r
131                         ExtraData=EdkSourceDir)\r
132     if not os.path.exists(EfiSourceDir):\r
133         if EfiSourceDir == EcpSourceDir:\r
134             EdkLogger.verbose("EFI_SOURCE = %s doesn't exist. R8 modules could not be built." % EfiSourceDir)\r
135         else:\r
136             EdkLogger.error("build", PARAMETER_INVALID, "EFI_SOURCE does not exist",\r
137                             ExtraData=EfiSourceDir)\r
138     elif ' ' in EfiSourceDir:\r
139         EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in EFI_SOURCE path",\r
140                         ExtraData=EfiSourceDir)\r
141 \r
142     # change absolute path to relative path to WORKSPACE\r
143     if EfiSourceDir.upper().find(WorkspaceDir.upper()) != 0:\r
144         EdkLogger.error("build", PARAMETER_INVALID, "EFI_SOURCE is not under WORKSPACE",\r
145                         ExtraData="WORKSPACE = %s\n    EFI_SOURCE = %s" % (WorkspaceDir, EfiSourceDir))\r
146     if EdkSourceDir.upper().find(WorkspaceDir.upper()) != 0:\r
147         EdkLogger.error("build", PARAMETER_INVALID, "EDK_SOURCE is not under WORKSPACE",\r
148                         ExtraData="WORKSPACE = %s\n    EDK_SOURCE = %s" % (WorkspaceDir, EdkSourceDir))\r
149     if EcpSourceDir.upper().find(WorkspaceDir.upper()) != 0:\r
150         EdkLogger.error("build", PARAMETER_INVALID, "ECP_SOURCE is not under WORKSPACE",\r
151                         ExtraData="WORKSPACE = %s\n    ECP_SOURCE = %s" % (WorkspaceDir, EcpSourceDir))\r
152 \r
153     # check EDK_TOOLS_PATH\r
154     if "EDK_TOOLS_PATH" not in os.environ:\r
155         EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",\r
156                         ExtraData="EDK_TOOLS_PATH")\r
157 \r
158     # check PATH\r
159     if "PATH" not in os.environ:\r
160         EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",\r
161                         ExtraData="PATH")\r
162 \r
163     GlobalData.gWorkspace = WorkspaceDir\r
164     GlobalData.gEfiSource = EfiSourceDir\r
165     GlobalData.gEdkSource = EdkSourceDir\r
166     GlobalData.gEcpSource = EcpSourceDir\r
167 \r
168 ## Get normalized file path\r
169 #\r
170 # Convert the path to be local format, and remove the WORKSPACE path at the\r
171 # beginning if the file path is given in full path.\r
172 #\r
173 # @param  FilePath      File path to be normalized\r
174 # @param  Workspace     Workspace path which the FilePath will be checked against\r
175 #\r
176 # @retval string        The normalized file path\r
177 #\r
178 def NormFile(FilePath, Workspace):\r
179     # check if the path is absolute or relative\r
180     if os.path.isabs(FilePath):\r
181         FileFullPath = os.path.normpath(FilePath)\r
182     else:\r
183         FileFullPath = os.path.normpath(os.path.join(Workspace, FilePath))\r
184 \r
185     # check if the file path exists or not\r
186     if not os.path.isfile(FileFullPath):\r
187         EdkLogger.error("build", FILE_NOT_FOUND, ExtraData="\t%s (Please give file in absolute path or relative to WORKSPACE)"  % FileFullPath)\r
188 \r
189     # remove workspace directory from the beginning part of the file path\r
190     if Workspace[-1] in ["\\", "/"]:\r
191         return FileFullPath[len(Workspace):]\r
192     else:\r
193         return FileFullPath[(len(Workspace) + 1):]\r
194 \r
195 ## Get the output of an external program\r
196 #\r
197 # This is the entrance method of thread reading output of an external program and\r
198 # putting them in STDOUT/STDERR of current program.\r
199 #\r
200 # @param  From      The stream message read from\r
201 # @param  To        The stream message put on\r
202 # @param  ExitFlag  The flag used to indicate stopping reading\r
203 #\r
204 def ReadMessage(From, To, ExitFlag):\r
205     while True:\r
206         # read one line a time\r
207         Line = From.readline()\r
208         # empty string means "end"\r
209         if Line != None and Line != "":\r
210             To(Line.rstrip())\r
211         else:\r
212             break\r
213         if ExitFlag.isSet():\r
214             break\r
215 \r
216 ## Launch an external program\r
217 #\r
218 # This method will call subprocess.Popen to execute an external program with\r
219 # given options in specified directory. Because of the dead-lock issue during\r
220 # redirecting output of the external program, threads are used to to do the\r
221 # redirection work.\r
222 #\r
223 # @param  Command               A list or string containing the call of the program\r
224 # @param  WorkingDir            The directory in which the program will be running\r
225 #\r
226 def LaunchCommand(Command, WorkingDir):\r
227     # if working directory doesn't exist, Popen() will raise an exception\r
228     if not os.path.isdir(WorkingDir):\r
229         EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=WorkingDir)\r
230 \r
231     Proc = None\r
232     EndOfProcedure = None\r
233     try:\r
234         # launch the command\r
235         Proc = Popen(Command, stdout=PIPE, stderr=PIPE, env=os.environ, cwd=WorkingDir, bufsize=-1)\r
236 \r
237         # launch two threads to read the STDOUT and STDERR\r
238         EndOfProcedure = Event()\r
239         EndOfProcedure.clear()\r
240         if Proc.stdout:\r
241             StdOutThread = Thread(target=ReadMessage, args=(Proc.stdout, EdkLogger.info, EndOfProcedure))\r
242             StdOutThread.setName("STDOUT-Redirector")\r
243             StdOutThread.setDaemon(False)\r
244             StdOutThread.start()\r
245 \r
246         if Proc.stderr:\r
247             StdErrThread = Thread(target=ReadMessage, args=(Proc.stderr, EdkLogger.quiet, EndOfProcedure))\r
248             StdErrThread.setName("STDERR-Redirector")\r
249             StdErrThread.setDaemon(False)\r
250             StdErrThread.start()\r
251 \r
252         # waiting for program exit\r
253         Proc.wait()\r
254     except: # in case of aborting\r
255         # terminate the threads redirecting the program output\r
256         if EndOfProcedure != None:\r
257             EndOfProcedure.set()\r
258         if Proc == None:\r
259             if type(Command) != type(""):\r
260                 Command = " ".join(Command)\r
261             EdkLogger.error("build", COMMAND_FAILURE, "Failed to start command", ExtraData="%s [%s]" % (Command, WorkingDir))\r
262 \r
263     if Proc.stdout:\r
264         StdOutThread.join()\r
265     if Proc.stderr:\r
266         StdErrThread.join()\r
267 \r
268     # check the return code of the program\r
269     if Proc.returncode != 0:\r
270         if type(Command) != type(""):\r
271             Command = " ".join(Command)\r
272         EdkLogger.error("build", COMMAND_FAILURE, ExtraData="%s [%s]" % (Command, WorkingDir))\r
273 \r
274 ## The smallest unit that can be built in multi-thread build mode\r
275 #\r
276 # This is the base class of build unit. The "Obj" parameter must provide\r
277 # __str__(), __eq__() and __hash__() methods. Otherwise there could be build units\r
278 # missing build.\r
279 #\r
280 # Currently the "Obj" should be only ModuleAutoGen or PlatformAutoGen objects.\r
281 #\r
282 class BuildUnit:\r
283     ## The constructor\r
284     #\r
285     #   @param  self        The object pointer\r
286     #   @param  Obj         The object the build is working on\r
287     #   @param  Target      The build target name, one of gSupportedTarget\r
288     #   @param  Dependency  The BuildUnit(s) which must be completed in advance\r
289     #   @param  WorkingDir  The directory build command starts in\r
290     #\r
291     def __init__(self, Obj, BuildCommand, Target, Dependency, WorkingDir="."):\r
292         self.BuildObject = Obj\r
293         self.Dependency = Dependency\r
294         self.WorkingDir = WorkingDir\r
295         self.Target = Target\r
296         self.BuildCommand = BuildCommand\r
297         if BuildCommand == None or len(BuildCommand) == 0:\r
298             EdkLogger.error("build", OPTION_MISSING, "No build command found for",\r
299                             ExtraData=str(Obj))\r
300 \r
301     ## str() method\r
302     #\r
303     #   It just returns the string representaion of self.BuildObject\r
304     #\r
305     #   @param  self        The object pointer\r
306     #\r
307     def __str__(self):\r
308         return str(self.BuildObject)\r
309 \r
310     ## "==" operator method\r
311     #\r
312     #   It just compares self.BuildObject with "Other". So self.BuildObject must\r
313     #   provide its own __eq__() method.\r
314     #\r
315     #   @param  self        The object pointer\r
316     #   @param  Other       The other BuildUnit object compared to\r
317     #\r
318     def __eq__(self, Other):\r
319         return Other != None and self.BuildObject == Other.BuildObject \\r
320                 and self.BuildObject.Arch == Other.BuildObject.Arch\r
321 \r
322     ## hash() method\r
323     #\r
324     #   It just returns the hash value of self.BuildObject which must be hashable.\r
325     #\r
326     #   @param  self        The object pointer\r
327     #\r
328     def __hash__(self):\r
329         return hash(self.BuildObject) + hash(self.BuildObject.Arch)\r
330 \r
331     def __repr__(self):\r
332         return repr(self.BuildObject)\r
333 \r
334 ## The smallest module unit that can be built by nmake/make command in multi-thread build mode\r
335 #\r
336 # This class is for module build by nmake/make build system. The "Obj" parameter\r
337 # must provide __str__(), __eq__() and __hash__() methods. Otherwise there could\r
338 # be make units missing build.\r
339 #\r
340 # Currently the "Obj" should be only ModuleAutoGen object.\r
341 #\r
342 class ModuleMakeUnit(BuildUnit):\r
343     ## The constructor\r
344     #\r
345     #   @param  self        The object pointer\r
346     #   @param  Obj         The ModuleAutoGen object the build is working on\r
347     #   @param  Target      The build target name, one of gSupportedTarget\r
348     #\r
349     def __init__(self, Obj, Target):\r
350         Dependency = [ModuleMakeUnit(La, Target) for La in Obj.LibraryAutoGenList]\r
351         BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir)\r
352         if Target in [None, "", "all"]:\r
353             self.Target = "tbuild"\r
354 \r
355 ## The smallest platform unit that can be built by nmake/make command in multi-thread build mode\r
356 #\r
357 # This class is for platform build by nmake/make build system. The "Obj" parameter\r
358 # must provide __str__(), __eq__() and __hash__() methods. Otherwise there could\r
359 # be make units missing build.\r
360 #\r
361 # Currently the "Obj" should be only PlatformAutoGen object.\r
362 #\r
363 class PlatformMakeUnit(BuildUnit):\r
364     ## The constructor\r
365     #\r
366     #   @param  self        The object pointer\r
367     #   @param  Obj         The PlatformAutoGen object the build is working on\r
368     #   @param  Target      The build target name, one of gSupportedTarget\r
369     #\r
370     def __init__(self, Obj, Target):\r
371         Dependency = [ModuleMakeUnit(Lib, Target) for Lib in self.BuildObject.LibraryAutoGenList]\r
372         Dependency.extend([ModuleMakeUnit(Mod, Target) for Mod in self.BuildObject.ModuleAutoGenList])\r
373         BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir)\r
374 \r
375 ## The class representing the task of a module build or platform build\r
376 #\r
377 # This class manages the build tasks in multi-thread build mode. Its jobs include\r
378 # scheduling thread running, catching thread error, monitor the thread status, etc.\r
379 #\r
380 class BuildTask:\r
381     # queue for tasks waiting for schedule\r
382     _PendingQueue = sdict()\r
383     _PendingQueueLock = threading.Lock()\r
384 \r
385     # queue for tasks ready for running\r
386     _ReadyQueue = sdict()\r
387     _ReadyQueueLock = threading.Lock()\r
388 \r
389     # queue for run tasks\r
390     _RunningQueue = sdict()\r
391     _RunningQueueLock = threading.Lock()\r
392 \r
393     # queue containing all build tasks, in case duplicate build\r
394     _TaskQueue = sdict()\r
395 \r
396     # flag indicating error occurs in a running thread\r
397     _ErrorFlag = threading.Event()\r
398     _ErrorFlag.clear()\r
399     _ErrorMessage = ""\r
400 \r
401     # BoundedSemaphore object used to control the number of running threads\r
402     _Thread = None\r
403 \r
404     # flag indicating if the scheduler is started or not\r
405     _SchedulerStopped = threading.Event()\r
406     _SchedulerStopped.set()\r
407 \r
408     ## Start the task scheduler thread\r
409     #\r
410     #   @param  MaxThreadNumber     The maximum thread number\r
411     #   @param  ExitFlag            Flag used to end the scheduler\r
412     #\r
413     @staticmethod\r
414     def StartScheduler(MaxThreadNumber, ExitFlag):\r
415         SchedulerThread = Thread(target=BuildTask.Scheduler, args=(MaxThreadNumber, ExitFlag))\r
416         SchedulerThread.setName("Build-Task-Scheduler")\r
417         SchedulerThread.setDaemon(False)\r
418         SchedulerThread.start()\r
419         # wait for the scheduler to be started, especially useful in Linux\r
420         while not BuildTask.IsOnGoing():\r
421             time.sleep(0.01)\r
422 \r
423     ## Scheduler method\r
424     #\r
425     #   @param  MaxThreadNumber     The maximum thread number\r
426     #   @param  ExitFlag            Flag used to end the scheduler\r
427     #\r
428     @staticmethod\r
429     def Scheduler(MaxThreadNumber, ExitFlag):\r
430         BuildTask._SchedulerStopped.clear()\r
431         try:\r
432             # use BoundedSemaphore to control the maximum running threads\r
433             BuildTask._Thread = BoundedSemaphore(MaxThreadNumber)\r
434             #\r
435             # scheduling loop, which will exits when no pending/ready task and\r
436             # indicated to do so, or there's error in running thread\r
437             #\r
438             while (len(BuildTask._PendingQueue) > 0 or len(BuildTask._ReadyQueue) > 0 \\r
439                    or not ExitFlag.isSet()) and not BuildTask._ErrorFlag.isSet():\r
440                 EdkLogger.debug(EdkLogger.DEBUG_8, "Pending Queue (%d), Ready Queue (%d)"\r
441                                 % (len(BuildTask._PendingQueue), len(BuildTask._ReadyQueue)))\r
442 \r
443                 # get all pending tasks\r
444                 BuildTask._PendingQueueLock.acquire()\r
445                 BuildObjectList = BuildTask._PendingQueue.keys()\r
446                 #\r
447                 # check if their dependency is resolved, and if true, move them\r
448                 # into ready queue\r
449                 #\r
450                 for BuildObject in BuildObjectList:\r
451                     Bt = BuildTask._PendingQueue[BuildObject]\r
452                     if Bt.IsReady():\r
453                         BuildTask._ReadyQueue[BuildObject] = BuildTask._PendingQueue.pop(BuildObject)\r
454                 BuildTask._PendingQueueLock.release()\r
455 \r
456                 # launch build thread until the maximum number of threads is reached\r
457                 while not BuildTask._ErrorFlag.isSet():\r
458                     # empty ready queue, do nothing further\r
459                     if len(BuildTask._ReadyQueue) == 0:\r
460                         break\r
461 \r
462                     # wait for active thread(s) exit\r
463                     BuildTask._Thread.acquire(True)\r
464 \r
465                     # start a new build thread\r
466                     Bo = BuildTask._ReadyQueue.keys()[0]\r
467                     Bt = BuildTask._ReadyQueue.pop(Bo)\r
468 \r
469                     # move into running queue\r
470                     BuildTask._RunningQueueLock.acquire()\r
471                     BuildTask._RunningQueue[Bo] = Bt\r
472                     BuildTask._RunningQueueLock.release()\r
473 \r
474                     Bt.Start()\r
475                     # avoid tense loop\r
476                     time.sleep(0.01)\r
477 \r
478                 # avoid tense loop\r
479                 time.sleep(0.01)\r
480 \r
481             # wait for all running threads exit\r
482             if BuildTask._ErrorFlag.isSet():\r
483                 EdkLogger.quiet("\nWaiting for all build threads exit...")\r
484             # while not BuildTask._ErrorFlag.isSet() and \\r
485             while len(BuildTask._RunningQueue) > 0:\r
486                 EdkLogger.verbose("Waiting for thread ending...(%d)" % len(BuildTask._RunningQueue))\r
487                 EdkLogger.debug(EdkLogger.DEBUG_8, "Threads [%s]" % ", ".join([Th.getName() for Th in threading.enumerate()]))\r
488                 # avoid tense loop\r
489                 time.sleep(0.1)\r
490         except BaseException, X:\r
491             #\r
492             # TRICK: hide the output of threads left runing, so that the user can\r
493             #        catch the error message easily\r
494             #\r
495             EdkLogger.SetLevel(EdkLogger.ERROR)\r
496             BuildTask._ErrorFlag.set()\r
497             BuildTask._ErrorMessage = "build thread scheduler error\n\t%s" % str(X)\r
498 \r
499         BuildTask._PendingQueue.clear()\r
500         BuildTask._ReadyQueue.clear()\r
501         BuildTask._RunningQueue.clear()\r
502         BuildTask._TaskQueue.clear()\r
503         BuildTask._SchedulerStopped.set()\r
504 \r
505     ## Wait for all running method exit\r
506     #\r
507     @staticmethod\r
508     def WaitForComplete():\r
509         BuildTask._SchedulerStopped.wait()\r
510 \r
511     ## Check if the scheduler is running or not\r
512     #\r
513     @staticmethod\r
514     def IsOnGoing():\r
515         return not BuildTask._SchedulerStopped.isSet()\r
516 \r
517     ## Abort the build\r
518     @staticmethod\r
519     def Abort():\r
520         if BuildTask.IsOnGoing():\r
521             BuildTask._ErrorFlag.set()\r
522             BuildTask.WaitForComplete()\r
523 \r
524     ## Check if there's error in running thread\r
525     #\r
526     #   Since the main thread cannot catch exceptions in other thread, we have to\r
527     #   use threading.Event to communicate this formation to main thread.\r
528     #\r
529     @staticmethod\r
530     def HasError():\r
531         return BuildTask._ErrorFlag.isSet()\r
532 \r
533     ## Get error message in running thread\r
534     #\r
535     #   Since the main thread cannot catch exceptions in other thread, we have to\r
536     #   use a static variable to communicate this message to main thread.\r
537     #\r
538     @staticmethod\r
539     def GetErrorMessage():\r
540         return BuildTask._ErrorMessage\r
541 \r
542     ## Factory method to create a BuildTask object\r
543     #\r
544     #   This method will check if a module is building or has been built. And if\r
545     #   true, just return the associated BuildTask object in the _TaskQueue. If\r
546     #   not, create and return a new BuildTask object. The new BuildTask object\r
547     #   will be appended to the _PendingQueue for scheduling later.\r
548     #\r
549     #   @param  BuildItem       A BuildUnit object representing a build object\r
550     #   @param  Dependency      The dependent build object of BuildItem\r
551     #\r
552     @staticmethod\r
553     def New(BuildItem, Dependency=None):\r
554         if BuildItem in BuildTask._TaskQueue:\r
555             Bt = BuildTask._TaskQueue[BuildItem]\r
556             return Bt\r
557 \r
558         Bt = BuildTask()\r
559         Bt._Init(BuildItem, Dependency)\r
560         BuildTask._TaskQueue[BuildItem] = Bt\r
561 \r
562         BuildTask._PendingQueueLock.acquire()\r
563         BuildTask._PendingQueue[BuildItem] = Bt\r
564         BuildTask._PendingQueueLock.release()\r
565 \r
566         return Bt\r
567 \r
568     ## The real constructor of BuildTask\r
569     #\r
570     #   @param  BuildItem       A BuildUnit object representing a build object\r
571     #   @param  Dependency      The dependent build object of BuildItem\r
572     #\r
573     def _Init(self, BuildItem, Dependency=None):\r
574         self.BuildItem = BuildItem\r
575 \r
576         self.DependencyList = []\r
577         if Dependency == None:\r
578             Dependency = BuildItem.Dependency\r
579         else:\r
580             Dependency.extend(BuildItem.Dependency)\r
581         self.AddDependency(Dependency)\r
582         # flag indicating build completes, used to avoid unnecessary re-build\r
583         self.CompleteFlag = False\r
584 \r
585     ## Check if all dependent build tasks are completed or not\r
586     #\r
587     def IsReady(self):\r
588         ReadyFlag = True\r
589         for Dep in self.DependencyList:\r
590             if Dep.CompleteFlag == True:\r
591                 continue\r
592             ReadyFlag = False\r
593             break\r
594 \r
595         return ReadyFlag\r
596 \r
597     ## Add dependent build task\r
598     #\r
599     #   @param  Dependency      The list of dependent build objects\r
600     #\r
601     def AddDependency(self, Dependency):\r
602         for Dep in Dependency:\r
603             self.DependencyList.append(BuildTask.New(Dep))    # BuildTask list\r
604 \r
605     ## The thread wrapper of LaunchCommand function\r
606     #\r
607     # @param  Command               A list or string contains the call of the command\r
608     # @param  WorkingDir            The directory in which the program will be running\r
609     #\r
610     def _CommandThread(self, Command, WorkingDir):\r
611         try:\r
612             LaunchCommand(Command, WorkingDir)\r
613             self.CompleteFlag = True\r
614         except:\r
615             #\r
616             # TRICK: hide the output of threads left runing, so that the user can\r
617             #        catch the error message easily\r
618             #\r
619             if not BuildTask._ErrorFlag.isSet():\r
620                 GlobalData.gBuildingModule = "%s [%s, %s, %s]" % (str(self.BuildItem.BuildObject),\r
621                                                                   self.BuildItem.BuildObject.Arch,\r
622                                                                   self.BuildItem.BuildObject.ToolChain,\r
623                                                                   self.BuildItem.BuildObject.BuildTarget\r
624                                                                  )\r
625             EdkLogger.SetLevel(EdkLogger.ERROR)\r
626             BuildTask._ErrorFlag.set()\r
627             BuildTask._ErrorMessage = "%s broken\n    %s [%s]" % \\r
628                                       (threading.currentThread().getName(), Command, WorkingDir)\r
629         # indicate there's a thread is available for another build task\r
630         BuildTask._RunningQueueLock.acquire()\r
631         BuildTask._RunningQueue.pop(self.BuildItem)\r
632         BuildTask._RunningQueueLock.release()\r
633         BuildTask._Thread.release()\r
634 \r
635     ## Start build task thread\r
636     #\r
637     def Start(self):\r
638         EdkLogger.quiet("Building ... %s" % repr(self.BuildItem))\r
639         Command = self.BuildItem.BuildCommand + [self.BuildItem.Target]\r
640         self.BuildTread = Thread(target=self._CommandThread, args=(Command, self.BuildItem.WorkingDir))\r
641         self.BuildTread.setName("build thread")\r
642         self.BuildTread.setDaemon(False)\r
643         self.BuildTread.start()\r
644 \r
645 ## The class implementing the EDK2 build process\r
646 #\r
647 #   The build process includes:\r
648 #       1. Load configuration from target.txt and tools_def.txt in $(WORKSPACE)/Conf\r
649 #       2. Parse DSC file of active platform\r
650 #       3. Parse FDF file if any\r
651 #       4. Establish build database, including parse all other files (module, package)\r
652 #       5. Create AutoGen files (C code file, depex file, makefile) if necessary\r
653 #       6. Call build command\r
654 #\r
655 class Build():\r
656     ## Constructor\r
657     #\r
658     # Constructor will load all necessary configurations, parse platform, modules\r
659     # and packages and the establish a database for AutoGen.\r
660     #\r
661     #   @param  Target              The build command target, one of gSupportedTarget\r
662     #   @param  WorkspaceDir        The directory of workspace\r
663     #   @param  Platform            The DSC file of active platform\r
664     #   @param  Module              The INF file of active module, if any\r
665     #   @param  Arch                The Arch list of platform or module\r
666     #   @param  ToolChain           The name list of toolchain\r
667     #   @param  BuildTarget         The "DEBUG" or "RELEASE" build\r
668     #   @param  FlashDefinition     The FDF file of active platform\r
669     #   @param  FdList=[]           The FD names to be individually built\r
670     #   @param  FvList=[]           The FV names to be individually built\r
671     #   @param  MakefileType        The type of makefile (for MSFT make or GNU make)\r
672     #   @param  SilentMode          Indicate multi-thread build mode\r
673     #   @param  ThreadNumber        The maximum number of thread if in multi-thread build mode\r
674     #   @param  SkipAutoGen         Skip AutoGen step\r
675     #   @param  Reparse             Re-parse all meta files\r
676     #   @param  SkuId               SKU id from command line\r
677     #\r
678     def __init__(self, Target, WorkspaceDir, Platform, Module, Arch, ToolChain,\r
679                  BuildTarget, FlashDefinition, FdList=[], FvList=[],\r
680                  MakefileType="nmake", SilentMode=False, ThreadNumber=2,\r
681                  SkipAutoGen=False, Reparse=False, SkuId=None, \r
682                  ReportFile=None, ReportType=None):\r
683 \r
684         self.WorkspaceDir = WorkspaceDir\r
685         self.Target         = Target\r
686         self.PlatformFile   = Platform\r
687         self.ModuleFile     = Module\r
688         self.ArchList       = Arch\r
689         self.ToolChainList  = ToolChain\r
690         self.BuildTargetList= BuildTarget\r
691         self.Fdf            = FlashDefinition\r
692         self.FdList         = FdList\r
693         self.FvList         = FvList\r
694         self.MakefileType   = MakefileType\r
695         self.SilentMode     = SilentMode\r
696         self.ThreadNumber   = ThreadNumber\r
697         self.SkipAutoGen    = SkipAutoGen\r
698         self.Reparse        = Reparse\r
699         self.SkuId          = SkuId\r
700         self.SpawnMode      = True\r
701         self.BuildReport    = BuildReport(ReportFile, ReportType)\r
702         self.TargetTxt      = TargetTxtClassObject()\r
703         self.ToolDef        = ToolDefClassObject()\r
704         self.Db             = WorkspaceDatabase(None, GlobalData.gGlobalDefines, self.Reparse)\r
705         #self.Db             = WorkspaceDatabase(None, {}, self.Reparse)\r
706         self.BuildDatabase  = self.Db.BuildObject\r
707         self.Platform       = None\r
708 \r
709         # print dot charater during doing some time-consuming work\r
710         self.Progress = Utils.Progressor()\r
711 \r
712         # parse target.txt, tools_def.txt, and platform file\r
713         #self.RestoreBuildData()\r
714         self.LoadConfiguration()\r
715         self.InitBuild()\r
716 \r
717         # print current build environment and configuration\r
718         EdkLogger.quiet("%-24s = %s" % ("WORKSPACE", os.environ["WORKSPACE"]))\r
719         EdkLogger.quiet("%-24s = %s" % ("ECP_SOURCE", os.environ["ECP_SOURCE"]))\r
720         EdkLogger.quiet("%-24s = %s" % ("EDK_SOURCE", os.environ["EDK_SOURCE"]))\r
721         EdkLogger.quiet("%-24s = %s" % ("EFI_SOURCE", os.environ["EFI_SOURCE"]))\r
722         EdkLogger.quiet("%-24s = %s" % ("EDK_TOOLS_PATH", os.environ["EDK_TOOLS_PATH"]))\r
723 \r
724         EdkLogger.info('\n%-24s = %s' % ("TARGET_ARCH", ' '.join(self.ArchList)))\r
725         EdkLogger.info('%-24s = %s' % ("TARGET", ' '.join(self.BuildTargetList)))\r
726         EdkLogger.info('%-24s = %s' % ("TOOL_CHAIN_TAG", ' '.join(self.ToolChainList)))\r
727 \r
728         EdkLogger.info('\n%-24s = %s' % ("Active Platform", self.PlatformFile))\r
729 \r
730         if self.Fdf != None and self.Fdf != "":\r
731             EdkLogger.info('%-24s = %s' % ("Flash Image Definition", self.Fdf))\r
732 \r
733         if self.ModuleFile != None and self.ModuleFile != "":\r
734             EdkLogger.info('%-24s = %s' % ("Active Module", self.ModuleFile))\r
735 \r
736         os.chdir(self.WorkspaceDir)\r
737         self.Progress.Start("\nProcessing meta-data")\r
738 \r
739     ## Load configuration\r
740     #\r
741     #   This method will parse target.txt and get the build configurations.\r
742     #\r
743     def LoadConfiguration(self):\r
744         #\r
745         # Check target.txt and tools_def.txt and Init them\r
746         #\r
747         BuildConfigurationFile = os.path.normpath(os.path.join(self.WorkspaceDir, gBuildConfiguration))\r
748         if os.path.isfile(BuildConfigurationFile) == True:\r
749             StatusCode = self.TargetTxt.LoadTargetTxtFile(BuildConfigurationFile)\r
750 \r
751             ToolDefinitionFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_CONF]\r
752             if ToolDefinitionFile == '':\r
753                 ToolDefinitionFile = gToolsDefinition\r
754             ToolDefinitionFile = os.path.normpath(os.path.join(self.WorkspaceDir, ToolDefinitionFile))\r
755             if os.path.isfile(ToolDefinitionFile) == True:\r
756                 StatusCode = self.ToolDef.LoadToolDefFile(ToolDefinitionFile)\r
757             else:\r
758                 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=ToolDefinitionFile)\r
759         else:\r
760             EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=BuildConfigurationFile)\r
761 \r
762         # if no ARCH given in command line, get it from target.txt\r
763         if self.ArchList == None or len(self.ArchList) == 0:\r
764             self.ArchList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET_ARCH]\r
765 \r
766         # if no build target given in command line, get it from target.txt\r
767         if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
768             self.BuildTargetList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET]\r
769 \r
770         # if no tool chain given in command line, get it from target.txt\r
771         if self.ToolChainList == None or len(self.ToolChainList) == 0:\r
772             self.ToolChainList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_TAG]\r
773             if self.ToolChainList == None or len(self.ToolChainList) == 0:\r
774                 EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, ExtraData="No toolchain given. Don't know how to build.\n")\r
775 \r
776         # check if the tool chains are defined or not\r
777         NewToolChainList = []\r
778         for ToolChain in self.ToolChainList:\r
779             if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG]:\r
780                 EdkLogger.warn("build", "Tool chain [%s] is not defined" % ToolChain)\r
781             else:\r
782                 NewToolChainList.append(ToolChain)\r
783         # if no tool chain available, break the build\r
784         if len(NewToolChainList) == 0:\r
785             EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,\r
786                             ExtraData="[%s] not defined. No toolchain available for build!\n" % ", ".join(self.ToolChainList))\r
787         else:\r
788             self.ToolChainList = NewToolChainList\r
789 \r
790         if self.ThreadNumber == None:\r
791             self.ThreadNumber = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER]\r
792             if self.ThreadNumber == '':\r
793                 self.ThreadNumber = 0\r
794             else:\r
795                 self.ThreadNumber = int(self.ThreadNumber, 0)\r
796 \r
797         if self.ThreadNumber == 0:\r
798             self.ThreadNumber = 1\r
799 \r
800         if not self.PlatformFile:\r
801             PlatformFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_ACTIVE_PLATFORM]\r
802             if not PlatformFile:\r
803                 # Try to find one in current directory\r
804                 WorkingDirectory = os.getcwd()\r
805                 FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.dsc')))\r
806                 FileNum = len(FileList)\r
807                 if FileNum >= 2:\r
808                     EdkLogger.error("build", OPTION_MISSING,\r
809                                     ExtraData="There are %d DSC files in %s. Use '-p' to specify one.\n" % (FileNum, WorkingDirectory))\r
810                 elif FileNum == 1:\r
811                     PlatformFile = FileList[0]\r
812                 else:\r
813                     EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,\r
814                                     ExtraData="No active platform specified in target.txt or command line! Nothing can be built.\n")\r
815 \r
816             self.PlatformFile = PathClass(NormFile(PlatformFile, self.WorkspaceDir), self.WorkspaceDir)\r
817             ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False)\r
818             if ErrorCode != 0:\r
819                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
820 \r
821     ## Initialize build configuration\r
822     #\r
823     #   This method will parse DSC file and merge the configurations from\r
824     #   command line and target.txt, then get the final build configurations.\r
825     #\r
826     def InitBuild(self):\r
827         ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc")\r
828         if ErrorCode != 0:\r
829             EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
830 \r
831         # create metafile database\r
832         self.Db.InitDatabase()\r
833 \r
834         # we need information in platform description file to determine how to build\r
835         self.Platform = self.BuildDatabase[self.PlatformFile, 'COMMON']\r
836         if not self.Fdf:\r
837             self.Fdf = self.Platform.FlashDefinition\r
838 \r
839         if self.SkuId == None or self.SkuId == '':\r
840             self.SkuId = self.Platform.SkuName\r
841 \r
842         # check FD/FV build target\r
843         if self.Fdf == None or self.Fdf == "":\r
844             if self.FdList != []:\r
845                 EdkLogger.info("No flash definition file found. FD [%s] will be ignored." % " ".join(self.FdList))\r
846                 self.FdList = []\r
847             if self.FvList != []:\r
848                 EdkLogger.info("No flash definition file found. FV [%s] will be ignored." % " ".join(self.FvList))\r
849                 self.FvList = []\r
850         else:\r
851             FdfParserObj = FdfParser(str(self.Fdf))\r
852             FdfParserObj.ParseFile()\r
853             for fvname in self.FvList:\r
854                 if fvname.upper() not in FdfParserObj.Profile.FvDict.keys():\r
855                     EdkLogger.error("build", OPTION_VALUE_INVALID,\r
856                                     "No such an FV in FDF file: %s" % fvname)\r
857 \r
858         #\r
859         # Merge Arch\r
860         #\r
861         if self.ArchList == None or len(self.ArchList) == 0:\r
862             ArchList = set(self.Platform.SupArchList)\r
863         else:\r
864             ArchList = set(self.ArchList) & set(self.Platform.SupArchList)\r
865         if len(ArchList) == 0:\r
866             EdkLogger.error("build", PARAMETER_INVALID,\r
867                             ExtraData = "Active platform supports [%s] only, but [%s] is given."\r
868                                         % (" ".join(self.Platform.SupArchList), " ".join(self.ArchList)))\r
869         elif len(ArchList) != len(self.ArchList):\r
870             SkippedArchList = set(self.ArchList).symmetric_difference(set(self.Platform.SupArchList))\r
871             EdkLogger.verbose("\nArch [%s] is ignored because active platform supports [%s] but [%s] is specified !"\r
872                            % (" ".join(SkippedArchList), " ".join(self.Platform.SupArchList), " ".join(self.ArchList)))\r
873         self.ArchList = tuple(ArchList)\r
874 \r
875         # Merge build target\r
876         if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
877             BuildTargetList = self.Platform.BuildTargets\r
878         else:\r
879             BuildTargetList = list(set(self.BuildTargetList) & set(self.Platform.BuildTargets))\r
880         if BuildTargetList == []:\r
881             EdkLogger.error("build", PARAMETER_INVALID, "Active platform only supports [%s], but [%s] is given"\r
882                                 % (" ".join(self.Platform.BuildTargets), " ".join(self.BuildTargetList)))\r
883         self.BuildTargetList = BuildTargetList\r
884 \r
885     ## Build a module or platform\r
886     #\r
887     # Create autogen code and makfile for a module or platform, and the launch\r
888     # "make" command to build it\r
889     #\r
890     #   @param  Target                      The target of build command\r
891     #   @param  Platform                    The platform file\r
892     #   @param  Module                      The module file\r
893     #   @param  BuildTarget                 The name of build target, one of "DEBUG", "RELEASE"\r
894     #   @param  ToolChain                   The name of toolchain to build\r
895     #   @param  Arch                        The arch of the module/platform\r
896     #   @param  CreateDepModuleCodeFile     Flag used to indicate creating code\r
897     #                                       for dependent modules/Libraries\r
898     #   @param  CreateDepModuleMakeFile     Flag used to indicate creating makefile\r
899     #                                       for dependent modules/Libraries\r
900     #\r
901     def _Build(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True):\r
902         if AutoGenObject == None:\r
903             return False\r
904 \r
905         # skip file generation for cleanxxx targets, run and fds target\r
906         if Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
907             # for target which must generate AutoGen code and makefile\r
908             if not self.SkipAutoGen or Target == 'genc':\r
909                 self.Progress.Start("Generating code")\r
910                 AutoGenObject.CreateCodeFile(CreateDepsCodeFile)\r
911                 self.Progress.Stop("done!")\r
912             if Target == "genc":\r
913                 return True\r
914 \r
915             if not self.SkipAutoGen or Target == 'genmake':\r
916                 self.Progress.Start("Generating makefile")\r
917                 AutoGenObject.CreateMakeFile(CreateDepsMakeFile)\r
918                 self.Progress.Stop("done!")\r
919             if Target == "genmake":\r
920                 return True\r
921         else:\r
922             # always recreate top/platform makefile when clean, just in case of inconsistency\r
923             AutoGenObject.CreateCodeFile(False)\r
924             AutoGenObject.CreateMakeFile(False)\r
925 \r
926         if EdkLogger.GetLevel() == EdkLogger.QUIET:\r
927             EdkLogger.quiet("Building ... %s" % repr(AutoGenObject))\r
928 \r
929         BuildCommand = AutoGenObject.BuildCommand\r
930         if BuildCommand == None or len(BuildCommand) == 0:\r
931             EdkLogger.error("build", OPTION_MISSING, ExtraData="No MAKE command found for [%s, %s, %s]" % Key)\r
932 \r
933         BuildCommand = BuildCommand + [Target]\r
934         LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir)\r
935         if Target == 'cleanall':\r
936             try:\r
937                 #os.rmdir(AutoGenObject.BuildDir)\r
938                 RemoveDirectory(AutoGenObject.BuildDir, True)\r
939             except WindowsError, X:\r
940                 EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X))\r
941         return True\r
942 \r
943     ## Build active platform for different build targets and different tool chains\r
944     #\r
945     def _BuildPlatform(self):\r
946         for BuildTarget in self.BuildTargetList:\r
947             for ToolChain in self.ToolChainList:\r
948                 Wa = WorkspaceAutoGen(\r
949                         self.WorkspaceDir,\r
950                         self.Platform,\r
951                         BuildTarget,\r
952                         ToolChain,\r
953                         self.ArchList,\r
954                         self.BuildDatabase,\r
955                         self.TargetTxt,\r
956                         self.ToolDef,\r
957                         self.Fdf,\r
958                         self.FdList,\r
959                         self.FvList,\r
960                         self.SkuId\r
961                         )\r
962                 self.BuildReport.AddPlatformReport(Wa)\r
963                 self.Progress.Stop("done!")\r
964                 self._Build(self.Target, Wa)\r
965 \r
966     ## Build active module for different build targets, different tool chains and different archs\r
967     #\r
968     def _BuildModule(self):\r
969         for BuildTarget in self.BuildTargetList:\r
970             for ToolChain in self.ToolChainList:\r
971                 #\r
972                 # module build needs platform build information, so get platform\r
973                 # AutoGen first\r
974                 #\r
975                 Wa = WorkspaceAutoGen(\r
976                         self.WorkspaceDir,\r
977                         self.Platform,\r
978                         BuildTarget,\r
979                         ToolChain,\r
980                         self.ArchList,\r
981                         self.BuildDatabase,\r
982                         self.TargetTxt,\r
983                         self.ToolDef,\r
984                         self.Fdf,\r
985                         self.FdList,\r
986                         self.FvList,\r
987                         self.SkuId\r
988                         )\r
989                 self.BuildReport.AddPlatformReport(Wa)\r
990                 Wa.CreateMakeFile(False)\r
991                 self.Progress.Stop("done!")\r
992                 MaList = []\r
993                 for Arch in self.ArchList:\r
994                     Ma = ModuleAutoGen(Wa, self.ModuleFile, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
995                     if Ma == None: continue\r
996                     MaList.append(Ma)\r
997                     self._Build(self.Target, Ma)\r
998                 if MaList == []:\r
999                     EdkLogger.error(\r
1000                                 'build',\r
1001                                 BUILD_ERROR,\r
1002                                 "Module for [%s] is not a component of active platform."\\r
1003                                 " Please make sure that the ARCH and inf file path are"\\r
1004                                 " given in the same as in [%s]" %\\r
1005                                     (', '.join(self.ArchList), self.Platform),\r
1006                                 ExtraData=self.ModuleFile\r
1007                                 )\r
1008 \r
1009     ## Build a platform in multi-thread mode\r
1010     #\r
1011     def _MultiThreadBuildPlatform(self):\r
1012         for BuildTarget in self.BuildTargetList:\r
1013             for ToolChain in self.ToolChainList:\r
1014                 Wa = WorkspaceAutoGen(\r
1015                         self.WorkspaceDir,\r
1016                         self.Platform,\r
1017                         BuildTarget,\r
1018                         ToolChain,\r
1019                         self.ArchList,\r
1020                         self.BuildDatabase,\r
1021                         self.TargetTxt,\r
1022                         self.ToolDef,\r
1023                         self.Fdf,\r
1024                         self.FdList,\r
1025                         self.FvList,\r
1026                         self.SkuId\r
1027                         )\r
1028                 self.BuildReport.AddPlatformReport(Wa)\r
1029                 Wa.CreateMakeFile(False)\r
1030 \r
1031                 # multi-thread exit flag\r
1032                 ExitFlag = threading.Event()\r
1033                 ExitFlag.clear()\r
1034                 for Arch in self.ArchList:\r
1035                     Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)\r
1036                     if Pa == None:\r
1037                         continue\r
1038                     for Module in Pa.Platform.Modules:\r
1039                         # Get ModuleAutoGen object to generate C code file and makefile\r
1040                         Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
1041                         if Ma == None:\r
1042                             continue\r
1043                         # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'\r
1044                         if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
1045                             # for target which must generate AutoGen code and makefile\r
1046                             if not self.SkipAutoGen or self.Target == 'genc':\r
1047                                 Ma.CreateCodeFile(True)\r
1048                             if self.Target == "genc":\r
1049                                 continue\r
1050 \r
1051                             if not self.SkipAutoGen or self.Target == 'genmake':\r
1052                                 Ma.CreateMakeFile(True)\r
1053                             if self.Target == "genmake":\r
1054                                 continue\r
1055                         self.Progress.Stop("done!")\r
1056                         # Generate build task for the module\r
1057                         Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))\r
1058                         # Break build if any build thread has error\r
1059                         if BuildTask.HasError():\r
1060                             # we need a full version of makefile for platform\r
1061                             ExitFlag.set()\r
1062                             BuildTask.WaitForComplete()\r
1063                             Pa.CreateMakeFile(False)\r
1064                             EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1065                         # Start task scheduler\r
1066                         if not BuildTask.IsOnGoing():\r
1067                             BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)\r
1068 \r
1069                     # in case there's an interruption. we need a full version of makefile for platform\r
1070                     Pa.CreateMakeFile(False)\r
1071                     if BuildTask.HasError():\r
1072                         EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1073 \r
1074                 #\r
1075                 # All modules have been put in build tasks queue. Tell task scheduler\r
1076                 # to exit if all tasks are completed\r
1077                 #\r
1078                 ExitFlag.set()\r
1079                 BuildTask.WaitForComplete()\r
1080 \r
1081                 #\r
1082                 # Check for build error, and raise exception if one\r
1083                 # has been signaled.\r
1084                 #\r
1085                 if BuildTask.HasError():\r
1086                     EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1087 \r
1088                 # Generate FD image if there's a FDF file found\r
1089                 if self.Fdf != '' and self.Target in ["", "all", "fds"]:\r
1090                     LaunchCommand(Wa.BuildCommand + ["fds"], Wa.MakeFileDir)\r
1091 \r
1092     ## Generate GuidedSectionTools.txt in the FV directories.\r
1093     #\r
1094     def CreateGuidedSectionToolsFile(self):\r
1095         for Arch in self.ArchList:\r
1096             for BuildTarget in self.BuildTargetList:\r
1097                 for ToolChain in self.ToolChainList:\r
1098                     FvDir = os.path.join(\r
1099                                 self.WorkspaceDir,\r
1100                                 self.Platform.OutputDirectory,\r
1101                                 '_'.join((BuildTarget, ToolChain)),\r
1102                                 'FV'\r
1103                                 )\r
1104                     if not os.path.exists(FvDir):\r
1105                         continue\r
1106                     # Build up the list of supported architectures for this build\r
1107                     prefix = '%s_%s_%s_' % (BuildTarget, ToolChain, Arch)\r
1108 \r
1109                     # Look through the tool definitions for GUIDed tools\r
1110                     guidAttribs = []\r
1111                     for (attrib, value) in self.ToolDef.ToolsDefTxtDictionary.iteritems():\r
1112                         if attrib.upper().endswith('_GUID'):\r
1113                             split = attrib.split('_')\r
1114                             thisPrefix = '_'.join(split[0:3]) + '_'\r
1115                             if thisPrefix == prefix:\r
1116                                 guid = self.ToolDef.ToolsDefTxtDictionary[attrib]\r
1117                                 guid = guid.lower()\r
1118                                 toolName = split[3]\r
1119                                 path = '_'.join(split[0:4]) + '_PATH'\r
1120                                 path = self.ToolDef.ToolsDefTxtDictionary[path]\r
1121                                 path = self.GetFullPathOfTool(path)\r
1122                                 guidAttribs.append((guid, toolName, path))\r
1123 \r
1124                     # Write out GuidedSecTools.txt\r
1125                     toolsFile = os.path.join(FvDir, 'GuidedSectionTools.txt')\r
1126                     toolsFile = open(toolsFile, 'wt')\r
1127                     for guidedSectionTool in guidAttribs:\r
1128                         print >> toolsFile, ' '.join(guidedSectionTool)\r
1129                     toolsFile.close()\r
1130 \r
1131     ## Returns the full path of the tool.\r
1132     #\r
1133     def GetFullPathOfTool (self, tool):\r
1134         if os.path.exists(tool):\r
1135             return os.path.realpath(tool)\r
1136         else:\r
1137             # We need to search for the tool using the\r
1138             # PATH environment variable.\r
1139             for dirInPath in os.environ['PATH'].split(os.pathsep):\r
1140                 foundPath = os.path.join(dirInPath, tool)\r
1141                 if os.path.exists(foundPath):\r
1142                     return os.path.realpath(foundPath)\r
1143 \r
1144         # If the tool was not found in the path then we just return\r
1145         # the input tool.\r
1146         return tool\r
1147 \r
1148     ## Launch the module or platform build\r
1149     #\r
1150     def Launch(self):\r
1151         if self.ModuleFile == None or self.ModuleFile == "":\r
1152             if not self.SpawnMode or self.Target not in ["", "all"]:\r
1153                 self.SpawnMode = False\r
1154                 self._BuildPlatform()\r
1155             else:\r
1156                 self._MultiThreadBuildPlatform()\r
1157             self.CreateGuidedSectionToolsFile()\r
1158         else:\r
1159             self.SpawnMode = False\r
1160             self._BuildModule()\r
1161 \r
1162     ## Do some clean-up works when error occurred\r
1163     def Relinquish(self):\r
1164         OldLogLevel = EdkLogger.GetLevel()\r
1165         EdkLogger.SetLevel(EdkLogger.ERROR)\r
1166         #self.DumpBuildData()\r
1167         Utils.Progressor.Abort()\r
1168         if self.SpawnMode == True:\r
1169             BuildTask.Abort()\r
1170         EdkLogger.SetLevel(OldLogLevel)\r
1171 \r
1172     def DumpBuildData(self):\r
1173         CacheDirectory = os.path.join(self.WorkspaceDir, gBuildCacheDir)\r
1174         Utils.CreateDirectory(CacheDirectory)\r
1175         Utils.DataDump(Utils.gFileTimeStampCache, os.path.join(CacheDirectory, "gFileTimeStampCache"))\r
1176         Utils.DataDump(Utils.gDependencyDatabase, os.path.join(CacheDirectory, "gDependencyDatabase"))\r
1177 \r
1178     def RestoreBuildData(self):\r
1179         FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gFileTimeStampCache")\r
1180         if Utils.gFileTimeStampCache == {} and os.path.isfile(FilePath):\r
1181             Utils.gFileTimeStampCache = Utils.DataRestore(FilePath)\r
1182             if Utils.gFileTimeStampCache == None:\r
1183                 Utils.gFileTimeStampCache = {}\r
1184 \r
1185         FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gDependencyDatabase")\r
1186         if Utils.gDependencyDatabase == {} and os.path.isfile(FilePath):\r
1187             Utils.gDependencyDatabase = Utils.DataRestore(FilePath)\r
1188             if Utils.gDependencyDatabase == None:\r
1189                 Utils.gDependencyDatabase = {}\r
1190 \r
1191 def ParseDefines(DefineList=[]):\r
1192     DefineDict = {}\r
1193     if DefineList != None:\r
1194         for Define in DefineList:\r
1195             DefineTokenList = Define.split("=", 1)\r
1196             if len(DefineTokenList) == 1:\r
1197                 DefineDict[DefineTokenList[0]] = ""\r
1198             else:\r
1199                 DefineDict[DefineTokenList[0]] = DefineTokenList[1].strip()\r
1200     return DefineDict\r
1201 \r
1202 gParamCheck = []\r
1203 def SingleCheckCallback(option, opt_str, value, parser):\r
1204     if option not in gParamCheck:\r
1205         setattr(parser.values, option.dest, value)\r
1206         gParamCheck.append(option)\r
1207     else:\r
1208         parser.error("Option %s only allows one instance in command line!" % option)\r
1209 \r
1210 ## Parse command line options\r
1211 #\r
1212 # Using standard Python module optparse to parse command line option of this tool.\r
1213 #\r
1214 #   @retval Opt   A optparse.Values object containing the parsed options\r
1215 #   @retval Args  Target of build command\r
1216 #\r
1217 def MyOptionParser():\r
1218     Parser = OptionParser(description=__copyright__,version=__version__,prog="build.exe",usage="%prog [options] [all|fds|genc|genmake|clean|cleanall|cleanlib|modules|libraries|run]")\r
1219     Parser.add_option("-a", "--arch", action="append", type="choice", choices=['IA32','X64','IPF','EBC','ARM'], dest="TargetArch",\r
1220         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
1221     Parser.add_option("-p", "--platform", action="callback", type="string", dest="PlatformFile", callback=SingleCheckCallback,\r
1222         help="Build the platform specified by the DSC file name argument, overriding target.txt's ACTIVE_PLATFORM definition.")\r
1223     Parser.add_option("-m", "--module", action="callback", type="string", dest="ModuleFile", callback=SingleCheckCallback,\r
1224         help="Build the module specified by the INF file name argument.")\r
1225     Parser.add_option("-b", "--buildtarget", action="append", type="choice", choices=['DEBUG','RELEASE'], dest="BuildTarget",\r
1226         help="BuildTarget is one of list: DEBUG, RELEASE, which overrides target.txt's TARGET definition. To specify more TARGET, please repeat this option.")\r
1227     Parser.add_option("-t", "--tagname", action="append", type="string", dest="ToolChain",\r
1228         help="Using the Tool Chain Tagname to build the platform, overriding target.txt's TOOL_CHAIN_TAG definition.")\r
1229     Parser.add_option("-x", "--sku-id", action="callback", type="string", dest="SkuId", callback=SingleCheckCallback,\r
1230         help="Using this name of SKU ID to build the platform, overriding SKUID_IDENTIFIER in DSC file.")\r
1231 \r
1232     Parser.add_option("-n", action="callback", type="int", dest="ThreadNumber", callback=SingleCheckCallback,\r
1233         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
1234 \r
1235     Parser.add_option("-f", "--fdf", action="callback", type="string", dest="FdfFile", callback=SingleCheckCallback,\r
1236         help="The name of the FDF file to use, which overrides the setting in the DSC file.")\r
1237     Parser.add_option("-r", "--rom-image", action="append", type="string", dest="RomImage", default=[],\r
1238         help="The name of FD to be generated. The name must be from [FD] section in FDF file.")\r
1239     Parser.add_option("-i", "--fv-image", action="append", type="string", dest="FvImage", default=[],\r
1240         help="The name of FV to be generated. The name must be from [FV] section in FDF file.")\r
1241 \r
1242     Parser.add_option("-u", "--skip-autogen", action="store_true", dest="SkipAutoGen", help="Skip AutoGen step.")\r
1243     Parser.add_option("-e", "--re-parse", action="store_true", dest="Reparse", help="Re-parse all meta-data files.")\r
1244 \r
1245     Parser.add_option("-c", "--case-insensitive", action="store_true", dest="CaseInsensitive", help="Don't check case of file name.")\r
1246 \r
1247     # Parser.add_option("-D", "--define", action="append", dest="Defines", metavar="NAME[=[VALUE]]",\r
1248     #     help="Define global macro which can be used in DSC/DEC/INF files.")\r
1249 \r
1250     Parser.add_option("-w", "--warning-as-error", action="store_true", dest="WarningAsError", help="Treat warning in tools as error.")\r
1251     Parser.add_option("-j", "--log", action="store", dest="LogFile", help="Put log in specified file as well as on console.")\r
1252 \r
1253     Parser.add_option("-s", "--silent", action="store_true", type=None, dest="SilentMode",\r
1254         help="Make use of silent mode of (n)make.")\r
1255     Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")\r
1256     Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\\r
1257                                                                                "including library instances selected, final dependency expression, "\\r
1258                                                                                "and warning messages, etc.")\r
1259     Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")\r
1260     Parser.add_option("-D", "--define", action="append", type="string", dest="Macros", help="Macro: \"Name [= Value]\".")\r
1261 \r
1262     Parser.add_option("-y", "--report-file", action="store", dest="ReportFile", help="Create/overwrite the report to the specified filename.")\r
1263     Parser.add_option("-Y", "--report-type", action="append", type="choice", choices=['ALL','PCD','LIBRARY','FLASH','DEPEX','BUILD_FLAGS','PREDICTION'], dest="ReportType",\r
1264         help="Flags that control the type of build report to generate.  Must be one of: [ALL, PCD, LIBRARY, FLASH, DEPEX, BUILD_FLAGS, PREDICTION].  To specify more than one flag, repeat this option on the command line.")\r
1265 \r
1266     (Opt, Args)=Parser.parse_args()\r
1267     return (Opt, Args)\r
1268 \r
1269 ## Tool entrance method\r
1270 #\r
1271 # This method mainly dispatch specific methods per the command line options.\r
1272 # If no error found, return zero value so the caller of this tool can know\r
1273 # if it's executed successfully or not.\r
1274 #\r
1275 #   @retval 0     Tool was successful\r
1276 #   @retval 1     Tool failed\r
1277 #\r
1278 def Main():\r
1279     StartTime = time.time()\r
1280 \r
1281     # Initialize log system\r
1282     EdkLogger.Initialize()\r
1283 \r
1284     #\r
1285     # Parse the options and args\r
1286     #\r
1287     (Option, Target) = MyOptionParser()\r
1288     GlobalData.gOptions = Option\r
1289     GlobalData.gCaseInsensitive = Option.CaseInsensitive\r
1290 \r
1291     # Set log level\r
1292     if Option.verbose != None:\r
1293         EdkLogger.SetLevel(EdkLogger.VERBOSE)\r
1294     elif Option.quiet != None:\r
1295         EdkLogger.SetLevel(EdkLogger.QUIET)\r
1296     elif Option.debug != None:\r
1297         EdkLogger.SetLevel(Option.debug + 1)\r
1298     else:\r
1299         EdkLogger.SetLevel(EdkLogger.INFO)\r
1300 \r
1301     if Option.LogFile != None:\r
1302         EdkLogger.SetLogFile(Option.LogFile)\r
1303 \r
1304     if Option.WarningAsError == True:\r
1305         EdkLogger.SetWarningAsError()\r
1306 \r
1307     if platform.platform().find("Windows") >= 0:\r
1308         GlobalData.gIsWindows = True\r
1309     else:\r
1310         GlobalData.gIsWindows = False\r
1311 \r
1312     EdkLogger.quiet(time.strftime("%H:%M:%S, %b.%d %Y ", time.localtime()) + "[%s]\n" % platform.platform())\r
1313     ReturnCode = 0\r
1314     MyBuild = None\r
1315     try:\r
1316         if len(Target) == 0:\r
1317             Target = "all"\r
1318         elif len(Target) >= 2:\r
1319             EdkLogger.error("build", OPTION_NOT_SUPPORTED, "More than one targets are not supported.",\r
1320                             ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))\r
1321         else:\r
1322             Target = Target[0].lower()\r
1323 \r
1324         if Target not in gSupportedTarget:\r
1325             EdkLogger.error("build", OPTION_NOT_SUPPORTED, "Not supported target [%s]." % Target,\r
1326                             ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))\r
1327 \r
1328         GlobalData.gGlobalDefines = ParseDefines(Option.Macros)\r
1329         #\r
1330         # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH\r
1331         #\r
1332         CheckEnvVariable()\r
1333         Workspace = os.getenv("WORKSPACE")\r
1334         #\r
1335         # Get files real name in workspace dir\r
1336         #\r
1337         GlobalData.gAllFiles = Utils.DirCache(Workspace)\r
1338 \r
1339         WorkingDirectory = os.getcwd()\r
1340         if not Option.ModuleFile:\r
1341             FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.inf')))\r
1342             FileNum = len(FileList)\r
1343             if FileNum >= 2:\r
1344                 EdkLogger.error("build", OPTION_NOT_SUPPORTED, "There are %d INF files in %s." % (FileNum, WorkingDirectory),\r
1345                                 ExtraData="Please use '-m <INF_FILE_PATH>' switch to choose one.")\r
1346             elif FileNum == 1:\r
1347                 Option.ModuleFile = NormFile(FileList[0], Workspace)\r
1348 \r
1349         if Option.ModuleFile:\r
1350             Option.ModuleFile = PathClass(Option.ModuleFile, Workspace)\r
1351             ErrorCode, ErrorInfo = Option.ModuleFile.Validate(".inf", False)\r
1352             if ErrorCode != 0:\r
1353                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
1354 \r
1355         if Option.PlatformFile != None:\r
1356             Option.PlatformFile = PathClass(Option.PlatformFile, Workspace)\r
1357             ErrorCode, ErrorInfo = Option.PlatformFile.Validate(".dsc", False)\r
1358             if ErrorCode != 0:\r
1359                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
1360 \r
1361         if Option.FdfFile != None:\r
1362             Option.FdfFile = PathClass(Option.FdfFile, Workspace)\r
1363             ErrorCode, ErrorInfo = Option.FdfFile.Validate(".fdf", False)\r
1364             if ErrorCode != 0:\r
1365                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
1366 \r
1367         MyBuild = Build(Target, Workspace, Option.PlatformFile, Option.ModuleFile,\r
1368                         Option.TargetArch, Option.ToolChain, Option.BuildTarget,\r
1369                         Option.FdfFile, Option.RomImage, Option.FvImage,\r
1370                         None, Option.SilentMode, Option.ThreadNumber,\r
1371                         Option.SkipAutoGen, Option.Reparse, Option.SkuId, \r
1372                         Option.ReportFile, Option.ReportType)\r
1373         MyBuild.Launch()\r
1374         #MyBuild.DumpBuildData()\r
1375     except FatalError, X:\r
1376         if MyBuild != None:\r
1377             # for multi-thread build exits safely\r
1378             MyBuild.Relinquish()\r
1379         if Option != None and Option.debug != None:\r
1380             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1381         ReturnCode = X.args[0]\r
1382     except Warning, X:\r
1383         # error from Fdf parser\r
1384         if MyBuild != None:\r
1385             # for multi-thread build exits safely\r
1386             MyBuild.Relinquish()\r
1387         if Option != None and Option.debug != None:\r
1388             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1389         else:\r
1390             EdkLogger.error(X.ToolName, FORMAT_INVALID, File=X.FileName, Line=X.LineNumber, ExtraData=X.Message, RaiseError = False)\r
1391         ReturnCode = FORMAT_INVALID\r
1392     except KeyboardInterrupt:\r
1393         ReturnCode = ABORT_ERROR\r
1394         if Option != None and Option.debug != None:\r
1395             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1396     except:\r
1397         if MyBuild != None:\r
1398             # for multi-thread build exits safely\r
1399             MyBuild.Relinquish()\r
1400 \r
1401         # try to get the meta-file from the object causing exception\r
1402         Tb = sys.exc_info()[-1]\r
1403         MetaFile = GlobalData.gProcessingFile\r
1404         while Tb != None:\r
1405             if 'self' in Tb.tb_frame.f_locals and hasattr(Tb.tb_frame.f_locals['self'], 'MetaFile'):\r
1406                 MetaFile = Tb.tb_frame.f_locals['self'].MetaFile\r
1407             Tb = Tb.tb_next\r
1408         EdkLogger.error(\r
1409                     "\nbuild",\r
1410                     CODE_ERROR,\r
1411                     "Unknown fatal error when processing [%s]" % MetaFile,\r
1412                     ExtraData="\n(Please send email to dev@buildtools.tianocore.org for help, attaching following call stack trace!)\n",\r
1413                     RaiseError=False\r
1414                     )\r
1415         EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1416         ReturnCode = CODE_ERROR\r
1417     finally:\r
1418         Utils.Progressor.Abort()\r
1419 \r
1420     if ReturnCode == 0:\r
1421         Conclusion = "Done"\r
1422     elif ReturnCode == ABORT_ERROR:\r
1423         Conclusion = "Aborted"\r
1424     else:\r
1425         Conclusion = "Failed"\r
1426     FinishTime = time.time()\r
1427     BuildDuration = time.strftime("%M:%S", time.gmtime(int(round(FinishTime - StartTime))))\r
1428     if MyBuild != None:\r
1429         MyBuild.BuildReport.GenerateReport(BuildDuration)\r
1430         MyBuild.Db.Close()\r
1431     EdkLogger.SetLevel(EdkLogger.QUIET)\r
1432     EdkLogger.quiet("\n- %s -\n%s [%s]" % (Conclusion, time.strftime("%H:%M:%S, %b.%d %Y", time.localtime()), BuildDuration))\r
1433 \r
1434     return ReturnCode\r
1435 \r
1436 if __name__ == '__main__':\r
1437     r = Main()\r
1438     ## 0-127 is a safe return range, and 1 is a standard default error\r
1439     if r < 0 or r > 127: r = 1\r
1440     sys.exit(r)\r
1441 \r