90ac1c2c1c37c657e59d346e5127198353691bae
[efi/basetools/.git] / Source / Python / build / build.py
1 ## @file\r
2 # build a platform or a module\r
3 #\r
4 #  Copyright (c) 2007 - 2010, 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 StringIO\r
21 import sys\r
22 import glob\r
23 import time\r
24 import platform\r
25 import traceback\r
26 \r
27 from struct import *\r
28 from threading import *\r
29 from optparse import OptionParser\r
30 from subprocess import *\r
31 from Common import Misc as Utils\r
32 \r
33 from Common.TargetTxtClassObject import *\r
34 from Common.ToolDefClassObject import *\r
35 from Common.DataType import *\r
36 from AutoGen.AutoGen import *\r
37 from Common.BuildToolError import *\r
38 from Workspace.WorkspaceDatabase import *\r
39 \r
40 from BuildReport import BuildReport\r
41 from GenPatchPcdTable.GenPatchPcdTable import *\r
42 from PatchPcdValue.PatchPcdValue import *\r
43 \r
44 import Common.EdkLogger\r
45 import Common.GlobalData as GlobalData\r
46 \r
47 # Version and Copyright\r
48 VersionNumber = "0.5"\r
49 __version__ = "%prog Version " + VersionNumber\r
50 __copyright__ = "Copyright (c) 2007 - 2010, Intel Corporation  All rights reserved."\r
51 \r
52 ## standard targets of build command\r
53 gSupportedTarget = ['all', 'genc', 'genmake', 'modules', 'libraries', 'fds', 'clean', 'cleanall', 'cleanlib', 'run']\r
54 \r
55 ## build configuration file\r
56 gBuildConfiguration = "Conf/target.txt"\r
57 gBuildCacheDir = "Conf/.cache"\r
58 gToolsDefinition = "Conf/tools_def.txt"\r
59 \r
60 ## Check environment PATH variable to make sure the specified tool is found\r
61 #\r
62 #   If the tool is found in the PATH, then True is returned\r
63 #   Otherwise, False is returned\r
64 #\r
65 def IsToolInPath(tool):\r
66     if os.environ.has_key('PATHEXT'):\r
67         extns = os.environ['PATHEXT'].split(os.path.pathsep)\r
68     else:\r
69         extns = ('',)\r
70     for pathDir in os.environ['PATH'].split(os.path.pathsep):\r
71         for ext in extns:\r
72             if os.path.exists(os.path.join(pathDir, tool + ext)):\r
73                 return True\r
74     return False\r
75 \r
76 ## Check environment variables\r
77 #\r
78 #  Check environment variables that must be set for build. Currently they are\r
79 #\r
80 #   WORKSPACE           The directory all packages/platforms start from\r
81 #   EDK_TOOLS_PATH      The directory contains all tools needed by the build\r
82 #   PATH                $(EDK_TOOLS_PATH)/Bin/<sys> must be set in PATH\r
83 #\r
84 #   If any of above environment variable is not set or has error, the build\r
85 #   will be broken.\r
86 #\r
87 def CheckEnvVariable():\r
88     # check WORKSPACE\r
89     if "WORKSPACE" not in os.environ:\r
90         EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",\r
91                         ExtraData="WORKSPACE")\r
92 \r
93     WorkspaceDir = os.path.normcase(os.path.normpath(os.environ["WORKSPACE"]))\r
94     if not os.path.exists(WorkspaceDir):\r
95         EdkLogger.error("build", FILE_NOT_FOUND, "WORKSPACE doesn't exist", ExtraData="%s" % WorkspaceDir)\r
96     elif ' ' in WorkspaceDir:\r
97         EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in WORKSPACE path",\r
98                         ExtraData=WorkspaceDir)\r
99     os.environ["WORKSPACE"] = WorkspaceDir\r
100 \r
101     #\r
102     # Check EFI_SOURCE (R8 build convention). EDK_SOURCE will always point to ECP\r
103     #\r
104     os.environ["ECP_SOURCE"] = os.path.join(WorkspaceDir, GlobalData.gEdkCompatibilityPkg)\r
105     if "EFI_SOURCE" not in os.environ:\r
106         os.environ["EFI_SOURCE"] = os.environ["ECP_SOURCE"]\r
107     if "EDK_SOURCE" not in os.environ:\r
108         os.environ["EDK_SOURCE"] = os.environ["ECP_SOURCE"]\r
109 \r
110     #\r
111     # Unify case of characters on case-insensitive systems\r
112     #\r
113     EfiSourceDir = os.path.normcase(os.path.normpath(os.environ["EFI_SOURCE"]))\r
114     EdkSourceDir = os.path.normcase(os.path.normpath(os.environ["EDK_SOURCE"]))\r
115     EcpSourceDir = os.path.normcase(os.path.normpath(os.environ["ECP_SOURCE"]))\r
116  \r
117     os.environ["EFI_SOURCE"] = EfiSourceDir\r
118     os.environ["EDK_SOURCE"] = EdkSourceDir\r
119     os.environ["ECP_SOURCE"] = EcpSourceDir\r
120     os.environ["EDK_TOOLS_PATH"] = os.path.normcase(os.environ["EDK_TOOLS_PATH"])\r
121     \r
122     if not os.path.exists(EcpSourceDir):\r
123         EdkLogger.verbose("ECP_SOURCE = %s doesn't exist. R8 modules could not be built." % EcpSourceDir)\r
124     elif ' ' in EcpSourceDir:\r
125         EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in ECP_SOURCE path",\r
126                         ExtraData=EcpSourceDir)\r
127     if not os.path.exists(EdkSourceDir):\r
128         if EdkSourceDir == EcpSourceDir:\r
129             EdkLogger.verbose("EDK_SOURCE = %s doesn't exist. R8 modules could not be built." % EdkSourceDir)\r
130         else:\r
131             EdkLogger.error("build", PARAMETER_INVALID, "EDK_SOURCE does not exist",\r
132                             ExtraData=EdkSourceDir)\r
133     elif ' ' in EdkSourceDir:\r
134         EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in EDK_SOURCE path",\r
135                         ExtraData=EdkSourceDir)\r
136     if not os.path.exists(EfiSourceDir):\r
137         if EfiSourceDir == EcpSourceDir:\r
138             EdkLogger.verbose("EFI_SOURCE = %s doesn't exist. R8 modules could not be built." % EfiSourceDir)\r
139         else:\r
140             EdkLogger.error("build", PARAMETER_INVALID, "EFI_SOURCE does not exist",\r
141                             ExtraData=EfiSourceDir)\r
142     elif ' ' in EfiSourceDir:\r
143         EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in EFI_SOURCE path",\r
144                         ExtraData=EfiSourceDir)\r
145 \r
146     # change absolute path to relative path to WORKSPACE\r
147     if EfiSourceDir.upper().find(WorkspaceDir.upper()) != 0:\r
148         EdkLogger.error("build", PARAMETER_INVALID, "EFI_SOURCE is not under WORKSPACE",\r
149                         ExtraData="WORKSPACE = %s\n    EFI_SOURCE = %s" % (WorkspaceDir, EfiSourceDir))\r
150     if EdkSourceDir.upper().find(WorkspaceDir.upper()) != 0:\r
151         EdkLogger.error("build", PARAMETER_INVALID, "EDK_SOURCE is not under WORKSPACE",\r
152                         ExtraData="WORKSPACE = %s\n    EDK_SOURCE = %s" % (WorkspaceDir, EdkSourceDir))\r
153     if EcpSourceDir.upper().find(WorkspaceDir.upper()) != 0:\r
154         EdkLogger.error("build", PARAMETER_INVALID, "ECP_SOURCE is not under WORKSPACE",\r
155                         ExtraData="WORKSPACE = %s\n    ECP_SOURCE = %s" % (WorkspaceDir, EcpSourceDir))\r
156 \r
157     # check EDK_TOOLS_PATH\r
158     if "EDK_TOOLS_PATH" not in os.environ:\r
159         EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",\r
160                         ExtraData="EDK_TOOLS_PATH")\r
161 \r
162     # check PATH\r
163     if "PATH" not in os.environ:\r
164         EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",\r
165                         ExtraData="PATH")\r
166 \r
167     GlobalData.gWorkspace = WorkspaceDir\r
168     GlobalData.gEfiSource = EfiSourceDir\r
169     GlobalData.gEdkSource = EdkSourceDir\r
170     GlobalData.gEcpSource = EcpSourceDir\r
171 \r
172 ## Get normalized file path\r
173 #\r
174 # Convert the path to be local format, and remove the WORKSPACE path at the\r
175 # beginning if the file path is given in full path.\r
176 #\r
177 # @param  FilePath      File path to be normalized\r
178 # @param  Workspace     Workspace path which the FilePath will be checked against\r
179 #\r
180 # @retval string        The normalized file path\r
181 #\r
182 def NormFile(FilePath, Workspace):\r
183     # check if the path is absolute or relative\r
184     if os.path.isabs(FilePath):\r
185         FileFullPath = os.path.normpath(FilePath)\r
186     else:\r
187         FileFullPath = os.path.normpath(os.path.join(Workspace, FilePath))\r
188 \r
189     # check if the file path exists or not\r
190     if not os.path.isfile(FileFullPath):\r
191         EdkLogger.error("build", FILE_NOT_FOUND, ExtraData="\t%s (Please give file in absolute path or relative to WORKSPACE)"  % FileFullPath)\r
192 \r
193     # remove workspace directory from the beginning part of the file path\r
194     if Workspace[-1] in ["\\", "/"]:\r
195         return FileFullPath[len(Workspace):]\r
196     else:\r
197         return FileFullPath[(len(Workspace) + 1):]\r
198 \r
199 ## Get the output of an external program\r
200 #\r
201 # This is the entrance method of thread reading output of an external program and\r
202 # putting them in STDOUT/STDERR of current program.\r
203 #\r
204 # @param  From      The stream message read from\r
205 # @param  To        The stream message put on\r
206 # @param  ExitFlag  The flag used to indicate stopping reading\r
207 #\r
208 def ReadMessage(From, To, ExitFlag):\r
209     while True:\r
210         # read one line a time\r
211         Line = From.readline()\r
212         # empty string means "end"\r
213         if Line != None and Line != "":\r
214             To(Line.rstrip())\r
215         else:\r
216             break\r
217         if ExitFlag.isSet():\r
218             break\r
219 \r
220 ## Launch an external program\r
221 #\r
222 # This method will call subprocess.Popen to execute an external program with\r
223 # given options in specified directory. Because of the dead-lock issue during\r
224 # redirecting output of the external program, threads are used to to do the\r
225 # redirection work.\r
226 #\r
227 # @param  Command               A list or string containing the call of the program\r
228 # @param  WorkingDir            The directory in which the program will be running\r
229 #\r
230 def LaunchCommand(Command, WorkingDir):\r
231     # if working directory doesn't exist, Popen() will raise an exception\r
232     if not os.path.isdir(WorkingDir):\r
233         EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=WorkingDir)\r
234 \r
235     Proc = None\r
236     EndOfProcedure = None\r
237     try:\r
238         # launch the command\r
239         Proc = Popen(Command, stdout=PIPE, stderr=PIPE, env=os.environ, cwd=WorkingDir, bufsize=-1)\r
240 \r
241         # launch two threads to read the STDOUT and STDERR\r
242         EndOfProcedure = Event()\r
243         EndOfProcedure.clear()\r
244         if Proc.stdout:\r
245             StdOutThread = Thread(target=ReadMessage, args=(Proc.stdout, EdkLogger.info, EndOfProcedure))\r
246             StdOutThread.setName("STDOUT-Redirector")\r
247             StdOutThread.setDaemon(False)\r
248             StdOutThread.start()\r
249 \r
250         if Proc.stderr:\r
251             StdErrThread = Thread(target=ReadMessage, args=(Proc.stderr, EdkLogger.quiet, EndOfProcedure))\r
252             StdErrThread.setName("STDERR-Redirector")\r
253             StdErrThread.setDaemon(False)\r
254             StdErrThread.start()\r
255 \r
256         # waiting for program exit\r
257         Proc.wait()\r
258     except: # in case of aborting\r
259         # terminate the threads redirecting the program output\r
260         if EndOfProcedure != None:\r
261             EndOfProcedure.set()\r
262         if Proc == None:\r
263             if type(Command) != type(""):\r
264                 Command = " ".join(Command)\r
265             EdkLogger.error("build", COMMAND_FAILURE, "Failed to start command", ExtraData="%s [%s]" % (Command, WorkingDir))\r
266 \r
267     if Proc.stdout:\r
268         StdOutThread.join()\r
269     if Proc.stderr:\r
270         StdErrThread.join()\r
271 \r
272     # check the return code of the program\r
273     if Proc.returncode != 0:\r
274         if type(Command) != type(""):\r
275             Command = " ".join(Command)\r
276         EdkLogger.error("build", COMMAND_FAILURE, ExtraData="%s [%s]" % (Command, WorkingDir))\r
277 \r
278 ## The smallest unit that can be built in multi-thread build mode\r
279 #\r
280 # This is the base class of build unit. The "Obj" parameter must provide\r
281 # __str__(), __eq__() and __hash__() methods. Otherwise there could be build units\r
282 # missing build.\r
283 #\r
284 # Currently the "Obj" should be only ModuleAutoGen or PlatformAutoGen objects.\r
285 #\r
286 class BuildUnit:\r
287     ## The constructor\r
288     #\r
289     #   @param  self        The object pointer\r
290     #   @param  Obj         The object the build is working on\r
291     #   @param  Target      The build target name, one of gSupportedTarget\r
292     #   @param  Dependency  The BuildUnit(s) which must be completed in advance\r
293     #   @param  WorkingDir  The directory build command starts in\r
294     #\r
295     def __init__(self, Obj, BuildCommand, Target, Dependency, WorkingDir="."):\r
296         self.BuildObject = Obj\r
297         self.Dependency = Dependency\r
298         self.WorkingDir = WorkingDir\r
299         self.Target = Target\r
300         self.BuildCommand = BuildCommand\r
301         if BuildCommand == None or len(BuildCommand) == 0:\r
302             EdkLogger.error("build", OPTION_MISSING, "No build command found for",\r
303                             ExtraData=str(Obj))\r
304 \r
305     ## str() method\r
306     #\r
307     #   It just returns the string representaion of self.BuildObject\r
308     #\r
309     #   @param  self        The object pointer\r
310     #\r
311     def __str__(self):\r
312         return str(self.BuildObject)\r
313 \r
314     ## "==" operator method\r
315     #\r
316     #   It just compares self.BuildObject with "Other". So self.BuildObject must\r
317     #   provide its own __eq__() method.\r
318     #\r
319     #   @param  self        The object pointer\r
320     #   @param  Other       The other BuildUnit object compared to\r
321     #\r
322     def __eq__(self, Other):\r
323         return Other != None and self.BuildObject == Other.BuildObject \\r
324                 and self.BuildObject.Arch == Other.BuildObject.Arch\r
325 \r
326     ## hash() method\r
327     #\r
328     #   It just returns the hash value of self.BuildObject which must be hashable.\r
329     #\r
330     #   @param  self        The object pointer\r
331     #\r
332     def __hash__(self):\r
333         return hash(self.BuildObject) + hash(self.BuildObject.Arch)\r
334 \r
335     def __repr__(self):\r
336         return repr(self.BuildObject)\r
337 \r
338 ## The smallest module unit that can be built by nmake/make command in multi-thread build mode\r
339 #\r
340 # This class is for module build by nmake/make build system. The "Obj" parameter\r
341 # must provide __str__(), __eq__() and __hash__() methods. Otherwise there could\r
342 # be make units missing build.\r
343 #\r
344 # Currently the "Obj" should be only ModuleAutoGen object.\r
345 #\r
346 class ModuleMakeUnit(BuildUnit):\r
347     ## The constructor\r
348     #\r
349     #   @param  self        The object pointer\r
350     #   @param  Obj         The ModuleAutoGen object the build is working on\r
351     #   @param  Target      The build target name, one of gSupportedTarget\r
352     #\r
353     def __init__(self, Obj, Target):\r
354         Dependency = [ModuleMakeUnit(La, Target) for La in Obj.LibraryAutoGenList]\r
355         BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir)\r
356         if Target in [None, "", "all"]:\r
357             self.Target = "tbuild"\r
358 \r
359 ## The smallest platform unit that can be built by nmake/make command in multi-thread build mode\r
360 #\r
361 # This class is for platform build by nmake/make build system. The "Obj" parameter\r
362 # must provide __str__(), __eq__() and __hash__() methods. Otherwise there could\r
363 # be make units missing build.\r
364 #\r
365 # Currently the "Obj" should be only PlatformAutoGen object.\r
366 #\r
367 class PlatformMakeUnit(BuildUnit):\r
368     ## The constructor\r
369     #\r
370     #   @param  self        The object pointer\r
371     #   @param  Obj         The PlatformAutoGen object the build is working on\r
372     #   @param  Target      The build target name, one of gSupportedTarget\r
373     #\r
374     def __init__(self, Obj, Target):\r
375         Dependency = [ModuleMakeUnit(Lib, Target) for Lib in self.BuildObject.LibraryAutoGenList]\r
376         Dependency.extend([ModuleMakeUnit(Mod, Target) for Mod in self.BuildObject.ModuleAutoGenList])\r
377         BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir)\r
378 \r
379 ## The class representing the task of a module build or platform build\r
380 #\r
381 # This class manages the build tasks in multi-thread build mode. Its jobs include\r
382 # scheduling thread running, catching thread error, monitor the thread status, etc.\r
383 #\r
384 class BuildTask:\r
385     # queue for tasks waiting for schedule\r
386     _PendingQueue = sdict()\r
387     _PendingQueueLock = threading.Lock()\r
388 \r
389     # queue for tasks ready for running\r
390     _ReadyQueue = sdict()\r
391     _ReadyQueueLock = threading.Lock()\r
392 \r
393     # queue for run tasks\r
394     _RunningQueue = sdict()\r
395     _RunningQueueLock = threading.Lock()\r
396 \r
397     # queue containing all build tasks, in case duplicate build\r
398     _TaskQueue = sdict()\r
399 \r
400     # flag indicating error occurs in a running thread\r
401     _ErrorFlag = threading.Event()\r
402     _ErrorFlag.clear()\r
403     _ErrorMessage = ""\r
404 \r
405     # BoundedSemaphore object used to control the number of running threads\r
406     _Thread = None\r
407 \r
408     # flag indicating if the scheduler is started or not\r
409     _SchedulerStopped = threading.Event()\r
410     _SchedulerStopped.set()\r
411 \r
412     ## Start the task scheduler thread\r
413     #\r
414     #   @param  MaxThreadNumber     The maximum thread number\r
415     #   @param  ExitFlag            Flag used to end the scheduler\r
416     #\r
417     @staticmethod\r
418     def StartScheduler(MaxThreadNumber, ExitFlag):\r
419         SchedulerThread = Thread(target=BuildTask.Scheduler, args=(MaxThreadNumber, ExitFlag))\r
420         SchedulerThread.setName("Build-Task-Scheduler")\r
421         SchedulerThread.setDaemon(False)\r
422         SchedulerThread.start()\r
423         # wait for the scheduler to be started, especially useful in Linux\r
424         while not BuildTask.IsOnGoing():\r
425             time.sleep(0.01)\r
426 \r
427     ## Scheduler method\r
428     #\r
429     #   @param  MaxThreadNumber     The maximum thread number\r
430     #   @param  ExitFlag            Flag used to end the scheduler\r
431     #\r
432     @staticmethod\r
433     def Scheduler(MaxThreadNumber, ExitFlag):\r
434         BuildTask._SchedulerStopped.clear()\r
435         try:\r
436             # use BoundedSemaphore to control the maximum running threads\r
437             BuildTask._Thread = BoundedSemaphore(MaxThreadNumber)\r
438             #\r
439             # scheduling loop, which will exits when no pending/ready task and\r
440             # indicated to do so, or there's error in running thread\r
441             #\r
442             while (len(BuildTask._PendingQueue) > 0 or len(BuildTask._ReadyQueue) > 0 \\r
443                    or not ExitFlag.isSet()) and not BuildTask._ErrorFlag.isSet():\r
444                 EdkLogger.debug(EdkLogger.DEBUG_8, "Pending Queue (%d), Ready Queue (%d)"\r
445                                 % (len(BuildTask._PendingQueue), len(BuildTask._ReadyQueue)))\r
446 \r
447                 # get all pending tasks\r
448                 BuildTask._PendingQueueLock.acquire()\r
449                 BuildObjectList = BuildTask._PendingQueue.keys()\r
450                 #\r
451                 # check if their dependency is resolved, and if true, move them\r
452                 # into ready queue\r
453                 #\r
454                 for BuildObject in BuildObjectList:\r
455                     Bt = BuildTask._PendingQueue[BuildObject]\r
456                     if Bt.IsReady():\r
457                         BuildTask._ReadyQueue[BuildObject] = BuildTask._PendingQueue.pop(BuildObject)\r
458                 BuildTask._PendingQueueLock.release()\r
459 \r
460                 # launch build thread until the maximum number of threads is reached\r
461                 while not BuildTask._ErrorFlag.isSet():\r
462                     # empty ready queue, do nothing further\r
463                     if len(BuildTask._ReadyQueue) == 0:\r
464                         break\r
465 \r
466                     # wait for active thread(s) exit\r
467                     BuildTask._Thread.acquire(True)\r
468 \r
469                     # start a new build thread\r
470                     Bo = BuildTask._ReadyQueue.keys()[0]\r
471                     Bt = BuildTask._ReadyQueue.pop(Bo)\r
472 \r
473                     # move into running queue\r
474                     BuildTask._RunningQueueLock.acquire()\r
475                     BuildTask._RunningQueue[Bo] = Bt\r
476                     BuildTask._RunningQueueLock.release()\r
477 \r
478                     Bt.Start()\r
479                     # avoid tense loop\r
480                     time.sleep(0.01)\r
481 \r
482                 # avoid tense loop\r
483                 time.sleep(0.01)\r
484 \r
485             # wait for all running threads exit\r
486             if BuildTask._ErrorFlag.isSet():\r
487                 EdkLogger.quiet("\nWaiting for all build threads exit...")\r
488             # while not BuildTask._ErrorFlag.isSet() and \\r
489             while len(BuildTask._RunningQueue) > 0:\r
490                 EdkLogger.verbose("Waiting for thread ending...(%d)" % len(BuildTask._RunningQueue))\r
491                 EdkLogger.debug(EdkLogger.DEBUG_8, "Threads [%s]" % ", ".join([Th.getName() for Th in threading.enumerate()]))\r
492                 # avoid tense loop\r
493                 time.sleep(0.1)\r
494         except BaseException, X:\r
495             #\r
496             # TRICK: hide the output of threads left runing, so that the user can\r
497             #        catch the error message easily\r
498             #\r
499             EdkLogger.SetLevel(EdkLogger.ERROR)\r
500             BuildTask._ErrorFlag.set()\r
501             BuildTask._ErrorMessage = "build thread scheduler error\n\t%s" % str(X)\r
502 \r
503         BuildTask._PendingQueue.clear()\r
504         BuildTask._ReadyQueue.clear()\r
505         BuildTask._RunningQueue.clear()\r
506         BuildTask._TaskQueue.clear()\r
507         BuildTask._SchedulerStopped.set()\r
508 \r
509     ## Wait for all running method exit\r
510     #\r
511     @staticmethod\r
512     def WaitForComplete():\r
513         BuildTask._SchedulerStopped.wait()\r
514 \r
515     ## Check if the scheduler is running or not\r
516     #\r
517     @staticmethod\r
518     def IsOnGoing():\r
519         return not BuildTask._SchedulerStopped.isSet()\r
520 \r
521     ## Abort the build\r
522     @staticmethod\r
523     def Abort():\r
524         if BuildTask.IsOnGoing():\r
525             BuildTask._ErrorFlag.set()\r
526             BuildTask.WaitForComplete()\r
527 \r
528     ## Check if there's error in running thread\r
529     #\r
530     #   Since the main thread cannot catch exceptions in other thread, we have to\r
531     #   use threading.Event to communicate this formation to main thread.\r
532     #\r
533     @staticmethod\r
534     def HasError():\r
535         return BuildTask._ErrorFlag.isSet()\r
536 \r
537     ## Get error message in running thread\r
538     #\r
539     #   Since the main thread cannot catch exceptions in other thread, we have to\r
540     #   use a static variable to communicate this message to main thread.\r
541     #\r
542     @staticmethod\r
543     def GetErrorMessage():\r
544         return BuildTask._ErrorMessage\r
545 \r
546     ## Factory method to create a BuildTask object\r
547     #\r
548     #   This method will check if a module is building or has been built. And if\r
549     #   true, just return the associated BuildTask object in the _TaskQueue. If\r
550     #   not, create and return a new BuildTask object. The new BuildTask object\r
551     #   will be appended to the _PendingQueue for scheduling later.\r
552     #\r
553     #   @param  BuildItem       A BuildUnit object representing a build object\r
554     #   @param  Dependency      The dependent build object of BuildItem\r
555     #\r
556     @staticmethod\r
557     def New(BuildItem, Dependency=None):\r
558         if BuildItem in BuildTask._TaskQueue:\r
559             Bt = BuildTask._TaskQueue[BuildItem]\r
560             return Bt\r
561 \r
562         Bt = BuildTask()\r
563         Bt._Init(BuildItem, Dependency)\r
564         BuildTask._TaskQueue[BuildItem] = Bt\r
565 \r
566         BuildTask._PendingQueueLock.acquire()\r
567         BuildTask._PendingQueue[BuildItem] = Bt\r
568         BuildTask._PendingQueueLock.release()\r
569 \r
570         return Bt\r
571 \r
572     ## The real constructor of BuildTask\r
573     #\r
574     #   @param  BuildItem       A BuildUnit object representing a build object\r
575     #   @param  Dependency      The dependent build object of BuildItem\r
576     #\r
577     def _Init(self, BuildItem, Dependency=None):\r
578         self.BuildItem = BuildItem\r
579 \r
580         self.DependencyList = []\r
581         if Dependency == None:\r
582             Dependency = BuildItem.Dependency\r
583         else:\r
584             Dependency.extend(BuildItem.Dependency)\r
585         self.AddDependency(Dependency)\r
586         # flag indicating build completes, used to avoid unnecessary re-build\r
587         self.CompleteFlag = False\r
588 \r
589     ## Check if all dependent build tasks are completed or not\r
590     #\r
591     def IsReady(self):\r
592         ReadyFlag = True\r
593         for Dep in self.DependencyList:\r
594             if Dep.CompleteFlag == True:\r
595                 continue\r
596             ReadyFlag = False\r
597             break\r
598 \r
599         return ReadyFlag\r
600 \r
601     ## Add dependent build task\r
602     #\r
603     #   @param  Dependency      The list of dependent build objects\r
604     #\r
605     def AddDependency(self, Dependency):\r
606         for Dep in Dependency:\r
607             self.DependencyList.append(BuildTask.New(Dep))    # BuildTask list\r
608 \r
609     ## The thread wrapper of LaunchCommand function\r
610     #\r
611     # @param  Command               A list or string contains the call of the command\r
612     # @param  WorkingDir            The directory in which the program will be running\r
613     #\r
614     def _CommandThread(self, Command, WorkingDir):\r
615         try:\r
616             LaunchCommand(Command, WorkingDir)\r
617             self.CompleteFlag = True\r
618         except:\r
619             #\r
620             # TRICK: hide the output of threads left runing, so that the user can\r
621             #        catch the error message easily\r
622             #\r
623             if not BuildTask._ErrorFlag.isSet():\r
624                 GlobalData.gBuildingModule = "%s [%s, %s, %s]" % (str(self.BuildItem.BuildObject),\r
625                                                                   self.BuildItem.BuildObject.Arch,\r
626                                                                   self.BuildItem.BuildObject.ToolChain,\r
627                                                                   self.BuildItem.BuildObject.BuildTarget\r
628                                                                  )\r
629             EdkLogger.SetLevel(EdkLogger.ERROR)\r
630             BuildTask._ErrorFlag.set()\r
631             BuildTask._ErrorMessage = "%s broken\n    %s [%s]" % \\r
632                                       (threading.currentThread().getName(), Command, WorkingDir)\r
633         # indicate there's a thread is available for another build task\r
634         BuildTask._RunningQueueLock.acquire()\r
635         BuildTask._RunningQueue.pop(self.BuildItem)\r
636         BuildTask._RunningQueueLock.release()\r
637         BuildTask._Thread.release()\r
638 \r
639     ## Start build task thread\r
640     #\r
641     def Start(self):\r
642         EdkLogger.quiet("Building ... %s" % repr(self.BuildItem))\r
643         Command = self.BuildItem.BuildCommand + [self.BuildItem.Target]\r
644         self.BuildTread = Thread(target=self._CommandThread, args=(Command, self.BuildItem.WorkingDir))\r
645         self.BuildTread.setName("build thread")\r
646         self.BuildTread.setDaemon(False)\r
647         self.BuildTread.start()\r
648 \r
649 ## The class contains the information related to EFI image\r
650 #\r
651 class PeImageInfo():\r
652     ## Constructor\r
653     #\r
654     # Constructor will load all required image information.\r
655     #\r
656     #   @param  FileName          The full file path of image. \r
657     #   @param  Size              The required memory size of image.\r
658     #   @param  EntryPointOffset  The entry point offset to image base.\r
659     #   @param  Guid              The GUID for image.\r
660     #   @param  OutpuDir          The output directory for image.\r
661     #\r
662     def __init__(self, BaseName, Guid, OutpuDir, ImageClass):\r
663         self.BaseName         = BaseName\r
664         self.Guid             = Guid\r
665         self.OutpuDir         = OutpuDir\r
666         self.Image            = ImageClass\r
667 \r
668 ## The class implementing the EDK2 build process\r
669 #\r
670 #   The build process includes:\r
671 #       1. Load configuration from target.txt and tools_def.txt in $(WORKSPACE)/Conf\r
672 #       2. Parse DSC file of active platform\r
673 #       3. Parse FDF file if any\r
674 #       4. Establish build database, including parse all other files (module, package)\r
675 #       5. Create AutoGen files (C code file, depex file, makefile) if necessary\r
676 #       6. Call build command\r
677 #\r
678 class Build():\r
679     ## Constructor\r
680     #\r
681     # Constructor will load all necessary configurations, parse platform, modules\r
682     # and packages and the establish a database for AutoGen.\r
683     #\r
684     #   @param  Target              The build command target, one of gSupportedTarget\r
685     #   @param  WorkspaceDir        The directory of workspace\r
686     #   @param  Platform            The DSC file of active platform\r
687     #   @param  Module              The INF file of active module, if any\r
688     #   @param  Arch                The Arch list of platform or module\r
689     #   @param  ToolChain           The name list of toolchain\r
690     #   @param  BuildTarget         The "DEBUG" or "RELEASE" build\r
691     #   @param  FlashDefinition     The FDF file of active platform\r
692     #   @param  FdList=[]           The FD names to be individually built\r
693     #   @param  FvList=[]           The FV names to be individually built\r
694     #   @param  MakefileType        The type of makefile (for MSFT make or GNU make)\r
695     #   @param  SilentMode          Indicate multi-thread build mode\r
696     #   @param  ThreadNumber        The maximum number of thread if in multi-thread build mode\r
697     #   @param  SkipAutoGen         Skip AutoGen step\r
698     #   @param  Reparse             Re-parse all meta files\r
699     #   @param  SkuId               SKU id from command line\r
700     #\r
701     def __init__(self, Target, WorkspaceDir, Platform, Module, Arch, ToolChain,\r
702                  BuildTarget, FlashDefinition, FdList=[], FvList=[],\r
703                  MakefileType="nmake", SilentMode=False, ThreadNumber=2,\r
704                  SkipAutoGen=False, Reparse=False, SkuId=None, \r
705                  ReportFile=None, ReportType=None):\r
706 \r
707         self.WorkspaceDir = WorkspaceDir\r
708         self.Target         = Target\r
709         self.PlatformFile   = Platform\r
710         self.ModuleFile     = Module\r
711         self.ArchList       = Arch\r
712         self.ToolChainList  = ToolChain\r
713         self.BuildTargetList= BuildTarget\r
714         self.Fdf            = FlashDefinition\r
715         self.FdList         = FdList\r
716         self.FvList         = FvList\r
717         self.MakefileType   = MakefileType\r
718         self.SilentMode     = SilentMode\r
719         self.ThreadNumber   = ThreadNumber\r
720         self.SkipAutoGen    = SkipAutoGen\r
721         self.Reparse        = Reparse\r
722         self.SkuId          = SkuId\r
723         self.SpawnMode      = True\r
724         self.BuildReport    = BuildReport(ReportFile, ReportType)\r
725         self.TargetTxt      = TargetTxtClassObject()\r
726         self.ToolDef        = ToolDefClassObject()\r
727         self.Db             = WorkspaceDatabase(None, GlobalData.gGlobalDefines, self.Reparse)\r
728         #self.Db             = WorkspaceDatabase(None, {}, self.Reparse)\r
729         self.BuildDatabase  = self.Db.BuildObject\r
730         self.Platform       = None\r
731         self.LoadFixAddress = 0\r
732 \r
733         # print dot charater during doing some time-consuming work\r
734         self.Progress = Utils.Progressor()\r
735 \r
736         # parse target.txt, tools_def.txt, and platform file\r
737         #self.RestoreBuildData()\r
738         self.LoadConfiguration()\r
739         self.InitBuild()\r
740 \r
741         # print current build environment and configuration\r
742         EdkLogger.quiet("%-24s = %s" % ("WORKSPACE", os.environ["WORKSPACE"]))\r
743         EdkLogger.quiet("%-24s = %s" % ("ECP_SOURCE", os.environ["ECP_SOURCE"]))\r
744         EdkLogger.quiet("%-24s = %s" % ("EDK_SOURCE", os.environ["EDK_SOURCE"]))\r
745         EdkLogger.quiet("%-24s = %s" % ("EFI_SOURCE", os.environ["EFI_SOURCE"]))\r
746         EdkLogger.quiet("%-24s = %s" % ("EDK_TOOLS_PATH", os.environ["EDK_TOOLS_PATH"]))\r
747 \r
748         EdkLogger.info('\n%-24s = %s' % ("TARGET_ARCH", ' '.join(self.ArchList)))\r
749         EdkLogger.info('%-24s = %s' % ("TARGET", ' '.join(self.BuildTargetList)))\r
750         EdkLogger.info('%-24s = %s' % ("TOOL_CHAIN_TAG", ' '.join(self.ToolChainList)))\r
751 \r
752         EdkLogger.info('\n%-24s = %s' % ("Active Platform", self.PlatformFile))\r
753 \r
754         if self.Fdf != None and self.Fdf != "":\r
755             EdkLogger.info('%-24s = %s' % ("Flash Image Definition", self.Fdf))\r
756 \r
757         if self.ModuleFile != None and self.ModuleFile != "":\r
758             EdkLogger.info('%-24s = %s' % ("Active Module", self.ModuleFile))\r
759 \r
760         os.chdir(self.WorkspaceDir)\r
761         self.Progress.Start("\nProcessing meta-data")\r
762 \r
763     ## Load configuration\r
764     #\r
765     #   This method will parse target.txt and get the build configurations.\r
766     #\r
767     def LoadConfiguration(self):\r
768         #\r
769         # Check target.txt and tools_def.txt and Init them\r
770         #\r
771         BuildConfigurationFile = os.path.normpath(os.path.join(self.WorkspaceDir, gBuildConfiguration))\r
772         if os.path.isfile(BuildConfigurationFile) == True:\r
773             StatusCode = self.TargetTxt.LoadTargetTxtFile(BuildConfigurationFile)\r
774 \r
775             ToolDefinitionFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_CONF]\r
776             if ToolDefinitionFile == '':\r
777                 ToolDefinitionFile = gToolsDefinition\r
778             ToolDefinitionFile = os.path.normpath(os.path.join(self.WorkspaceDir, ToolDefinitionFile))\r
779             if os.path.isfile(ToolDefinitionFile) == True:\r
780                 StatusCode = self.ToolDef.LoadToolDefFile(ToolDefinitionFile)\r
781             else:\r
782                 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=ToolDefinitionFile)\r
783         else:\r
784             EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=BuildConfigurationFile)\r
785 \r
786         # if no ARCH given in command line, get it from target.txt\r
787         if self.ArchList == None or len(self.ArchList) == 0:\r
788             self.ArchList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET_ARCH]\r
789 \r
790         # if no build target given in command line, get it from target.txt\r
791         if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
792             self.BuildTargetList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET]\r
793 \r
794         # if no tool chain given in command line, get it from target.txt\r
795         if self.ToolChainList == None or len(self.ToolChainList) == 0:\r
796             self.ToolChainList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_TAG]\r
797             if self.ToolChainList == None or len(self.ToolChainList) == 0:\r
798                 EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, ExtraData="No toolchain given. Don't know how to build.\n")\r
799 \r
800         # check if the tool chains are defined or not\r
801         NewToolChainList = []\r
802         for ToolChain in self.ToolChainList:\r
803             if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG]:\r
804                 EdkLogger.warn("build", "Tool chain [%s] is not defined" % ToolChain)\r
805             else:\r
806                 NewToolChainList.append(ToolChain)\r
807         # if no tool chain available, break the build\r
808         if len(NewToolChainList) == 0:\r
809             EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,\r
810                             ExtraData="[%s] not defined. No toolchain available for build!\n" % ", ".join(self.ToolChainList))\r
811         else:\r
812             self.ToolChainList = NewToolChainList\r
813 \r
814         if self.ThreadNumber == None:\r
815             self.ThreadNumber = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER]\r
816             if self.ThreadNumber == '':\r
817                 self.ThreadNumber = 0\r
818             else:\r
819                 self.ThreadNumber = int(self.ThreadNumber, 0)\r
820 \r
821         if self.ThreadNumber == 0:\r
822             self.ThreadNumber = 1\r
823 \r
824         if not self.PlatformFile:\r
825             PlatformFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_ACTIVE_PLATFORM]\r
826             if not PlatformFile:\r
827                 # Try to find one in current directory\r
828                 WorkingDirectory = os.getcwd()\r
829                 FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.dsc')))\r
830                 FileNum = len(FileList)\r
831                 if FileNum >= 2:\r
832                     EdkLogger.error("build", OPTION_MISSING,\r
833                                     ExtraData="There are %d DSC files in %s. Use '-p' to specify one.\n" % (FileNum, WorkingDirectory))\r
834                 elif FileNum == 1:\r
835                     PlatformFile = FileList[0]\r
836                 else:\r
837                     EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,\r
838                                     ExtraData="No active platform specified in target.txt or command line! Nothing can be built.\n")\r
839 \r
840             self.PlatformFile = PathClass(NormFile(PlatformFile, self.WorkspaceDir), self.WorkspaceDir)\r
841             ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False)\r
842             if ErrorCode != 0:\r
843                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
844 \r
845     ## Initialize build configuration\r
846     #\r
847     #   This method will parse DSC file and merge the configurations from\r
848     #   command line and target.txt, then get the final build configurations.\r
849     #\r
850     def InitBuild(self):\r
851         ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc")\r
852         if ErrorCode != 0:\r
853             EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
854 \r
855         # create metafile database\r
856         self.Db.InitDatabase()\r
857 \r
858         # we need information in platform description file to determine how to build\r
859         self.Platform = self.BuildDatabase[self.PlatformFile, 'COMMON']\r
860         if not self.Fdf:\r
861             self.Fdf = self.Platform.FlashDefinition\r
862         \r
863         LoadFixAddressString = None\r
864         if TAB_FIX_LOAD_TOP_MEMORY_ADDRESS in GlobalData.gGlobalDefines:\r
865             LoadFixAddressString = GlobalData.gGlobalDefines[TAB_FIX_LOAD_TOP_MEMORY_ADDRESS]\r
866         else:\r
867             LoadFixAddressString = self.Platform.LoadFixAddress\r
868 \r
869         if LoadFixAddressString != None and LoadFixAddressString != '':\r
870             try:\r
871                 if LoadFixAddressString.upper().startswith('0X'):\r
872                     self.LoadFixAddress = int (LoadFixAddressString, 16)\r
873                 else:\r
874                     self.LoadFixAddress = int (LoadFixAddressString)\r
875             except:
876                 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS %s is not valid dec or hex string" % (LoadFixAddressString))\r
877             if self.LoadFixAddress < 0:\r
878                 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS can't be set to negative value %s" % (LoadFixAddressString))\r
879 \r
880         if self.SkuId == None or self.SkuId == '':\r
881             self.SkuId = self.Platform.SkuName\r
882 \r
883         # check FD/FV build target\r
884         if self.Fdf == None or self.Fdf == "":\r
885             if self.FdList != []:\r
886                 EdkLogger.info("No flash definition file found. FD [%s] will be ignored." % " ".join(self.FdList))\r
887                 self.FdList = []\r
888             if self.FvList != []:\r
889                 EdkLogger.info("No flash definition file found. FV [%s] will be ignored." % " ".join(self.FvList))\r
890                 self.FvList = []\r
891         else:\r
892             FdfParserObj = FdfParser(str(self.Fdf))\r
893             FdfParserObj.ParseFile()\r
894             for fvname in self.FvList:\r
895                 if fvname.upper() not in FdfParserObj.Profile.FvDict.keys():\r
896                     EdkLogger.error("build", OPTION_VALUE_INVALID,\r
897                                     "No such an FV in FDF file: %s" % fvname)\r
898 \r
899         #\r
900         # Merge Arch\r
901         #\r
902         if self.ArchList == None or len(self.ArchList) == 0:\r
903             ArchList = set(self.Platform.SupArchList)\r
904         else:\r
905             ArchList = set(self.ArchList) & set(self.Platform.SupArchList)\r
906         if len(ArchList) == 0:\r
907             EdkLogger.error("build", PARAMETER_INVALID,\r
908                             ExtraData = "Active platform supports [%s] only, but [%s] is given."\r
909                                         % (" ".join(self.Platform.SupArchList), " ".join(self.ArchList)))\r
910         elif len(ArchList) != len(self.ArchList):\r
911             SkippedArchList = set(self.ArchList).symmetric_difference(set(self.Platform.SupArchList))\r
912             EdkLogger.verbose("\nArch [%s] is ignored because active platform supports [%s] but [%s] is specified !"\r
913                            % (" ".join(SkippedArchList), " ".join(self.Platform.SupArchList), " ".join(self.ArchList)))\r
914         self.ArchList = tuple(ArchList)\r
915 \r
916         # Merge build target\r
917         if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
918             BuildTargetList = self.Platform.BuildTargets\r
919         else:\r
920             BuildTargetList = list(set(self.BuildTargetList) & set(self.Platform.BuildTargets))\r
921         if BuildTargetList == []:\r
922             EdkLogger.error("build", PARAMETER_INVALID, "Active platform only supports [%s], but [%s] is given"\r
923                                 % (" ".join(self.Platform.BuildTargets), " ".join(self.BuildTargetList)))\r
924         self.BuildTargetList = BuildTargetList\r
925 \r
926     ## Build a module or platform\r
927     #\r
928     # Create autogen code and makfile for a module or platform, and the launch\r
929     # "make" command to build it\r
930     #\r
931     #   @param  Target                      The target of build command\r
932     #   @param  Platform                    The platform file\r
933     #   @param  Module                      The module file\r
934     #   @param  BuildTarget                 The name of build target, one of "DEBUG", "RELEASE"\r
935     #   @param  ToolChain                   The name of toolchain to build\r
936     #   @param  Arch                        The arch of the module/platform\r
937     #   @param  CreateDepModuleCodeFile     Flag used to indicate creating code\r
938     #                                       for dependent modules/Libraries\r
939     #   @param  CreateDepModuleMakeFile     Flag used to indicate creating makefile\r
940     #                                       for dependent modules/Libraries\r
941     #\r
942     def _Build(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True):\r
943         if AutoGenObject == None:\r
944             return False\r
945 \r
946         # skip file generation for cleanxxx targets, run and fds target\r
947         if Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
948             # for target which must generate AutoGen code and makefile\r
949             if not self.SkipAutoGen or Target == 'genc':\r
950                 self.Progress.Start("Generating code")\r
951                 AutoGenObject.CreateCodeFile(CreateDepsCodeFile)\r
952                 self.Progress.Stop("done!")\r
953             if Target == "genc":\r
954                 return True\r
955 \r
956             if not self.SkipAutoGen or Target == 'genmake':\r
957                 self.Progress.Start("Generating makefile")\r
958                 AutoGenObject.CreateMakeFile(CreateDepsMakeFile)\r
959                 self.Progress.Stop("done!")\r
960             if Target == "genmake":\r
961                 return True\r
962         else:\r
963             # always recreate top/platform makefile when clean, just in case of inconsistency\r
964             AutoGenObject.CreateCodeFile(False)\r
965             AutoGenObject.CreateMakeFile(False)\r
966 \r
967         if EdkLogger.GetLevel() == EdkLogger.QUIET:\r
968             EdkLogger.quiet("Building ... %s" % repr(AutoGenObject))\r
969 \r
970         BuildCommand = AutoGenObject.BuildCommand\r
971         if BuildCommand == None or len(BuildCommand) == 0:\r
972             EdkLogger.error("build", OPTION_MISSING, ExtraData="No MAKE command found for [%s, %s, %s]" % Key)\r
973 \r
974         BuildCommand = BuildCommand + [Target]\r
975         LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir)\r
976         if Target == 'cleanall':\r
977             try:\r
978                 #os.rmdir(AutoGenObject.BuildDir)\r
979                 RemoveDirectory(AutoGenObject.BuildDir, True)\r
980             except WindowsError, X:\r
981                 EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X))\r
982         return True\r
983 \r
984     ## Rebase module image and Get function address for the inpug module list.\r
985     #\r
986     def _RebaseModule (self, MapBuffer, BaseAddress, ModuleList, AddrIsOffset = True, ModeIsSmm = False):\r
987         if ModeIsSmm:\r
988             AddrIsOffset = False\r
989         InfFileNameList = ModuleList.keys()\r
990         #InfFileNameList.sort()\r
991         for InfFile in InfFileNameList:\r
992             sys.stdout.write (".")
993             sys.stdout.flush()
994             ModuleInfo = ModuleList[InfFile]\r
995             ModuleName = ModuleInfo.BaseName\r
996             ## for SMM module in SMRAM, the SMRAM will be allocated from base to top.\r
997             if not ModeIsSmm:\r
998                 BaseAddress = BaseAddress - ModuleInfo.Image.Size\r
999                 #\r
1000                 # Update Image to new BaseAddress by GenFw tool\r
1001                 #\r
1002                 LaunchCommand(["GenFw", "--rebase", str(BaseAddress), "-r", ModuleInfo.Image.FileName], ModuleInfo.OutpuDir)\r
1003             else:\r
1004                 #\r
1005                 # Set new address to the section header only for SMM driver.\r
1006                 #\r
1007                 LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleInfo.Image.FileName], ModuleInfo.OutpuDir)\r
1008             #\r
1009             # Add general information.\r
1010             #\r
1011             if ModeIsSmm:\r
1012                 MapBuffer.write('\n\n%s (Fix SMRAM Offset,   BaseAddress=0x%010X,  EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint))\r
1013             elif AddrIsOffset:\r
1014                 MapBuffer.write('\n\n%s (Fix Memory Offset,  BaseAddress=-0x%010X, EntryPoint=-0x%010X)\n' % (ModuleName, 0 - BaseAddress, 0 - (BaseAddress + ModuleInfo.Image.EntryPoint)))\r
1015             else:\r
1016                 MapBuffer.write('\n\n%s (Fix Memory Address, BaseAddress=0x%010X,  EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint))\r
1017             #\r
1018             # Add guid and general seciton section.\r
1019             #\r
1020             TextSectionAddress = 0\r
1021             DataSectionAddress = 0\r
1022             for SectionHeader in ModuleInfo.Image.SectionHeaderList:\r
1023                 if SectionHeader[0] == '.text':\r
1024                     TextSectionAddress = SectionHeader[1]\r
1025                 elif SectionHeader[1] == '.data':\r
1026                     DataSectionAddress = SectionHeader[1]\r
1027             if AddrIsOffset:\r
1028                 MapBuffer.write('(GUID=%s, .textbaseaddress=-0x%010X, .databaseaddress=-0x%010X)\n\n' % (ModuleInfo.Guid, 0 - (BaseAddress + TextSectionAddress), 0 - (BaseAddress + DataSectionAddress))) \r
1029             else:\r
1030                 MapBuffer.write('(GUID=%s, .textbaseaddress=0x%010X, .databaseaddress=0x%010X)\n\n' % (ModuleInfo.Guid, BaseAddress + TextSectionAddress, BaseAddress + DataSectionAddress)) \r
1031             #\r
1032             # Add funtion address\r
1033             #\r
1034             ImageMapTable = ModuleInfo.Image.FileName.replace('.efi', '.map')\r
1035             if not os.path.exists(ImageMapTable):\r
1036                 continue\r
1037             ImageMap = open (ImageMapTable, 'r')\r
1038             for LinStr in ImageMap:\r
1039                 if len (LinStr.strip()) == 0:\r
1040                     continue\r
1041                 StrList = LinStr.split()\r
1042                 if len (StrList) > 4:\r
1043                     if StrList[3] == 'f' or StrList[3] =='F':\r
1044                         if AddrIsOffset:\r
1045                             MapBuffer.write('  -0x%010X    %s\n' % (0 - (BaseAddress + int (StrList[2], 16)), StrList[1]))\r
1046                         else:\r
1047                             MapBuffer.write('  0x%010X    %s\n' % (BaseAddress + int (StrList[2], 16), StrList[1]))\r
1048             ImageMap.close()\r
1049 \r
1050             #\r
1051             # for SMM module in SMRAM, the SMRAM will be allocated from base to top.\r
1052             #\r
1053             if ModeIsSmm:\r
1054                 BaseAddress = BaseAddress + ModuleInfo.Image.Size\r
1055 \r
1056     ## Collect MAP information of all FVs\r
1057     #\r
1058     def _CollectFvMapBuffer (self, MapBuffer, Wa):\r
1059         if self.Fdf != '':\r
1060             # First get the XIP base address for FV map file.\r
1061             for FvName in Wa.FdfProfile.FvDict.keys():\r
1062                 FvMapBuffer = os.path.join(Wa.FvDir, FvName + '.Fv.map')\r
1063                 if not os.path.exists(FvMapBuffer):\r
1064                     continue\r
1065                 FvMap = open (FvMapBuffer, 'r')\r
1066                 #skip FV size information\r
1067                 FvMap.readline()\r
1068                 FvMap.readline()\r
1069                 FvMap.readline()\r
1070                 FvMap.readline()\r
1071                 MapBuffer.write(FvMap.read())\r
1072                 FvMap.close()\r
1073 \r
1074     ## Collect MAP information of all modules\r
1075     #\r
1076     def _CollectModuleMapBuffer (self, MapBuffer, ModuleList):\r
1077         sys.stdout.write ("Generate Load Module At Fix Address Map")
1078         sys.stdout.flush()
1079         PatchEfiImageList = []\r
1080         PeiModuleList  = {}\r
1081         BtModuleList   = {}\r
1082         RtModuleList   = {}\r
1083         SmmModuleList  = {}\r
1084         PeiSize = 0\r
1085         BtSize  = 0\r
1086         RtSize  = 0\r
1087         # reserve 4K size in SMRAM to make SMM module address not from 0.\r
1088         SmmSize = 0x1000\r
1089         for Module in ModuleList:\r
1090             GlobalData.gProcessingFile = "%s [%s, %s, %s]" % (Module.MetaFile, Module.Arch, Module.ToolChain, Module.BuildTarget)\r
1091             \r
1092             OutputImageFile = ''\r
1093             for ResultFile in Module.CodaTargetList:\r
1094                 if str(ResultFile.Target).endswith('.efi'):\r
1095                     #\r
1096                     # module list for PEI, DXE, RUNTIME and SMM\r
1097                     #\r
1098                     OutputImageFile = os.path.join(Module.OutputDir, Module.Name + '.efi')\r
1099                     ImageClass = PeImageClass (OutputImageFile)\r
1100                     if not ImageClass.IsValid:\r
1101                         EdkLogger.error("build", FILE_PARSE_FAILURE, ExtraData=ImageClass.ErrorInfo)\r
1102                     ImageInfo = PeImageInfo(Module.Name, Module.Guid, Module.OutputDir, ImageClass)\r
1103                     if Module.ModuleType in ['PEI_CORE', 'PEIM', 'COMBINED_PEIM_DRIVER','PIC_PEIM', 'RELOCATABLE_PEIM', 'DXE_CORE']:\r
1104                         PeiModuleList[Module.MetaFile] = ImageInfo\r
1105                         PeiSize += ImageClass.Size\r
1106                     elif Module.ModuleType in ['BS_DRIVER', 'DXE_DRIVER', 'UEFI_DRIVER']:\r
1107                         BtModuleList[Module.MetaFile] = ImageInfo\r
1108                         BtSize += ImageClass.Size\r
1109                     elif Module.ModuleType in ['DXE_RUNTIME_DRIVER', 'RT_DRIVER', 'DXE_SAL_DRIVER', 'SAL_RT_DRIVER']:\r
1110                         RtModuleList[Module.MetaFile] = ImageInfo\r
1111                         RtSize += ImageClass.Size\r
1112                     elif Module.ModuleType in ['SMM_CORE', 'DXE_SMM_DRIVER']:\r
1113                         SmmModuleList[Module.MetaFile] = ImageInfo\r
1114                         SmmSize += ImageClass.Size\r
1115                         if Module.ModuleType == 'DXE_SMM_DRIVER':\r
1116                             PiSpecVersion = 0
1117                             if 'PI_SPECIFICATION_VERSION' in Module.Module.Specification:
1118                                 PiSpecVersion = Module.Module.Specification['PI_SPECIFICATION_VERSION']
1119                             # for PI specification < PI1.1, DXE_SMM_DRIVER also runs as BOOT time driver.\r
1120                             if PiSpecVersion < 0x0001000A:\r
1121                                 BtModuleList[Module.MetaFile] = ImageInfo\r
1122                                 BtSize += ImageClass.Size\r
1123                     break\r
1124             #\r
1125             # EFI image is final target.\r
1126             # Check EFI image contains patchable FixAddress related PCDs.\r
1127             #\r
1128             if OutputImageFile != '':\r
1129                 ModuleIsPatch = False\r
1130                 for Pcd in Module.ModulePcdList:\r
1131                     if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_LIST:\r
1132                         ModuleIsPatch = True\r
1133                         break\r
1134                 if not ModuleIsPatch:\r
1135                     for Pcd in Module.LibraryPcdList:\r
1136                         if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_LIST:\r
1137                             ModuleIsPatch = True\r
1138                             break\r
1139                 \r
1140                 if not ModuleIsPatch:\r
1141                     continue\r
1142                 #\r
1143                 # Module includes the patchable load fix address PCDs.\r
1144                 # It will be fixed up later. \r
1145                 #\r
1146                 PatchEfiImageList.append (OutputImageFile)\r
1147         \r
1148         #\r
1149         # Patch FixAddress related PCDs into EFI image\r
1150         #\r
1151         for EfiImage in PatchEfiImageList: \r
1152             EfiImageMap = EfiImage.replace('.efi', '.map')\r
1153             if not os.path.exists(EfiImageMap):\r
1154                 continue\r
1155             #\r
1156             # Get PCD offset in EFI image by GenPatchPcdTable function\r
1157             #\r
1158             PcdTable = parsePcdInfoFromMapFile(EfiImageMap, EfiImage) 
1159             #\r
1160             # Patch real PCD value by PatchPcdValue tool\r
1161             #\r
1162             for PcdInfo in PcdTable:\r
1163                 ReturnValue = 0\r
1164                 if PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE:\r
1165                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE_DATA_TYPE, str (PeiSize/0x1000))\r
1166                 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE:\r
1167                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE_DATA_TYPE, str (BtSize/0x1000))\r
1168                 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE:\r
1169                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE_DATA_TYPE, str (RtSize/0x1000))\r
1170                 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE:\r
1171                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE_DATA_TYPE, str (SmmSize/0x1000))\r
1172                 if ReturnValue != 0:\r
1173                     EdkLogger.error("build", PARAMETER_INVALID, "Patch PCD value failed", ExtraData=ErrorInfo)\r
1174         #\r
1175         # Get Top Memory address\r
1176         #\r
1177         TopMemoryAddress = 0\r
1178         if self.LoadFixAddress == 0xFFFFFFFFFFFFFFFF:\r
1179             TopMemoryAddress = 0\r
1180         else:\r
1181             TopMemoryAddress = self.LoadFixAddress\r
1182             if TopMemoryAddress < RtSize + BtSize + PeiSize:\r
1183                 EdkLogger.error("build", PARAMETER_INVALID, "TopMemoryAddress is too low to load driver")\r
1184         \r
1185         MapBuffer.write('PEI_CODE_PAGE_NUMBER      = 0x%x\n' % (PeiSize/0x1000))\r
1186         MapBuffer.write('BOOT_CODE_PAGE_NUMBER     = 0x%x\n' % (BtSize/0x1000))\r
1187         MapBuffer.write('RUNTIME_CODE_PAGE_NUMBER  = 0x%x\n' % (RtSize/0x1000))\r
1188         MapBuffer.write('SMM_CODE_PAGE_NUMBER      = 0x%x\n' % (SmmSize/0x1000))\r
1189         \r
1190         PeiBaseAddr = TopMemoryAddress - RtSize - BtSize\r
1191         BtBaseAddr  = TopMemoryAddress - RtSize\r
1192         RtBaseAddr  = TopMemoryAddress\r
1193 \r
1194         self._RebaseModule (MapBuffer, PeiBaseAddr, PeiModuleList, TopMemoryAddress == 0)\r
1195         self._RebaseModule (MapBuffer, BtBaseAddr, BtModuleList, TopMemoryAddress == 0)\r
1196         self._RebaseModule (MapBuffer, RtBaseAddr, RtModuleList, TopMemoryAddress == 0)\r
1197         self._RebaseModule (MapBuffer, 0x1000, SmmModuleList, AddrIsOffset = False, ModeIsSmm = True)\r
1198         MapBuffer.write('\n\n')\r
1199         sys.stdout.write ("\n")
1200         sys.stdout.flush()
1201     \r
1202     ## Save platform Map file\r
1203     #\r
1204     def _SaveMapFile (self, MapBuffer, Wa):\r
1205         #\r
1206         # Map file path is got.\r
1207         #\r
1208         MapFilePath = os.path.join(Wa.BuildDir, Wa.Name + '.map')\r
1209         #\r
1210         # Save address map into MAP file.\r
1211         #\r
1212         SaveFileOnChange(MapFilePath, MapBuffer.getvalue(), False)\r
1213         MapBuffer.close()\r
1214         sys.stdout.write ("\nLoad Module At Fix Address Map file saved to %s\n" %(MapFilePath))
1215         sys.stdout.flush()
1216 \r
1217     ## Build active platform for different build targets and different tool chains\r
1218     #\r
1219     def _BuildPlatform(self):\r
1220         for BuildTarget in self.BuildTargetList:\r
1221             for ToolChain in self.ToolChainList:\r
1222                 Wa = WorkspaceAutoGen(\r
1223                         self.WorkspaceDir,\r
1224                         self.Platform,\r
1225                         BuildTarget,\r
1226                         ToolChain,\r
1227                         self.ArchList,\r
1228                         self.BuildDatabase,\r
1229                         self.TargetTxt,\r
1230                         self.ToolDef,\r
1231                         self.Fdf,\r
1232                         self.FdList,\r
1233                         self.FvList,\r
1234                         self.SkuId\r
1235                         )\r
1236                 self.BuildReport.AddPlatformReport(Wa)\r
1237                 self.Progress.Stop("done!")\r
1238                 self._Build(self.Target, Wa)\r
1239                 \r
1240                 # Create MAP file when Load Fix Address is enabled.\r
1241                 if self.Target in ["", "all", "fds"] and self.LoadFixAddress != 0:\r
1242                     for Arch in self.ArchList:\r
1243                         #\r
1244                         # Check whether the set fix address is above 4G for 32bit image.\r
1245                         #\r
1246                         if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
1247                             EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS can't be set to larger than 4G for the platorm with IA32 arch modules")\r
1248                     #\r
1249                     # Get Module List\r
1250                     #\r
1251                     ModuleList = []\r
1252                     for Pa in Wa.AutoGenObjectList:\r
1253                         for Ma in Pa.ModuleAutoGenList:\r
1254                             if Ma == None:\r
1255                                 continue\r
1256                             if not Ma.IsLibrary:\r
1257                                 ModuleList.append (Ma)\r
1258 \r
1259                     MapBuffer = StringIO('')\r
1260                     #\r
1261                     # Rebase module to the preferred memory address before GenFds\r
1262                     #\r
1263                     self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
1264                     if self.Fdf != '':\r
1265                         #\r
1266                         # create FDS again for the updated EFI image\r
1267                         #\r
1268                         self._Build("fds", Wa)\r
1269                         #\r
1270                         # Create MAP file for all platform FVs after GenFds.\r
1271                         #\r
1272                         self._CollectFvMapBuffer(MapBuffer, Wa)\r
1273                     #\r
1274                     # Save MAP buffer into MAP file.\r
1275                     #\r
1276                     self._SaveMapFile (MapBuffer, Wa)\r
1277 \r
1278     ## Build active module for different build targets, different tool chains and different archs\r
1279     #\r
1280     def _BuildModule(self):\r
1281         for BuildTarget in self.BuildTargetList:\r
1282             for ToolChain in self.ToolChainList:\r
1283                 #\r
1284                 # module build needs platform build information, so get platform\r
1285                 # AutoGen first\r
1286                 #\r
1287                 Wa = WorkspaceAutoGen(\r
1288                         self.WorkspaceDir,\r
1289                         self.Platform,\r
1290                         BuildTarget,\r
1291                         ToolChain,\r
1292                         self.ArchList,\r
1293                         self.BuildDatabase,\r
1294                         self.TargetTxt,\r
1295                         self.ToolDef,\r
1296                         self.Fdf,\r
1297                         self.FdList,\r
1298                         self.FvList,\r
1299                         self.SkuId\r
1300                         )\r
1301                 self.BuildReport.AddPlatformReport(Wa)\r
1302                 Wa.CreateMakeFile(False)\r
1303                 self.Progress.Stop("done!")\r
1304                 MaList = []\r
1305                 for Arch in self.ArchList:\r
1306                     Ma = ModuleAutoGen(Wa, self.ModuleFile, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
1307                     if Ma == None: continue\r
1308                     MaList.append(Ma)\r
1309                     self._Build(self.Target, Ma)\r
1310                 if MaList == []:\r
1311                     EdkLogger.error(\r
1312                                 'build',\r
1313                                 BUILD_ERROR,\r
1314                                 "Module for [%s] is not a component of active platform."\\r
1315                                 " Please make sure that the ARCH and inf file path are"\\r
1316                                 " given in the same as in [%s]" %\\r
1317                                     (', '.join(self.ArchList), self.Platform),\r
1318                                 ExtraData=self.ModuleFile\r
1319                                 )\r
1320 \r
1321     ## Build a platform in multi-thread mode\r
1322     #\r
1323     def _MultiThreadBuildPlatform(self):\r
1324         for BuildTarget in self.BuildTargetList:\r
1325             for ToolChain in self.ToolChainList:\r
1326                 Wa = WorkspaceAutoGen(\r
1327                         self.WorkspaceDir,\r
1328                         self.Platform,\r
1329                         BuildTarget,\r
1330                         ToolChain,\r
1331                         self.ArchList,\r
1332                         self.BuildDatabase,\r
1333                         self.TargetTxt,\r
1334                         self.ToolDef,\r
1335                         self.Fdf,\r
1336                         self.FdList,\r
1337                         self.FvList,\r
1338                         self.SkuId\r
1339                         )\r
1340                 self.BuildReport.AddPlatformReport(Wa)\r
1341                 Wa.CreateMakeFile(False)\r
1342 \r
1343                 # multi-thread exit flag\r
1344                 ExitFlag = threading.Event()\r
1345                 ExitFlag.clear()\r
1346                 for Arch in self.ArchList:\r
1347                     Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)\r
1348                     if Pa == None:\r
1349                         continue\r
1350                     for Module in Pa.Platform.Modules:\r
1351                         # Get ModuleAutoGen object to generate C code file and makefile\r
1352                         Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
1353                         if Ma == None:\r
1354                             continue\r
1355                         # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'\r
1356                         if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
1357                             # for target which must generate AutoGen code and makefile\r
1358                             if not self.SkipAutoGen or self.Target == 'genc':\r
1359                                 Ma.CreateCodeFile(True)\r
1360                             if self.Target == "genc":\r
1361                                 continue\r
1362 \r
1363                             if not self.SkipAutoGen or self.Target == 'genmake':\r
1364                                 Ma.CreateMakeFile(True)\r
1365                             if self.Target == "genmake":\r
1366                                 continue\r
1367                         self.Progress.Stop("done!")\r
1368                         # Generate build task for the module\r
1369                         Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))\r
1370                         # Break build if any build thread has error\r
1371                         if BuildTask.HasError():\r
1372                             # we need a full version of makefile for platform\r
1373                             ExitFlag.set()\r
1374                             BuildTask.WaitForComplete()\r
1375                             Pa.CreateMakeFile(False)\r
1376                             EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1377                         # Start task scheduler\r
1378                         if not BuildTask.IsOnGoing():\r
1379                             BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)\r
1380 \r
1381                     # in case there's an interruption. we need a full version of makefile for platform\r
1382                     Pa.CreateMakeFile(False)\r
1383                     if BuildTask.HasError():\r
1384                         EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1385 \r
1386                 #\r
1387                 # All modules have been put in build tasks queue. Tell task scheduler\r
1388                 # to exit if all tasks are completed\r
1389                 #\r
1390                 ExitFlag.set()\r
1391                 BuildTask.WaitForComplete()\r
1392 \r
1393                 #\r
1394                 # Check for build error, and raise exception if one\r
1395                 # has been signaled.\r
1396                 #\r
1397                 if BuildTask.HasError():\r
1398                     EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1399 \r
1400                 # Create MAP file when Load Fix Address is enabled.\r
1401                 if self.Target in ["", "all", "fds"] and self.LoadFixAddress != 0:\r
1402                     for Arch in self.ArchList:\r
1403                         #\r
1404                         # Check whether the set fix address is above 4G for 32bit image.\r
1405                         #\r
1406                         if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
1407                             EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS can't be set to larger than 4G for the platorm with IA32 arch modules")\r
1408                     #\r
1409                     # Get Module List\r
1410                     #\r
1411                     ModuleList = []\r
1412                     for Pa in Wa.AutoGenObjectList:\r
1413                         for Ma in Pa.ModuleAutoGenList:\r
1414                             if Ma == None:\r
1415                                 continue\r
1416                             if not Ma.IsLibrary:\r
1417                                 ModuleList.append (Ma)\r
1418                     #\r
1419                     # Rebase module to the preferred memory address before GenFds\r
1420                     #\r
1421                     MapBuffer = StringIO('')\r
1422                     self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
1423 \r
1424                 # Generate FD image if there's a FDF file found\r
1425                 if self.Fdf != '' and self.Target in ["", "all", "fds"]:\r
1426                     LaunchCommand(Wa.BuildCommand + ["fds"], Wa.MakeFileDir)\r
1427 \r
1428                 # Create MAP file for all platform FV after GenFds\r
1429                 if self.Target in ["", "all", "fds"] and self.LoadFixAddress != 0:\r
1430                     if self.Fdf != '':\r
1431                         #\r
1432                         # Create MAP file for all platform FVs after GenFds.\r
1433                         #\r
1434                         self._CollectFvMapBuffer(MapBuffer, Wa)\r
1435                     #\r
1436                     # Save MAP buffer into MAP file.\r
1437                     #\r
1438                     self._SaveMapFile(MapBuffer, Wa)\r
1439 \r
1440     ## Generate GuidedSectionTools.txt in the FV directories.\r
1441     #\r
1442     def CreateGuidedSectionToolsFile(self):\r
1443         for Arch in self.ArchList:\r
1444             for BuildTarget in self.BuildTargetList:\r
1445                 for ToolChain in self.ToolChainList:\r
1446                     FvDir = os.path.join(\r
1447                                 self.WorkspaceDir,\r
1448                                 self.Platform.OutputDirectory,\r
1449                                 '_'.join((BuildTarget, ToolChain)),\r
1450                                 'FV'\r
1451                                 )\r
1452                     if not os.path.exists(FvDir):\r
1453                         continue\r
1454                     # Build up the list of supported architectures for this build\r
1455                     prefix = '%s_%s_%s_' % (BuildTarget, ToolChain, Arch)\r
1456 \r
1457                     # Look through the tool definitions for GUIDed tools\r
1458                     guidAttribs = []\r
1459                     for (attrib, value) in self.ToolDef.ToolsDefTxtDictionary.iteritems():\r
1460                         if attrib.upper().endswith('_GUID'):\r
1461                             split = attrib.split('_')\r
1462                             thisPrefix = '_'.join(split[0:3]) + '_'\r
1463                             if thisPrefix == prefix:\r
1464                                 guid = self.ToolDef.ToolsDefTxtDictionary[attrib]\r
1465                                 guid = guid.lower()\r
1466                                 toolName = split[3]\r
1467                                 path = '_'.join(split[0:4]) + '_PATH'\r
1468                                 path = self.ToolDef.ToolsDefTxtDictionary[path]\r
1469                                 path = self.GetFullPathOfTool(path)\r
1470                                 guidAttribs.append((guid, toolName, path))\r
1471 \r
1472                     # Write out GuidedSecTools.txt\r
1473                     toolsFile = os.path.join(FvDir, 'GuidedSectionTools.txt')\r
1474                     toolsFile = open(toolsFile, 'wt')\r
1475                     for guidedSectionTool in guidAttribs:\r
1476                         print >> toolsFile, ' '.join(guidedSectionTool)\r
1477                     toolsFile.close()\r
1478 \r
1479     ## Returns the full path of the tool.\r
1480     #\r
1481     def GetFullPathOfTool (self, tool):\r
1482         if os.path.exists(tool):\r
1483             return os.path.realpath(tool)\r
1484         else:\r
1485             # We need to search for the tool using the\r
1486             # PATH environment variable.\r
1487             for dirInPath in os.environ['PATH'].split(os.pathsep):\r
1488                 foundPath = os.path.join(dirInPath, tool)\r
1489                 if os.path.exists(foundPath):\r
1490                     return os.path.realpath(foundPath)\r
1491 \r
1492         # If the tool was not found in the path then we just return\r
1493         # the input tool.\r
1494         return tool\r
1495 \r
1496     ## Launch the module or platform build\r
1497     #\r
1498     def Launch(self):\r
1499         if self.ModuleFile == None or self.ModuleFile == "":\r
1500             if not self.SpawnMode or self.Target not in ["", "all"]:\r
1501                 self.SpawnMode = False\r
1502                 self._BuildPlatform()\r
1503             else:\r
1504                 self._MultiThreadBuildPlatform()\r
1505             self.CreateGuidedSectionToolsFile()\r
1506         else:\r
1507             self.SpawnMode = False\r
1508             self._BuildModule()\r
1509 \r
1510     ## Do some clean-up works when error occurred\r
1511     def Relinquish(self):\r
1512         OldLogLevel = EdkLogger.GetLevel()\r
1513         EdkLogger.SetLevel(EdkLogger.ERROR)\r
1514         #self.DumpBuildData()\r
1515         Utils.Progressor.Abort()\r
1516         if self.SpawnMode == True:\r
1517             BuildTask.Abort()\r
1518         EdkLogger.SetLevel(OldLogLevel)\r
1519 \r
1520     def DumpBuildData(self):\r
1521         CacheDirectory = os.path.join(self.WorkspaceDir, gBuildCacheDir)\r
1522         Utils.CreateDirectory(CacheDirectory)\r
1523         Utils.DataDump(Utils.gFileTimeStampCache, os.path.join(CacheDirectory, "gFileTimeStampCache"))\r
1524         Utils.DataDump(Utils.gDependencyDatabase, os.path.join(CacheDirectory, "gDependencyDatabase"))\r
1525 \r
1526     def RestoreBuildData(self):\r
1527         FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gFileTimeStampCache")\r
1528         if Utils.gFileTimeStampCache == {} and os.path.isfile(FilePath):\r
1529             Utils.gFileTimeStampCache = Utils.DataRestore(FilePath)\r
1530             if Utils.gFileTimeStampCache == None:\r
1531                 Utils.gFileTimeStampCache = {}\r
1532 \r
1533         FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gDependencyDatabase")\r
1534         if Utils.gDependencyDatabase == {} and os.path.isfile(FilePath):\r
1535             Utils.gDependencyDatabase = Utils.DataRestore(FilePath)\r
1536             if Utils.gDependencyDatabase == None:\r
1537                 Utils.gDependencyDatabase = {}\r
1538 \r
1539 def ParseDefines(DefineList=[]):\r
1540     DefineDict = {}\r
1541     if DefineList != None:\r
1542         for Define in DefineList:\r
1543             DefineTokenList = Define.split("=", 1)\r
1544             if len(DefineTokenList) == 1:\r
1545                 DefineDict[DefineTokenList[0]] = ""\r
1546             else:\r
1547                 DefineDict[DefineTokenList[0]] = DefineTokenList[1].strip()\r
1548     return DefineDict\r
1549 \r
1550 gParamCheck = []\r
1551 def SingleCheckCallback(option, opt_str, value, parser):\r
1552     if option not in gParamCheck:\r
1553         setattr(parser.values, option.dest, value)\r
1554         gParamCheck.append(option)\r
1555     else:\r
1556         parser.error("Option %s only allows one instance in command line!" % option)\r
1557 \r
1558 ## Parse command line options\r
1559 #\r
1560 # Using standard Python module optparse to parse command line option of this tool.\r
1561 #\r
1562 #   @retval Opt   A optparse.Values object containing the parsed options\r
1563 #   @retval Args  Target of build command\r
1564 #\r
1565 def MyOptionParser():\r
1566     Parser = OptionParser(description=__copyright__,version=__version__,prog="build.exe",usage="%prog [options] [all|fds|genc|genmake|clean|cleanall|cleanlib|modules|libraries|run]")\r
1567     Parser.add_option("-a", "--arch", action="append", type="choice", choices=['IA32','X64','IPF','EBC','ARM'], dest="TargetArch",\r
1568         help="ARCHS is one of list: IA32, X64, IPF, ARM or EBC, which overrides target.txt's TARGET_ARCH definition. To specify more archs, please repeat this option.")\r
1569     Parser.add_option("-p", "--platform", action="callback", type="string", dest="PlatformFile", callback=SingleCheckCallback,\r
1570         help="Build the platform specified by the DSC file name argument, overriding target.txt's ACTIVE_PLATFORM definition.")\r
1571     Parser.add_option("-m", "--module", action="callback", type="string", dest="ModuleFile", callback=SingleCheckCallback,\r
1572         help="Build the module specified by the INF file name argument.")\r
1573     Parser.add_option("-b", "--buildtarget", action="append", type="choice", choices=['DEBUG','RELEASE'], dest="BuildTarget",\r
1574         help="BuildTarget is one of list: DEBUG, RELEASE, which overrides target.txt's TARGET definition. To specify more TARGET, please repeat this option.")\r
1575     Parser.add_option("-t", "--tagname", action="append", type="string", dest="ToolChain",\r
1576         help="Using the Tool Chain Tagname to build the platform, overriding target.txt's TOOL_CHAIN_TAG definition.")\r
1577     Parser.add_option("-x", "--sku-id", action="callback", type="string", dest="SkuId", callback=SingleCheckCallback,\r
1578         help="Using this name of SKU ID to build the platform, overriding SKUID_IDENTIFIER in DSC file.")\r
1579 \r
1580     Parser.add_option("-n", action="callback", type="int", dest="ThreadNumber", callback=SingleCheckCallback,\r
1581         help="Build the platform using multi-threaded compiler. The value overrides target.txt's MAX_CONCURRENT_THREAD_NUMBER. Less than 2 will disable multi-thread builds.")\r
1582 \r
1583     Parser.add_option("-f", "--fdf", action="callback", type="string", dest="FdfFile", callback=SingleCheckCallback,\r
1584         help="The name of the FDF file to use, which overrides the setting in the DSC file.")\r
1585     Parser.add_option("-r", "--rom-image", action="append", type="string", dest="RomImage", default=[],\r
1586         help="The name of FD to be generated. The name must be from [FD] section in FDF file.")\r
1587     Parser.add_option("-i", "--fv-image", action="append", type="string", dest="FvImage", default=[],\r
1588         help="The name of FV to be generated. The name must be from [FV] section in FDF file.")\r
1589 \r
1590     Parser.add_option("-u", "--skip-autogen", action="store_true", dest="SkipAutoGen", help="Skip AutoGen step.")\r
1591     Parser.add_option("-e", "--re-parse", action="store_true", dest="Reparse", help="Re-parse all meta-data files.")\r
1592 \r
1593     Parser.add_option("-c", "--case-insensitive", action="store_true", dest="CaseInsensitive", help="Don't check case of file name.")\r
1594 \r
1595     # Parser.add_option("-D", "--define", action="append", dest="Defines", metavar="NAME[=[VALUE]]",\r
1596     #     help="Define global macro which can be used in DSC/DEC/INF files.")\r
1597 \r
1598     Parser.add_option("-w", "--warning-as-error", action="store_true", dest="WarningAsError", help="Treat warning in tools as error.")\r
1599     Parser.add_option("-j", "--log", action="store", dest="LogFile", help="Put log in specified file as well as on console.")\r
1600 \r
1601     Parser.add_option("-s", "--silent", action="store_true", type=None, dest="SilentMode",\r
1602         help="Make use of silent mode of (n)make.")\r
1603     Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")\r
1604     Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\\r
1605                                                                                "including library instances selected, final dependency expression, "\\r
1606                                                                                "and warning messages, etc.")\r
1607     Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")\r
1608     Parser.add_option("-D", "--define", action="append", type="string", dest="Macros", help="Macro: \"Name [= Value]\".")\r
1609 \r
1610     Parser.add_option("-y", "--report-file", action="store", dest="ReportFile", help="Create/overwrite the report to the specified filename.")\r
1611     Parser.add_option("-Y", "--report-type", action="append", type="choice", choices=['ALL','PCD','LIBRARY','FLASH','DEPEX','BUILD_FLAGS','PREDICTION'], dest="ReportType",\r
1612         help="Flags that control the type of build report to generate.  Must be one of: [ALL, PCD, LIBRARY, FLASH, DEPEX, BUILD_FLAGS, PREDICTION].  To specify more than one flag, repeat this option on the command line.")\r
1613 \r
1614     (Opt, Args)=Parser.parse_args()\r
1615     return (Opt, Args)\r
1616 \r
1617 ## Tool entrance method\r
1618 #\r
1619 # This method mainly dispatch specific methods per the command line options.\r
1620 # If no error found, return zero value so the caller of this tool can know\r
1621 # if it's executed successfully or not.\r
1622 #\r
1623 #   @retval 0     Tool was successful\r
1624 #   @retval 1     Tool failed\r
1625 #\r
1626 def Main():\r
1627     StartTime = time.time()\r
1628 \r
1629     # Initialize log system\r
1630     EdkLogger.Initialize()\r
1631 \r
1632     #\r
1633     # Parse the options and args\r
1634     #\r
1635     (Option, Target) = MyOptionParser()\r
1636     GlobalData.gOptions = Option\r
1637     GlobalData.gCaseInsensitive = Option.CaseInsensitive\r
1638 \r
1639     # Set log level\r
1640     if Option.verbose != None:\r
1641         EdkLogger.SetLevel(EdkLogger.VERBOSE)\r
1642     elif Option.quiet != None:\r
1643         EdkLogger.SetLevel(EdkLogger.QUIET)\r
1644     elif Option.debug != None:\r
1645         EdkLogger.SetLevel(Option.debug + 1)\r
1646     else:\r
1647         EdkLogger.SetLevel(EdkLogger.INFO)\r
1648 \r
1649     if Option.LogFile != None:\r
1650         EdkLogger.SetLogFile(Option.LogFile)\r
1651 \r
1652     if Option.WarningAsError == True:\r
1653         EdkLogger.SetWarningAsError()\r
1654 \r
1655     if platform.platform().find("Windows") >= 0:\r
1656         GlobalData.gIsWindows = True\r
1657     else:\r
1658         GlobalData.gIsWindows = False\r
1659 \r
1660     EdkLogger.quiet(time.strftime("%H:%M:%S, %b.%d %Y ", time.localtime()) + "[%s]\n" % platform.platform())\r
1661     ReturnCode = 0\r
1662     MyBuild = None\r
1663     try:\r
1664         if len(Target) == 0:\r
1665             Target = "all"\r
1666         elif len(Target) >= 2:\r
1667             EdkLogger.error("build", OPTION_NOT_SUPPORTED, "More than one targets are not supported.",\r
1668                             ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))\r
1669         else:\r
1670             Target = Target[0].lower()\r
1671 \r
1672         if Target not in gSupportedTarget:\r
1673             EdkLogger.error("build", OPTION_NOT_SUPPORTED, "Not supported target [%s]." % Target,\r
1674                             ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))\r
1675 \r
1676         GlobalData.gGlobalDefines = ParseDefines(Option.Macros)\r
1677         #\r
1678         # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH\r
1679         #\r
1680         CheckEnvVariable()\r
1681         Workspace = os.getenv("WORKSPACE")\r
1682         #\r
1683         # Get files real name in workspace dir\r
1684         #\r
1685         GlobalData.gAllFiles = Utils.DirCache(Workspace)\r
1686 \r
1687         WorkingDirectory = os.getcwd()\r
1688         if not Option.ModuleFile:\r
1689             FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.inf')))\r
1690             FileNum = len(FileList)\r
1691             if FileNum >= 2:\r
1692                 EdkLogger.error("build", OPTION_NOT_SUPPORTED, "There are %d INF files in %s." % (FileNum, WorkingDirectory),\r
1693                                 ExtraData="Please use '-m <INF_FILE_PATH>' switch to choose one.")\r
1694             elif FileNum == 1:\r
1695                 Option.ModuleFile = NormFile(FileList[0], Workspace)\r
1696 \r
1697         if Option.ModuleFile:\r
1698             if os.path.isabs (Option.ModuleFile):\r
1699                 if os.path.normcase (os.path.normpath(Option.ModuleFile)).find (Workspace) == 0:\r
1700                     Option.ModuleFile = NormFile(os.path.normpath(Option.ModuleFile), Workspace)\r
1701             Option.ModuleFile = PathClass(Option.ModuleFile, Workspace)\r
1702             ErrorCode, ErrorInfo = Option.ModuleFile.Validate(".inf", False)\r
1703             if ErrorCode != 0:\r
1704                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
1705 \r
1706         if Option.PlatformFile != None:\r
1707             if os.path.isabs (Option.PlatformFile):\r
1708                 if os.path.normcase (os.path.normpath(Option.PlatformFile)).find (Workspace) == 0:\r
1709                     Option.PlatformFile = NormFile(os.path.normpath(Option.PlatformFile), Workspace)\r
1710             Option.PlatformFile = PathClass(Option.PlatformFile, Workspace)\r
1711             ErrorCode, ErrorInfo = Option.PlatformFile.Validate(".dsc", False)\r
1712             if ErrorCode != 0:\r
1713                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
1714 \r
1715         if Option.FdfFile != None:\r
1716             if os.path.isabs (Option.FdfFile):\r
1717                 if os.path.normcase (os.path.normpath(Option.FdfFile)).find (Workspace) == 0:\r
1718                     Option.FdfFile = NormFile(os.path.normpath(Option.FdfFile), Workspace)\r
1719             Option.FdfFile = PathClass(Option.FdfFile, Workspace)\r
1720             ErrorCode, ErrorInfo = Option.FdfFile.Validate(".fdf", False)\r
1721             if ErrorCode != 0:\r
1722                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
1723 \r
1724         MyBuild = Build(Target, Workspace, Option.PlatformFile, Option.ModuleFile,\r
1725                         Option.TargetArch, Option.ToolChain, Option.BuildTarget,\r
1726                         Option.FdfFile, Option.RomImage, Option.FvImage,\r
1727                         None, Option.SilentMode, Option.ThreadNumber,\r
1728                         Option.SkipAutoGen, Option.Reparse, Option.SkuId, \r
1729                         Option.ReportFile, Option.ReportType)\r
1730         MyBuild.Launch()\r
1731         #MyBuild.DumpBuildData()\r
1732     except FatalError, X:\r
1733         if MyBuild != None:\r
1734             # for multi-thread build exits safely\r
1735             MyBuild.Relinquish()\r
1736         if Option != None and Option.debug != None:\r
1737             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1738         ReturnCode = X.args[0]\r
1739     except Warning, X:\r
1740         # error from Fdf parser\r
1741         if MyBuild != None:\r
1742             # for multi-thread build exits safely\r
1743             MyBuild.Relinquish()\r
1744         if Option != None and Option.debug != None:\r
1745             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1746         else:\r
1747             EdkLogger.error(X.ToolName, FORMAT_INVALID, File=X.FileName, Line=X.LineNumber, ExtraData=X.Message, RaiseError = False)\r
1748         ReturnCode = FORMAT_INVALID\r
1749     except KeyboardInterrupt:\r
1750         ReturnCode = ABORT_ERROR\r
1751         if Option != None and Option.debug != None:\r
1752             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1753     except:\r
1754         if MyBuild != None:\r
1755             # for multi-thread build exits safely\r
1756             MyBuild.Relinquish()\r
1757 \r
1758         # try to get the meta-file from the object causing exception\r
1759         Tb = sys.exc_info()[-1]\r
1760         MetaFile = GlobalData.gProcessingFile\r
1761         while Tb != None:\r
1762             if 'self' in Tb.tb_frame.f_locals and hasattr(Tb.tb_frame.f_locals['self'], 'MetaFile'):\r
1763                 MetaFile = Tb.tb_frame.f_locals['self'].MetaFile\r
1764             Tb = Tb.tb_next\r
1765         EdkLogger.error(\r
1766                     "\nbuild",\r
1767                     CODE_ERROR,\r
1768                     "Unknown fatal error when processing [%s]" % MetaFile,\r
1769                     ExtraData="\n(Please send email to dev@buildtools.tianocore.org for help, attaching following call stack trace!)\n",\r
1770                     RaiseError=False\r
1771                     )\r
1772         EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1773         ReturnCode = CODE_ERROR\r
1774     finally:\r
1775         Utils.Progressor.Abort()\r
1776 \r
1777     if ReturnCode == 0:\r
1778         Conclusion = "Done"\r
1779     elif ReturnCode == ABORT_ERROR:\r
1780         Conclusion = "Aborted"\r
1781     else:\r
1782         Conclusion = "Failed"\r
1783     FinishTime = time.time()\r
1784     BuildDuration = time.strftime("%M:%S", time.gmtime(int(round(FinishTime - StartTime))))\r
1785     if MyBuild != None:\r
1786         MyBuild.BuildReport.GenerateReport(BuildDuration)\r
1787         MyBuild.Db.Close()\r
1788     EdkLogger.SetLevel(EdkLogger.QUIET)\r
1789     EdkLogger.quiet("\n- %s -\n%s [%s]" % (Conclusion, time.strftime("%H:%M:%S, %b.%d %Y", time.localtime()), BuildDuration))\r
1790 \r
1791     return ReturnCode\r
1792 \r
1793 if __name__ == '__main__':\r
1794     r = Main()\r
1795     ## 0-127 is a safe return range, and 1 is a standard default error\r
1796     if r < 0 or r > 127: r = 1\r
1797     sys.exit(r)\r
1798 \r