Moved the clear action to the place before exiting scheduler
[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         SchedulerThread = Thread(target=BuildTask.Scheduler, args=(MaxThreadNumber, ExitFlag))\r
325         SchedulerThread.setName("Build-Task-Scheduler")\r
326         SchedulerThread.setDaemon(False)\r
327         SchedulerThread.start()\r
328 \r
329     ## Scheduler method\r
330     #\r
331     #   @param  MaxThreadNumber     The maximum thread number\r
332     #   @param  ExitFlag            Flag used to end the scheduler\r
333     #\r
334     @staticmethod\r
335     def Scheduler(MaxThreadNumber, ExitFlag):\r
336         BuildTask._SchedulerStopped.clear()\r
337         try:\r
338             # use BoundedSemaphore to control the maximum running threads\r
339             BuildTask._Thread = BoundedSemaphore(MaxThreadNumber)\r
340             #\r
341             # scheduling loop, which will exits when no pending/ready task and\r
342             # indicated to do so, or there's error in running thread\r
343             #\r
344             while (len(BuildTask._PendingQueue) > 0 or len(BuildTask._ReadyQueue) > 0 \\r
345                    or not ExitFlag.isSet()) and not BuildTask._ErrorFlag.isSet():\r
346                 EdkLogger.debug(EdkLogger.DEBUG_9, "Pending Queue (%d), Ready Queue (%d)"\r
347                                 % (len(BuildTask._PendingQueue), len(BuildTask._ReadyQueue)))\r
348                 # get all pending tasks\r
349                 BuildTask._PendingQueueLock.acquire()\r
350                 BuildObjectList = BuildTask._PendingQueue.keys()\r
351                 BuildTask._PendingQueueLock.release()\r
352 \r
353                 #\r
354                 # check if their dependency is resolved, and if true, move them\r
355                 # into ready queue\r
356                 #\r
357                 for BuildObject in BuildObjectList:\r
358                     Bt = BuildTask._PendingQueue[BuildObject]\r
359                     if Bt.IsReady():\r
360                         BuildTask._PendingQueueLock.acquire()\r
361                         BuildTask._ReadyQueue[BuildObject] = BuildTask._PendingQueue.pop(BuildObject)\r
362                         BuildTask._PendingQueueLock.release()\r
363 \r
364                 # launch build thread until the maximum number of threads is reached\r
365                 while not BuildTask._ErrorFlag.isSet():\r
366                     # empty ready queue, do nothing further\r
367                     if len(BuildTask._ReadyQueue) == 0:\r
368                         break\r
369 \r
370                     # wait for active thread(s) exit\r
371                     BuildTask._Thread.acquire(True)\r
372 \r
373                     # start a new build thread\r
374                     Bo = BuildTask._ReadyQueue.keys()[0]\r
375                     Bt = BuildTask._ReadyQueue.pop(Bo)\r
376 \r
377                     # move into running queue\r
378                     BuildTask._RunningQueueLock.acquire()\r
379                     BuildTask._RunningQueue[Bo] = Bt\r
380                     BuildTask._RunningQueueLock.release()\r
381 \r
382                     Bt.Start()\r
383 \r
384                 # avoid tense loop\r
385                 time.sleep(0.01)\r
386 \r
387             # wait for all running threads exit\r
388             if BuildTask._ErrorFlag.isSet():\r
389                 EdkLogger.quiet("\nWaiting for all Command-Threads exit...")\r
390             # while not BuildTask._ErrorFlag.isSet() and \\r
391             while len(BuildTask._RunningQueue) > 0:\r
392                 EdkLogger.verbose("Waiting for thread ending...(%d)" % len(BuildTask._RunningQueue))\r
393                 EdkLogger.debug(EdkLogger.DEBUG_9, "Threads [%s]" % ", ".join([Th.getName() for Th in threading.enumerate()]))\r
394                 # avoid tense loop\r
395                 time.sleep(0.1)\r
396         except BaseException, X:\r
397             #\r
398             # TRICK: hide the output of threads left runing, so that the user can\r
399             #        catch the error message easily\r
400             #\r
401             EdkLogger.SetLevel(EdkLogger.QUIET)\r
402             BuildTask._ErrorFlag.set()\r
403             BuildTask._ErrorMessage = "build thread scheduler error\n\t%s" % str(X)\r
404 \r
405         BuildTask._PendingQueue.clear()\r
406         BuildTask._ReadyQueue.clear()\r
407         BuildTask._RunningQueue.clear()\r
408         BuildTask._TaskQueue.clear()\r
409         BuildTask._SchedulerStopped.set()\r
410 \r
411     ## Wait for all running method exit\r
412     #\r
413     @staticmethod\r
414     def WaitForComplete():\r
415         BuildTask._SchedulerStopped.wait()\r
416 \r
417     ## Check if the scheduler is running or not\r
418     #\r
419     @staticmethod\r
420     def IsOnGoing():\r
421         return not BuildTask._SchedulerStopped.isSet()\r
422 \r
423     ## Abort the build\r
424     @staticmethod\r
425     def Abort():\r
426         if BuildTask.IsOnGoing():\r
427             BuildTask._ErrorFlag.set()\r
428             BuildTask.WaitForComplete()\r
429 \r
430     ## Check if there's error in running thread\r
431     #\r
432     #   Since the main thread cannot catch exceptions in other thread, we have to\r
433     #   use threading.Event to communicate this formation to main thread.\r
434     #\r
435     @staticmethod\r
436     def HasError():\r
437         return BuildTask._ErrorFlag.isSet()\r
438 \r
439     ## Get error message in running thread\r
440     #\r
441     #   Since the main thread cannot catch exceptions in other thread, we have to\r
442     #   use a static variable to communicate this message to main thread.\r
443     #\r
444     @staticmethod\r
445     def GetErrorMessage():\r
446         return BuildTask._ErrorMessage\r
447 \r
448     ## Factory method to create a BuildTask object\r
449     #\r
450     #   This method will check if a module is building or has been built. And if\r
451     #   true, just return the associated BuildTask object in the _TaskQueue. If\r
452     #   not, create and return a new BuildTask object. The new BuildTask object\r
453     #   will be appended to the _PendingQueue for scheduling later.\r
454     #\r
455     #   @param  BuildItem       A BuildUnit object representing a build object\r
456     #   @param  Dependency      The dependent build object of BuildItem\r
457     #\r
458     @staticmethod\r
459     def New(BuildItem, Dependency=None):\r
460         if BuildItem in BuildTask._TaskQueue:\r
461             Bt = BuildTask._TaskQueue[BuildItem]\r
462             return Bt\r
463 \r
464         Bt = BuildTask()\r
465         Bt._Init(BuildItem, Dependency)\r
466         BuildTask._TaskQueue[BuildItem] = Bt\r
467 \r
468         BuildTask._PendingQueueLock.acquire()\r
469         BuildTask._PendingQueue[BuildItem] = Bt\r
470         BuildTask._PendingQueueLock.release()\r
471 \r
472         return Bt\r
473 \r
474     ## The real constructor of BuildTask\r
475     #\r
476     #   @param  BuildItem       A BuildUnit object representing a build object\r
477     #   @param  Dependency      The dependent build object of BuildItem\r
478     #\r
479     def _Init(self, BuildItem, Dependency=None):\r
480         self.BuildItem = BuildItem\r
481 \r
482         self.DependencyList = []\r
483         if Dependency == None:\r
484             Dependency = BuildItem.Dependency\r
485         else:\r
486             Dependency.extend(BuildItem.Dependency)\r
487         self.AddDependency(Dependency)\r
488         # flag indicating build completes, used to avoid unnecessary re-build\r
489         self.CompleteFlag = False\r
490 \r
491     ## Check if all dependent build tasks are completed or not\r
492     #\r
493     def IsReady(self):\r
494         ReadyFlag = True\r
495         for Dep in self.DependencyList:\r
496             if Dep.CompleteFlag == True:\r
497                 continue\r
498             ReadyFlag = False\r
499             break\r
500 \r
501         return ReadyFlag\r
502 \r
503     ## Add dependent build task\r
504     #\r
505     #   @param  Dependency      The list of dependent build objects\r
506     #\r
507     def AddDependency(self, Dependency):\r
508         for Dep in Dependency:\r
509             self.DependencyList.append(BuildTask.New(Dep))    # BuildTask list\r
510 \r
511     ## The thread wrapper of LaunchCommand function\r
512     #\r
513     # @param  Command               A list or string contains the call of the command\r
514     # @param  WorkingDir            The directory in which the program will be running\r
515     #\r
516     def _CommandThread(self, Command, WorkingDir):\r
517         try:\r
518             LaunchCommand(Command, WorkingDir)\r
519             self.CompleteFlag = True\r
520         except:\r
521             #\r
522             # TRICK: hide the output of threads left runing, so that the user can\r
523             #        catch the error message easily\r
524             #\r
525             EdkLogger.SetLevel(EdkLogger.QUIET)\r
526             BuildTask._ErrorFlag.set()\r
527             BuildTask._ErrorMessage = "%s broken\n    %s [%s]" % \\r
528                                       (threading.currentThread().getName(), Command, WorkingDir)\r
529         # indicate there's a thread is available for another build task\r
530         BuildTask._RunningQueueLock.acquire()\r
531         BuildTask._RunningQueue.pop(self.BuildItem)\r
532         BuildTask._RunningQueueLock.release()\r
533         BuildTask._Thread.release()\r
534 \r
535     ## Start build task thread\r
536     #\r
537     def Start(self):\r
538         Command = self.BuildItem.BuildCommand + (self.BuildItem.Target,)\r
539         self.BuildTread = Thread(target=self._CommandThread, args=(Command, self.BuildItem.WorkingDir))\r
540         self.BuildTread.setName("Command-Thread")\r
541         self.BuildTread.setDaemon(False)\r
542         self.BuildTread.start()\r
543 \r
544 ## The class implementing the EDK2 build process\r
545 #\r
546 #   The build process includes:\r
547 #       1. Load configuration from target.txt and tools_def.txt in $(WORKSPACE)/Conf\r
548 #       2. Parse DSC file of active platform\r
549 #       3. Parse FDF file if any\r
550 #       4. Establish build database, including parse all other files (module, package)\r
551 #       5. Create AutoGen files (C code file, depex file, makefile) if necessary\r
552 #       6. Call build command\r
553 #\r
554 class Build():\r
555     ## Constructor\r
556     #\r
557     # Constructor will load all necessary configurations, parse platform, modules\r
558     # and packages and the establish a database for AutoGen.\r
559     #\r
560     #   @param  Target              The build command target, one of gSupportedTarget\r
561     #   @param  WorkspaceDir        The directory of workspace\r
562     #   @param  Platform            The DSC file of active platform\r
563     #   @param  Module              The INF file of active module, if any\r
564     #   @param  Arch                The Arch list of platform or module\r
565     #   @param  ToolChain           The name list of toolchain\r
566     #   @param  BuildTarget         The "DEBUG" or "RELEASE" build\r
567     #   @param  FlashDefinition     The FDF file of active platform\r
568     #   @param  FdList=[]           The FD names to be individually built\r
569     #   @param  FvList=[]           The FV names to be individually built\r
570     #   @param  MakefileType        The type of makefile (for MSFT make or GNU make)\r
571     #   @param  SpawnMode           Indicate multi-thread build mode\r
572     #   @param  ThreadNumber        The maximum number of thread if in multi-thread build mode\r
573     #\r
574     def __init__(self, Target, WorkspaceDir, Platform, Module, Arch, ToolChain, BuildTarget,\r
575                  FlashDefinition, FdList=[], FvList=[], MakefileType="nmake", SpawnMode=False, ThreadNumber=2):\r
576 \r
577         self.WorkspaceDir = WorkspaceDir\r
578 \r
579         self.Target         = Target\r
580         self.PlatformFile   = Platform\r
581         self.ModuleFile     = Module\r
582         self.ArchList       = Arch\r
583         self.ToolChainList  = ToolChain\r
584         self.BuildTargetList= BuildTarget\r
585         self.Fdf            = FlashDefinition\r
586         self.FdList         = FdList\r
587         self.FvList         = FvList\r
588         self.MakefileType   = MakefileType\r
589         self.SpawnMode      = SpawnMode\r
590         self.ThreadNumber   = ThreadNumber\r
591 \r
592         self.TargetTxt    = TargetTxtClassObject()\r
593         self.ToolDef      = ToolDefClassObject()\r
594 \r
595         # print dot charater during doing some time-consuming work\r
596         self.Progress = Utils.Progressor()\r
597 \r
598         # parse target.txt, tools_def.txt, and platform file\r
599         self.Progress.Start("Loading build configuration")\r
600         self.RestoreBuildData()\r
601         self.LoadConfiguration()\r
602         self.Progress.Stop("done!")\r
603 \r
604         self.Progress.Start("Parsing platform/modules/packages")\r
605         self.InitBuild()\r
606         self.Progress.Stop("done!")\r
607 \r
608         # print current build environment and configuration\r
609         EdkLogger.info('')\r
610         EdkLogger.quiet("%-24s = %s" % ("WORKSPACE", os.environ["WORKSPACE"]))\r
611         EdkLogger.quiet("%-24s = %s" % ("EDK_TOOLS_PATH", os.environ["EDK_TOOLS_PATH"]))\r
612         EdkLogger.info('')\r
613         EdkLogger.info('%-24s = %s' % ("TARGET_ARCH", ' '.join(self.ArchList)))\r
614         EdkLogger.info('%-24s = %s' % ("TARGET", ' '.join(self.BuildTargetList)))\r
615         EdkLogger.info('%-24s = %s' % ("TOOL_CHAIN_TAG", ' '.join(self.ToolChainList)))\r
616 \r
617         EdkLogger.info('')\r
618         if self.PlatformFile != None and self.PlatformFile != "":\r
619             EdkLogger.info('%-24s = %s' % ("Active Platform", self.PlatformFile))\r
620 \r
621         if self.Fdf != None and self.Fdf != "":\r
622             EdkLogger.info('%-24s = %s' % ("Flash Image Definition", self.Fdf))\r
623 \r
624         if self.ModuleFile != None and self.ModuleFile != "":\r
625             EdkLogger.info('%-24s = %s' % ("Active Module", self.ModuleFile))\r
626 \r
627         if self.SpawnMode:\r
628             EdkLogger.verbose('%-24s = %s' % ("Max Thread Number", self.ThreadNumber))\r
629 \r
630         # establish build database, INF/DEC files will be parsed in this stage\r
631         self.Progress.Start("\nEstablishing build database")\r
632         try:\r
633             if self.Fdf != None and self.Fdf != "":\r
634                 FdfFile = os.path.join(self.WorkspaceDir, self.Fdf)\r
635 \r
636                 Fdf = FdfParser(FdfFile)\r
637                 Fdf.ParseFile()\r
638 \r
639                 PcdSet = Fdf.Profile.PcdDict\r
640             else:\r
641                 PcdSet = {}\r
642 \r
643             self.Ewb.GenBuildDatabase(PcdSet)\r
644             self.Platform = self.Ewb.Build[self.ArchList[0]].PlatformDatabase[self.PlatformFile]\r
645 \r
646         except BaseException, X:\r
647             if isinstance(X, Warning):\r
648                 EdkLogger.error(X.ToolName, BUILD_ERROR, X.message, X.FileName, X.LineNumber, RaiseError = False)\r
649             raise\r
650         self.Progress.Stop("done!")\r
651 \r
652     ## Load configuration\r
653     #\r
654     #   This method will parse target.txt and get the build configurations.\r
655     #\r
656     def LoadConfiguration(self):\r
657         #\r
658         # Check target.txt and tools_def.txt and Init them\r
659         #\r
660         BuildConfigurationFile = os.path.normpath(os.path.join(self.WorkspaceDir, gBuildConfiguration))\r
661         if os.path.isfile(BuildConfigurationFile) == True:\r
662             StatusCode = self.TargetTxt.LoadTargetTxtFile(BuildConfigurationFile)\r
663 \r
664             ToolDefinitionFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_CONF]\r
665             ToolDefinitionFile = os.path.normpath(os.path.join(self.WorkspaceDir, ToolDefinitionFile))\r
666             if os.path.isfile(ToolDefinitionFile) == True:\r
667                 StatusCode = self.ToolDef.LoadToolDefFile(ToolDefinitionFile)\r
668             else:\r
669                 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=ToolDefinitionFile)\r
670         else:\r
671             EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=BuildConfigurationFile)\r
672 \r
673         # if no ARCH given in command line, get it from target.txt\r
674         if self.ArchList == None or len(self.ArchList) == 0:\r
675             self.ArchList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET_ARCH]\r
676             if len(self.ArchList) == 0:\r
677                 self.ArchList = ARCH_LIST\r
678 \r
679         # if no build target given in command line, get it from target.txt\r
680         if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
681             self.BuildTargetList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET]\r
682             if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
683                 self.BuildTargetList = ['DEBUG', 'RELEASE']\r
684 \r
685         # if no tool chain given in command line, get it from target.txt\r
686         if self.ToolChainList == None or len(self.ToolChainList) == 0:\r
687             self.ToolChainList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_TAG]\r
688             if self.ToolChainList == None or len(self.ToolChainList) == 0:\r
689                 EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, ExtraData="No toolchain given. Don't know how to build.\n")\r
690 \r
691         # check if the tool chains are defined or not\r
692         NewToolChainList = []\r
693         for ToolChain in self.ToolChainList:\r
694             if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG]:\r
695                 EdkLogger.warn("build", "Tool chain [%s] is not defined" % ToolChain)\r
696             else:\r
697                 NewToolChainList.append(ToolChain)\r
698         # if no tool chain available, break the build\r
699         if len(NewToolChainList) == 0:\r
700             EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,\r
701                             ExtraData="[%s] not defined. No toolchain available for build!\n" % ", ".join(self.ToolChainList))\r
702         else:\r
703             self.ToolChainList = NewToolChainList\r
704 \r
705         if self.ThreadNumber == None or self.ThreadNumber == "":\r
706             self.ThreadNumber = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER]\r
707             if self.ThreadNumber == '':\r
708                 self.ThreadNumber = 0\r
709             else:\r
710                 self.ThreadNumber = int(self.ThreadNumber, 0)\r
711 \r
712         if self.ThreadNumber == 0:\r
713             self.SpawnMode = False\r
714         elif self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_MULTIPLE_THREAD].lower() in ["enable", "true"]:\r
715             self.SpawnMode = True\r
716 \r
717         if self.PlatformFile == None:\r
718             self.PlatformFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_ACTIVE_PLATFORM]\r
719             if self.PlatformFile == None or self.PlatformFile == "":\r
720                 WorkingDirectory = os.getcwd()\r
721                 FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.dsc')))\r
722                 FileNum = len(FileList)\r
723                 if FileNum >= 2:\r
724                     EdkLogger.error("build", None, "There are %d DSC files in %s.\n" % (FileNum, WorkingDirectory))\r
725                 elif FileNum == 1:\r
726                     self.PlatformFile = NormFile(FileList[0], self.WorkspaceDir)\r
727             else:\r
728                 self.PlatformFile = NormFile(self.PlatformFile, self.WorkspaceDir)\r
729 \r
730     ## Initialize build configuration\r
731     #\r
732     #   This method will parse DSC file and merge the configurations from\r
733     #   command line and target.txt, then get the final build configurations.\r
734     #\r
735     def InitBuild(self):\r
736         if self.PlatformFile == None or self.PlatformFile == "":\r
737             EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE,\r
738                             ExtraData="No active platform specified in target.txt or command line! Nothing can be built.\n")\r
739 \r
740         Wb = WorkspaceBuild(self.PlatformFile, self.WorkspaceDir)\r
741         PcdSet = {}\r
742         if self.Fdf != None:\r
743             self.Fdf = NormFile(self.Fdf, self.WorkspaceDir)\r
744             Wb.Fdf = self.Fdf\r
745         else:\r
746             self.Fdf = Wb.Fdf\r
747 \r
748         # parse FDF file to get PCD information\r
749         if self.Fdf != None and self.Fdf != "":\r
750             if Wb.Fdf == None or Wb.Fdf == "":\r
751                 Wb.Fdf = self.Fdf\r
752 \r
753             Wb.FdTargetList.extend(self.FdList)\r
754             Wb.FvTargetList.extend(self.FvList)\r
755         else:\r
756             PcdSet = {}\r
757             if self.FdList != []:\r
758                 EdkLogger.info("No flash definition file found. FD [%s] will be ignored." % " ".join(self.FdList))\r
759             if self.FvList != []:\r
760                 EdkLogger.info("No flash definition file found. FV [%s] will be ignored." % " ".join(self.FvList))\r
761 \r
762         Wb.TargetTxt = self.TargetTxt\r
763         Wb.ToolDef = self.ToolDef\r
764         #\r
765         # Merge Arch\r
766         #\r
767         if self.ArchList == None or len(self.ArchList) == 0:\r
768             ArchList = Wb.SupArchList\r
769         else:\r
770             ArchList = list(set(self.ArchList) & set(Wb.SupArchList))\r
771         if len(ArchList) == 0:\r
772             EdkLogger.error("build", PARAMETER_INVALID,\r
773                             ExtraData = "Active platform supports [%s] only, but [%s] is given."\r
774                                         % (" ".join(Wb.SupArchList), " ".join(self.ArchList)))\r
775         elif len(ArchList) != len(self.ArchList):\r
776             SkippedArchList = set(self.ArchList).symmetric_difference(set(Wb.SupArchList))\r
777             EdkLogger.verbose("\nArch [%s] is ignored because active platform supports [%s] but [%s] is specified !"\r
778                            % (" ".join(SkippedArchList), " ".join(Wb.SupArchList), " ".join(self.ArchList)))\r
779         self.ArchList = ArchList\r
780 \r
781         # Merge build target\r
782         if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
783             BuildTargetList = Wb.BuildTarget\r
784         else:\r
785             BuildTargetList = list(set(self.BuildTargetList) & set(Wb.BuildTarget))\r
786         if BuildTargetList == []:\r
787             EdkLogger.error("build", PARAMETER_INVALID, "Active platform only supports [%s], but [%s] is given"\r
788                                 % (" ".join(Wb.BuildTarget), " ".join(self.BuildTargetList)))\r
789         self.BuildTargetList = BuildTargetList\r
790 \r
791         self.Ewb = Wb\r
792 \r
793     ## Build a module or platform\r
794     #\r
795     # Create autogen code and makfile for a module or platform, and the launch\r
796     # "make" command to build it\r
797     #\r
798     #   @param  Target                      The target of build command\r
799     #   @param  Platform                    The platform file\r
800     #   @param  Module                      The module file\r
801     #   @param  BuildTarget                 The name of build target, one of "DEBUG", "RELEASE"\r
802     #   @param  ToolChain                   The name of toolchain to build\r
803     #   @param  Arch                        The arch of the module/platform\r
804     #   @param  CreateDepModuleCodeFile     Flag used to indicate creating code\r
805     #                                       for dependent modules/Libraries\r
806     #   @param  CreateDepModuleMakeFile     Flag used to indicate creating makefile\r
807     #                                       for dependent modules/Libraries\r
808     #\r
809     def _Build(self, Target, Platform, Module, BuildTarget, ToolChain, Arch, CreateDepModuleCodeFile=True, CreateDepModuleMakeFile=True):\r
810         # skip file generation for some targets\r
811         if Target not in ['clean', 'cleanlib', 'cleanall', 'run']:\r
812             #\r
813             # no need to generate code/makefile for dependent modules/libraries\r
814             # if the target is 'fds'\r
815             #\r
816             if Target == 'fds':\r
817                 CreateDepModuleCodeFile = False\r
818                 CreateDepModuleMakeFile = False\r
819                 CreateDepModuleAutoGenObject = True\r
820             else:\r
821                 CreateDepModuleAutoGenObject = False\r
822 \r
823             if Module != None:\r
824                 self.Progress.Start("Generating code/makefile for module")\r
825                 AutoGenResult = ModuleAutoGen.New(self.Ewb, Platform, Module, BuildTarget,\r
826                                                   ToolChain, Arch)\r
827             else:\r
828                 self.Progress.Start("Generating code/makefile for platform")\r
829                 AutoGenResult = PlatformAutoGen.New(self.Ewb, Platform, BuildTarget,\r
830                                                     ToolChain, Arch, CreateDepModuleAutoGenObject)\r
831             if AutoGenResult == None:\r
832                 return\r
833 \r
834             # for target which must generate AutoGen code and makefile\r
835             AutoGenResult.CreateCodeFile(CreateDepModuleCodeFile)\r
836             if Target == "genc":\r
837                 self.Progress.Stop("done!")\r
838                 return\r
839 \r
840             AutoGenResult.CreateMakeFile(CreateDepModuleMakeFile)\r
841             if Target == "genmake":\r
842                 self.Progress.Stop("done!")\r
843                 return\r
844 \r
845             self.Progress.Stop("done!")\r
846         else:\r
847             if Module != None:\r
848                 AutoGenResult = ModuleAutoGen.New(self.Ewb, Platform, Module, BuildTarget,\r
849                                                   ToolChain, Arch)\r
850             else:\r
851                 AutoGenResult = PlatformAutoGen.New(self.Ewb, Platform, BuildTarget,\r
852                                                     ToolChain, Arch, False)\r
853             if AutoGenResult == None:\r
854                 return\r
855 \r
856         EdkLogger.info("")\r
857         BuildCommand = AutoGenResult.GetBuildCommand()\r
858         if BuildCommand == None or len(BuildCommand) == 0:\r
859             EdkLogger.error("build", OPTION_MISSING, ExtraData="No MAKE command found for [%s, %s, %s]" % Key)\r
860 \r
861         BuildCommand = BuildCommand + (Target,)\r
862         LaunchCommand(BuildCommand, os.path.join(self.WorkspaceDir, AutoGenResult.GetMakeFileDir()))\r
863 \r
864     ## Build active platform for different build targets and different tool chains\r
865     #\r
866     def _BuildPlatform(self):\r
867         for BuildTarget in self.BuildTargetList:\r
868             for ToolChain in self.ToolChainList:\r
869                 self._Build(self.Target, self.PlatformFile, None, BuildTarget, ToolChain, self.ArchList)\r
870 \r
871     ## Build active module for different build targets, different tool chains and different archs\r
872     #\r
873     def _BuildModule(self):\r
874         for BuildTarget in self.BuildTargetList:\r
875             for ToolChain in self.ToolChainList:\r
876                 #\r
877                 # module build needs platform build information, so get platform\r
878                 # AutoGen first\r
879                 #\r
880                 if self.Target == 'fds':\r
881                     #\r
882                     # we need to re-generate the command in platform's Makefile\r
883                     # in case the user changed command line option for 'fds' target\r
884                     #\r
885                     PlatformAutoGen.New(self.Ewb, self.PlatformFile, BuildTarget, ToolChain, self.ArchList, True)\r
886                     self._Build("genmake", self.PlatformFile, None, BuildTarget, ToolChain, self.ArchList, False, False)\r
887                 else:\r
888                     PlatformAutoGen.New(self.Ewb, self.PlatformFile, BuildTarget, ToolChain, self.ArchList)\r
889 \r
890                 for Arch in self.ArchList:\r
891                     self._Build(self.Target, self.PlatformFile, self.ModuleFile, BuildTarget, ToolChain, Arch)\r
892 \r
893     ## Build a platform in multi-thread mode\r
894     #\r
895     def _MultiThreadBuildPlatform(self):\r
896         for BuildTarget in self.BuildTargetList:\r
897             for ToolChain in self.ToolChainList:\r
898                 Pa = PlatformAutoGen.New(self.Ewb, self.PlatformFile, BuildTarget, ToolChain, self.ArchList)\r
899                 if Pa == None:\r
900                     continue\r
901                 # multi-thread exit flag\r
902                 ExitFlag = threading.Event()\r
903                 ExitFlag.clear()\r
904                 for Arch in self.ArchList:\r
905                     for Module in Pa.Platform[Arch].Modules:\r
906                         # Get ModuleAutoGen object to generate C code file and makefile\r
907                         Ma = ModuleAutoGen.New(self.Ewb, self.PlatformFile, Module, BuildTarget, ToolChain, Arch)\r
908                         if Ma == None:\r
909                             continue\r
910                         # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'\r
911                         if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
912                             # for target which must generate AutoGen code and makefile\r
913                             Ma.CreateCodeFile(True)\r
914                             if self.Target == "genc":\r
915                                 continue\r
916 \r
917                             Ma.CreateMakeFile(True)\r
918                             if self.Target == "genmake":\r
919                                 continue\r
920                         # Generate build task for the module\r
921                         Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))\r
922                         # Break build if any build thread has error\r
923                         if BuildTask.HasError():\r
924                             # we need a full version of makefile for platform\r
925                             ExitFlag.set()\r
926                             BuildTask.WaitForComplete()\r
927                             Pa.CreateModuleAutoGen()\r
928                             Pa.CreateMakeFile(False)\r
929                             EdkLogger.error("build", BUILD_ERROR, BuildTask.GetErrorMessage())\r
930                         # Start task scheduler\r
931                         if not BuildTask.IsOnGoing():\r
932                             BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)\r
933 \r
934                 #\r
935                 # All modules have been put in build tasks queue. Tell task scheduler\r
936                 # to exit if all tasks are completed\r
937                 #\r
938                 ExitFlag.set()\r
939                 BuildTask.WaitForComplete()\r
940                 # in case there's an interruption. we need a full version of makefile for platform\r
941                 Pa.CreateModuleAutoGen()\r
942                 Pa.CreateMakeFile(False)\r
943                 if BuildTask.HasError():\r
944                     EdkLogger.error("build", BUILD_ERROR, BuildTask.GetErrorMessage())\r
945 \r
946                 # Generate FD image if there's a FDF file found\r
947                 if self.Fdf != '' and self.Target in ["", "all", "fds"]:\r
948                     LaunchCommand(Pa.GetBuildCommand() + ("fds",), Pa.GetMakeFileDir())\r
949 \r
950     ## Generate GuidedSectionTools.txt in the FV directories.\r
951     #\r
952     def CreateGuidedSectionToolsFile(self):\r
953         for Dsc in self.Ewb.DscDatabase.keys():\r
954             for Arch in self.ArchList:\r
955                 for BuildTarget in self.BuildTargetList:\r
956                     for ToolChain in self.ToolChainList:\r
957                         FvDir = os.path.join(\r
958                                     self.WorkspaceDir,\r
959                                     self.Ewb.Build[Arch].PlatformDatabase[Dsc].OutputDirectory,\r
960                                     '_'.join((BuildTarget, ToolChain)),\r
961                                     'FV'\r
962                                     )\r
963                         if not os.path.exists(FvDir):\r
964                             continue \r
965                         # Build up the list of supported architectures for this build\r
966                         prefix = '%s_%s_%s_' % (BuildTarget, ToolChain, Arch)\r
967 \r
968                         # Look through the tool definitions for GUIDed tools\r
969                         guidAttribs = []\r
970                         for (attrib, value) in self.ToolDef.ToolsDefTxtDictionary.iteritems():\r
971                             if attrib.upper().endswith('_GUID'):\r
972                                 split = attrib.split('_')\r
973                                 thisPrefix = '_'.join(split[0:3]) + '_'\r
974                                 if thisPrefix == prefix:\r
975                                     guid = self.ToolDef.ToolsDefTxtDictionary[attrib]\r
976                                     guid = guid.lower()\r
977                                     toolName = split[3]\r
978                                     path = '_'.join(split[0:4]) + '_PATH'\r
979                                     path = self.ToolDef.ToolsDefTxtDictionary[path]\r
980                                     path = self.GetFullPathOfTool(path)\r
981                                     guidAttribs.append((guid, toolName, path))\r
982 \r
983                         # Write out GuidedSecTools.txt\r
984                         toolsFile = os.path.join(FvDir, 'GuidedSectionTools.txt')\r
985                         toolsFile = open(toolsFile, 'wt')\r
986                         for guidedSectionTool in guidAttribs:\r
987                             print >> toolsFile, ' '.join(guidedSectionTool)\r
988                         toolsFile.close()\r
989 \r
990     ## Returns the full path of the tool.\r
991     #\r
992     def GetFullPathOfTool (self, tool):\r
993         if os.path.exists(tool):\r
994             return os.path.realpath(tool)\r
995         else:\r
996             # We need to search for the tool using the\r
997             # PATH environment variable.\r
998             for dirInPath in os.environ['PATH'].split(os.pathsep):\r
999                 foundPath = os.path.join(dirInPath, tool)\r
1000                 if os.path.exists(foundPath):\r
1001                     return os.path.realpath(foundPath)\r
1002 \r
1003         # If the tool was not found in the path then we just return\r
1004         # the input tool.\r
1005         return tool\r
1006 \r
1007     ## Launch the module or platform build\r
1008     #\r
1009     def Launch(self):\r
1010         if self.ModuleFile == None or self.ModuleFile == "":\r
1011             if not self.SpawnMode or self.Target not in ["", "all"]:\r
1012                 self.SpawnMode = False\r
1013                 self._BuildPlatform()\r
1014             else:\r
1015                 self._MultiThreadBuildPlatform()\r
1016             self.CreateGuidedSectionToolsFile()\r
1017         else:\r
1018             self.SpawnMode = False\r
1019             self._BuildModule()\r
1020 \r
1021     ## Do some clean-up works when error occurred\r
1022     def Relinquish(self):\r
1023         self.DumpBuildData()\r
1024         Utils.Progressor.Abort()\r
1025         if self.SpawnMode == True:\r
1026             BuildTask.Abort()\r
1027 \r
1028     def DumpBuildData(self):\r
1029         CacheDirectory = os.path.join(self.WorkspaceDir, gBuildCacheDir)\r
1030         Utils.CreateDirectory(CacheDirectory)\r
1031         Utils.DataDump(Utils.gFileTimeStampCache, os.path.join(CacheDirectory, "gFileTimeStampCache"))\r
1032         Utils.DataDump(Utils.gDependencyDatabase, os.path.join(CacheDirectory, "gDependencyDatabase"))\r
1033 \r
1034     def RestoreBuildData(self):\r
1035         FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gFileTimeStampCache")\r
1036         if Utils.gFileTimeStampCache == {} and os.path.isfile(FilePath):\r
1037             Utils.gFileTimeStampCache = Utils.DataRestore(FilePath)\r
1038             if Utils.gFileTimeStampCache == None:\r
1039                 Utils.gFileTimeStampCache = {}\r
1040         \r
1041         FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gDependencyDatabase")\r
1042         if Utils.gDependencyDatabase == {} and os.path.isfile(FilePath):\r
1043             Utils.gDependencyDatabase = Utils.DataRestore(FilePath)\r
1044             if Utils.gDependencyDatabase == None:\r
1045                 Utils.gDependencyDatabase = {}\r
1046 \r
1047 ## Parse command line options\r
1048 #\r
1049 # Using standard Python module optparse to parse command line option of this tool.\r
1050 #\r
1051 #   @retval Opt   A optparse.Values object containing the parsed options\r
1052 #   @retval Args  Target of build command\r
1053 #\r
1054 def MyOptionParser():\r
1055     Parser = OptionParser(description=__copyright__,version=__version__,prog="build.exe",usage="%prog [options] [target]")\r
1056     Parser.add_option("-a", "--arch", action="append", type="choice", choices=['IA32','X64','IPF','EBC'], dest="TargetArch",\r
1057         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
1058     Parser.add_option("-p", "--platform", action="store", type="string", dest="PlatformFile",\r
1059         help="Build the platform specified by the DSC file name argument, overrides target.txt's ACTIVE_PLATFORM definition.")\r
1060     Parser.add_option("-m", "--module", action="store", type="string", dest="ModuleFile",\r
1061         help="Build the module specified by the INF file name argument.")\r
1062     Parser.add_option("-b", "--buildtarget", action="append", type="choice", choices=['DEBUG','RELEASE'], dest="BuildTarget",\r
1063         help="BuildTarget is one of list: DEBUG, RELEASE, which overrides target.txt's TARGET definition. To specify more TARGET, please repeat this option.")\r
1064     Parser.add_option("-t", "--tagname", action="append", type="string", dest="ToolChain",\r
1065         help="Using the Tool Chain Tagname to build the platform, overrides target.txt's TOOL_CHAIN_TAG definition.")\r
1066     Parser.add_option("-s", "--spawn", action="store_true", type=None, dest="SpawnMode",\r
1067         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
1068     Parser.add_option("-n", action="store", type="int", dest="ThreadNumber",\r
1069         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
1070     Parser.add_option("-f", "--fdf", action="store", type="string", dest="FdfFile",\r
1071         help="The name of the FDF file to use, which overrides the setting in the DSC file.")\r
1072     Parser.add_option("-r", "--rom-image", action="append", type="string", dest="RomImage", default=[],\r
1073         help="The name of FD to be generated. The name must be from [FD] section in FDF file.")\r
1074     Parser.add_option("-i", "--fv-image", action="append", type="string", dest="FvImage", default=[],\r
1075         help="The name of FV to be generated. The name must be from [FV] section in FDF file.")\r
1076     Parser.add_option("-k", "--msft", action="store_const", dest="MakefileType", const="nmake", help="Make Option: Generate only NMAKE Makefiles: Makefile")\r
1077     Parser.add_option("-g", "--gcc", action="store_const", dest="MakefileType", const="gmake", help="Make Option: Generate only GMAKE Makefiles: GNUmakefile")\r
1078     Parser.add_option("-l", "--all", action="store_const", dest="MakefileType", const="all", help="Make Option: Generate both NMAKE and GMAKE makefiles.")\r
1079 \r
1080     Parser.add_option("-w", "--warning-as-error", action="store_true", dest="WarningAsError", help="Treat warning in tools as error.")\r
1081     Parser.add_option("-j", "--log", action="store", dest="LogFile", help="Putlog in specified file as well as on console.")\r
1082     Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")\r
1083     Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\\r
1084                                                                                "including library instances selected, final dependency expression, "\\r
1085                                                                                "and warning messages, etc.")\r
1086     Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")\r
1087 \r
1088     (Opt, Args)=Parser.parse_args()\r
1089     return (Opt, Args)\r
1090 \r
1091 ## Tool entrance method\r
1092 #\r
1093 # This method mainly dispatch specific methods per the command line options.\r
1094 # If no error found, return zero value so the caller of this tool can know\r
1095 # if it's executed successfully or not.\r
1096 #\r
1097 #   @retval 0     Tool was successful\r
1098 #   @retval 1     Tool failed\r
1099 #\r
1100 def Main():\r
1101     StartTime = time.clock()\r
1102     #\r
1103     # Parse the options and args\r
1104     #\r
1105     (Option, Target) = MyOptionParser()\r
1106 \r
1107     if len(Target) == 0:\r
1108         Target = "all"\r
1109     elif len(Target) >= 2:\r
1110         EdkLogger.error("build", OPTION_NOT_SUPPORTED, "More than on targets are not supported.",\r
1111                         ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))\r
1112     else:\r
1113         Target = Target[0].lower()\r
1114 \r
1115     if Option.verbose != None:\r
1116         EdkLogger.SetLevel(EdkLogger.VERBOSE)\r
1117     elif Option.quiet != None:\r
1118         EdkLogger.SetLevel(EdkLogger.QUIET)\r
1119     elif Option.debug != None:\r
1120         EdkLogger.SetLevel(Option.debug + 1)\r
1121     else:\r
1122         EdkLogger.SetLevel(EdkLogger.INFO)\r
1123 \r
1124     if Option.LogFile != None:\r
1125         EdkLogger.SetLogFile(Option.LogFile)\r
1126 \r
1127     if Option.WarningAsError == True:\r
1128         EdkLogger.SetWarningAsError()\r
1129 \r
1130     EdkLogger.quiet(time.strftime("%H:%M:%S, %b.%d %Y ", time.localtime()) + "[00:00]" + "\n")\r
1131     ReturnCode = 0\r
1132     MyBuild = None\r
1133     try:\r
1134         #\r
1135         # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH\r
1136         #\r
1137         CheckEnvVariable()\r
1138         Workspace = os.getenv("WORKSPACE")\r
1139 \r
1140         WorkingDirectory = os.getcwd()\r
1141         if Option.ModuleFile != None:\r
1142             Option.ModuleFile = NormFile(Option.ModuleFile, Workspace)\r
1143         else:\r
1144             FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.inf')))\r
1145             FileNum = len(FileList)\r
1146             if FileNum >= 2:\r
1147                 EdkLogger.error("build", None, "There are %d INF files in %s.\n" % (FileNum, WorkingDirectory))\r
1148             elif FileNum == 1:\r
1149                 Option.ModuleFile = NormFile(FileList[0], Workspace)\r
1150 \r
1151         if Option.PlatformFile != None:\r
1152             Option.PlatformFile = NormFile(Option.PlatformFile, Workspace)\r
1153 \r
1154         if Option.FdfFile != None:\r
1155             Option.FdfFile = NormFile(Option.FdfFile, Workspace)\r
1156 \r
1157         MyBuild = Build(Target, Workspace, Option.PlatformFile, Option.ModuleFile, Option.TargetArch,\r
1158                         Option.ToolChain, Option.BuildTarget, Option.FdfFile, Option.RomImage, Option.FvImage,\r
1159                         Option.MakefileType, Option.SpawnMode, Option.ThreadNumber)\r
1160         MyBuild.Launch()\r
1161         MyBuild.DumpBuildData()\r
1162     except BaseException, X:\r
1163         if MyBuild != None:\r
1164             # for multi-thread build exits safely\r
1165             MyBuild.Relinquish()\r
1166         EdkLogger.quiet("")\r
1167         if Option != None and Option.debug != None:\r
1168             EdkLogger.quiet(traceback.format_exc())\r
1169         else:\r
1170             EdkLogger.quiet(str(X))\r
1171         ReturnCode = 1\r
1172     finally:\r
1173         Utils.Progressor.Abort()\r
1174 \r
1175     FinishTime = time.clock()\r
1176     BuildDuration = time.strftime("%M:%S", time.gmtime(int(round(FinishTime - StartTime))))\r
1177     EdkLogger.quiet("\n%s [%s]" % (time.strftime("%H:%M:%S, %b.%d %Y", time.localtime()), BuildDuration))\r
1178 \r
1179     return ReturnCode\r
1180 \r
1181 if __name__ == '__main__':\r
1182     sys.exit(Main())\r