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