f9f5f4164643b36a051f3bfbf1431499fcbf7141
[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", OPTION_MISSING, \r
725                                     ExtraData="There are %d DSC files in %s. Use '-p' to specify one.\n" % (FileNum, WorkingDirectory))\r
726                 elif FileNum == 1:\r
727                     self.PlatformFile = NormFile(FileList[0], self.WorkspaceDir)\r
728             else:\r
729                 self.PlatformFile = NormFile(self.PlatformFile, self.WorkspaceDir)\r
730 \r
731     ## Initialize build configuration\r
732     #\r
733     #   This method will parse DSC file and merge the configurations from\r
734     #   command line and target.txt, then get the final build configurations.\r
735     #\r
736     def InitBuild(self):\r
737         if self.PlatformFile == None or self.PlatformFile == "":\r
738             EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE,\r
739                             ExtraData="No active platform specified in target.txt or command line! Nothing can be built.\n")\r
740 \r
741         Wb = WorkspaceBuild(self.PlatformFile, self.WorkspaceDir)\r
742         PcdSet = {}\r
743         if self.Fdf != None:\r
744             self.Fdf = NormFile(self.Fdf, self.WorkspaceDir)\r
745             Wb.Fdf = self.Fdf\r
746         else:\r
747             self.Fdf = Wb.Fdf\r
748 \r
749         # parse FDF file to get PCD information\r
750         if self.Fdf != None and self.Fdf != "":\r
751             if Wb.Fdf == None or Wb.Fdf == "":\r
752                 Wb.Fdf = self.Fdf\r
753 \r
754             Wb.FdTargetList.extend(self.FdList)\r
755             Wb.FvTargetList.extend(self.FvList)\r
756         else:\r
757             PcdSet = {}\r
758             if self.FdList != []:\r
759                 EdkLogger.info("No flash definition file found. FD [%s] will be ignored." % " ".join(self.FdList))\r
760             if self.FvList != []:\r
761                 EdkLogger.info("No flash definition file found. FV [%s] will be ignored." % " ".join(self.FvList))\r
762 \r
763         Wb.TargetTxt = self.TargetTxt\r
764         Wb.ToolDef = self.ToolDef\r
765         #\r
766         # Merge Arch\r
767         #\r
768         if self.ArchList == None or len(self.ArchList) == 0:\r
769             ArchList = Wb.SupArchList\r
770         else:\r
771             ArchList = list(set(self.ArchList) & set(Wb.SupArchList))\r
772         if len(ArchList) == 0:\r
773             EdkLogger.error("build", PARAMETER_INVALID,\r
774                             ExtraData = "Active platform supports [%s] only, but [%s] is given."\r
775                                         % (" ".join(Wb.SupArchList), " ".join(self.ArchList)))\r
776         elif len(ArchList) != len(self.ArchList):\r
777             SkippedArchList = set(self.ArchList).symmetric_difference(set(Wb.SupArchList))\r
778             EdkLogger.verbose("\nArch [%s] is ignored because active platform supports [%s] but [%s] is specified !"\r
779                            % (" ".join(SkippedArchList), " ".join(Wb.SupArchList), " ".join(self.ArchList)))\r
780         self.ArchList = ArchList\r
781 \r
782         # Merge build target\r
783         if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
784             BuildTargetList = Wb.BuildTarget\r
785         else:\r
786             BuildTargetList = list(set(self.BuildTargetList) & set(Wb.BuildTarget))\r
787         if BuildTargetList == []:\r
788             EdkLogger.error("build", PARAMETER_INVALID, "Active platform only supports [%s], but [%s] is given"\r
789                                 % (" ".join(Wb.BuildTarget), " ".join(self.BuildTargetList)))\r
790         self.BuildTargetList = BuildTargetList\r
791 \r
792         self.Ewb = Wb\r
793 \r
794     ## Build a module or platform\r
795     #\r
796     # Create autogen code and makfile for a module or platform, and the launch\r
797     # "make" command to build it\r
798     #\r
799     #   @param  Target                      The target of build command\r
800     #   @param  Platform                    The platform file\r
801     #   @param  Module                      The module file\r
802     #   @param  BuildTarget                 The name of build target, one of "DEBUG", "RELEASE"\r
803     #   @param  ToolChain                   The name of toolchain to build\r
804     #   @param  Arch                        The arch of the module/platform\r
805     #   @param  CreateDepModuleCodeFile     Flag used to indicate creating code\r
806     #                                       for dependent modules/Libraries\r
807     #   @param  CreateDepModuleMakeFile     Flag used to indicate creating makefile\r
808     #                                       for dependent modules/Libraries\r
809     #\r
810     def _Build(self, Target, Platform, Module, BuildTarget, ToolChain, Arch, CreateDepModuleCodeFile=True, CreateDepModuleMakeFile=True):\r
811         # skip file generation for some targets\r
812         if Target not in ['clean', 'cleanlib', 'cleanall', 'run']:\r
813             #\r
814             # no need to generate code/makefile for dependent modules/libraries\r
815             # if the target is 'fds'\r
816             #\r
817             if Target == 'fds':\r
818                 CreateDepModuleCodeFile = False\r
819                 CreateDepModuleMakeFile = False\r
820                 CreateDepModuleAutoGenObject = True\r
821             else:\r
822                 CreateDepModuleAutoGenObject = False\r
823 \r
824             if Module != None:\r
825                 self.Progress.Start("Generating code/makefile for module")\r
826                 AutoGenResult = ModuleAutoGen.New(self.Ewb, Platform, Module, BuildTarget,\r
827                                                   ToolChain, Arch)\r
828             else:\r
829                 self.Progress.Start("Generating code/makefile for platform")\r
830                 AutoGenResult = PlatformAutoGen.New(self.Ewb, Platform, BuildTarget,\r
831                                                     ToolChain, Arch, CreateDepModuleAutoGenObject)\r
832             if AutoGenResult == None:\r
833                 return\r
834 \r
835             # for target which must generate AutoGen code and makefile\r
836             AutoGenResult.CreateCodeFile(CreateDepModuleCodeFile)\r
837             if Target == "genc":\r
838                 self.Progress.Stop("done!")\r
839                 return\r
840 \r
841             AutoGenResult.CreateMakeFile(CreateDepModuleMakeFile)\r
842             if Target == "genmake":\r
843                 self.Progress.Stop("done!")\r
844                 return\r
845 \r
846             self.Progress.Stop("done!")\r
847         else:\r
848             if Module != None:\r
849                 AutoGenResult = ModuleAutoGen.New(self.Ewb, Platform, Module, BuildTarget,\r
850                                                   ToolChain, Arch)\r
851             else:\r
852                 AutoGenResult = PlatformAutoGen.New(self.Ewb, Platform, BuildTarget,\r
853                                                     ToolChain, Arch, False)\r
854             if AutoGenResult == None:\r
855                 return\r
856 \r
857         EdkLogger.info("")\r
858         BuildCommand = AutoGenResult.GetBuildCommand()\r
859         if BuildCommand == None or len(BuildCommand) == 0:\r
860             EdkLogger.error("build", OPTION_MISSING, ExtraData="No MAKE command found for [%s, %s, %s]" % Key)\r
861 \r
862         BuildCommand = BuildCommand + (Target,)\r
863         LaunchCommand(BuildCommand, os.path.join(self.WorkspaceDir, AutoGenResult.GetMakeFileDir()))\r
864 \r
865     ## Build active platform for different build targets and different tool chains\r
866     #\r
867     def _BuildPlatform(self):\r
868         for BuildTarget in self.BuildTargetList:\r
869             for ToolChain in self.ToolChainList:\r
870                 self._Build(self.Target, self.PlatformFile, None, BuildTarget, ToolChain, self.ArchList)\r
871 \r
872     ## Build active module for different build targets, different tool chains and different archs\r
873     #\r
874     def _BuildModule(self):\r
875         for BuildTarget in self.BuildTargetList:\r
876             for ToolChain in self.ToolChainList:\r
877                 #\r
878                 # module build needs platform build information, so get platform\r
879                 # AutoGen first\r
880                 #\r
881                 if self.Target == 'fds':\r
882                     #\r
883                     # we need to re-generate the command in platform's Makefile\r
884                     # in case the user changed command line option for 'fds' target\r
885                     #\r
886                     PlatformAutoGen.New(self.Ewb, self.PlatformFile, BuildTarget, ToolChain, self.ArchList, True)\r
887                     self._Build("genmake", self.PlatformFile, None, BuildTarget, ToolChain, self.ArchList, False, False)\r
888                 else:\r
889                     PlatformAutoGen.New(self.Ewb, self.PlatformFile, BuildTarget, ToolChain, self.ArchList)\r
890 \r
891                 for Arch in self.ArchList:\r
892                     self._Build(self.Target, self.PlatformFile, self.ModuleFile, BuildTarget, ToolChain, Arch)\r
893 \r
894     ## Build a platform in multi-thread mode\r
895     #\r
896     def _MultiThreadBuildPlatform(self):\r
897         for BuildTarget in self.BuildTargetList:\r
898             for ToolChain in self.ToolChainList:\r
899                 Pa = PlatformAutoGen.New(self.Ewb, self.PlatformFile, BuildTarget, ToolChain, self.ArchList)\r
900                 if Pa == None:\r
901                     continue\r
902                 # multi-thread exit flag\r
903                 ExitFlag = threading.Event()\r
904                 ExitFlag.clear()\r
905                 for Arch in self.ArchList:\r
906                     for Module in Pa.Platform[Arch].Modules:\r
907                         # Get ModuleAutoGen object to generate C code file and makefile\r
908                         Ma = ModuleAutoGen.New(self.Ewb, self.PlatformFile, Module, BuildTarget, ToolChain, Arch)\r
909                         if Ma == None:\r
910                             continue\r
911                         # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'\r
912                         if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
913                             # for target which must generate AutoGen code and makefile\r
914                             Ma.CreateCodeFile(True)\r
915                             if self.Target == "genc":\r
916                                 continue\r
917 \r
918                             Ma.CreateMakeFile(True)\r
919                             if self.Target == "genmake":\r
920                                 continue\r
921                         # Generate build task for the module\r
922                         Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))\r
923                         # Break build if any build thread has error\r
924                         if BuildTask.HasError():\r
925                             # we need a full version of makefile for platform\r
926                             ExitFlag.set()\r
927                             BuildTask.WaitForComplete()\r
928                             Pa.CreateModuleAutoGen()\r
929                             Pa.CreateMakeFile(False)\r
930                             EdkLogger.error("build", BUILD_ERROR, BuildTask.GetErrorMessage())\r
931                         # Start task scheduler\r
932                         if not BuildTask.IsOnGoing():\r
933                             BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)\r
934 \r
935                 #\r
936                 # All modules have been put in build tasks queue. Tell task scheduler\r
937                 # to exit if all tasks are completed\r
938                 #\r
939                 ExitFlag.set()\r
940                 BuildTask.WaitForComplete()\r
941                 # in case there's an interruption. we need a full version of makefile for platform\r
942                 Pa.CreateModuleAutoGen()\r
943                 Pa.CreateMakeFile(False)\r
944                 if BuildTask.HasError():\r
945                     EdkLogger.error("build", BUILD_ERROR, BuildTask.GetErrorMessage())\r
946 \r
947                 # Generate FD image if there's a FDF file found\r
948                 if self.Fdf != '' and self.Target in ["", "all", "fds"]:\r
949                     LaunchCommand(Pa.GetBuildCommand() + ("fds",), Pa.GetMakeFileDir())\r
950 \r
951     ## Generate GuidedSectionTools.txt in the FV directories.\r
952     #\r
953     def CreateGuidedSectionToolsFile(self):\r
954         for Dsc in self.Ewb.DscDatabase.keys():\r
955             for Arch in self.ArchList:\r
956                 for BuildTarget in self.BuildTargetList:\r
957                     for ToolChain in self.ToolChainList:\r
958                         FvDir = os.path.join(\r
959                                     self.WorkspaceDir,\r
960                                     self.Ewb.Build[Arch].PlatformDatabase[Dsc].OutputDirectory,\r
961                                     '_'.join((BuildTarget, ToolChain)),\r
962                                     'FV'\r
963                                     )\r
964                         if not os.path.exists(FvDir):\r
965                             continue \r
966                         # Build up the list of supported architectures for this build\r
967                         prefix = '%s_%s_%s_' % (BuildTarget, ToolChain, Arch)\r
968 \r
969                         # Look through the tool definitions for GUIDed tools\r
970                         guidAttribs = []\r
971                         for (attrib, value) in self.ToolDef.ToolsDefTxtDictionary.iteritems():\r
972                             if attrib.upper().endswith('_GUID'):\r
973                                 split = attrib.split('_')\r
974                                 thisPrefix = '_'.join(split[0:3]) + '_'\r
975                                 if thisPrefix == prefix:\r
976                                     guid = self.ToolDef.ToolsDefTxtDictionary[attrib]\r
977                                     guid = guid.lower()\r
978                                     toolName = split[3]\r
979                                     path = '_'.join(split[0:4]) + '_PATH'\r
980                                     path = self.ToolDef.ToolsDefTxtDictionary[path]\r
981                                     path = self.GetFullPathOfTool(path)\r
982                                     guidAttribs.append((guid, toolName, path))\r
983 \r
984                         # Write out GuidedSecTools.txt\r
985                         toolsFile = os.path.join(FvDir, 'GuidedSectionTools.txt')\r
986                         toolsFile = open(toolsFile, 'wt')\r
987                         for guidedSectionTool in guidAttribs:\r
988                             print >> toolsFile, ' '.join(guidedSectionTool)\r
989                         toolsFile.close()\r
990 \r
991     ## Returns the full path of the tool.\r
992     #\r
993     def GetFullPathOfTool (self, tool):\r
994         if os.path.exists(tool):\r
995             return os.path.realpath(tool)\r
996         else:\r
997             # We need to search for the tool using the\r
998             # PATH environment variable.\r
999             for dirInPath in os.environ['PATH'].split(os.pathsep):\r
1000                 foundPath = os.path.join(dirInPath, tool)\r
1001                 if os.path.exists(foundPath):\r
1002                     return os.path.realpath(foundPath)\r
1003 \r
1004         # If the tool was not found in the path then we just return\r
1005         # the input tool.\r
1006         return tool\r
1007 \r
1008     ## Launch the module or platform build\r
1009     #\r
1010     def Launch(self):\r
1011         if self.ModuleFile == None or self.ModuleFile == "":\r
1012             if not self.SpawnMode or self.Target not in ["", "all"]:\r
1013                 self.SpawnMode = False\r
1014                 self._BuildPlatform()\r
1015             else:\r
1016                 self._MultiThreadBuildPlatform()\r
1017             self.CreateGuidedSectionToolsFile()\r
1018         else:\r
1019             self.SpawnMode = False\r
1020             self._BuildModule()\r
1021 \r
1022     ## Do some clean-up works when error occurred\r
1023     def Relinquish(self):\r
1024         self.DumpBuildData()\r
1025         Utils.Progressor.Abort()\r
1026         if self.SpawnMode == True:\r
1027             BuildTask.Abort()\r
1028 \r
1029     def DumpBuildData(self):\r
1030         CacheDirectory = os.path.join(self.WorkspaceDir, gBuildCacheDir)\r
1031         Utils.CreateDirectory(CacheDirectory)\r
1032         Utils.DataDump(Utils.gFileTimeStampCache, os.path.join(CacheDirectory, "gFileTimeStampCache"))\r
1033         Utils.DataDump(Utils.gDependencyDatabase, os.path.join(CacheDirectory, "gDependencyDatabase"))\r
1034 \r
1035     def RestoreBuildData(self):\r
1036         FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gFileTimeStampCache")\r
1037         if Utils.gFileTimeStampCache == {} and os.path.isfile(FilePath):\r
1038             Utils.gFileTimeStampCache = Utils.DataRestore(FilePath)\r
1039             if Utils.gFileTimeStampCache == None:\r
1040                 Utils.gFileTimeStampCache = {}\r
1041         \r
1042         FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gDependencyDatabase")\r
1043         if Utils.gDependencyDatabase == {} and os.path.isfile(FilePath):\r
1044             Utils.gDependencyDatabase = Utils.DataRestore(FilePath)\r
1045             if Utils.gDependencyDatabase == None:\r
1046                 Utils.gDependencyDatabase = {}\r
1047 \r
1048 ## Parse command line options\r
1049 #\r
1050 # Using standard Python module optparse to parse command line option of this tool.\r
1051 #\r
1052 #   @retval Opt   A optparse.Values object containing the parsed options\r
1053 #   @retval Args  Target of build command\r
1054 #\r
1055 def MyOptionParser():\r
1056     Parser = OptionParser(description=__copyright__,version=__version__,prog="build.exe",usage="%prog [options] [target]")\r
1057     Parser.add_option("-a", "--arch", action="append", type="choice", choices=['IA32','X64','IPF','EBC'], dest="TargetArch",\r
1058         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
1059     Parser.add_option("-p", "--platform", action="store", type="string", dest="PlatformFile",\r
1060         help="Build the platform specified by the DSC file name argument, overrides target.txt's ACTIVE_PLATFORM definition.")\r
1061     Parser.add_option("-m", "--module", action="store", type="string", dest="ModuleFile",\r
1062         help="Build the module specified by the INF file name argument.")\r
1063     Parser.add_option("-b", "--buildtarget", action="append", type="choice", choices=['DEBUG','RELEASE'], dest="BuildTarget",\r
1064         help="BuildTarget is one of list: DEBUG, RELEASE, which overrides target.txt's TARGET definition. To specify more TARGET, please repeat this option.")\r
1065     Parser.add_option("-t", "--tagname", action="append", type="string", dest="ToolChain",\r
1066         help="Using the Tool Chain Tagname to build the platform, overrides target.txt's TOOL_CHAIN_TAG definition.")\r
1067     Parser.add_option("-s", "--spawn", action="store_true", type=None, dest="SpawnMode",\r
1068         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
1069     Parser.add_option("-n", action="store", type="int", dest="ThreadNumber",\r
1070         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
1071     Parser.add_option("-f", "--fdf", action="store", type="string", dest="FdfFile",\r
1072         help="The name of the FDF file to use, which overrides the setting in the DSC file.")\r
1073     Parser.add_option("-r", "--rom-image", action="append", type="string", dest="RomImage", default=[],\r
1074         help="The name of FD to be generated. The name must be from [FD] section in FDF file.")\r
1075     Parser.add_option("-i", "--fv-image", action="append", type="string", dest="FvImage", default=[],\r
1076         help="The name of FV to be generated. The name must be from [FV] section in FDF file.")\r
1077     Parser.add_option("-k", "--msft", action="store_const", dest="MakefileType", const="nmake", help="Make Option: Generate only NMAKE Makefiles: Makefile")\r
1078     Parser.add_option("-g", "--gcc", action="store_const", dest="MakefileType", const="gmake", help="Make Option: Generate only GMAKE Makefiles: GNUmakefile")\r
1079     Parser.add_option("-l", "--all", action="store_const", dest="MakefileType", const="all", help="Make Option: Generate both NMAKE and GMAKE makefiles.")\r
1080 \r
1081     Parser.add_option("-w", "--warning-as-error", action="store_true", dest="WarningAsError", help="Treat warning in tools as error.")\r
1082     Parser.add_option("-j", "--log", action="store", dest="LogFile", help="Putlog in specified file as well as on console.")\r
1083     Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")\r
1084     Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\\r
1085                                                                                "including library instances selected, final dependency expression, "\\r
1086                                                                                "and warning messages, etc.")\r
1087     Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")\r
1088 \r
1089     (Opt, Args)=Parser.parse_args()\r
1090     return (Opt, Args)\r
1091 \r
1092 ## Tool entrance method\r
1093 #\r
1094 # This method mainly dispatch specific methods per the command line options.\r
1095 # If no error found, return zero value so the caller of this tool can know\r
1096 # if it's executed successfully or not.\r
1097 #\r
1098 #   @retval 0     Tool was successful\r
1099 #   @retval 1     Tool failed\r
1100 #\r
1101 def Main():\r
1102     StartTime = time.clock()\r
1103     #\r
1104     # Parse the options and args\r
1105     #\r
1106     (Option, Target) = MyOptionParser()\r
1107 \r
1108     if len(Target) == 0:\r
1109         Target = "all"\r
1110     elif len(Target) >= 2:\r
1111         EdkLogger.error("build", OPTION_NOT_SUPPORTED, "More than on targets are not supported.",\r
1112                         ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))\r
1113     else:\r
1114         Target = Target[0].lower()\r
1115 \r
1116     if Option.verbose != None:\r
1117         EdkLogger.SetLevel(EdkLogger.VERBOSE)\r
1118     elif Option.quiet != None:\r
1119         EdkLogger.SetLevel(EdkLogger.QUIET)\r
1120     elif Option.debug != None:\r
1121         EdkLogger.SetLevel(Option.debug + 1)\r
1122     else:\r
1123         EdkLogger.SetLevel(EdkLogger.INFO)\r
1124 \r
1125     if Option.LogFile != None:\r
1126         EdkLogger.SetLogFile(Option.LogFile)\r
1127 \r
1128     if Option.WarningAsError == True:\r
1129         EdkLogger.SetWarningAsError()\r
1130 \r
1131     EdkLogger.quiet(time.strftime("%H:%M:%S, %b.%d %Y ", time.localtime()) + "[00:00]" + "\n")\r
1132     ReturnCode = 0\r
1133     MyBuild = None\r
1134     try:\r
1135         #\r
1136         # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH\r
1137         #\r
1138         CheckEnvVariable()\r
1139         Workspace = os.getenv("WORKSPACE")\r
1140 \r
1141         WorkingDirectory = os.getcwd()\r
1142         if Option.ModuleFile != None:\r
1143             Option.ModuleFile = NormFile(Option.ModuleFile, Workspace)\r
1144         else:\r
1145             FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.inf')))\r
1146             FileNum = len(FileList)\r
1147             if FileNum >= 2:\r
1148                 EdkLogger.error("build", None, "There are %d INF files in %s.\n" % (FileNum, WorkingDirectory))\r
1149             elif FileNum == 1:\r
1150                 Option.ModuleFile = NormFile(FileList[0], Workspace)\r
1151 \r
1152         if Option.PlatformFile != None:\r
1153             Option.PlatformFile = NormFile(Option.PlatformFile, Workspace)\r
1154 \r
1155         if Option.FdfFile != None:\r
1156             Option.FdfFile = NormFile(Option.FdfFile, Workspace)\r
1157 \r
1158         MyBuild = Build(Target, Workspace, Option.PlatformFile, Option.ModuleFile, Option.TargetArch,\r
1159                         Option.ToolChain, Option.BuildTarget, Option.FdfFile, Option.RomImage, Option.FvImage,\r
1160                         Option.MakefileType, Option.SpawnMode, Option.ThreadNumber)\r
1161         MyBuild.Launch()\r
1162         MyBuild.DumpBuildData()\r
1163     except BaseException, X:\r
1164         EdkLogger.SetLevel(EdkLogger.QUIET)\r
1165         if MyBuild != None:\r
1166             # for multi-thread build exits safely\r
1167             MyBuild.Relinquish()\r
1168         EdkLogger.quiet("")\r
1169         if Option != None and Option.debug != None:\r
1170             EdkLogger.quiet(traceback.format_exc())\r
1171         else:\r
1172             EdkLogger.quiet(str(X))\r
1173         ReturnCode = 1\r
1174     finally:\r
1175         Utils.Progressor.Abort()\r
1176 \r
1177     FinishTime = time.clock()\r
1178     BuildDuration = time.strftime("%M:%S", time.gmtime(int(round(FinishTime - StartTime))))\r
1179     EdkLogger.quiet("\n%s [%s]" % (time.strftime("%H:%M:%S, %b.%d %Y", time.localtime()), BuildDuration))\r
1180 \r
1181     return ReturnCode\r
1182 \r
1183 if __name__ == '__main__':\r
1184     sys.exit(Main())\r