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