Fixed multi-target build issue.
[people/mcb30/basetools.git] / Source / Python / build / build.py
1 ## @file\r
2 # build a platform or a module\r
3 #\r
4 #  Copyright (c) 2007, 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 sys\r
20 import glob\r
21 import time\r
22 import traceback\r
23 \r
24 from threading import *\r
25 from optparse import OptionParser\r
26 from subprocess import *\r
27 from Common import Misc as Utils\r
28 \r
29 from Common.TargetTxtClassObject import *\r
30 from Common.ToolDefClassObject import *\r
31 from Common.EdkIIWorkspaceBuild import *\r
32 from Common.DataType import *\r
33 from AutoGen.AutoGen import *\r
34 from GenFds.FdfParser import *\r
35 from Common.BuildToolError import *\r
36 #from Common.Misc import *\r
37 import Common.EdkLogger\r
38 \r
39 # Version and Copyright\r
40 VersionNumber = "0.02"\r
41 __version__ = "%prog Version " + VersionNumber\r
42 __copyright__ = "Copyright (c) 2007, Intel Corporation  All rights reserved."\r
43 \r
44 ## standard targets of build command\r
45 gSupportedTarget = ['all', 'genc', 'genmake', 'modules', 'libraries', 'fds', 'clean', 'cleanall', 'cleanlib', 'run']\r
46 \r
47 ## build configuration file\r
48 gBuildConfiguration = "Conf/target.txt"\r
49 gBuildCacheDir = "Conf/.cache"\r
50 \r
51 ## Check environment variables\r
52 #\r
53 #  Check environment variables that must be set for build. Currently they are\r
54 #\r
55 #   WORKSPACE           The directory all packages/platforms start from\r
56 #   EDK_TOOLS_PATH      The directory contains all tools needed by the build\r
57 #   PATH                $(EDK_TOOLS_PATH)/Bin/<sys> must be set in PATH\r
58 #\r
59 #   If any of above environment variable is not set or has error, the build\r
60 #   will be broken.\r
61 #\r
62 def CheckEnvVariable():\r
63     # check WORKSPACE\r
64     if "WORKSPACE" not in os.environ:\r
65         EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Please set environment variable: WORKSPACE!\n")\r
66 \r
67     # check EDK_TOOLS_PATH\r
68     if "EDK_TOOLS_PATH" not in os.environ == None:\r
69         EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Please set environment variable: EDK_TOOLS_PATH!\n")\r
70 \r
71     # check PATH\r
72     if "PATH" not in os.environ:\r
73         EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Please set environment variable: PATH!\n")\r
74 \r
75     PathString = os.environ["PATH"]\r
76     ToolPath = os.path.normpath(os.path.join(os.environ["EDK_TOOLS_PATH"], 'Bin', sys.platform.title()))\r
77 \r
78     if PathString.find(ToolPath) == -1:\r
79         os.environ['PATH'] = os.path.pathsep.join((os.environ['PATH'], ToolPath))\r
80             \r
81         EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Please execute %s to set %s in environment variable: PATH!\n"\r
82                             % (os.path.normpath(os.path.join(PathString, 'edksetup.bat')), ToolPath))\r
83 \r
84 ## Get normalized file path\r
85 #\r
86 # Convert the path to be local format, and remove the WORKSPACE path at the\r
87 # beginning if the file path is given in full path.\r
88 #\r
89 # @param  FilePath      File path to be normalized\r
90 # @param  Workspace     Workspace path which the FilePath will be checked against\r
91 #\r
92 # @retval string        The normalized file path\r
93 #\r
94 def NormFile(FilePath, Workspace):\r
95     # check if the path is absolute or relative\r
96     if os.path.isabs(FilePath):\r
97         FileFullPath = os.path.normpath(FilePath)\r
98     else:\r
99         FileFullPath = os.path.normpath(os.path.join(Workspace, FilePath))\r
100 \r
101     # check if the file path exists or not\r
102     if not os.path.isfile(FileFullPath):\r
103         EdkLogger.error("build", FILE_NOT_FOUND, ExtraData="\t%s (Please give file in absolute path or relative to WORKSPACE)"  % FileFullPath)\r
104 \r
105     # remove workspace directory from the beginning part of the file path\r
106     if Workspace[-1] in ["\\", "/"]:\r
107         return FileFullPath[len(Workspace):]\r
108     else:\r
109         return FileFullPath[(len(Workspace) + 1):]\r
110 \r
111 ## Get the output of an external program\r
112 #\r
113 # This is the entrance method of thread reading output of an external program and\r
114 # putting them in STDOUT/STDERR of current program.\r
115 #\r
116 # @param  From      The stream message read from\r
117 # @param  To        The stream message put on\r
118 # @param  ExitFlag  The flag used to indicate stopping reading\r
119 #\r
120 def ReadMessage(From, To, ExitFlag):\r
121     while True:\r
122         # read one line a time\r
123         Line = From.readline()\r
124         # empty string means "end"\r
125         if Line != None and Line != "":\r
126             To(Line.rstrip())\r
127         else:\r
128             break\r
129         if ExitFlag.isSet():\r
130             break\r
131 \r
132 ## Launch an external program\r
133 #\r
134 # This method will call subprocess.Popen to execute an external program with\r
135 # given options in specified directory. Because of the dead-lock issue during\r
136 # redirecting output of the external program, threads are used to to do the\r
137 # redirection work.\r
138 #\r
139 # @param  Command               A list or string containing the call of the program\r
140 # @param  WorkingDir            The directory in which the program will be running\r
141 #\r
142 def LaunchCommand(Command, WorkingDir):\r
143     # if working directory doesn't exist, Popen() will raise an exception\r
144     if not os.path.isdir(WorkingDir):\r
145         EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=WorkingDir)\r
146 \r
147     Proc = None\r
148     EndOfProcedure = None\r
149     try:\r
150         # launch the command\r
151         Proc = Popen(Command, stdout=PIPE, stderr=PIPE, env=os.environ, cwd=WorkingDir)\r
152 \r
153         # launch two threads to read the STDOUT and STDERR\r
154         EndOfProcedure = Event()\r
155         EndOfProcedure.clear()\r
156         if Proc.stdout:\r
157             StdOutThread = Thread(target=ReadMessage, args=(Proc.stdout, EdkLogger.info, EndOfProcedure))\r
158             StdOutThread.setName("STDOUT-Redirector")\r
159             StdOutThread.setDaemon(False)\r
160             StdOutThread.start()\r
161 \r
162         if Proc.stderr:\r
163             StdErrThread = Thread(target=ReadMessage, args=(Proc.stderr, EdkLogger.quiet, EndOfProcedure))\r
164             StdErrThread.setName("STDERR-Redirector")\r
165             StdErrThread.setDaemon(False)\r
166             StdErrThread.start()\r
167 \r
168         # waiting for program exit\r
169         Proc.wait()\r
170     except: # in case of aborting\r
171         # terminate the threads redirecting the program output\r
172         if EndOfProcedure != None:\r
173             EndOfProcedure.set()\r
174         if Proc == None:\r
175             if type(Command) != type(""):\r
176                 Command = " ".join(Command)\r
177             EdkLogger.error("build", COMMAND_FAILURE, "Failed to start command", ExtraData="%s [%s]" % (Command, WorkingDir))\r
178 \r
179     if Proc.stdout:\r
180         StdOutThread.join()\r
181     if Proc.stderr:\r
182         StdErrThread.join()\r
183 \r
184     # check the return code of the program\r
185     if Proc.returncode != 0:\r
186         if type(Command) != type(""):\r
187             Command = " ".join(Command)\r
188         EdkLogger.error("build", COMMAND_FAILURE, ExtraData="%s [%s]" % (Command, WorkingDir))\r
189 \r
190 ## The smallest unit that can be built in multi-thread build mode\r
191 #\r
192 # This is the base class of build unit. The "Obj" parameter must provide\r
193 # __str__(), __eq__() and __hash__() methods. Otherwise there could be build units\r
194 # missing build.\r
195 #\r
196 # Currently the "Obj" should be only ModuleAutoGen or PlatformAutoGen objects.\r
197 #\r
198 class BuildUnit:\r
199     ## The constructor\r
200     #\r
201     #   @param  self        The object pointer\r
202     #   @param  Obj         The object the build is working on\r
203     #   @param  Target      The build target name, one of gSupportedTarget\r
204     #   @param  Dependency  The BuildUnit(s) which must be completed in advance\r
205     #   @param  WorkingDir  The directory build command starts in\r
206     #\r
207     def __init__(self, Obj, BuildComamnd, Target, Dependency, WorkingDir="."):\r
208         self.BuildObject = Obj\r
209         self.Dependency = Dependency\r
210         self.WorkingDir = WorkingDir\r
211         self.Target = Target\r
212         self.BuildCommand = BuildComamnd\r
213 \r
214     ## str() method\r
215     #\r
216     #   It just returns the string representaion of self.BuildObject\r
217     #\r
218     #   @param  self        The object pointer\r
219     #\r
220     def __str__(self):\r
221         return str(self.BuildObject)\r
222 \r
223     ## "==" operator method\r
224     #\r
225     #   It just compares self.BuildObject with "Other". So self.BuildObject must\r
226     #   provide its own __eq__() method.\r
227     #\r
228     #   @param  self        The object pointer\r
229     #   @param  Other       The other BuildUnit object compared to\r
230     #\r
231     def __eq__(self, Other):\r
232         return Other != None and self.BuildObject == Other.BuildObject\r
233 \r
234     ## hash() method\r
235     #\r
236     #   It just returns the hash value of self.BuildObject which must be hashable.\r
237     #\r
238     #   @param  self        The object pointer\r
239     #\r
240     def __hash__(self):\r
241         return hash(self.BuildObject)\r
242 \r
243 ## The smallest module unit that can be built by nmake/make command in multi-thread build mode\r
244 #\r
245 # This class is for module build by nmake/make build system. The "Obj" parameter\r
246 # must provide __str__(), __eq__() and __hash__() methods. Otherwise there could\r
247 # be make units missing build.\r
248 #\r
249 # Currently the "Obj" should be only ModuleAutoGen object.\r
250 #\r
251 class ModuleMakeUnit(BuildUnit):\r
252     ## The constructor\r
253     #\r
254     #   @param  self        The object pointer\r
255     #   @param  Obj         The ModuleAutoGen object the build is working on\r
256     #   @param  Target      The build target name, one of gSupportedTarget\r
257     #\r
258     def __init__(self, Obj, Target):\r
259         Dependency = [ModuleMakeUnit(La, Target) for La in Obj.BuildInfo.LibraryAutoGenList]\r
260         BuildUnit.__init__(self, Obj, Obj.GetBuildCommand(), Target, Dependency, Obj.GetMakeFileDir())\r
261         if Target in [None, "", "all"]:\r
262             self.Target = "pbuild"\r
263 \r
264 ## The smallest platform unit that can be built by nmake/make command in multi-thread build mode\r
265 #\r
266 # This class is for platform build by nmake/make build system. The "Obj" parameter\r
267 # must provide __str__(), __eq__() and __hash__() methods. Otherwise there could\r
268 # be make units missing build.\r
269 #\r
270 # Currently the "Obj" should be only PlatformAutoGen object.\r
271 #\r
272 class PlatformMakeUnit(BuildUnit):\r
273     ## The constructor\r
274     #\r
275     #   @param  self        The object pointer\r
276     #   @param  Obj         The PlatformAutoGen object the build is working on\r
277     #   @param  Target      The build target name, one of gSupportedTarget\r
278     #\r
279     def __init__(self, Obj, Target):\r
280         Dependency = [ModuleMakeUnit(Lib, Target) for Lib in self.BuildObject.BuildInfo.LibraryAutoGenList]\r
281         Dependency.extend([ModuleMakeUnit(Mod, Target) for Mod in self.BuildObject.BuildInfo.ModuleAutoGenList])\r
282         BuildUnit.__init__(self, Obj, Obj.GetBuildCommand(), Target, Dependency, Obj.GetMakeFileDir())\r
283 \r
284 ## The class representing the task of a module build or platform build\r
285 #\r
286 # This class manages the build tasks in multi-thread build mode. Its jobs include\r
287 # scheduling thread running, catching thread error, monitor the thread status, etc.\r
288 #\r
289 class BuildTask:\r
290     # queue for tasks waiting for schedule\r
291     _PendingQueue = sdict()\r
292     _PendingQueueLock = threading.Lock()\r
293 \r
294     # queue for tasks ready for running\r
295     _ReadyQueue = sdict()\r
296     _ReadyQueueLock = threading.Lock()\r
297 \r
298     # queue for run tasks\r
299     _RunningQueue = sdict()\r
300     _RunningQueueLock = threading.Lock()\r
301 \r
302     # queue containing all build tasks, in case duplicate build\r
303     _TaskQueue = sdict()\r
304 \r
305     # flag indicating error occurs in a running thread\r
306     _ErrorFlag = threading.Event()\r
307     _ErrorFlag.clear()\r
308     _ErrorMessage = ""\r
309 \r
310     # BoundedSemaphore object used to control the number of running threads\r
311     _Thread = None\r
312 \r
313     # flag indicating if the scheduler is started or not\r
314     _SchedulerStopped = threading.Event()\r
315     _SchedulerStopped.set()\r
316 \r
317     ## Start the task scheduler thread\r
318     #\r
319     #   @param  MaxThreadNumber     The maximum thread number\r
320     #   @param  ExitFlag            Flag used to end the scheduler\r
321     #\r
322     @staticmethod\r
323     def StartScheduler(MaxThreadNumber, ExitFlag):\r
324         BuildTask._PendingQueue.clear()\r
325         BuildTask._ReadyQueue.clear()\r
326         BuildTask._RunningQueue.clear()\r
327         BuildTask._TaskQueue.clear()\r
328         BuildTask._ErrorFlag.clear()\r
329         BuildTask._ErrorMessage = ""\r
330         BuildTask._Thread = None\r
331 \r
332         SchedulerThread = Thread(target=BuildTask.Scheduler, args=(MaxThreadNumber, ExitFlag))\r
333         SchedulerThread.setName("Build-Task-Scheduler")\r
334         SchedulerThread.setDaemon(False)\r
335         SchedulerThread.start()\r
336 \r
337     ## Scheduler method\r
338     #\r
339     #   @param  MaxThreadNumber     The maximum thread number\r
340     #   @param  ExitFlag            Flag used to end the scheduler\r
341     #\r
342     @staticmethod\r
343     def Scheduler(MaxThreadNumber, ExitFlag):\r
344         BuildTask._SchedulerStopped.clear()\r
345         try:\r
346             # use BoundedSemaphore to control the maximum running threads\r
347             BuildTask._Thread = BoundedSemaphore(MaxThreadNumber)\r
348             #\r
349             # scheduling loop, which will exits when no pending/ready task and\r
350             # indicated to do so, or there's error in running thread\r
351             #\r
352             while (len(BuildTask._PendingQueue) > 0 or len(BuildTask._ReadyQueue) > 0 \\r
353                    or not ExitFlag.isSet()) and not BuildTask._ErrorFlag.isSet():\r
354                 EdkLogger.debug(EdkLogger.DEBUG_9, "Pending Queue (%d), Ready Queue (%d)"\r
355                                 % (len(BuildTask._PendingQueue), len(BuildTask._ReadyQueue)))\r
356                 # get all pending tasks\r
357                 BuildTask._PendingQueueLock.acquire()\r
358                 BuildObjectList = BuildTask._PendingQueue.keys()\r
359                 BuildTask._PendingQueueLock.release()\r
360 \r
361                 #\r
362                 # check if their dependency is resolved, and if true, move them\r
363                 # into ready queue\r
364                 #\r
365                 for BuildObject in BuildObjectList:\r
366                     Bt = BuildTask._PendingQueue[BuildObject]\r
367                     if Bt.IsReady():\r
368                         BuildTask._PendingQueueLock.acquire()\r
369                         BuildTask._ReadyQueue[BuildObject] = BuildTask._PendingQueue.pop(BuildObject)\r
370                         BuildTask._PendingQueueLock.release()\r
371 \r
372                 # launch build thread until the maximum number of threads is reached\r
373                 while not BuildTask._ErrorFlag.isSet():\r
374                     # empty ready queue, do nothing further\r
375                     if len(BuildTask._ReadyQueue) == 0:\r
376                         break\r
377 \r
378                     # wait for active thread(s) exit\r
379                     BuildTask._Thread.acquire(True)\r
380 \r
381                     # start a new build thread\r
382                     Bo = BuildTask._ReadyQueue.keys()[0]\r
383                     Bt = BuildTask._ReadyQueue.pop(Bo)\r
384 \r
385                     # move into running queue\r
386                     BuildTask._RunningQueueLock.acquire()\r
387                     BuildTask._RunningQueue[Bo] = Bt\r
388                     BuildTask._RunningQueueLock.release()\r
389 \r
390                     Bt.Start()\r
391 \r
392                 # avoid tense loop\r
393                 time.sleep(0.01)\r
394 \r
395             # wait for all running threads exit\r
396             if BuildTask._ErrorFlag.isSet():\r
397                 EdkLogger.quiet("\nWaiting for all Command-Threads exit...")\r
398             # while not BuildTask._ErrorFlag.isSet() and \\r
399             while len(BuildTask._RunningQueue) > 0:\r
400                 EdkLogger.verbose("Waiting for thread ending...(%d)" % len(BuildTask._RunningQueue))\r
401                 EdkLogger.debug(EdkLogger.DEBUG_9, "Threads [%s]" % ", ".join([Th.getName() for Th in threading.enumerate()]))\r
402                 # avoid tense loop\r
403                 time.sleep(0.1)\r
404         except BaseException, X:\r
405             #\r
406             # TRICK: hide the output of threads left runing, so that the user can\r
407             #        catch the error message easily\r
408             #\r
409             EdkLogger.SetLevel(EdkLogger.QUIET)\r
410             BuildTask._ErrorFlag.set()\r
411             BuildTask._ErrorMessage = "build thread scheduler error\n\t%s" % str(X)\r
412         BuildTask._SchedulerStopped.set()\r
413 \r
414     ## Wait for all running method exit\r
415     #\r
416     @staticmethod\r
417     def WaitForComplete():\r
418         BuildTask._SchedulerStopped.wait()\r
419 \r
420     ## Check if the scheduler is running or not\r
421     #\r
422     @staticmethod\r
423     def IsOnGoing():\r
424         return not BuildTask._SchedulerStopped.isSet()\r
425 \r
426     ## Abort the build\r
427     @staticmethod\r
428     def Abort():\r
429         if BuildTask.IsOnGoing():\r
430             BuildTask._ErrorFlag.set()\r
431             BuildTask.WaitForComplete()\r
432 \r
433     ## Check if there's error in running thread\r
434     #\r
435     #   Since the main thread cannot catch exceptions in other thread, we have to\r
436     #   use threading.Event to communicate this formation to main thread.\r
437     #\r
438     @staticmethod\r
439     def HasError():\r
440         return BuildTask._ErrorFlag.isSet()\r
441 \r
442     ## Get error message in running thread\r
443     #\r
444     #   Since the main thread cannot catch exceptions in other thread, we have to\r
445     #   use a static variable to communicate this message to main thread.\r
446     #\r
447     @staticmethod\r
448     def GetErrorMessage():\r
449         return BuildTask._ErrorMessage\r
450 \r
451     ## Factory method to create a BuildTask object\r
452     #\r
453     #   This method will check if a module is building or has been built. And if\r
454     #   true, just return the associated BuildTask object in the _TaskQueue. If\r
455     #   not, create and return a new BuildTask object. The new BuildTask object\r
456     #   will be appended to the _PendingQueue for scheduling later.\r
457     #\r
458     #   @param  BuildItem       A BuildUnit object representing a build object\r
459     #   @param  Dependency      The dependent build object of BuildItem\r
460     #\r
461     @staticmethod\r
462     def New(BuildItem, Dependency=None):\r
463         if BuildItem in BuildTask._TaskQueue:\r
464             Bt = BuildTask._TaskQueue[BuildItem]\r
465             return Bt\r
466 \r
467         Bt = BuildTask()\r
468         Bt._Init(BuildItem, Dependency)\r
469         BuildTask._TaskQueue[BuildItem] = Bt\r
470 \r
471         BuildTask._PendingQueueLock.acquire()\r
472         BuildTask._PendingQueue[BuildItem] = Bt\r
473         BuildTask._PendingQueueLock.release()\r
474 \r
475         return Bt\r
476 \r
477     ## The real constructor of BuildTask\r
478     #\r
479     #   @param  BuildItem       A BuildUnit object representing a build object\r
480     #   @param  Dependency      The dependent build object of BuildItem\r
481     #\r
482     def _Init(self, BuildItem, Dependency=None):\r
483         self.BuildItem = BuildItem\r
484 \r
485         self.DependencyList = []\r
486         if Dependency == None:\r
487             Dependency = BuildItem.Dependency\r
488         else:\r
489             Dependency.extend(BuildItem.Dependency)\r
490         self.AddDependency(Dependency)\r
491         # flag indicating build completes, used to avoid unnecessary re-build\r
492         self.CompleteFlag = False\r
493 \r
494     ## Check if all dependent build tasks are completed or not\r
495     #\r
496     def IsReady(self):\r
497         ReadyFlag = True\r
498         for Dep in self.DependencyList:\r
499             if Dep.CompleteFlag == True:\r
500                 continue\r
501             ReadyFlag = False\r
502             break\r
503 \r
504         return ReadyFlag\r
505 \r
506     ## Add dependent build task\r
507     #\r
508     #   @param  Dependency      The list of dependent build objects\r
509     #\r
510     def AddDependency(self, Dependency):\r
511         for Dep in Dependency:\r
512             self.DependencyList.append(BuildTask.New(Dep))    # BuildTask list\r
513 \r
514     ## The thread wrapper of LaunchCommand function\r
515     #\r
516     # @param  Command               A list or string contains the call of the command\r
517     # @param  WorkingDir            The directory in which the program will be running\r
518     #\r
519     def _CommandThread(self, Command, WorkingDir):\r
520         try:\r
521             LaunchCommand(Command, WorkingDir)\r
522             self.CompleteFlag = True\r
523         except:\r
524             #\r
525             # TRICK: hide the output of threads left runing, so that the user can\r
526             #        catch the error message easily\r
527             #\r
528             EdkLogger.SetLevel(EdkLogger.QUIET)\r
529             BuildTask._ErrorFlag.set()\r
530             BuildTask._ErrorMessage = "%s broken\n    %s [%s]" % \\r
531                                       (threading.currentThread().getName(), Command, WorkingDir)\r
532         # indicate there's a thread is available for another build task\r
533         BuildTask._RunningQueueLock.acquire()\r
534         BuildTask._RunningQueue.pop(self.BuildItem)\r
535         BuildTask._RunningQueueLock.release()\r
536         BuildTask._Thread.release()\r
537 \r
538     ## Start build task thread\r
539     #\r
540     def Start(self):\r
541         Command = self.BuildItem.BuildCommand + (self.BuildItem.Target,)\r
542         self.BuildTread = Thread(target=self._CommandThread, args=(Command, self.BuildItem.WorkingDir))\r
543         self.BuildTread.setName("Command-Thread")\r
544         self.BuildTread.setDaemon(False)\r
545         self.BuildTread.start()\r
546 \r
547 ## The class implementing the EDK2 build process\r
548 #\r
549 #   The build process includes:\r
550 #       1. Load configuration from target.txt and tools_def.txt in $(WORKSPACE)/Conf\r
551 #       2. Parse DSC file of active platform\r
552 #       3. Parse FDF file if any\r
553 #       4. Establish build database, including parse all other files (module, package)\r
554 #       5. Create AutoGen files (C code file, depex file, makefile) if necessary\r
555 #       6. Call build command\r
556 #\r
557 class Build():\r
558     ## Constructor\r
559     #\r
560     # Constructor will load all necessary configurations, parse platform, modules\r
561     # and packages and the establish a database for AutoGen.\r
562     #\r
563     #   @param  Target              The build command target, one of gSupportedTarget\r
564     #   @param  WorkspaceDir        The directory of workspace\r
565     #   @param  Platform            The DSC file of active platform\r
566     #   @param  Module              The INF file of active module, if any\r
567     #   @param  Arch                The Arch list of platform or module\r
568     #   @param  ToolChain           The name list of toolchain\r
569     #   @param  BuildTarget         The "DEBUG" or "RELEASE" build\r
570     #   @param  FlashDefinition     The FDF file of active platform\r
571     #   @param  FdList=[]           The FD names to be individually built\r
572     #   @param  FvList=[]           The FV names to be individually built\r
573     #   @param  MakefileType        The type of makefile (for MSFT make or GNU make)\r
574     #   @param  SpawnMode           Indicate multi-thread build mode\r
575     #   @param  ThreadNumber        The maximum number of thread if in multi-thread build mode\r
576     #\r
577     def __init__(self, Target, WorkspaceDir, Platform, Module, Arch, ToolChain, BuildTarget,\r
578                  FlashDefinition, FdList=[], FvList=[], MakefileType="nmake", SpawnMode=False, ThreadNumber=2):\r
579 \r
580         self.WorkspaceDir = WorkspaceDir\r
581 \r
582         self.Target         = Target\r
583         self.PlatformFile   = Platform\r
584         self.ModuleFile     = Module\r
585         self.ArchList       = Arch\r
586         self.ToolChainList  = ToolChain\r
587         self.BuildTargetList= BuildTarget\r
588         self.Fdf            = FlashDefinition\r
589         self.FdList         = FdList\r
590         self.FvList         = FvList\r
591         self.MakefileType   = MakefileType\r
592         self.SpawnMode      = SpawnMode\r
593         self.ThreadNumber   = ThreadNumber\r
594 \r
595         self.TargetTxt    = TargetTxtClassObject()\r
596         self.ToolDef      = ToolDefClassObject()\r
597 \r
598         # print dot charater during doing some time-consuming work\r
599         self.Progress = Utils.Progressor()\r
600 \r
601         # parse target.txt, tools_def.txt, and platform file\r
602         self.Progress.Start("Loading build configuration")\r
603         self.RestoreBuildData()\r
604         self.LoadConfiguration()\r
605         self.Progress.Stop("done!")\r
606 \r
607         self.Progress.Start("Parsing platform/modules/packages")\r
608         self.InitBuild()\r
609         self.Progress.Stop("done!")\r
610 \r
611         # print current build environment and configuration\r
612         EdkLogger.info('')\r
613         EdkLogger.quiet("%-24s = %s" % ("WORKSPACE", os.environ["WORKSPACE"]))\r
614         EdkLogger.quiet("%-24s = %s" % ("EDK_TOOLS_PATH", os.environ["EDK_TOOLS_PATH"]))\r
615         EdkLogger.info('')\r
616         EdkLogger.info('%-24s = %s' % ("TARGET_ARCH", ' '.join(self.ArchList)))\r
617         EdkLogger.info('%-24s = %s' % ("TARGET", ' '.join(self.BuildTargetList)))\r
618         EdkLogger.info('%-24s = %s' % ("TOOL_CHAIN_TAG", ' '.join(self.ToolChainList)))\r
619 \r
620         EdkLogger.info('')\r
621         if self.PlatformFile != None and self.PlatformFile != "":\r
622             EdkLogger.info('%-24s = %s' % ("Active Platform", self.PlatformFile))\r
623 \r
624         if self.Fdf != None and self.Fdf != "":\r
625             EdkLogger.info('%-24s = %s' % ("Flash Image Definition", self.Fdf))\r
626 \r
627         if self.ModuleFile != None and self.ModuleFile != "":\r
628             EdkLogger.info('%-24s = %s' % ("Active Module", self.ModuleFile))\r
629 \r
630         if self.SpawnMode:\r
631             EdkLogger.verbose('%-24s = %s' % ("Max Thread Number", self.ThreadNumber))\r
632 \r
633         # establish build database, INF/DEC files will be parsed in this stage\r
634         self.Progress.Start("\nEstablishing build database")\r
635         try:\r
636             if self.Fdf != None and self.Fdf != "":\r
637                 FdfFile = os.path.join(self.WorkspaceDir, self.Fdf)\r
638 \r
639                 Fdf = FdfParser(FdfFile)\r
640                 Fdf.ParseFile()\r
641 \r
642                 PcdSet = Fdf.Profile.PcdDict\r
643             else:\r
644                 PcdSet = {}\r
645 \r
646             self.Ewb.GenBuildDatabase(PcdSet)\r
647             self.Platform = self.Ewb.Build[self.ArchList[0]].PlatformDatabase[self.PlatformFile]\r
648 \r
649         except BaseException, X:\r
650             if isinstance(X, Warning):\r
651                 EdkLogger.error(X.ToolName, BUILD_ERROR, X.message, X.FileName, X.LineNumber, RaiseError = False)\r
652             raise\r
653         self.Progress.Stop("done!")\r
654 \r
655     ## Load configuration\r
656     #\r
657     #   This method will parse target.txt and get the build configurations.\r
658     #\r
659     def LoadConfiguration(self):\r
660         #\r
661         # Check target.txt and tools_def.txt and Init them\r
662         #\r
663         BuildConfigurationFile = os.path.normpath(os.path.join(self.WorkspaceDir, gBuildConfiguration))\r
664         if os.path.isfile(BuildConfigurationFile) == True:\r
665             StatusCode = self.TargetTxt.LoadTargetTxtFile(BuildConfigurationFile)\r
666 \r
667             ToolDefinitionFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_CONF]\r
668             ToolDefinitionFile = os.path.normpath(os.path.join(self.WorkspaceDir, ToolDefinitionFile))\r
669             if os.path.isfile(ToolDefinitionFile) == True:\r
670                 StatusCode = self.ToolDef.LoadToolDefFile(ToolDefinitionFile)\r
671             else:\r
672                 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=ToolDefinitionFile)\r
673         else:\r
674             EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=BuildConfigurationFile)\r
675 \r
676         # if no ARCH given in command line, get it from target.txt\r
677         if self.ArchList == None or len(self.ArchList) == 0:\r
678             self.ArchList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET_ARCH]\r
679             if len(self.ArchList) == 0:\r
680                 self.ArchList = ARCH_LIST\r
681 \r
682         # if no build target given in command line, get it from target.txt\r
683         if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
684             self.BuildTargetList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET]\r
685             if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
686                 self.BuildTargetList = ['DEBUG', 'RELEASE']\r
687 \r
688         # if no tool chain given in command line, get it from target.txt\r
689         if self.ToolChainList == None or len(self.ToolChainList) == 0:\r
690             self.ToolChainList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_TAG]\r
691             if self.ToolChainList == None or len(self.ToolChainList) == 0:\r
692                 EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, ExtraData="No toolchain given. Don't know how to build.\n")\r
693 \r
694         # check if the tool chains are defined or not\r
695         NewToolChainList = []\r
696         for ToolChain in self.ToolChainList:\r
697             if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG]:\r
698                 EdkLogger.warn("build", "Tool chain [%s] is not defined" % ToolChain)\r
699             else:\r
700                 NewToolChainList.append(ToolChain)\r
701         # if no tool chain available, break the build\r
702         if len(NewToolChainList) == 0:\r
703             EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,\r
704                             ExtraData="[%s] not defined. No toolchain available for build!\n" % ", ".join(self.ToolChainList))\r
705         else:\r
706             self.ToolChainList = NewToolChainList\r
707 \r
708         if self.ThreadNumber == None or self.ThreadNumber == "":\r
709             self.ThreadNumber = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER]\r
710             if self.ThreadNumber == '':\r
711                 self.ThreadNumber = 0\r
712             else:\r
713                 self.ThreadNumber = int(self.ThreadNumber, 0)\r
714 \r
715         if self.ThreadNumber == 0:\r
716             self.SpawnMode = False\r
717         elif self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_MULTIPLE_THREAD].lower() in ["enable", "true"]:\r
718             self.SpawnMode = True\r
719 \r
720         if self.PlatformFile == None:\r
721             self.PlatformFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_ACTIVE_PLATFORM]\r
722             if self.PlatformFile == None or self.PlatformFile == "":\r
723                 WorkingDirectory = os.getcwd()\r
724                 FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.dsc')))\r
725                 FileNum = len(FileList)\r
726                 if FileNum >= 2:\r
727                     EdkLogger.error("build", None, "There are %d DSC files in %s.\n" % (FileNum, WorkingDirectory))\r
728                 elif FileNum == 1:\r
729                     self.PlatformFile = NormFile(FileList[0], self.WorkspaceDir)\r
730             else:\r
731                 self.PlatformFile = NormFile(self.PlatformFile, self.WorkspaceDir)\r
732 \r
733     ## Initialize build configuration\r
734     #\r
735     #   This method will parse DSC file and merge the configurations from\r
736     #   command line and target.txt, then get the final build configurations.\r
737     #\r
738     def InitBuild(self):\r
739         if self.PlatformFile == None or self.PlatformFile == "":\r
740             EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE,\r
741                             ExtraData="No active platform specified in target.txt or command line! Nothing can be built.\n")\r
742 \r
743         Wb = WorkspaceBuild(self.PlatformFile, self.WorkspaceDir)\r
744         PcdSet = {}\r
745         if self.Fdf != None:\r
746             self.Fdf = NormFile(self.Fdf, self.WorkspaceDir)\r
747             Wb.Fdf = self.Fdf\r
748         else:\r
749             self.Fdf = Wb.Fdf\r
750 \r
751         # parse FDF file to get PCD information\r
752         if self.Fdf != None and self.Fdf != "":\r
753             if Wb.Fdf == None or Wb.Fdf == "":\r
754                 Wb.Fdf = self.Fdf\r
755 \r
756             Wb.FdTargetList.extend(self.FdList)\r
757             Wb.FvTargetList.extend(self.FvList)\r
758         else:\r
759             PcdSet = {}\r
760             if self.FdList != []:\r
761                 EdkLogger.info("No flash definition file found. FD [%s] will be ignored." % " ".join(self.FdList))\r
762             if self.FvList != []:\r
763                 EdkLogger.info("No flash definition file found. FV [%s] will be ignored." % " ".join(self.FvList))\r
764 \r
765         Wb.TargetTxt = self.TargetTxt\r
766         Wb.ToolDef = self.ToolDef\r
767         #\r
768         # Merge Arch\r
769         #\r
770         if self.ArchList == None or len(self.ArchList) == 0:\r
771             ArchList = Wb.SupArchList\r
772         else:\r
773             ArchList = list(set(self.ArchList) & set(Wb.SupArchList))\r
774         if len(ArchList) == 0:\r
775             EdkLogger.error("build", PARAMETER_INVALID,\r
776                             ExtraData = "Active platform supports [%s] only, but [%s] is given."\r
777                                         % (" ".join(Wb.SupArchList), " ".join(self.ArchList)))\r
778         elif len(ArchList) != len(self.ArchList):\r
779             SkippedArchList = set(self.ArchList).symmetric_difference(set(Wb.SupArchList))\r
780             EdkLogger.verbose("\nArch [%s] is ignored because active platform supports [%s] but [%s] is specified !"\r
781                            % (" ".join(SkippedArchList), " ".join(Wb.SupArchList), " ".join(self.ArchList)))\r
782         self.ArchList = ArchList\r
783 \r
784         # Merge build target\r
785         if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
786             BuildTargetList = Wb.BuildTarget\r
787         else:\r
788             BuildTargetList = list(set(self.BuildTargetList) & set(Wb.BuildTarget))\r
789         if BuildTargetList == []:\r
790             EdkLogger.error("build", PARAMETER_INVALID, "Active platform only supports [%s], but [%s] is given"\r
791                                 % (" ".join(Wb.BuildTarget), " ".join(self.BuildTargetList)))\r
792         self.BuildTargetList = BuildTargetList\r
793 \r
794         self.Ewb = Wb\r
795 \r
796     ## Build a module or platform\r
797     #\r
798     # Create autogen code and makfile for a module or platform, and the launch\r
799     # "make" command to build it\r
800     #\r
801     #   @param  Target                      The target of build command\r
802     #   @param  Platform                    The platform file\r
803     #   @param  Module                      The module file\r
804     #   @param  BuildTarget                 The name of build target, one of "DEBUG", "RELEASE"\r
805     #   @param  ToolChain                   The name of toolchain to build\r
806     #   @param  Arch                        The arch of the module/platform\r
807     #   @param  CreateDepModuleCodeFile     Flag used to indicate creating code\r
808     #                                       for dependent modules/Libraries\r
809     #   @param  CreateDepModuleMakeFile     Flag used to indicate creating makefile\r
810     #                                       for dependent modules/Libraries\r
811     #\r
812     def _Build(self, Target, Platform, Module, BuildTarget, ToolChain, Arch, CreateDepModuleCodeFile=True, CreateDepModuleMakeFile=True):\r
813         # skip file generation for some targets\r
814         if Target not in ['clean', 'cleanlib', 'cleanall', 'run']:\r
815             #\r
816             # no need to generate code/makefile for dependent modules/libraries\r
817             # if the target is 'fds'\r
818             #\r
819             if Target == 'fds':\r
820                 CreateDepModuleCodeFile = False\r
821                 CreateDepModuleMakeFile = False\r
822                 CreateDepModuleAutoGenObject = True\r
823             else:\r
824                 CreateDepModuleAutoGenObject = False\r
825 \r
826             if Module != None:\r
827                 self.Progress.Start("Generating code/makefile for module")\r
828                 AutoGenResult = ModuleAutoGen.New(self.Ewb, Platform, Module, BuildTarget,\r
829                                                   ToolChain, Arch)\r
830             else:\r
831                 self.Progress.Start("Generating code/makefile for platform")\r
832                 AutoGenResult = PlatformAutoGen.New(self.Ewb, Platform, BuildTarget,\r
833                                                     ToolChain, Arch, CreateDepModuleAutoGenObject)\r
834             if AutoGenResult == None:\r
835                 return\r
836 \r
837             # for target which must generate AutoGen code and makefile\r
838             AutoGenResult.CreateCodeFile(CreateDepModuleCodeFile)\r
839             if Target == "genc":\r
840                 self.Progress.Stop("done!")\r
841                 return\r
842 \r
843             AutoGenResult.CreateMakeFile(CreateDepModuleMakeFile)\r
844             if Target == "genmake":\r
845                 self.Progress.Stop("done!")\r
846                 return\r
847 \r
848             self.Progress.Stop("done!")\r
849         else:\r
850             if Module != None:\r
851                 AutoGenResult = ModuleAutoGen.New(self.Ewb, Platform, Module, BuildTarget,\r
852                                                   ToolChain, Arch)\r
853             else:\r
854                 AutoGenResult = PlatformAutoGen.New(self.Ewb, Platform, BuildTarget,\r
855                                                     ToolChain, Arch, False)\r
856             if AutoGenResult == None:\r
857                 return\r
858 \r
859         EdkLogger.info("")\r
860         BuildCommand = AutoGenResult.GetBuildCommand()\r
861         if BuildCommand == None or len(BuildCommand) == 0:\r
862             EdkLogger.error("build", OPTION_MISSING, ExtraData="No MAKE command found for [%s, %s, %s]" % Key)\r
863 \r
864         BuildCommand = BuildCommand + (Target,)\r
865         LaunchCommand(BuildCommand, os.path.join(self.WorkspaceDir, AutoGenResult.GetMakeFileDir()))\r
866 \r
867     ## Build active platform for different build targets and different tool chains\r
868     #\r
869     def _BuildPlatform(self):\r
870         for BuildTarget in self.BuildTargetList:\r
871             for ToolChain in self.ToolChainList:\r
872                 self._Build(self.Target, self.PlatformFile, None, BuildTarget, ToolChain, self.ArchList)\r
873 \r
874     ## Build active module for different build targets, different tool chains and different archs\r
875     #\r
876     def _BuildModule(self):\r
877         for BuildTarget in self.BuildTargetList:\r
878             for ToolChain in self.ToolChainList:\r
879                 #\r
880                 # module build needs platform build information, so get platform\r
881                 # AutoGen first\r
882                 #\r
883                 if self.Target == 'fds':\r
884                     #\r
885                     # we need to re-generate the command in platform's Makefile\r
886                     # in case the user changed command line option for 'fds' target\r
887                     #\r
888                     PlatformAutoGen.New(self.Ewb, self.PlatformFile, BuildTarget, ToolChain, self.ArchList, True)\r
889                     self._Build("genmake", self.PlatformFile, None, BuildTarget, ToolChain, self.ArchList, False, False)\r
890                 else:\r
891                     PlatformAutoGen.New(self.Ewb, self.PlatformFile, BuildTarget, ToolChain, self.ArchList)\r
892 \r
893                 for Arch in self.ArchList:\r
894                     self._Build(self.Target, self.PlatformFile, self.ModuleFile, BuildTarget, ToolChain, Arch)\r
895 \r
896     ## Build a platform in multi-thread mode\r
897     #\r
898     def _MultiThreadBuildPlatform(self):\r
899         for BuildTarget in self.BuildTargetList:\r
900             for ToolChain in self.ToolChainList:\r
901                 Pa = PlatformAutoGen.New(self.Ewb, self.PlatformFile, BuildTarget, ToolChain, self.ArchList)\r
902                 if Pa == None:\r
903                     continue\r
904                 # multi-thread exit flag\r
905                 ExitFlag = threading.Event()\r
906                 ExitFlag.clear()\r
907                 for Arch in self.ArchList:\r
908                     for Module in Pa.Platform[Arch].Modules:\r
909                         # Get ModuleAutoGen object to generate C code file and makefile\r
910                         Ma = ModuleAutoGen.New(self.Ewb, self.PlatformFile, Module, BuildTarget, ToolChain, Arch)\r
911                         if Ma == None:\r
912                             continue\r
913                         # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'\r
914                         if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
915                             # for target which must generate AutoGen code and makefile\r
916                             Ma.CreateCodeFile(True)\r
917                             if self.Target == "genc":\r
918                                 continue\r
919 \r
920                             Ma.CreateMakeFile(True)\r
921                             if self.Target == "genmake":\r
922                                 continue\r
923                         # Generate build task for the module\r
924                         Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))\r
925                         # Break build if any build thread has error\r
926                         if BuildTask.HasError():\r
927                             # we need a full version of makefile for platform\r
928                             ExitFlag.set()\r
929                             BuildTask.WaitForComplete()\r
930                             Pa.CreateModuleAutoGen()\r
931                             Pa.CreateMakeFile(False)\r
932                             EdkLogger.error("build", BUILD_ERROR, BuildTask.GetErrorMessage())\r
933                         # Start task scheduler\r
934                         if not BuildTask.IsOnGoing():\r
935                             BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)\r
936 \r
937                 #\r
938                 # All modules have been put in build tasks queue. Tell task scheduler\r
939                 # to exit if all tasks are completed\r
940                 #\r
941                 ExitFlag.set()\r
942                 BuildTask.WaitForComplete()\r
943                 # in case there's an interruption. we need a full version of makefile for platform\r
944                 Pa.CreateModuleAutoGen()\r
945                 Pa.CreateMakeFile(False)\r
946                 if BuildTask.HasError():\r
947                     EdkLogger.error("build", BUILD_ERROR, BuildTask.GetErrorMessage())\r
948 \r
949                 # Generate FD image if there's a FDF file found\r
950                 if self.Fdf != '' and self.Target in ["", "all", "fds"]:\r
951                     LaunchCommand(Pa.GetBuildCommand() + ("fds",), Pa.GetMakeFileDir())\r
952 \r
953     ## Generate GuidedSectionTools.txt in the FV directories.\r
954     #\r
955     def CreateGuidedSectionToolsFile(self):\r
956         for Dsc in self.Ewb.DscDatabase.keys():\r
957             for Arch in self.ArchList:\r
958                 for BuildTarget in self.BuildTargetList:\r
959                     for ToolChain in self.ToolChainList:\r
960                         FvDir = os.path.join(\r
961                                     self.WorkspaceDir,\r
962                                     self.Ewb.Build[Arch].PlatformDatabase[Dsc].OutputDirectory,\r
963                                     '_'.join((BuildTarget, ToolChain)),\r
964                                     'FV'\r
965                                     )\r
966                         if not os.path.exists(FvDir):\r
967                             continue \r
968                         # Build up the list of supported architectures for this build\r
969                         prefix = '%s_%s_%s_' % (BuildTarget, ToolChain, Arch)\r
970 \r
971                         # Look through the tool definitions for GUIDed tools\r
972                         guidAttribs = []\r
973                         for (attrib, value) in self.ToolDef.ToolsDefTxtDictionary.iteritems():\r
974                             if attrib.upper().endswith('_GUID'):\r
975                                 split = attrib.split('_')\r
976                                 thisPrefix = '_'.join(split[0:3]) + '_'\r
977                                 if thisPrefix == prefix:\r
978                                     guid = self.ToolDef.ToolsDefTxtDictionary[attrib]\r
979                                     guid = guid.lower()\r
980                                     toolName = split[3]\r
981                                     path = '_'.join(split[0:4]) + '_PATH'\r
982                                     path = self.ToolDef.ToolsDefTxtDictionary[path]\r
983                                     path = self.GetFullPathOfTool(path)\r
984                                     guidAttribs.append((guid, toolName, path))\r
985 \r
986                         # Write out GuidedSecTools.txt\r
987                         toolsFile = os.path.join(FvDir, 'GuidedSectionTools.txt')\r
988                         toolsFile = open(toolsFile, 'wt')\r
989                         for guidedSectionTool in guidAttribs:\r
990                             print >> toolsFile, ' '.join(guidedSectionTool)\r
991                         toolsFile.close()\r
992 \r
993     ## Returns the full path of the tool.\r
994     #\r
995     def GetFullPathOfTool (self, tool):\r
996         if os.path.exists(tool):\r
997             return os.path.realpath(tool)\r
998         else:\r
999             # We need to search for the tool using the\r
1000             # PATH environment variable.\r
1001             for dirInPath in os.environ['PATH'].split(os.pathsep):\r
1002                 foundPath = os.path.join(dirInPath, tool)\r
1003                 if os.path.exists(foundPath):\r
1004                     return os.path.realpath(foundPath)\r
1005 \r
1006         # If the tool was not found in the path then we just return\r
1007         # the input tool.\r
1008         return tool\r
1009 \r
1010     ## Launch the module or platform build\r
1011     #\r
1012     def Launch(self):\r
1013         if self.ModuleFile == None or self.ModuleFile == "":\r
1014             if not self.SpawnMode or self.Target not in ["", "all"]:\r
1015                 self.SpawnMode = False\r
1016                 self._BuildPlatform()\r
1017             else:\r
1018                 self._MultiThreadBuildPlatform()\r
1019             self.CreateGuidedSectionToolsFile()\r
1020         else:\r
1021             self.SpawnMode = False\r
1022             self._BuildModule()\r
1023 \r
1024     ## Do some clean-up works when error occurred\r
1025     def Relinquish(self):\r
1026         self.DumpBuildData()\r
1027         Utils.Progressor.Abort()\r
1028         if self.SpawnMode == True:\r
1029             BuildTask.Abort()\r
1030 \r
1031     def DumpBuildData(self):\r
1032         CacheDirectory = os.path.join(self.WorkspaceDir, gBuildCacheDir)\r
1033         Utils.CreateDirectory(CacheDirectory)\r
1034         Utils.DataDump(Utils.gFileTimeStampCache, os.path.join(CacheDirectory, "gFileTimeStampCache"))\r
1035         Utils.DataDump(Utils.gDependencyDatabase, os.path.join(CacheDirectory, "gDependencyDatabase"))\r
1036 \r
1037     def RestoreBuildData(self):\r
1038         FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gFileTimeStampCache")\r
1039         if Utils.gFileTimeStampCache == {} and os.path.isfile(FilePath):\r
1040             Utils.gFileTimeStampCache = Utils.DataRestore(FilePath)\r
1041             if Utils.gFileTimeStampCache == None:\r
1042                 Utils.gFileTimeStampCache = {}\r
1043         \r
1044         FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gDependencyDatabase")\r
1045         if Utils.gDependencyDatabase == {} and os.path.isfile(FilePath):\r
1046             Utils.gDependencyDatabase = Utils.DataRestore(FilePath)\r
1047             if Utils.gDependencyDatabase == None:\r
1048                 Utils.gDependencyDatabase = {}\r
1049 \r
1050 ## Parse command line options\r
1051 #\r
1052 # Using standard Python module optparse to parse command line option of this tool.\r
1053 #\r
1054 #   @retval Opt   A optparse.Values object containing the parsed options\r
1055 #   @retval Args  Target of build command\r
1056 #\r
1057 def MyOptionParser():\r
1058     Parser = OptionParser(description=__copyright__,version=__version__,prog="build.exe",usage="%prog [options] [target]")\r
1059     Parser.add_option("-a", "--arch", action="append", type="choice", choices=['IA32','X64','IPF','EBC'], dest="TargetArch",\r
1060         help="ARCHS is one of list: IA32, X64, IPF or EBC, which overrides target.txt's TARGET_ARCH definition. To specify more archs, please repeat this option.")\r
1061     Parser.add_option("-p", "--platform", action="store", type="string", dest="PlatformFile",\r
1062         help="Build the platform specified by the DSC file name argument, overrides target.txt's ACTIVE_PLATFORM definition.")\r
1063     Parser.add_option("-m", "--module", action="store", type="string", dest="ModuleFile",\r
1064         help="Build the module specified by the INF file name argument.")\r
1065     Parser.add_option("-b", "--buildtarget", action="append", type="choice", choices=['DEBUG','RELEASE'], dest="BuildTarget",\r
1066         help="BuildTarget is one of list: DEBUG, RELEASE, which overrides target.txt's TARGET definition. To specify more TARGET, please repeat this option.")\r
1067     Parser.add_option("-t", "--tagname", action="append", type="string", dest="ToolChain",\r
1068         help="Using the Tool Chain Tagname to build the platform, overrides target.txt's TOOL_CHAIN_TAG definition.")\r
1069     Parser.add_option("-s", "--spawn", action="store_true", type=None, dest="SpawnMode",\r
1070         help="If this flag is specified, as soon as a module can be built, the build will start, without waiting for AutoGen to complete remaining modules. While this option provides feedback that looks fast, due to overhead of the AutoGen function, this option is slower than letting AutoGen complete before starting the MAKE phase.")\r
1071     Parser.add_option("-n", action="store", type="int", dest="ThreadNumber",\r
1072         help="Build the platform using multi-threaded compiler, this option must combine with spawn option. The value overrides target.txt's MULTIPLE_THREAD and MAX_CONCURRENT_THREAD_NUMBER, less than 2 will disable multi-thread builds.")\r
1073     Parser.add_option("-f", "--fdf", action="store", type="string", dest="FdfFile",\r
1074         help="The name of the FDF file to use, which overrides the setting in the DSC file.")\r
1075     Parser.add_option("-r", "--rom-image", action="append", type="string", dest="RomImage", default=[],\r
1076         help="The name of FD to be generated. The name must be from [FD] section in FDF file.")\r
1077     Parser.add_option("-i", "--fv-image", action="append", type="string", dest="FvImage", default=[],\r
1078         help="The name of FV to be generated. The name must be from [FV] section in FDF file.")\r
1079     Parser.add_option("-k", "--msft", action="store_const", dest="MakefileType", const="nmake", help="Make Option: Generate only NMAKE Makefiles: Makefile")\r
1080     Parser.add_option("-g", "--gcc", action="store_const", dest="MakefileType", const="gmake", help="Make Option: Generate only GMAKE Makefiles: GNUmakefile")\r
1081     Parser.add_option("-l", "--all", action="store_const", dest="MakefileType", const="all", help="Make Option: Generate both NMAKE and GMAKE makefiles.")\r
1082 \r
1083     Parser.add_option("-w", "--warning-as-error", action="store_true", dest="WarningAsError", help="Treat warning in tools as error.")\r
1084     Parser.add_option("-j", "--log", action="store", dest="LogFile", help="Putlog in specified file as well as on console.")\r
1085     Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")\r
1086     Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\\r
1087                                                                                "including library instances selected, final dependency expression, "\\r
1088                                                                                "and warning messages, etc.")\r
1089     Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")\r
1090 \r
1091     (Opt, Args)=Parser.parse_args()\r
1092     return (Opt, Args)\r
1093 \r
1094 ## Tool entrance method\r
1095 #\r
1096 # This method mainly dispatch specific methods per the command line options.\r
1097 # If no error found, return zero value so the caller of this tool can know\r
1098 # if it's executed successfully or not.\r
1099 #\r
1100 #   @retval 0     Tool was successful\r
1101 #   @retval 1     Tool failed\r
1102 #\r
1103 def Main():\r
1104     StartTime = time.clock()\r
1105     #\r
1106     # Parse the options and args\r
1107     #\r
1108     (Option, Target) = MyOptionParser()\r
1109 \r
1110     if len(Target) == 0:\r
1111         Target = "all"\r
1112     elif len(Target) >= 2:\r
1113         EdkLogger.error("build", OPTION_NOT_SUPPORTED, "More than on targets are not supported.",\r
1114                         ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))\r
1115     else:\r
1116         Target = Target[0].lower()\r
1117 \r
1118     if Option.verbose != None:\r
1119         EdkLogger.SetLevel(EdkLogger.VERBOSE)\r
1120     elif Option.quiet != None:\r
1121         EdkLogger.SetLevel(EdkLogger.QUIET)\r
1122     elif Option.debug != None:\r
1123         EdkLogger.SetLevel(Option.debug + 1)\r
1124     else:\r
1125         EdkLogger.SetLevel(EdkLogger.INFO)\r
1126 \r
1127     if Option.LogFile != None:\r
1128         EdkLogger.SetLogFile(Option.LogFile)\r
1129 \r
1130     if Option.WarningAsError == True:\r
1131         EdkLogger.SetWarningAsError()\r
1132 \r
1133     EdkLogger.quiet(time.strftime("%H:%M:%S, %b.%d %Y ", time.localtime()) + "[00:00]" + "\n")\r
1134     ReturnCode = 0\r
1135     MyBuild = None\r
1136     try:\r
1137         #\r
1138         # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH\r
1139         #\r
1140         CheckEnvVariable()\r
1141         Workspace = os.getenv("WORKSPACE")\r
1142 \r
1143         WorkingDirectory = os.getcwd()\r
1144         if Option.ModuleFile != None:\r
1145             Option.ModuleFile = NormFile(Option.ModuleFile, Workspace)\r
1146         else:\r
1147             FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.inf')))\r
1148             FileNum = len(FileList)\r
1149             if FileNum >= 2:\r
1150                 EdkLogger.error("build", None, "There are %d INF files in %s.\n" % (FileNum, WorkingDirectory))\r
1151             elif FileNum == 1:\r
1152                 Option.ModuleFile = NormFile(FileList[0], Workspace)\r
1153 \r
1154         if Option.PlatformFile != None:\r
1155             Option.PlatformFile = NormFile(Option.PlatformFile, Workspace)\r
1156 \r
1157         if Option.FdfFile != None:\r
1158             Option.FdfFile = NormFile(Option.FdfFile, Workspace)\r
1159 \r
1160         MyBuild = Build(Target, Workspace, Option.PlatformFile, Option.ModuleFile, Option.TargetArch,\r
1161                         Option.ToolChain, Option.BuildTarget, Option.FdfFile, Option.RomImage, Option.FvImage,\r
1162                         Option.MakefileType, Option.SpawnMode, Option.ThreadNumber)\r
1163         MyBuild.Launch()\r
1164         MyBuild.DumpBuildData()\r
1165     except BaseException, X:\r
1166         if MyBuild != None:\r
1167             # for multi-thread build exits safely\r
1168             MyBuild.Relinquish()\r
1169         EdkLogger.quiet("")\r
1170         if Option != None and Option.debug != None:\r
1171             EdkLogger.quiet(traceback.format_exc())\r
1172         else:\r
1173             EdkLogger.quiet(str(X))\r
1174         ReturnCode = 1\r
1175     finally:\r
1176         Utils.Progressor.Abort()\r
1177 \r
1178     FinishTime = time.clock()\r
1179     BuildDuration = time.strftime("%M:%S", time.gmtime(int(round(FinishTime - StartTime))))\r
1180     EdkLogger.quiet("\n%s [%s]" % (time.strftime("%H:%M:%S, %b.%d %Y", time.localtime()), BuildDuration))\r
1181 \r
1182     return ReturnCode\r
1183 \r
1184 if __name__ == '__main__':\r
1185     sys.exit(Main())\r