Added more error checks
[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, BuildComamnd, Target, Dependency, WorkingDir="."):\r
264         self.BuildObject = Obj\r
265         self.Dependency = Dependency\r
266         self.WorkingDir = WorkingDir\r
267         self.Target = Target\r
268         self.BuildCommand = BuildComamnd\r
269         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 \r
389     ## Scheduler method\r
390     #\r
391     #   @param  MaxThreadNumber     The maximum thread number\r
392     #   @param  ExitFlag            Flag used to end the scheduler\r
393     #\r
394     @staticmethod\r
395     def Scheduler(MaxThreadNumber, ExitFlag):\r
396         BuildTask._SchedulerStopped.clear()\r
397         try:\r
398             # use BoundedSemaphore to control the maximum running threads\r
399             BuildTask._Thread = BoundedSemaphore(MaxThreadNumber)\r
400             #\r
401             # scheduling loop, which will exits when no pending/ready task and\r
402             # indicated to do so, or there's error in running thread\r
403             #\r
404             while (len(BuildTask._PendingQueue) > 0 or len(BuildTask._ReadyQueue) > 0 \\r
405                    or not ExitFlag.isSet()) and not BuildTask._ErrorFlag.isSet():\r
406                 EdkLogger.debug(EdkLogger.DEBUG_8, "Pending Queue (%d), Ready Queue (%d)"\r
407                                 % (len(BuildTask._PendingQueue), len(BuildTask._ReadyQueue)))\r
408                 # get all pending tasks\r
409                 BuildTask._PendingQueueLock.acquire()\r
410                 BuildObjectList = BuildTask._PendingQueue.keys()\r
411                 BuildTask._PendingQueueLock.release()\r
412 \r
413                 #\r
414                 # check if their dependency is resolved, and if true, move them\r
415                 # into ready queue\r
416                 #\r
417                 for BuildObject in BuildObjectList:\r
418                     Bt = BuildTask._PendingQueue[BuildObject]\r
419                     if Bt.IsReady():\r
420                         BuildTask._PendingQueueLock.acquire()\r
421                         BuildTask._ReadyQueue[BuildObject] = BuildTask._PendingQueue.pop(BuildObject)\r
422                         BuildTask._PendingQueueLock.release()\r
423 \r
424                 # launch build thread until the maximum number of threads is reached\r
425                 while not BuildTask._ErrorFlag.isSet():\r
426                     # empty ready queue, do nothing further\r
427                     if len(BuildTask._ReadyQueue) == 0:\r
428                         break\r
429 \r
430                     # wait for active thread(s) exit\r
431                     BuildTask._Thread.acquire(True)\r
432 \r
433                     # start a new build thread\r
434                     Bo = BuildTask._ReadyQueue.keys()[0]\r
435                     Bt = BuildTask._ReadyQueue.pop(Bo)\r
436 \r
437                     # move into running queue\r
438                     BuildTask._RunningQueueLock.acquire()\r
439                     BuildTask._RunningQueue[Bo] = Bt\r
440                     BuildTask._RunningQueueLock.release()\r
441 \r
442                     Bt.Start()\r
443 \r
444                 # avoid tense loop\r
445                 time.sleep(0.01)\r
446 \r
447             # wait for all running threads exit\r
448             if BuildTask._ErrorFlag.isSet():\r
449                 EdkLogger.quiet("\nWaiting for all build threads exit...")\r
450             # while not BuildTask._ErrorFlag.isSet() and \\r
451             while len(BuildTask._RunningQueue) > 0:\r
452                 EdkLogger.verbose("Waiting for thread ending...(%d)" % len(BuildTask._RunningQueue))\r
453                 EdkLogger.debug(EdkLogger.DEBUG_8, "Threads [%s]" % ", ".join([Th.getName() for Th in threading.enumerate()]))\r
454                 # avoid tense loop\r
455                 time.sleep(0.1)\r
456         except BaseException, X:\r
457             #\r
458             # TRICK: hide the output of threads left runing, so that the user can\r
459             #        catch the error message easily\r
460             #\r
461             EdkLogger.SetLevel(EdkLogger.QUIET)\r
462             BuildTask._ErrorFlag.set()\r
463             BuildTask._ErrorMessage = "build thread scheduler error\n\t%s" % str(X)\r
464 \r
465         BuildTask._PendingQueue.clear()\r
466         BuildTask._ReadyQueue.clear()\r
467         BuildTask._RunningQueue.clear()\r
468         BuildTask._TaskQueue.clear()\r
469         BuildTask._SchedulerStopped.set()\r
470 \r
471     ## Wait for all running method exit\r
472     #\r
473     @staticmethod\r
474     def WaitForComplete():\r
475         BuildTask._SchedulerStopped.wait()\r
476 \r
477     ## Check if the scheduler is running or not\r
478     #\r
479     @staticmethod\r
480     def IsOnGoing():\r
481         return not BuildTask._SchedulerStopped.isSet()\r
482 \r
483     ## Abort the build\r
484     @staticmethod\r
485     def Abort():\r
486         if BuildTask.IsOnGoing():\r
487             BuildTask._ErrorFlag.set()\r
488             BuildTask.WaitForComplete()\r
489 \r
490     ## Check if there's error in running thread\r
491     #\r
492     #   Since the main thread cannot catch exceptions in other thread, we have to\r
493     #   use threading.Event to communicate this formation to main thread.\r
494     #\r
495     @staticmethod\r
496     def HasError():\r
497         return BuildTask._ErrorFlag.isSet()\r
498 \r
499     ## Get error message in running thread\r
500     #\r
501     #   Since the main thread cannot catch exceptions in other thread, we have to\r
502     #   use a static variable to communicate this message to main thread.\r
503     #\r
504     @staticmethod\r
505     def GetErrorMessage():\r
506         return BuildTask._ErrorMessage\r
507 \r
508     ## Factory method to create a BuildTask object\r
509     #\r
510     #   This method will check if a module is building or has been built. And if\r
511     #   true, just return the associated BuildTask object in the _TaskQueue. If\r
512     #   not, create and return a new BuildTask object. The new BuildTask object\r
513     #   will be appended to the _PendingQueue for scheduling later.\r
514     #\r
515     #   @param  BuildItem       A BuildUnit object representing a build object\r
516     #   @param  Dependency      The dependent build object of BuildItem\r
517     #\r
518     @staticmethod\r
519     def New(BuildItem, Dependency=None):\r
520         if BuildItem in BuildTask._TaskQueue:\r
521             Bt = BuildTask._TaskQueue[BuildItem]\r
522             return Bt\r
523 \r
524         Bt = BuildTask()\r
525         Bt._Init(BuildItem, Dependency)\r
526         BuildTask._TaskQueue[BuildItem] = Bt\r
527 \r
528         BuildTask._PendingQueueLock.acquire()\r
529         BuildTask._PendingQueue[BuildItem] = Bt\r
530         BuildTask._PendingQueueLock.release()\r
531 \r
532         return Bt\r
533 \r
534     ## The real constructor of BuildTask\r
535     #\r
536     #   @param  BuildItem       A BuildUnit object representing a build object\r
537     #   @param  Dependency      The dependent build object of BuildItem\r
538     #\r
539     def _Init(self, BuildItem, Dependency=None):\r
540         self.BuildItem = BuildItem\r
541 \r
542         self.DependencyList = []\r
543         if Dependency == None:\r
544             Dependency = BuildItem.Dependency\r
545         else:\r
546             Dependency.extend(BuildItem.Dependency)\r
547         self.AddDependency(Dependency)\r
548         # flag indicating build completes, used to avoid unnecessary re-build\r
549         self.CompleteFlag = False\r
550 \r
551     ## Check if all dependent build tasks are completed or not\r
552     #\r
553     def IsReady(self):\r
554         ReadyFlag = True\r
555         for Dep in self.DependencyList:\r
556             if Dep.CompleteFlag == True:\r
557                 continue\r
558             ReadyFlag = False\r
559             break\r
560 \r
561         return ReadyFlag\r
562 \r
563     ## Add dependent build task\r
564     #\r
565     #   @param  Dependency      The list of dependent build objects\r
566     #\r
567     def AddDependency(self, Dependency):\r
568         for Dep in Dependency:\r
569             self.DependencyList.append(BuildTask.New(Dep))    # BuildTask list\r
570 \r
571     ## The thread wrapper of LaunchCommand function\r
572     #\r
573     # @param  Command               A list or string contains the call of the command\r
574     # @param  WorkingDir            The directory in which the program will be running\r
575     #\r
576     def _CommandThread(self, Command, WorkingDir):\r
577         try:\r
578             LaunchCommand(Command, WorkingDir)\r
579             self.CompleteFlag = True\r
580         except:\r
581             #\r
582             # TRICK: hide the output of threads left runing, so that the user can\r
583             #        catch the error message easily\r
584             #\r
585             EdkLogger.SetLevel(EdkLogger.QUIET)\r
586             BuildTask._ErrorFlag.set()\r
587             BuildTask._ErrorMessage = "%s broken\n    %s [%s]" % \\r
588                                       (threading.currentThread().getName(), Command, WorkingDir)\r
589         # indicate there's a thread is available for another build task\r
590         BuildTask._RunningQueueLock.acquire()\r
591         BuildTask._RunningQueue.pop(self.BuildItem)\r
592         BuildTask._RunningQueueLock.release()\r
593         BuildTask._Thread.release()\r
594 \r
595     ## Start build task thread\r
596     #\r
597     def Start(self):\r
598         Command = self.BuildItem.BuildCommand + (self.BuildItem.Target,)\r
599         self.BuildTread = Thread(target=self._CommandThread, args=(Command, self.BuildItem.WorkingDir))\r
600         self.BuildTread.setName("build thread")\r
601         self.BuildTread.setDaemon(False)\r
602         self.BuildTread.start()\r
603 \r
604 ## The class implementing the EDK2 build process\r
605 #\r
606 #   The build process includes:\r
607 #       1. Load configuration from target.txt and tools_def.txt in $(WORKSPACE)/Conf\r
608 #       2. Parse DSC file of active platform\r
609 #       3. Parse FDF file if any\r
610 #       4. Establish build database, including parse all other files (module, package)\r
611 #       5. Create AutoGen files (C code file, depex file, makefile) if necessary\r
612 #       6. Call build command\r
613 #\r
614 class Build():\r
615     ## Constructor\r
616     #\r
617     # Constructor will load all necessary configurations, parse platform, modules\r
618     # and packages and the establish a database for AutoGen.\r
619     #\r
620     #   @param  Target              The build command target, one of gSupportedTarget\r
621     #   @param  WorkspaceDir        The directory of workspace\r
622     #   @param  Platform            The DSC file of active platform\r
623     #   @param  Module              The INF file of active module, if any\r
624     #   @param  Arch                The Arch list of platform or module\r
625     #   @param  ToolChain           The name list of toolchain\r
626     #   @param  BuildTarget         The "DEBUG" or "RELEASE" build\r
627     #   @param  FlashDefinition     The FDF file of active platform\r
628     #   @param  FdList=[]           The FD names to be individually built\r
629     #   @param  FvList=[]           The FV names to be individually built\r
630     #   @param  MakefileType        The type of makefile (for MSFT make or GNU make)\r
631     #   @param  SpawnMode           Indicate multi-thread build mode\r
632     #   @param  ThreadNumber        The maximum number of thread if in multi-thread build mode\r
633     #\r
634     def __init__(self, Target, WorkspaceDir, Platform, Module, Arch, ToolChain, \r
635                  BuildTarget, FlashDefinition, FdList=[], FvList=[], \r
636                  MakefileType="nmake", SpawnMode=False, ThreadNumber=2,\r
637                  SkipAutoGen=False, Reparse=False, SkuId=None):\r
638 \r
639         self.WorkspaceDir = WorkspaceDir\r
640         os.chdir(self.WorkspaceDir)\r
641 \r
642         self.Target         = Target\r
643         self.PlatformFile   = Platform\r
644         self.ModuleFile     = Module\r
645         self.ArchList       = Arch\r
646         self.ToolChainList  = ToolChain\r
647         self.BuildTargetList= BuildTarget\r
648         self.Fdf            = FlashDefinition\r
649         self.FdList         = FdList\r
650         self.FvList         = FvList\r
651         self.MakefileType   = MakefileType\r
652         self.SpawnMode      = SpawnMode\r
653         self.ThreadNumber   = ThreadNumber\r
654         self.SkipAutoGen    = SkipAutoGen\r
655         self.Reparse        = Reparse\r
656         self.SkuId          = SkuId\r
657 \r
658         self.TargetTxt      = TargetTxtClassObject()\r
659         self.ToolDef        = ToolDefClassObject()\r
660         self.Db             = WorkspaceDatabase(None, GlobalData.gGlobalDefines, self.Reparse)\r
661         self.BuildDatabase  = self.Db.BuildObject\r
662         self.Platform       = None\r
663 \r
664         # print dot charater during doing some time-consuming work\r
665         self.Progress = Utils.Progressor()\r
666 \r
667         # parse target.txt, tools_def.txt, and platform file\r
668         #self.RestoreBuildData()\r
669         self.LoadConfiguration()\r
670         self.InitBuild()\r
671 \r
672         # print current build environment and configuration\r
673         EdkLogger.quiet("%-24s = %s" % ("WORKSPACE", os.environ["WORKSPACE"]))\r
674         EdkLogger.quiet("%-24s = %s" % ("EDK_SOURCE", os.environ["EDK_SOURCE"]))\r
675         EdkLogger.quiet("%-24s = %s" % ("EFI_SOURCE", os.environ["EFI_SOURCE"]))\r
676         EdkLogger.quiet("%-24s = %s" % ("EDK_TOOLS_PATH", os.environ["EDK_TOOLS_PATH"]))\r
677 \r
678         EdkLogger.info('\n%-24s = %s' % ("TARGET_ARCH", ' '.join(self.ArchList)))\r
679         EdkLogger.info('%-24s = %s' % ("TARGET", ' '.join(self.BuildTargetList)))\r
680         EdkLogger.info('%-24s = %s' % ("TOOL_CHAIN_TAG", ' '.join(self.ToolChainList)))\r
681 \r
682         EdkLogger.info('\n%-24s = %s' % ("Active Platform", self.PlatformFile))\r
683 \r
684         if self.Fdf != None and self.Fdf != "":\r
685             EdkLogger.info('%-24s = %s' % ("Flash Image Definition", self.Fdf))\r
686 \r
687         if self.ModuleFile != None and self.ModuleFile != "":\r
688             EdkLogger.info('%-24s = %s' % ("Active Module", self.ModuleFile))\r
689 \r
690         if self.SpawnMode:\r
691             EdkLogger.verbose('%-24s = %s' % ("Max Thread Number", self.ThreadNumber))\r
692 \r
693         self.Progress.Start("\nProcessing meta-data")\r
694 \r
695     ## Load configuration\r
696     #\r
697     #   This method will parse target.txt and get the build configurations.\r
698     #\r
699     def LoadConfiguration(self):\r
700         #\r
701         # Check target.txt and tools_def.txt and Init them\r
702         #\r
703         BuildConfigurationFile = os.path.normpath(os.path.join(self.WorkspaceDir, gBuildConfiguration))\r
704         if os.path.isfile(BuildConfigurationFile) == True:\r
705             StatusCode = self.TargetTxt.LoadTargetTxtFile(BuildConfigurationFile)\r
706 \r
707             ToolDefinitionFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_CONF]\r
708             ToolDefinitionFile = os.path.normpath(os.path.join(self.WorkspaceDir, ToolDefinitionFile))\r
709             if os.path.isfile(ToolDefinitionFile) == True:\r
710                 StatusCode = self.ToolDef.LoadToolDefFile(ToolDefinitionFile)\r
711             else:\r
712                 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=ToolDefinitionFile)\r
713         else:\r
714             EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=BuildConfigurationFile)\r
715 \r
716         # if no ARCH given in command line, get it from target.txt\r
717         if self.ArchList == None or len(self.ArchList) == 0:\r
718             self.ArchList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET_ARCH]\r
719             if len(self.ArchList) == 0:\r
720                 self.ArchList = ARCH_LIST\r
721 \r
722         # if no build target given in command line, get it from target.txt\r
723         if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
724             self.BuildTargetList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET]\r
725             if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
726                 self.BuildTargetList = ['DEBUG', 'RELEASE']\r
727 \r
728         # if no tool chain given in command line, get it from target.txt\r
729         if self.ToolChainList == None or len(self.ToolChainList) == 0:\r
730             self.ToolChainList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_TAG]\r
731             if self.ToolChainList == None or len(self.ToolChainList) == 0:\r
732                 EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, ExtraData="No toolchain given. Don't know how to build.\n")\r
733 \r
734         # check if the tool chains are defined or not\r
735         NewToolChainList = []\r
736         for ToolChain in self.ToolChainList:\r
737             if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG]:\r
738                 EdkLogger.warn("build", "Tool chain [%s] is not defined" % ToolChain)\r
739             else:\r
740                 NewToolChainList.append(ToolChain)\r
741         # if no tool chain available, break the build\r
742         if len(NewToolChainList) == 0:\r
743             EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,\r
744                             ExtraData="[%s] not defined. No toolchain available for build!\n" % ", ".join(self.ToolChainList))\r
745         else:\r
746             self.ToolChainList = NewToolChainList\r
747 \r
748         if self.ThreadNumber == None or self.ThreadNumber == "":\r
749             self.ThreadNumber = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER]\r
750             if self.ThreadNumber == '':\r
751                 self.ThreadNumber = 0\r
752             else:\r
753                 self.ThreadNumber = int(self.ThreadNumber, 0)\r
754 \r
755         if self.ThreadNumber == 0:\r
756             self.SpawnMode = False\r
757         elif self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_MULTIPLE_THREAD].lower() in ["enable", "true"]:\r
758             self.SpawnMode = True\r
759 \r
760         if self.PlatformFile == None:\r
761             self.PlatformFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_ACTIVE_PLATFORM]\r
762             if self.PlatformFile == None or self.PlatformFile == "":\r
763                 WorkingDirectory = os.getcwd()\r
764                 FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.dsc')))\r
765                 FileNum = len(FileList)\r
766                 if FileNum >= 2:\r
767                     EdkLogger.error("build", OPTION_MISSING, \r
768                                     ExtraData="There are %d DSC files in %s. Use '-p' to specify one.\n" % (FileNum, WorkingDirectory))\r
769                 elif FileNum == 1:\r
770                     self.PlatformFile = NormFile(FileList[0], self.WorkspaceDir)\r
771             else:\r
772                 self.PlatformFile = NormFile(self.PlatformFile, self.WorkspaceDir)\r
773 \r
774     ## Initialize build configuration\r
775     #\r
776     #   This method will parse DSC file and merge the configurations from\r
777     #   command line and target.txt, then get the final build configurations.\r
778     #\r
779     def InitBuild(self):\r
780         if self.PlatformFile == None or self.PlatformFile == "":\r
781             EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE,\r
782                             ExtraData="No active platform specified in target.txt or command line! Nothing can be built.\n")\r
783         if not os.path.isfile(self.PlatformFile):\r
784             EdkLogger.error("AutoGen", FILE_NOT_FOUND, ExtraData = self.PlatformFile)\r
785 \r
786         # create metafile database\r
787         self.Db.InitDatabase()\r
788 \r
789         # we need information in platform description file to determine how to build\r
790         self.Platform = self.BuildDatabase[self.PlatformFile, 'COMMON']\r
791         if self.Fdf != None:\r
792             self.Fdf = NormFile(self.Fdf, self.WorkspaceDir)\r
793         else:\r
794             self.Fdf = self.Platform.FlashDefinition\r
795 \r
796         if self.SkuId == None or self.SkuId == '':\r
797             self.SkuId = self.Platform.SkuName\r
798 \r
799         # check FD/FV build target\r
800         if self.Fdf == None or self.Fdf == "":\r
801             if self.FdList != []:\r
802                 EdkLogger.info("No flash definition file found. FD [%s] will be ignored." % " ".join(self.FdList))\r
803                 self.FdList = []\r
804             if self.FvList != []:\r
805                 EdkLogger.info("No flash definition file found. FV [%s] will be ignored." % " ".join(self.FvList))\r
806                 self.FvList = []\r
807 \r
808         #\r
809         # Merge Arch\r
810         #\r
811         if self.ArchList == None or len(self.ArchList) == 0:\r
812             ArchList = set(self.Platform.SupArchList)\r
813         else:\r
814             ArchList = set(self.ArchList) & set(self.Platform.SupArchList)\r
815         if len(ArchList) == 0:\r
816             EdkLogger.error("build", PARAMETER_INVALID,\r
817                             ExtraData = "Active platform supports [%s] only, but [%s] is given."\r
818                                         % (" ".join(self.Platform.SupArchList), " ".join(self.ArchList)))\r
819         elif len(ArchList) != len(self.ArchList):\r
820             SkippedArchList = set(self.ArchList).symmetric_difference(set(self.Platform.SupArchList))\r
821             EdkLogger.verbose("\nArch [%s] is ignored because active platform supports [%s] but [%s] is specified !"\r
822                            % (" ".join(SkippedArchList), " ".join(self.Platform.SupArchList), " ".join(self.ArchList)))\r
823         self.ArchList = tuple(ArchList)\r
824 \r
825         # Merge build target\r
826         if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
827             BuildTargetList = self.Platform.BuildTarget\r
828         else:\r
829             BuildTargetList = list(set(self.BuildTargetList) & set(self.Platform.BuildTargets))\r
830         if BuildTargetList == []:\r
831             EdkLogger.error("build", PARAMETER_INVALID, "Active platform only supports [%s], but [%s] is given"\r
832                                 % (" ".join(self.Platform.BuildTargets), " ".join(self.BuildTargetList)))\r
833         self.BuildTargetList = BuildTargetList\r
834 \r
835     ## Build a module or platform\r
836     #\r
837     # Create autogen code and makfile for a module or platform, and the launch\r
838     # "make" command to build it\r
839     #\r
840     #   @param  Target                      The target of build command\r
841     #   @param  Platform                    The platform file\r
842     #   @param  Module                      The module file\r
843     #   @param  BuildTarget                 The name of build target, one of "DEBUG", "RELEASE"\r
844     #   @param  ToolChain                   The name of toolchain to build\r
845     #   @param  Arch                        The arch of the module/platform\r
846     #   @param  CreateDepModuleCodeFile     Flag used to indicate creating code\r
847     #                                       for dependent modules/Libraries\r
848     #   @param  CreateDepModuleMakeFile     Flag used to indicate creating makefile\r
849     #                                       for dependent modules/Libraries\r
850     #\r
851     def _Build(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True):\r
852         if AutoGenObject == None:\r
853             return False\r
854 \r
855         # skip file generation for cleanxxx targets, run and fds target\r
856         if Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
857             # for target which must generate AutoGen code and makefile\r
858             if not self.SkipAutoGen or Target == 'genc':\r
859                 self.Progress.Start("Generating code")\r
860                 AutoGenObject.CreateCodeFile(CreateDepsCodeFile)\r
861                 self.Progress.Stop("done!")\r
862             if Target == "genc":\r
863                 return True\r
864     \r
865             if not self.SkipAutoGen or Target == 'genmake':\r
866                 self.Progress.Start("Generating makefile")\r
867                 AutoGenObject.CreateMakeFile(CreateDepsMakeFile)\r
868                 self.Progress.Stop("done!")\r
869             if Target == "genmake":\r
870                 return True\r
871         else:\r
872             # always recreate top/platform makefile when clean, just in case of inconsistency\r
873             AutoGenObject.CreateMakeFile(False)\r
874 \r
875         BuildCommand = AutoGenObject.BuildCommand\r
876         if BuildCommand == None or len(BuildCommand) == 0:\r
877             EdkLogger.error("build", OPTION_MISSING, ExtraData="No MAKE command found for [%s, %s, %s]" % Key)\r
878 \r
879         BuildCommand = BuildCommand + (Target,)\r
880         LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir)\r
881         return True\r
882 \r
883     ## Build active platform for different build targets and different tool chains\r
884     #\r
885     def _BuildPlatform(self):\r
886         for BuildTarget in self.BuildTargetList:\r
887             for ToolChain in self.ToolChainList:\r
888                 Wa = WorkspaceAutoGen(\r
889                         self.WorkspaceDir,\r
890                         self.Platform,\r
891                         BuildTarget,\r
892                         ToolChain,\r
893                         self.ArchList,\r
894                         self.BuildDatabase,\r
895                         self.TargetTxt,\r
896                         self.ToolDef,\r
897                         self.Fdf,\r
898                         self.FdList,\r
899                         self.FvList,\r
900                         self.SkuId\r
901                         )\r
902                 self.Progress.Stop("done!")\r
903                 self._Build(self.Target, Wa)\r
904 \r
905     ## Build active module for different build targets, different tool chains and different archs\r
906     #\r
907     def _BuildModule(self):\r
908         for BuildTarget in self.BuildTargetList:\r
909             for ToolChain in self.ToolChainList:\r
910                 #\r
911                 # module build needs platform build information, so get platform\r
912                 # AutoGen first\r
913                 #\r
914                 Wa = WorkspaceAutoGen(\r
915                         self.WorkspaceDir,\r
916                         self.Platform,\r
917                         BuildTarget,\r
918                         ToolChain,\r
919                         self.ArchList,\r
920                         self.BuildDatabase,\r
921                         self.TargetTxt,\r
922                         self.ToolDef,\r
923                         self.Fdf,\r
924                         self.FdList,\r
925                         self.FvList,\r
926                         self.SkuId\r
927                         )                \r
928                 Wa.CreateMakeFile(False)\r
929                 self.Progress.Stop("done!")\r
930                 MaList = []\r
931                 for Arch in self.ArchList:\r
932                     Ma = ModuleAutoGen(Wa, self.ModuleFile, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
933                     if Ma == None: continue\r
934                     MaList.append(Ma)\r
935                     self._Build(self.Target, Ma)\r
936                 if MaList == []:\r
937                     EdkLogger.error(\r
938                                 'build',\r
939                                 BUILD_ERROR,\r
940                                 "Module for [%s] is not employed by active platform" % ', '.join(self.ArchList),\r
941                                 ExtraData=self.ModuleFile\r
942                                 )\r
943 \r
944     ## Build a platform in multi-thread mode\r
945     #\r
946     def _MultiThreadBuildPlatform(self):\r
947         for BuildTarget in self.BuildTargetList:\r
948             for ToolChain in self.ToolChainList:\r
949                 Wa = WorkspaceAutoGen(\r
950                         self.WorkspaceDir,\r
951                         self.Platform,\r
952                         BuildTarget,\r
953                         ToolChain,\r
954                         self.ArchList,\r
955                         self.BuildDatabase,\r
956                         self.TargetTxt,\r
957                         self.ToolDef,\r
958                         self.Fdf,\r
959                         self.FdList,\r
960                         self.FvList,\r
961                         self.SkuId\r
962                         )                \r
963                 Wa.CreateMakeFile(False)\r
964 \r
965                 # multi-thread exit flag\r
966                 ExitFlag = threading.Event()\r
967                 ExitFlag.clear()\r
968                 for Arch in self.ArchList:\r
969                     Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)\r
970                     if Pa == None:\r
971                         continue\r
972                     for Module in Pa.Platform.Modules:\r
973                         # Get ModuleAutoGen object to generate C code file and makefile\r
974                         Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
975                         if Ma == None:\r
976                             continue\r
977                         # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'\r
978                         if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
979                             # for target which must generate AutoGen code and makefile\r
980                             if not self.SkipAutoGen or self.Target == 'genc':\r
981                                 Ma.CreateCodeFile(True)\r
982                             if self.Target == "genc":\r
983                                 continue\r
984 \r
985                             if not self.SkipAutoGen or self.Target == 'genmake':\r
986                                 Ma.CreateMakeFile(True)\r
987                             if self.Target == "genmake":\r
988                                 continue\r
989                         self.Progress.Stop("done!")\r
990                         # Generate build task for the module\r
991                         Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))\r
992                         # Break build if any build thread has error\r
993                         if BuildTask.HasError():\r
994                             # we need a full version of makefile for platform\r
995                             ExitFlag.set()\r
996                             BuildTask.WaitForComplete()\r
997                             Pa.CreateMakeFile(False)\r
998                             EdkLogger.error("build", BUILD_ERROR, BuildTask.GetErrorMessage())\r
999                         # Start task scheduler\r
1000                         if not BuildTask.IsOnGoing():\r
1001                             BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)\r
1002 \r
1003                     # in case there's an interruption. we need a full version of makefile for platform\r
1004                     Pa.CreateMakeFile(False)\r
1005                     if BuildTask.HasError():\r
1006                         EdkLogger.error("build", BUILD_ERROR, BuildTask.GetErrorMessage())\r
1007 \r
1008                 #\r
1009                 # All modules have been put in build tasks queue. Tell task scheduler\r
1010                 # to exit if all tasks are completed\r
1011                 #\r
1012                 ExitFlag.set()\r
1013                 BuildTask.WaitForComplete()\r
1014 \r
1015                 #\r
1016                 # Check for build error, and raise exception if one\r
1017                 # has been signaled.\r
1018                 #\r
1019                 if BuildTask.HasError():\r
1020                     EdkLogger.error("build", BUILD_ERROR, BuildTask.GetErrorMessage())\r
1021 \r
1022                 # Generate FD image if there's a FDF file found\r
1023                 if self.Fdf != '' and self.Target in ["", "all", "fds"]:\r
1024                     LaunchCommand(Wa.BuildCommand + ("fds",), Wa.MakeFileDir)\r
1025 \r
1026     ## Generate GuidedSectionTools.txt in the FV directories.\r
1027     #\r
1028     def CreateGuidedSectionToolsFile(self):\r
1029         for Arch in self.ArchList:\r
1030             for BuildTarget in self.BuildTargetList:\r
1031                 for ToolChain in self.ToolChainList:\r
1032                     FvDir = os.path.join(\r
1033                                 self.WorkspaceDir,\r
1034                                 self.Platform.OutputDirectory,\r
1035                                 '_'.join((BuildTarget, ToolChain)),\r
1036                                 'FV'\r
1037                                 )\r
1038                     if not os.path.exists(FvDir):\r
1039                         continue \r
1040                     # Build up the list of supported architectures for this build\r
1041                     prefix = '%s_%s_%s_' % (BuildTarget, ToolChain, Arch)\r
1042 \r
1043                     # Look through the tool definitions for GUIDed tools\r
1044                     guidAttribs = []\r
1045                     for (attrib, value) in self.ToolDef.ToolsDefTxtDictionary.iteritems():\r
1046                         if attrib.upper().endswith('_GUID'):\r
1047                             split = attrib.split('_')\r
1048                             thisPrefix = '_'.join(split[0:3]) + '_'\r
1049                             if thisPrefix == prefix:\r
1050                                 guid = self.ToolDef.ToolsDefTxtDictionary[attrib]\r
1051                                 guid = guid.lower()\r
1052                                 toolName = split[3]\r
1053                                 path = '_'.join(split[0:4]) + '_PATH'\r
1054                                 path = self.ToolDef.ToolsDefTxtDictionary[path]\r
1055                                 path = self.GetFullPathOfTool(path)\r
1056                                 guidAttribs.append((guid, toolName, path))\r
1057 \r
1058                     # Write out GuidedSecTools.txt\r
1059                     toolsFile = os.path.join(FvDir, 'GuidedSectionTools.txt')\r
1060                     toolsFile = open(toolsFile, 'wt')\r
1061                     for guidedSectionTool in guidAttribs:\r
1062                         print >> toolsFile, ' '.join(guidedSectionTool)\r
1063                     toolsFile.close()\r
1064 \r
1065     ## Returns the full path of the tool.\r
1066     #\r
1067     def GetFullPathOfTool (self, tool):\r
1068         if os.path.exists(tool):\r
1069             return os.path.realpath(tool)\r
1070         else:\r
1071             # We need to search for the tool using the\r
1072             # PATH environment variable.\r
1073             for dirInPath in os.environ['PATH'].split(os.pathsep):\r
1074                 foundPath = os.path.join(dirInPath, tool)\r
1075                 if os.path.exists(foundPath):\r
1076                     return os.path.realpath(foundPath)\r
1077 \r
1078         # If the tool was not found in the path then we just return\r
1079         # the input tool.\r
1080         return tool\r
1081 \r
1082     ## Launch the module or platform build\r
1083     #\r
1084     def Launch(self):\r
1085         if self.ModuleFile == None or self.ModuleFile == "":\r
1086             if not self.SpawnMode or self.Target not in ["", "all"]:\r
1087                 self.SpawnMode = False\r
1088                 self._BuildPlatform()\r
1089             else:\r
1090                 self._MultiThreadBuildPlatform()\r
1091             self.CreateGuidedSectionToolsFile()\r
1092         else:\r
1093             self.SpawnMode = False\r
1094             self._BuildModule()\r
1095 \r
1096     ## Do some clean-up works when error occurred\r
1097     def Relinquish(self):\r
1098         self.DumpBuildData()\r
1099         Utils.Progressor.Abort()\r
1100         if self.SpawnMode == True:\r
1101             BuildTask.Abort()\r
1102 \r
1103     def DumpBuildData(self):\r
1104         CacheDirectory = os.path.join(self.WorkspaceDir, gBuildCacheDir)\r
1105         Utils.CreateDirectory(CacheDirectory)\r
1106         Utils.DataDump(Utils.gFileTimeStampCache, os.path.join(CacheDirectory, "gFileTimeStampCache"))\r
1107         Utils.DataDump(Utils.gDependencyDatabase, os.path.join(CacheDirectory, "gDependencyDatabase"))\r
1108 \r
1109     def RestoreBuildData(self):\r
1110         FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gFileTimeStampCache")\r
1111         if Utils.gFileTimeStampCache == {} and os.path.isfile(FilePath):\r
1112             Utils.gFileTimeStampCache = Utils.DataRestore(FilePath)\r
1113             if Utils.gFileTimeStampCache == None:\r
1114                 Utils.gFileTimeStampCache = {}\r
1115         \r
1116         FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gDependencyDatabase")\r
1117         if Utils.gDependencyDatabase == {} and os.path.isfile(FilePath):\r
1118             Utils.gDependencyDatabase = Utils.DataRestore(FilePath)\r
1119             if Utils.gDependencyDatabase == None:\r
1120                 Utils.gDependencyDatabase = {}\r
1121 \r
1122 def ParseDefines(DefineList=[]):\r
1123     DefineDict = {}\r
1124     if DefineList != None:\r
1125         for Define in DefineList:\r
1126             DefineTokenList = Define.split("=", 1)\r
1127             if len(DefineTokenList) == 1:\r
1128                 DefineDict[DefineTokenList[0]] = ""\r
1129             else:\r
1130                 DefineDict[DefineTokenList[0]] = DefineTokenList[1].strip()\r
1131     return DefineDict\r
1132 \r
1133 ## Parse command line options\r
1134 #\r
1135 # Using standard Python module optparse to parse command line option of this tool.\r
1136 #\r
1137 #   @retval Opt   A optparse.Values object containing the parsed options\r
1138 #   @retval Args  Target of build command\r
1139 #\r
1140 def MyOptionParser():\r
1141     Parser = OptionParser(description=__copyright__,version=__version__,prog="build.exe",usage="%prog [options] [target]")\r
1142     Parser.add_option("-a", "--arch", action="append", type="choice", choices=['IA32','X64','IPF','EBC'], dest="TargetArch",\r
1143         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
1144     Parser.add_option("-p", "--platform", action="store", type="string", dest="PlatformFile",\r
1145         help="Build the platform specified by the DSC file name argument, overriding target.txt's ACTIVE_PLATFORM definition.")\r
1146     Parser.add_option("-m", "--module", action="store", type="string", dest="ModuleFile",\r
1147         help="Build the module specified by the INF file name argument.")\r
1148     Parser.add_option("-b", "--buildtarget", action="append", type="choice", choices=['DEBUG','RELEASE'], dest="BuildTarget",\r
1149         help="BuildTarget is one of list: DEBUG, RELEASE, which overrides target.txt's TARGET definition. To specify more TARGET, please repeat this option.")\r
1150     Parser.add_option("-t", "--tagname", action="append", type="string", dest="ToolChain",\r
1151         help="Using the Tool Chain Tagname to build the platform, overriding target.txt's TOOL_CHAIN_TAG definition.")\r
1152     Parser.add_option("-x", "--sku-id", action="store", type="string", dest="SkuId",\r
1153         help="Using this name of SKU ID to build the platform, overriding SKUID_IDENTIFIER in DSC file.")\r
1154 \r
1155     Parser.add_option("-s", "--spawn", action="store_true", type=None, dest="SpawnMode",\r
1156         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
1157     Parser.add_option("-n", action="store", type="int", dest="ThreadNumber",\r
1158         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
1159 \r
1160     Parser.add_option("-f", "--fdf", action="store", type="string", dest="FdfFile",\r
1161         help="The name of the FDF file to use, which overrides the setting in the DSC file.")\r
1162     Parser.add_option("-r", "--rom-image", action="append", type="string", dest="RomImage", default=[],\r
1163         help="The name of FD to be generated. The name must be from [FD] section in FDF file.")\r
1164     Parser.add_option("-i", "--fv-image", action="append", type="string", dest="FvImage", default=[],\r
1165         help="The name of FV to be generated. The name must be from [FV] section in FDF file.")\r
1166 \r
1167     Parser.add_option("-k", "--msft", action="store_const", dest="MakefileType", const="nmake", help="Make Option: Generate only NMAKE Makefiles: Makefile")\r
1168     Parser.add_option("-g", "--gcc", action="store_const", dest="MakefileType", const="gmake", help="Make Option: Generate only GMAKE Makefiles: GNUmakefile")\r
1169     Parser.add_option("-l", "--all", action="store_const", dest="MakefileType", const="all", help="Make Option: Generate both NMAKE and GMAKE makefiles.")\r
1170 \r
1171     Parser.add_option("-u", "--skip-autogen", action="store_true", dest="SkipAutoGen", help="Skip AutoGen step.")\r
1172     Parser.add_option("-e", "--re-parse", action="store_true", dest="Reparse", help="Re-parse all meta-data files.")\r
1173 \r
1174     # Parser.add_option("-D", action="append", dest="Defines", metavar="NAME[=[VALUE]]",\r
1175     #     help="Define global macro which can be used in DSC/DEC/INF files.")\r
1176 \r
1177     Parser.add_option("-w", "--warning-as-error", action="store_true", dest="WarningAsError", help="Treat warning in tools as error.")\r
1178     Parser.add_option("-j", "--log", action="store", dest="LogFile", help="Putlog in specified file as well as on console.")\r
1179     Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")\r
1180     Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\\r
1181                                                                                "including library instances selected, final dependency expression, "\\r
1182                                                                                "and warning messages, etc.")\r
1183     Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")\r
1184 \r
1185     (Opt, Args)=Parser.parse_args()\r
1186     return (Opt, Args)\r
1187 \r
1188 ## Tool entrance method\r
1189 #\r
1190 # This method mainly dispatch specific methods per the command line options.\r
1191 # If no error found, return zero value so the caller of this tool can know\r
1192 # if it's executed successfully or not.\r
1193 #\r
1194 #   @retval 0     Tool was successful\r
1195 #   @retval 1     Tool failed\r
1196 #\r
1197 def Main():\r
1198     StartTime = time.clock()\r
1199 \r
1200     # Initialize log system\r
1201     EdkLogger.Initialize()\r
1202 \r
1203     #\r
1204     # Parse the options and args\r
1205     #\r
1206     (Option, Target) = MyOptionParser()\r
1207 \r
1208     # Set log level\r
1209     if Option.verbose != None:\r
1210         EdkLogger.SetLevel(EdkLogger.VERBOSE)\r
1211     elif Option.quiet != None:\r
1212         EdkLogger.SetLevel(EdkLogger.QUIET)\r
1213     elif Option.debug != None:\r
1214         EdkLogger.SetLevel(Option.debug + 1)\r
1215     else:\r
1216         EdkLogger.SetLevel(EdkLogger.INFO)\r
1217 \r
1218     if Option.LogFile != None:\r
1219         EdkLogger.SetLogFile(Option.LogFile)\r
1220 \r
1221     if Option.WarningAsError == True:\r
1222         EdkLogger.SetWarningAsError()\r
1223 \r
1224     EdkLogger.quiet(time.strftime("%H:%M:%S, %b.%d %Y ", time.localtime()) + "[00:00]" + "\n")\r
1225     ReturnCode = 0\r
1226     MyBuild = None\r
1227     try:\r
1228         if len(Target) == 0:\r
1229             Target = "all"\r
1230         elif len(Target) >= 2:\r
1231             EdkLogger.error("build", OPTION_NOT_SUPPORTED, "More than on targets is not supported.",\r
1232                             ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))\r
1233         else:\r
1234             Target = Target[0].lower()\r
1235     \r
1236         if Target not in gSupportedTarget:\r
1237             EdkLogger.error("build", OPTION_NOT_SUPPORTED, "Not supported target [%s]." % Target,\r
1238                             ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))\r
1239     \r
1240         # GlobalData.gGlobalDefines = ParseDefines(Option.Defines)\r
1241         #\r
1242         # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH\r
1243         #\r
1244         CheckEnvVariable()\r
1245         Workspace = os.getenv("WORKSPACE")\r
1246 \r
1247         WorkingDirectory = os.getcwd()\r
1248         if Option.ModuleFile != None:\r
1249             Option.ModuleFile = NormFile(Option.ModuleFile, Workspace)\r
1250         else:\r
1251             FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.inf')))\r
1252             FileNum = len(FileList)\r
1253             if FileNum >= 2:\r
1254                 EdkLogger.error("build", None, "There are %d INF files in %s.\n" % (FileNum, WorkingDirectory))\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         EdkLogger.quiet("")\r
1277         if Option != None and Option.debug != None:\r
1278             EdkLogger.quiet(traceback.format_exc())\r
1279         else:\r
1280             EdkLogger.quiet(str(X))\r
1281         ReturnCode = 1\r
1282     except BaseException, X:\r
1283         EdkLogger.quiet("\n\nPython...")\r
1284         EdkLogger.quiet(traceback.format_exc())\r
1285         ReturnCode = 1\r
1286     finally:\r
1287         Utils.Progressor.Abort()\r
1288 \r
1289     if MyBuild != None:\r
1290         MyBuild.Db.Close()\r
1291 \r
1292     FinishTime = time.clock()\r
1293     BuildDuration = time.strftime("%M:%S", time.gmtime(int(round(FinishTime - StartTime))))\r
1294     EdkLogger.quiet("\n%s [%s]" % (time.strftime("%H:%M:%S, %b.%d %Y", time.localtime()), BuildDuration))\r
1295 \r
1296     return ReturnCode\r
1297 \r
1298 if __name__ == '__main__':\r
1299     sys.exit(Main())\r