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