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