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