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