Update ImageSize to align 4K page size for load module at fix address feature.
[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         ImageClass.Size       = (ImageClass.Size / 0x1000 + 1) * 0x1000\r
667         self.Image            = ImageClass\r
668 \r
669 ## The class implementing the EDK2 build process\r
670 #\r
671 #   The build process includes:\r
672 #       1. Load configuration from target.txt and tools_def.txt in $(WORKSPACE)/Conf\r
673 #       2. Parse DSC file of active platform\r
674 #       3. Parse FDF file if any\r
675 #       4. Establish build database, including parse all other files (module, package)\r
676 #       5. Create AutoGen files (C code file, depex file, makefile) if necessary\r
677 #       6. Call build command\r
678 #\r
679 class Build():\r
680     ## Constructor\r
681     #\r
682     # Constructor will load all necessary configurations, parse platform, modules\r
683     # and packages and the establish a database for AutoGen.\r
684     #\r
685     #   @param  Target              The build command target, one of gSupportedTarget\r
686     #   @param  WorkspaceDir        The directory of workspace\r
687     #   @param  Platform            The DSC file of active platform\r
688     #   @param  Module              The INF file of active module, if any\r
689     #   @param  Arch                The Arch list of platform or module\r
690     #   @param  ToolChain           The name list of toolchain\r
691     #   @param  BuildTarget         The "DEBUG" or "RELEASE" build\r
692     #   @param  FlashDefinition     The FDF file of active platform\r
693     #   @param  FdList=[]           The FD names to be individually built\r
694     #   @param  FvList=[]           The FV names to be individually built\r
695     #   @param  MakefileType        The type of makefile (for MSFT make or GNU make)\r
696     #   @param  SilentMode          Indicate multi-thread build mode\r
697     #   @param  ThreadNumber        The maximum number of thread if in multi-thread build mode\r
698     #   @param  SkipAutoGen         Skip AutoGen step\r
699     #   @param  Reparse             Re-parse all meta files\r
700     #   @param  SkuId               SKU id from command line\r
701     #\r
702     def __init__(self, Target, WorkspaceDir, Platform, Module, Arch, ToolChain,\r
703                  BuildTarget, FlashDefinition, FdList=[], FvList=[],\r
704                  MakefileType="nmake", SilentMode=False, ThreadNumber=2,\r
705                  SkipAutoGen=False, Reparse=False, SkuId=None, \r
706                  ReportFile=None, ReportType=None):\r
707 \r
708         self.WorkspaceDir = WorkspaceDir\r
709         self.Target         = Target\r
710         self.PlatformFile   = Platform\r
711         self.ModuleFile     = Module\r
712         self.ArchList       = Arch\r
713         self.ToolChainList  = ToolChain\r
714         self.BuildTargetList= BuildTarget\r
715         self.Fdf            = FlashDefinition\r
716         self.FdList         = FdList\r
717         self.FvList         = FvList\r
718         self.MakefileType   = MakefileType\r
719         self.SilentMode     = SilentMode\r
720         self.ThreadNumber   = ThreadNumber\r
721         self.SkipAutoGen    = SkipAutoGen\r
722         self.Reparse        = Reparse\r
723         self.SkuId          = SkuId\r
724         self.SpawnMode      = True\r
725         self.BuildReport    = BuildReport(ReportFile, ReportType)\r
726         self.TargetTxt      = TargetTxtClassObject()\r
727         self.ToolDef        = ToolDefClassObject()\r
728         self.Db             = WorkspaceDatabase(None, GlobalData.gGlobalDefines, self.Reparse)\r
729         #self.Db             = WorkspaceDatabase(None, {}, self.Reparse)\r
730         self.BuildDatabase  = self.Db.BuildObject\r
731         self.Platform       = None\r
732         self.LoadFixAddress = 0\r
733 \r
734         # print dot charater during doing some time-consuming work\r
735         self.Progress = Utils.Progressor()\r
736 \r
737         # parse target.txt, tools_def.txt, and platform file\r
738         #self.RestoreBuildData()\r
739         self.LoadConfiguration()\r
740         self.InitBuild()\r
741 \r
742         # print current build environment and configuration\r
743         EdkLogger.quiet("%-24s = %s" % ("WORKSPACE", os.environ["WORKSPACE"]))\r
744         EdkLogger.quiet("%-24s = %s" % ("ECP_SOURCE", os.environ["ECP_SOURCE"]))\r
745         EdkLogger.quiet("%-24s = %s" % ("EDK_SOURCE", os.environ["EDK_SOURCE"]))\r
746         EdkLogger.quiet("%-24s = %s" % ("EFI_SOURCE", os.environ["EFI_SOURCE"]))\r
747         EdkLogger.quiet("%-24s = %s" % ("EDK_TOOLS_PATH", os.environ["EDK_TOOLS_PATH"]))\r
748 \r
749         EdkLogger.info('\n%-24s = %s' % ("TARGET_ARCH", ' '.join(self.ArchList)))\r
750         EdkLogger.info('%-24s = %s' % ("TARGET", ' '.join(self.BuildTargetList)))\r
751         EdkLogger.info('%-24s = %s' % ("TOOL_CHAIN_TAG", ' '.join(self.ToolChainList)))\r
752 \r
753         EdkLogger.info('\n%-24s = %s' % ("Active Platform", self.PlatformFile))\r
754 \r
755         if self.Fdf != None and self.Fdf != "":\r
756             EdkLogger.info('%-24s = %s' % ("Flash Image Definition", self.Fdf))\r
757 \r
758         if self.ModuleFile != None and self.ModuleFile != "":\r
759             EdkLogger.info('%-24s = %s' % ("Active Module", self.ModuleFile))\r
760 \r
761         os.chdir(self.WorkspaceDir)\r
762         self.Progress.Start("\nProcessing meta-data")\r
763 \r
764     ## Load configuration\r
765     #\r
766     #   This method will parse target.txt and get the build configurations.\r
767     #\r
768     def LoadConfiguration(self):\r
769         #\r
770         # Check target.txt and tools_def.txt and Init them\r
771         #\r
772         BuildConfigurationFile = os.path.normpath(os.path.join(self.WorkspaceDir, gBuildConfiguration))\r
773         if os.path.isfile(BuildConfigurationFile) == True:\r
774             StatusCode = self.TargetTxt.LoadTargetTxtFile(BuildConfigurationFile)\r
775 \r
776             ToolDefinitionFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_CONF]\r
777             if ToolDefinitionFile == '':\r
778                 ToolDefinitionFile = gToolsDefinition\r
779             ToolDefinitionFile = os.path.normpath(os.path.join(self.WorkspaceDir, ToolDefinitionFile))\r
780             if os.path.isfile(ToolDefinitionFile) == True:\r
781                 StatusCode = self.ToolDef.LoadToolDefFile(ToolDefinitionFile)\r
782             else:\r
783                 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=ToolDefinitionFile)\r
784         else:\r
785             EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=BuildConfigurationFile)\r
786 \r
787         # if no ARCH given in command line, get it from target.txt\r
788         if self.ArchList == None or len(self.ArchList) == 0:\r
789             self.ArchList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET_ARCH]\r
790 \r
791         # if no build target given in command line, get it from target.txt\r
792         if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
793             self.BuildTargetList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET]\r
794 \r
795         # if no tool chain given in command line, get it from target.txt\r
796         if self.ToolChainList == None or len(self.ToolChainList) == 0:\r
797             self.ToolChainList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_TAG]\r
798             if self.ToolChainList == None or len(self.ToolChainList) == 0:\r
799                 EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, ExtraData="No toolchain given. Don't know how to build.\n")\r
800 \r
801         # check if the tool chains are defined or not\r
802         NewToolChainList = []\r
803         for ToolChain in self.ToolChainList:\r
804             if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG]:\r
805                 EdkLogger.warn("build", "Tool chain [%s] is not defined" % ToolChain)\r
806             else:\r
807                 NewToolChainList.append(ToolChain)\r
808         # if no tool chain available, break the build\r
809         if len(NewToolChainList) == 0:\r
810             EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,\r
811                             ExtraData="[%s] not defined. No toolchain available for build!\n" % ", ".join(self.ToolChainList))\r
812         else:\r
813             self.ToolChainList = NewToolChainList\r
814 \r
815         if self.ThreadNumber == None:\r
816             self.ThreadNumber = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER]\r
817             if self.ThreadNumber == '':\r
818                 self.ThreadNumber = 0\r
819             else:\r
820                 self.ThreadNumber = int(self.ThreadNumber, 0)\r
821 \r
822         if self.ThreadNumber == 0:\r
823             self.ThreadNumber = 1\r
824 \r
825         if not self.PlatformFile:\r
826             PlatformFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_ACTIVE_PLATFORM]\r
827             if not PlatformFile:\r
828                 # Try to find one in current directory\r
829                 WorkingDirectory = os.getcwd()\r
830                 FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.dsc')))\r
831                 FileNum = len(FileList)\r
832                 if FileNum >= 2:\r
833                     EdkLogger.error("build", OPTION_MISSING,\r
834                                     ExtraData="There are %d DSC files in %s. Use '-p' to specify one.\n" % (FileNum, WorkingDirectory))\r
835                 elif FileNum == 1:\r
836                     PlatformFile = FileList[0]\r
837                 else:\r
838                     EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,\r
839                                     ExtraData="No active platform specified in target.txt or command line! Nothing can be built.\n")\r
840 \r
841             self.PlatformFile = PathClass(NormFile(PlatformFile, self.WorkspaceDir), self.WorkspaceDir)\r
842             ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False)\r
843             if ErrorCode != 0:\r
844                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
845 \r
846     ## Initialize build configuration\r
847     #\r
848     #   This method will parse DSC file and merge the configurations from\r
849     #   command line and target.txt, then get the final build configurations.\r
850     #\r
851     def InitBuild(self):\r
852         ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc")\r
853         if ErrorCode != 0:\r
854             EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
855 \r
856         # create metafile database\r
857         self.Db.InitDatabase()\r
858 \r
859         # we need information in platform description file to determine how to build\r
860         self.Platform = self.BuildDatabase[self.PlatformFile, 'COMMON']\r
861         if not self.Fdf:\r
862             self.Fdf = self.Platform.FlashDefinition\r
863         \r
864         LoadFixAddressString = None\r
865         if TAB_FIX_LOAD_TOP_MEMORY_ADDRESS in GlobalData.gGlobalDefines:\r
866             LoadFixAddressString = GlobalData.gGlobalDefines[TAB_FIX_LOAD_TOP_MEMORY_ADDRESS]\r
867         else:\r
868             LoadFixAddressString = self.Platform.LoadFixAddress\r
869 \r
870         if LoadFixAddressString != None and LoadFixAddressString != '':\r
871             try:\r
872                 if LoadFixAddressString.upper().startswith('0X'):\r
873                     self.LoadFixAddress = int (LoadFixAddressString, 16)\r
874                 else:\r
875                     self.LoadFixAddress = int (LoadFixAddressString)\r
876             except:
877                 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS %s is not valid dec or hex string" % (LoadFixAddressString))\r
878             if self.LoadFixAddress < 0:\r
879                 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS can't be set to negative value %s" % (LoadFixAddressString))\r
880 \r
881         if self.SkuId == None or self.SkuId == '':\r
882             self.SkuId = self.Platform.SkuName\r
883 \r
884         # check FD/FV build target\r
885         if self.Fdf == None or self.Fdf == "":\r
886             if self.FdList != []:\r
887                 EdkLogger.info("No flash definition file found. FD [%s] will be ignored." % " ".join(self.FdList))\r
888                 self.FdList = []\r
889             if self.FvList != []:\r
890                 EdkLogger.info("No flash definition file found. FV [%s] will be ignored." % " ".join(self.FvList))\r
891                 self.FvList = []\r
892         else:\r
893             FdfParserObj = FdfParser(str(self.Fdf))\r
894             FdfParserObj.ParseFile()\r
895             for fvname in self.FvList:\r
896                 if fvname.upper() not in FdfParserObj.Profile.FvDict.keys():\r
897                     EdkLogger.error("build", OPTION_VALUE_INVALID,\r
898                                     "No such an FV in FDF file: %s" % fvname)\r
899 \r
900         #\r
901         # Merge Arch\r
902         #\r
903         if self.ArchList == None or len(self.ArchList) == 0:\r
904             ArchList = set(self.Platform.SupArchList)\r
905         else:\r
906             ArchList = set(self.ArchList) & set(self.Platform.SupArchList)\r
907         if len(ArchList) == 0:\r
908             EdkLogger.error("build", PARAMETER_INVALID,\r
909                             ExtraData = "Active platform supports [%s] only, but [%s] is given."\r
910                                         % (" ".join(self.Platform.SupArchList), " ".join(self.ArchList)))\r
911         elif len(ArchList) != len(self.ArchList):\r
912             SkippedArchList = set(self.ArchList).symmetric_difference(set(self.Platform.SupArchList))\r
913             EdkLogger.verbose("\nArch [%s] is ignored because active platform supports [%s] but [%s] is specified !"\r
914                            % (" ".join(SkippedArchList), " ".join(self.Platform.SupArchList), " ".join(self.ArchList)))\r
915         self.ArchList = tuple(ArchList)\r
916 \r
917         # Merge build target\r
918         if self.BuildTargetList == None or len(self.BuildTargetList) == 0:\r
919             BuildTargetList = self.Platform.BuildTargets\r
920         else:\r
921             BuildTargetList = list(set(self.BuildTargetList) & set(self.Platform.BuildTargets))\r
922         if BuildTargetList == []:\r
923             EdkLogger.error("build", PARAMETER_INVALID, "Active platform only supports [%s], but [%s] is given"\r
924                                 % (" ".join(self.Platform.BuildTargets), " ".join(self.BuildTargetList)))\r
925         self.BuildTargetList = BuildTargetList\r
926 \r
927     ## Build a module or platform\r
928     #\r
929     # Create autogen code and makfile for a module or platform, and the launch\r
930     # "make" command to build it\r
931     #\r
932     #   @param  Target                      The target of build command\r
933     #   @param  Platform                    The platform file\r
934     #   @param  Module                      The module file\r
935     #   @param  BuildTarget                 The name of build target, one of "DEBUG", "RELEASE"\r
936     #   @param  ToolChain                   The name of toolchain to build\r
937     #   @param  Arch                        The arch of the module/platform\r
938     #   @param  CreateDepModuleCodeFile     Flag used to indicate creating code\r
939     #                                       for dependent modules/Libraries\r
940     #   @param  CreateDepModuleMakeFile     Flag used to indicate creating makefile\r
941     #                                       for dependent modules/Libraries\r
942     #\r
943     def _Build(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True):\r
944         if AutoGenObject == None:\r
945             return False\r
946 \r
947         # skip file generation for cleanxxx targets, run and fds target\r
948         if Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
949             # for target which must generate AutoGen code and makefile\r
950             if not self.SkipAutoGen or Target == 'genc':\r
951                 self.Progress.Start("Generating code")\r
952                 AutoGenObject.CreateCodeFile(CreateDepsCodeFile)\r
953                 self.Progress.Stop("done!")\r
954             if Target == "genc":\r
955                 return True\r
956 \r
957             if not self.SkipAutoGen or Target == 'genmake':\r
958                 self.Progress.Start("Generating makefile")\r
959                 AutoGenObject.CreateMakeFile(CreateDepsMakeFile)\r
960                 self.Progress.Stop("done!")\r
961             if Target == "genmake":\r
962                 return True\r
963         else:\r
964             # always recreate top/platform makefile when clean, just in case of inconsistency\r
965             AutoGenObject.CreateCodeFile(False)\r
966             AutoGenObject.CreateMakeFile(False)\r
967 \r
968         if EdkLogger.GetLevel() == EdkLogger.QUIET:\r
969             EdkLogger.quiet("Building ... %s" % repr(AutoGenObject))\r
970 \r
971         BuildCommand = AutoGenObject.BuildCommand\r
972         if BuildCommand == None or len(BuildCommand) == 0:\r
973             EdkLogger.error("build", OPTION_MISSING, ExtraData="No MAKE command found for [%s, %s, %s]" % Key)\r
974 \r
975         BuildCommand = BuildCommand + [Target]\r
976         LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir)\r
977         if Target == 'cleanall':\r
978             try:\r
979                 #os.rmdir(AutoGenObject.BuildDir)\r
980                 RemoveDirectory(AutoGenObject.BuildDir, True)\r
981             except WindowsError, X:\r
982                 EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X))\r
983         return True\r
984 \r
985     ## Rebase module image and Get function address for the inpug module list.\r
986     #\r
987     def _RebaseModule (self, MapBuffer, BaseAddress, ModuleList, AddrIsOffset = True, ModeIsSmm = False):\r
988         if ModeIsSmm:\r
989             AddrIsOffset = False\r
990         InfFileNameList = ModuleList.keys()\r
991         #InfFileNameList.sort()\r
992         for InfFile in InfFileNameList:\r
993             sys.stdout.write (".")
994             sys.stdout.flush()
995             ModuleInfo = ModuleList[InfFile]\r
996             ModuleName = ModuleInfo.BaseName\r
997             ## for SMM module in SMRAM, the SMRAM will be allocated from base to top.\r
998             if not ModeIsSmm:\r
999                 BaseAddress = BaseAddress - ModuleInfo.Image.Size\r
1000                 #\r
1001                 # Update Image to new BaseAddress by GenFw tool\r
1002                 #\r
1003                 LaunchCommand(["GenFw", "--rebase", str(BaseAddress), "-r", ModuleInfo.Image.FileName], ModuleInfo.OutpuDir)\r
1004             else:\r
1005                 #\r
1006                 # Set new address to the section header only for SMM driver.\r
1007                 #\r
1008                 LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleInfo.Image.FileName], ModuleInfo.OutpuDir)\r
1009             #\r
1010             # Add general information.\r
1011             #\r
1012             if ModeIsSmm:\r
1013                 MapBuffer.write('\n\n%s (Fix SMRAM Offset,   BaseAddress=0x%010X,  EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint))\r
1014             elif AddrIsOffset:\r
1015                 MapBuffer.write('\n\n%s (Fix Memory Offset,  BaseAddress=-0x%010X, EntryPoint=-0x%010X)\n' % (ModuleName, 0 - BaseAddress, 0 - (BaseAddress + ModuleInfo.Image.EntryPoint)))\r
1016             else:\r
1017                 MapBuffer.write('\n\n%s (Fix Memory Address, BaseAddress=0x%010X,  EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint))\r
1018             #\r
1019             # Add guid and general seciton section.\r
1020             #\r
1021             TextSectionAddress = 0\r
1022             DataSectionAddress = 0\r
1023             for SectionHeader in ModuleInfo.Image.SectionHeaderList:\r
1024                 if SectionHeader[0] == '.text':\r
1025                     TextSectionAddress = SectionHeader[1]\r
1026                 elif SectionHeader[1] == '.data':\r
1027                     DataSectionAddress = SectionHeader[1]\r
1028             if AddrIsOffset:\r
1029                 MapBuffer.write('(GUID=%s, .textbaseaddress=-0x%010X, .databaseaddress=-0x%010X)\n\n' % (ModuleInfo.Guid, 0 - (BaseAddress + TextSectionAddress), 0 - (BaseAddress + DataSectionAddress))) \r
1030             else:\r
1031                 MapBuffer.write('(GUID=%s, .textbaseaddress=0x%010X, .databaseaddress=0x%010X)\n\n' % (ModuleInfo.Guid, BaseAddress + TextSectionAddress, BaseAddress + DataSectionAddress)) \r
1032             #\r
1033             # Add funtion address\r
1034             #\r
1035             ImageMapTable = ModuleInfo.Image.FileName.replace('.efi', '.map')\r
1036             if not os.path.exists(ImageMapTable):\r
1037                 continue\r
1038             ImageMap = open (ImageMapTable, 'r')\r
1039             for LinStr in ImageMap:\r
1040                 if len (LinStr.strip()) == 0:\r
1041                     continue\r
1042                 StrList = LinStr.split()\r
1043                 if len (StrList) > 4:\r
1044                     if StrList[3] == 'f' or StrList[3] =='F':\r
1045                         if AddrIsOffset:\r
1046                             MapBuffer.write('  -0x%010X    %s\n' % (0 - (BaseAddress + int (StrList[2], 16)), StrList[1]))\r
1047                         else:\r
1048                             MapBuffer.write('  0x%010X    %s\n' % (BaseAddress + int (StrList[2], 16), StrList[1]))\r
1049             ImageMap.close()\r
1050 \r
1051             #\r
1052             # for SMM module in SMRAM, the SMRAM will be allocated from base to top.\r
1053             #\r
1054             if ModeIsSmm:\r
1055                 BaseAddress = BaseAddress + ModuleInfo.Image.Size\r
1056 \r
1057     ## Collect MAP information of all FVs\r
1058     #\r
1059     def _CollectFvMapBuffer (self, MapBuffer, Wa):\r
1060         if self.Fdf != '':\r
1061             # First get the XIP base address for FV map file.\r
1062             for FvName in Wa.FdfProfile.FvDict.keys():\r
1063                 FvMapBuffer = os.path.join(Wa.FvDir, FvName + '.Fv.map')\r
1064                 if not os.path.exists(FvMapBuffer):\r
1065                     continue\r
1066                 FvMap = open (FvMapBuffer, 'r')\r
1067                 #skip FV size information\r
1068                 FvMap.readline()\r
1069                 FvMap.readline()\r
1070                 FvMap.readline()\r
1071                 FvMap.readline()\r
1072                 MapBuffer.write(FvMap.read())\r
1073                 FvMap.close()\r
1074 \r
1075     ## Collect MAP information of all modules\r
1076     #\r
1077     def _CollectModuleMapBuffer (self, MapBuffer, ModuleList):\r
1078         sys.stdout.write ("Generate Load Module At Fix Address Map")
1079         sys.stdout.flush()
1080         PatchEfiImageList = []\r
1081         PeiModuleList  = {}\r
1082         BtModuleList   = {}\r
1083         RtModuleList   = {}\r
1084         SmmModuleList  = {}\r
1085         PeiSize = 0\r
1086         BtSize  = 0\r
1087         RtSize  = 0\r
1088         # reserve 4K size in SMRAM to make SMM module address not from 0.\r
1089         SmmSize = 0x1000\r
1090         for Module in ModuleList:\r
1091             GlobalData.gProcessingFile = "%s [%s, %s, %s]" % (Module.MetaFile, Module.Arch, Module.ToolChain, Module.BuildTarget)\r
1092             \r
1093             OutputImageFile = ''\r
1094             for ResultFile in Module.CodaTargetList:\r
1095                 if str(ResultFile.Target).endswith('.efi'):\r
1096                     #\r
1097                     # module list for PEI, DXE, RUNTIME and SMM\r
1098                     #\r
1099                     OutputImageFile = os.path.join(Module.OutputDir, Module.Name + '.efi')\r
1100                     ImageClass = PeImageClass (OutputImageFile)\r
1101                     if not ImageClass.IsValid:\r
1102                         EdkLogger.error("build", FILE_PARSE_FAILURE, ExtraData=ImageClass.ErrorInfo)\r
1103                     ImageInfo = PeImageInfo(Module.Name, Module.Guid, Module.OutputDir, ImageClass)\r
1104                     if Module.ModuleType in ['PEI_CORE', 'PEIM', 'COMBINED_PEIM_DRIVER','PIC_PEIM', 'RELOCATABLE_PEIM', 'DXE_CORE']:\r
1105                         PeiModuleList[Module.MetaFile] = ImageInfo\r
1106                         PeiSize += ImageClass.Size\r
1107                     elif Module.ModuleType in ['BS_DRIVER', 'DXE_DRIVER', 'UEFI_DRIVER']:\r
1108                         BtModuleList[Module.MetaFile] = ImageInfo\r
1109                         BtSize += ImageClass.Size\r
1110                     elif Module.ModuleType in ['DXE_RUNTIME_DRIVER', 'RT_DRIVER', 'DXE_SAL_DRIVER', 'SAL_RT_DRIVER']:\r
1111                         RtModuleList[Module.MetaFile] = ImageInfo\r
1112                         RtSize += ImageClass.Size\r
1113                     elif Module.ModuleType in ['SMM_CORE', 'DXE_SMM_DRIVER']:\r
1114                         SmmModuleList[Module.MetaFile] = ImageInfo\r
1115                         SmmSize += ImageClass.Size\r
1116                         if Module.ModuleType == 'DXE_SMM_DRIVER':\r
1117                             PiSpecVersion = 0
1118                             if 'PI_SPECIFICATION_VERSION' in Module.Module.Specification:
1119                                 PiSpecVersion = Module.Module.Specification['PI_SPECIFICATION_VERSION']
1120                             # for PI specification < PI1.1, DXE_SMM_DRIVER also runs as BOOT time driver.\r
1121                             if PiSpecVersion < 0x0001000A:\r
1122                                 BtModuleList[Module.MetaFile] = ImageInfo\r
1123                                 BtSize += ImageClass.Size\r
1124                     break\r
1125             #\r
1126             # EFI image is final target.\r
1127             # Check EFI image contains patchable FixAddress related PCDs.\r
1128             #\r
1129             if OutputImageFile != '':\r
1130                 ModuleIsPatch = False\r
1131                 for Pcd in Module.ModulePcdList:\r
1132                     if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_LIST:\r
1133                         ModuleIsPatch = True\r
1134                         break\r
1135                 if not ModuleIsPatch:\r
1136                     for Pcd in Module.LibraryPcdList:\r
1137                         if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_LIST:\r
1138                             ModuleIsPatch = True\r
1139                             break\r
1140                 \r
1141                 if not ModuleIsPatch:\r
1142                     continue\r
1143                 #\r
1144                 # Module includes the patchable load fix address PCDs.\r
1145                 # It will be fixed up later. \r
1146                 #\r
1147                 PatchEfiImageList.append (OutputImageFile)\r
1148         \r
1149         #\r
1150         # Patch FixAddress related PCDs into EFI image\r
1151         #\r
1152         for EfiImage in PatchEfiImageList: \r
1153             EfiImageMap = EfiImage.replace('.efi', '.map')\r
1154             if not os.path.exists(EfiImageMap):\r
1155                 continue\r
1156             #\r
1157             # Get PCD offset in EFI image by GenPatchPcdTable function\r
1158             #\r
1159             PcdTable = parsePcdInfoFromMapFile(EfiImageMap, EfiImage) 
1160             #\r
1161             # Patch real PCD value by PatchPcdValue tool\r
1162             #\r
1163             for PcdInfo in PcdTable:\r
1164                 ReturnValue = 0\r
1165                 if PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE:\r
1166                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE_DATA_TYPE, str (PeiSize/0x1000))\r
1167                 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE:\r
1168                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE_DATA_TYPE, str (BtSize/0x1000))\r
1169                 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE:\r
1170                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE_DATA_TYPE, str (RtSize/0x1000))\r
1171                 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE:\r
1172                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE_DATA_TYPE, str (SmmSize/0x1000))\r
1173                 if ReturnValue != 0:\r
1174                     EdkLogger.error("build", PARAMETER_INVALID, "Patch PCD value failed", ExtraData=ErrorInfo)\r
1175         #\r
1176         # Get Top Memory address\r
1177         #\r
1178         TopMemoryAddress = 0\r
1179         if self.LoadFixAddress == 0xFFFFFFFFFFFFFFFF:\r
1180             TopMemoryAddress = 0\r
1181         else:\r
1182             TopMemoryAddress = self.LoadFixAddress\r
1183             if TopMemoryAddress < RtSize + BtSize + PeiSize:\r
1184                 EdkLogger.error("build", PARAMETER_INVALID, "TopMemoryAddress is too low to load driver")\r
1185         \r
1186         MapBuffer.write('PEI_CODE_PAGE_NUMBER      = 0x%x\n' % (PeiSize/0x1000))\r
1187         MapBuffer.write('BOOT_CODE_PAGE_NUMBER     = 0x%x\n' % (BtSize/0x1000))\r
1188         MapBuffer.write('RUNTIME_CODE_PAGE_NUMBER  = 0x%x\n' % (RtSize/0x1000))\r
1189         MapBuffer.write('SMM_CODE_PAGE_NUMBER      = 0x%x\n' % (SmmSize/0x1000))\r
1190         \r
1191         PeiBaseAddr = TopMemoryAddress - RtSize - BtSize\r
1192         BtBaseAddr  = TopMemoryAddress - RtSize\r
1193         RtBaseAddr  = TopMemoryAddress\r
1194 \r
1195         self._RebaseModule (MapBuffer, PeiBaseAddr, PeiModuleList, TopMemoryAddress == 0)\r
1196         self._RebaseModule (MapBuffer, BtBaseAddr, BtModuleList, TopMemoryAddress == 0)\r
1197         self._RebaseModule (MapBuffer, RtBaseAddr, RtModuleList, TopMemoryAddress == 0)\r
1198         self._RebaseModule (MapBuffer, 0x1000, SmmModuleList, AddrIsOffset = False, ModeIsSmm = True)\r
1199         MapBuffer.write('\n\n')\r
1200         sys.stdout.write ("\n")
1201         sys.stdout.flush()
1202     \r
1203     ## Save platform Map file\r
1204     #\r
1205     def _SaveMapFile (self, MapBuffer, Wa):\r
1206         #\r
1207         # Map file path is got.\r
1208         #\r
1209         MapFilePath = os.path.join(Wa.BuildDir, Wa.Name + '.map')\r
1210         #\r
1211         # Save address map into MAP file.\r
1212         #\r
1213         SaveFileOnChange(MapFilePath, MapBuffer.getvalue(), False)\r
1214         MapBuffer.close()\r
1215         sys.stdout.write ("\nLoad Module At Fix Address Map file saved to %s\n" %(MapFilePath))
1216         sys.stdout.flush()
1217 \r
1218     ## Build active platform for different build targets and different tool chains\r
1219     #\r
1220     def _BuildPlatform(self):\r
1221         for BuildTarget in self.BuildTargetList:\r
1222             for ToolChain in self.ToolChainList:\r
1223                 Wa = WorkspaceAutoGen(\r
1224                         self.WorkspaceDir,\r
1225                         self.Platform,\r
1226                         BuildTarget,\r
1227                         ToolChain,\r
1228                         self.ArchList,\r
1229                         self.BuildDatabase,\r
1230                         self.TargetTxt,\r
1231                         self.ToolDef,\r
1232                         self.Fdf,\r
1233                         self.FdList,\r
1234                         self.FvList,\r
1235                         self.SkuId\r
1236                         )\r
1237                 self.BuildReport.AddPlatformReport(Wa)\r
1238                 self.Progress.Stop("done!")\r
1239                 self._Build(self.Target, Wa)\r
1240                 \r
1241                 # Create MAP file when Load Fix Address is enabled.\r
1242                 if self.Target in ["", "all", "fds"] and self.LoadFixAddress != 0:\r
1243                     for Arch in self.ArchList:\r
1244                         #\r
1245                         # Check whether the set fix address is above 4G for 32bit image.\r
1246                         #\r
1247                         if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
1248                             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
1249                     #\r
1250                     # Get Module List\r
1251                     #\r
1252                     ModuleList = []\r
1253                     for Pa in Wa.AutoGenObjectList:\r
1254                         for Ma in Pa.ModuleAutoGenList:\r
1255                             if Ma == None:\r
1256                                 continue\r
1257                             if not Ma.IsLibrary:\r
1258                                 ModuleList.append (Ma)\r
1259 \r
1260                     MapBuffer = StringIO('')\r
1261                     #\r
1262                     # Rebase module to the preferred memory address before GenFds\r
1263                     #\r
1264                     self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
1265                     if self.Fdf != '':\r
1266                         #\r
1267                         # create FDS again for the updated EFI image\r
1268                         #\r
1269                         self._Build("fds", Wa)\r
1270                         #\r
1271                         # Create MAP file for all platform FVs after GenFds.\r
1272                         #\r
1273                         self._CollectFvMapBuffer(MapBuffer, Wa)\r
1274                     #\r
1275                     # Save MAP buffer into MAP file.\r
1276                     #\r
1277                     self._SaveMapFile (MapBuffer, Wa)\r
1278 \r
1279     ## Build active module for different build targets, different tool chains and different archs\r
1280     #\r
1281     def _BuildModule(self):\r
1282         for BuildTarget in self.BuildTargetList:\r
1283             for ToolChain in self.ToolChainList:\r
1284                 #\r
1285                 # module build needs platform build information, so get platform\r
1286                 # AutoGen first\r
1287                 #\r
1288                 Wa = WorkspaceAutoGen(\r
1289                         self.WorkspaceDir,\r
1290                         self.Platform,\r
1291                         BuildTarget,\r
1292                         ToolChain,\r
1293                         self.ArchList,\r
1294                         self.BuildDatabase,\r
1295                         self.TargetTxt,\r
1296                         self.ToolDef,\r
1297                         self.Fdf,\r
1298                         self.FdList,\r
1299                         self.FvList,\r
1300                         self.SkuId\r
1301                         )\r
1302                 self.BuildReport.AddPlatformReport(Wa)\r
1303                 Wa.CreateMakeFile(False)\r
1304                 self.Progress.Stop("done!")\r
1305                 MaList = []\r
1306                 for Arch in self.ArchList:\r
1307                     Ma = ModuleAutoGen(Wa, self.ModuleFile, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
1308                     if Ma == None: continue\r
1309                     MaList.append(Ma)\r
1310                     self._Build(self.Target, Ma)\r
1311                 if MaList == []:\r
1312                     EdkLogger.error(\r
1313                                 'build',\r
1314                                 BUILD_ERROR,\r
1315                                 "Module for [%s] is not a component of active platform."\\r
1316                                 " Please make sure that the ARCH and inf file path are"\\r
1317                                 " given in the same as in [%s]" %\\r
1318                                     (', '.join(self.ArchList), self.Platform),\r
1319                                 ExtraData=self.ModuleFile\r
1320                                 )\r
1321 \r
1322     ## Build a platform in multi-thread mode\r
1323     #\r
1324     def _MultiThreadBuildPlatform(self):\r
1325         for BuildTarget in self.BuildTargetList:\r
1326             for ToolChain in self.ToolChainList:\r
1327                 Wa = WorkspaceAutoGen(\r
1328                         self.WorkspaceDir,\r
1329                         self.Platform,\r
1330                         BuildTarget,\r
1331                         ToolChain,\r
1332                         self.ArchList,\r
1333                         self.BuildDatabase,\r
1334                         self.TargetTxt,\r
1335                         self.ToolDef,\r
1336                         self.Fdf,\r
1337                         self.FdList,\r
1338                         self.FvList,\r
1339                         self.SkuId\r
1340                         )\r
1341                 self.BuildReport.AddPlatformReport(Wa)\r
1342                 Wa.CreateMakeFile(False)\r
1343 \r
1344                 # multi-thread exit flag\r
1345                 ExitFlag = threading.Event()\r
1346                 ExitFlag.clear()\r
1347                 for Arch in self.ArchList:\r
1348                     Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)\r
1349                     if Pa == None:\r
1350                         continue\r
1351                     for Module in Pa.Platform.Modules:\r
1352                         # Get ModuleAutoGen object to generate C code file and makefile\r
1353                         Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
1354                         if Ma == None:\r
1355                             continue\r
1356                         # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'\r
1357                         if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
1358                             # for target which must generate AutoGen code and makefile\r
1359                             if not self.SkipAutoGen or self.Target == 'genc':\r
1360                                 Ma.CreateCodeFile(True)\r
1361                             if self.Target == "genc":\r
1362                                 continue\r
1363 \r
1364                             if not self.SkipAutoGen or self.Target == 'genmake':\r
1365                                 Ma.CreateMakeFile(True)\r
1366                             if self.Target == "genmake":\r
1367                                 continue\r
1368                         self.Progress.Stop("done!")\r
1369                         # Generate build task for the module\r
1370                         Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))\r
1371                         # Break build if any build thread has error\r
1372                         if BuildTask.HasError():\r
1373                             # we need a full version of makefile for platform\r
1374                             ExitFlag.set()\r
1375                             BuildTask.WaitForComplete()\r
1376                             Pa.CreateMakeFile(False)\r
1377                             EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1378                         # Start task scheduler\r
1379                         if not BuildTask.IsOnGoing():\r
1380                             BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)\r
1381 \r
1382                     # in case there's an interruption. we need a full version of makefile for platform\r
1383                     Pa.CreateMakeFile(False)\r
1384                     if BuildTask.HasError():\r
1385                         EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1386 \r
1387                 #\r
1388                 # All modules have been put in build tasks queue. Tell task scheduler\r
1389                 # to exit if all tasks are completed\r
1390                 #\r
1391                 ExitFlag.set()\r
1392                 BuildTask.WaitForComplete()\r
1393 \r
1394                 #\r
1395                 # Check for build error, and raise exception if one\r
1396                 # has been signaled.\r
1397                 #\r
1398                 if BuildTask.HasError():\r
1399                     EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1400 \r
1401                 # Create MAP file when Load Fix Address is enabled.\r
1402                 if self.Target in ["", "all", "fds"] and self.LoadFixAddress != 0:\r
1403                     for Arch in self.ArchList:\r
1404                         #\r
1405                         # Check whether the set fix address is above 4G for 32bit image.\r
1406                         #\r
1407                         if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
1408                             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
1409                     #\r
1410                     # Get Module List\r
1411                     #\r
1412                     ModuleList = []\r
1413                     for Pa in Wa.AutoGenObjectList:\r
1414                         for Ma in Pa.ModuleAutoGenList:\r
1415                             if Ma == None:\r
1416                                 continue\r
1417                             if not Ma.IsLibrary:\r
1418                                 ModuleList.append (Ma)\r
1419                     #\r
1420                     # Rebase module to the preferred memory address before GenFds\r
1421                     #\r
1422                     MapBuffer = StringIO('')\r
1423                     self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
1424 \r
1425                 # Generate FD image if there's a FDF file found\r
1426                 if self.Fdf != '' and self.Target in ["", "all", "fds"]:\r
1427                     LaunchCommand(Wa.BuildCommand + ["fds"], Wa.MakeFileDir)\r
1428 \r
1429                 # Create MAP file for all platform FV after GenFds\r
1430                 if self.Target in ["", "all", "fds"] and self.LoadFixAddress != 0:\r
1431                     if self.Fdf != '':\r
1432                         #\r
1433                         # Create MAP file for all platform FVs after GenFds.\r
1434                         #\r
1435                         self._CollectFvMapBuffer(MapBuffer, Wa)\r
1436                     #\r
1437                     # Save MAP buffer into MAP file.\r
1438                     #\r
1439                     self._SaveMapFile(MapBuffer, Wa)\r
1440 \r
1441     ## Generate GuidedSectionTools.txt in the FV directories.\r
1442     #\r
1443     def CreateGuidedSectionToolsFile(self):\r
1444         for Arch in self.ArchList:\r
1445             for BuildTarget in self.BuildTargetList:\r
1446                 for ToolChain in self.ToolChainList:\r
1447                     FvDir = os.path.join(\r
1448                                 self.WorkspaceDir,\r
1449                                 self.Platform.OutputDirectory,\r
1450                                 '_'.join((BuildTarget, ToolChain)),\r
1451                                 'FV'\r
1452                                 )\r
1453                     if not os.path.exists(FvDir):\r
1454                         continue\r
1455                     # Build up the list of supported architectures for this build\r
1456                     prefix = '%s_%s_%s_' % (BuildTarget, ToolChain, Arch)\r
1457 \r
1458                     # Look through the tool definitions for GUIDed tools\r
1459                     guidAttribs = []\r
1460                     for (attrib, value) in self.ToolDef.ToolsDefTxtDictionary.iteritems():\r
1461                         if attrib.upper().endswith('_GUID'):\r
1462                             split = attrib.split('_')\r
1463                             thisPrefix = '_'.join(split[0:3]) + '_'\r
1464                             if thisPrefix == prefix:\r
1465                                 guid = self.ToolDef.ToolsDefTxtDictionary[attrib]\r
1466                                 guid = guid.lower()\r
1467                                 toolName = split[3]\r
1468                                 path = '_'.join(split[0:4]) + '_PATH'\r
1469                                 path = self.ToolDef.ToolsDefTxtDictionary[path]\r
1470                                 path = self.GetFullPathOfTool(path)\r
1471                                 guidAttribs.append((guid, toolName, path))\r
1472 \r
1473                     # Write out GuidedSecTools.txt\r
1474                     toolsFile = os.path.join(FvDir, 'GuidedSectionTools.txt')\r
1475                     toolsFile = open(toolsFile, 'wt')\r
1476                     for guidedSectionTool in guidAttribs:\r
1477                         print >> toolsFile, ' '.join(guidedSectionTool)\r
1478                     toolsFile.close()\r
1479 \r
1480     ## Returns the full path of the tool.\r
1481     #\r
1482     def GetFullPathOfTool (self, tool):\r
1483         if os.path.exists(tool):\r
1484             return os.path.realpath(tool)\r
1485         else:\r
1486             # We need to search for the tool using the\r
1487             # PATH environment variable.\r
1488             for dirInPath in os.environ['PATH'].split(os.pathsep):\r
1489                 foundPath = os.path.join(dirInPath, tool)\r
1490                 if os.path.exists(foundPath):\r
1491                     return os.path.realpath(foundPath)\r
1492 \r
1493         # If the tool was not found in the path then we just return\r
1494         # the input tool.\r
1495         return tool\r
1496 \r
1497     ## Launch the module or platform build\r
1498     #\r
1499     def Launch(self):\r
1500         if self.ModuleFile == None or self.ModuleFile == "":\r
1501             if not self.SpawnMode or self.Target not in ["", "all"]:\r
1502                 self.SpawnMode = False\r
1503                 self._BuildPlatform()\r
1504             else:\r
1505                 self._MultiThreadBuildPlatform()\r
1506             self.CreateGuidedSectionToolsFile()\r
1507         else:\r
1508             self.SpawnMode = False\r
1509             self._BuildModule()\r
1510 \r
1511     ## Do some clean-up works when error occurred\r
1512     def Relinquish(self):\r
1513         OldLogLevel = EdkLogger.GetLevel()\r
1514         EdkLogger.SetLevel(EdkLogger.ERROR)\r
1515         #self.DumpBuildData()\r
1516         Utils.Progressor.Abort()\r
1517         if self.SpawnMode == True:\r
1518             BuildTask.Abort()\r
1519         EdkLogger.SetLevel(OldLogLevel)\r
1520 \r
1521     def DumpBuildData(self):\r
1522         CacheDirectory = os.path.join(self.WorkspaceDir, gBuildCacheDir)\r
1523         Utils.CreateDirectory(CacheDirectory)\r
1524         Utils.DataDump(Utils.gFileTimeStampCache, os.path.join(CacheDirectory, "gFileTimeStampCache"))\r
1525         Utils.DataDump(Utils.gDependencyDatabase, os.path.join(CacheDirectory, "gDependencyDatabase"))\r
1526 \r
1527     def RestoreBuildData(self):\r
1528         FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gFileTimeStampCache")\r
1529         if Utils.gFileTimeStampCache == {} and os.path.isfile(FilePath):\r
1530             Utils.gFileTimeStampCache = Utils.DataRestore(FilePath)\r
1531             if Utils.gFileTimeStampCache == None:\r
1532                 Utils.gFileTimeStampCache = {}\r
1533 \r
1534         FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gDependencyDatabase")\r
1535         if Utils.gDependencyDatabase == {} and os.path.isfile(FilePath):\r
1536             Utils.gDependencyDatabase = Utils.DataRestore(FilePath)\r
1537             if Utils.gDependencyDatabase == None:\r
1538                 Utils.gDependencyDatabase = {}\r
1539 \r
1540 def ParseDefines(DefineList=[]):\r
1541     DefineDict = {}\r
1542     if DefineList != None:\r
1543         for Define in DefineList:\r
1544             DefineTokenList = Define.split("=", 1)\r
1545             if len(DefineTokenList) == 1:\r
1546                 DefineDict[DefineTokenList[0]] = ""\r
1547             else:\r
1548                 DefineDict[DefineTokenList[0]] = DefineTokenList[1].strip()\r
1549     return DefineDict\r
1550 \r
1551 gParamCheck = []\r
1552 def SingleCheckCallback(option, opt_str, value, parser):\r
1553     if option not in gParamCheck:\r
1554         setattr(parser.values, option.dest, value)\r
1555         gParamCheck.append(option)\r
1556     else:\r
1557         parser.error("Option %s only allows one instance in command line!" % option)\r
1558 \r
1559 ## Parse command line options\r
1560 #\r
1561 # Using standard Python module optparse to parse command line option of this tool.\r
1562 #\r
1563 #   @retval Opt   A optparse.Values object containing the parsed options\r
1564 #   @retval Args  Target of build command\r
1565 #\r
1566 def MyOptionParser():\r
1567     Parser = OptionParser(description=__copyright__,version=__version__,prog="build.exe",usage="%prog [options] [all|fds|genc|genmake|clean|cleanall|cleanlib|modules|libraries|run]")\r
1568     Parser.add_option("-a", "--arch", action="append", type="choice", choices=['IA32','X64','IPF','EBC','ARM'], dest="TargetArch",\r
1569         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
1570     Parser.add_option("-p", "--platform", action="callback", type="string", dest="PlatformFile", callback=SingleCheckCallback,\r
1571         help="Build the platform specified by the DSC file name argument, overriding target.txt's ACTIVE_PLATFORM definition.")\r
1572     Parser.add_option("-m", "--module", action="callback", type="string", dest="ModuleFile", callback=SingleCheckCallback,\r
1573         help="Build the module specified by the INF file name argument.")\r
1574     Parser.add_option("-b", "--buildtarget", action="append", type="choice", choices=['DEBUG','RELEASE'], dest="BuildTarget",\r
1575         help="BuildTarget is one of list: DEBUG, RELEASE, which overrides target.txt's TARGET definition. To specify more TARGET, please repeat this option.")\r
1576     Parser.add_option("-t", "--tagname", action="append", type="string", dest="ToolChain",\r
1577         help="Using the Tool Chain Tagname to build the platform, overriding target.txt's TOOL_CHAIN_TAG definition.")\r
1578     Parser.add_option("-x", "--sku-id", action="callback", type="string", dest="SkuId", callback=SingleCheckCallback,\r
1579         help="Using this name of SKU ID to build the platform, overriding SKUID_IDENTIFIER in DSC file.")\r
1580 \r
1581     Parser.add_option("-n", action="callback", type="int", dest="ThreadNumber", callback=SingleCheckCallback,\r
1582         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
1583 \r
1584     Parser.add_option("-f", "--fdf", action="callback", type="string", dest="FdfFile", callback=SingleCheckCallback,\r
1585         help="The name of the FDF file to use, which overrides the setting in the DSC file.")\r
1586     Parser.add_option("-r", "--rom-image", action="append", type="string", dest="RomImage", default=[],\r
1587         help="The name of FD to be generated. The name must be from [FD] section in FDF file.")\r
1588     Parser.add_option("-i", "--fv-image", action="append", type="string", dest="FvImage", default=[],\r
1589         help="The name of FV to be generated. The name must be from [FV] section in FDF file.")\r
1590 \r
1591     Parser.add_option("-u", "--skip-autogen", action="store_true", dest="SkipAutoGen", help="Skip AutoGen step.")\r
1592     Parser.add_option("-e", "--re-parse", action="store_true", dest="Reparse", help="Re-parse all meta-data files.")\r
1593 \r
1594     Parser.add_option("-c", "--case-insensitive", action="store_true", dest="CaseInsensitive", help="Don't check case of file name.")\r
1595 \r
1596     # Parser.add_option("-D", "--define", action="append", dest="Defines", metavar="NAME[=[VALUE]]",\r
1597     #     help="Define global macro which can be used in DSC/DEC/INF files.")\r
1598 \r
1599     Parser.add_option("-w", "--warning-as-error", action="store_true", dest="WarningAsError", help="Treat warning in tools as error.")\r
1600     Parser.add_option("-j", "--log", action="store", dest="LogFile", help="Put log in specified file as well as on console.")\r
1601 \r
1602     Parser.add_option("-s", "--silent", action="store_true", type=None, dest="SilentMode",\r
1603         help="Make use of silent mode of (n)make.")\r
1604     Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")\r
1605     Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\\r
1606                                                                                "including library instances selected, final dependency expression, "\\r
1607                                                                                "and warning messages, etc.")\r
1608     Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")\r
1609     Parser.add_option("-D", "--define", action="append", type="string", dest="Macros", help="Macro: \"Name [= Value]\".")\r
1610 \r
1611     Parser.add_option("-y", "--report-file", action="store", dest="ReportFile", help="Create/overwrite the report to the specified filename.")\r
1612     Parser.add_option("-Y", "--report-type", action="append", type="choice", choices=['ALL','PCD','LIBRARY','FLASH','DEPEX','BUILD_FLAGS','PREDICTION'], dest="ReportType",\r
1613         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
1614 \r
1615     (Opt, Args)=Parser.parse_args()\r
1616     return (Opt, Args)\r
1617 \r
1618 ## Tool entrance method\r
1619 #\r
1620 # This method mainly dispatch specific methods per the command line options.\r
1621 # If no error found, return zero value so the caller of this tool can know\r
1622 # if it's executed successfully or not.\r
1623 #\r
1624 #   @retval 0     Tool was successful\r
1625 #   @retval 1     Tool failed\r
1626 #\r
1627 def Main():\r
1628     StartTime = time.time()\r
1629 \r
1630     # Initialize log system\r
1631     EdkLogger.Initialize()\r
1632 \r
1633     #\r
1634     # Parse the options and args\r
1635     #\r
1636     (Option, Target) = MyOptionParser()\r
1637     GlobalData.gOptions = Option\r
1638     GlobalData.gCaseInsensitive = Option.CaseInsensitive\r
1639 \r
1640     # Set log level\r
1641     if Option.verbose != None:\r
1642         EdkLogger.SetLevel(EdkLogger.VERBOSE)\r
1643     elif Option.quiet != None:\r
1644         EdkLogger.SetLevel(EdkLogger.QUIET)\r
1645     elif Option.debug != None:\r
1646         EdkLogger.SetLevel(Option.debug + 1)\r
1647     else:\r
1648         EdkLogger.SetLevel(EdkLogger.INFO)\r
1649 \r
1650     if Option.LogFile != None:\r
1651         EdkLogger.SetLogFile(Option.LogFile)\r
1652 \r
1653     if Option.WarningAsError == True:\r
1654         EdkLogger.SetWarningAsError()\r
1655 \r
1656     if platform.platform().find("Windows") >= 0:\r
1657         GlobalData.gIsWindows = True\r
1658     else:\r
1659         GlobalData.gIsWindows = False\r
1660 \r
1661     EdkLogger.quiet(time.strftime("%H:%M:%S, %b.%d %Y ", time.localtime()) + "[%s]\n" % platform.platform())\r
1662     ReturnCode = 0\r
1663     MyBuild = None\r
1664     try:\r
1665         if len(Target) == 0:\r
1666             Target = "all"\r
1667         elif len(Target) >= 2:\r
1668             EdkLogger.error("build", OPTION_NOT_SUPPORTED, "More than one targets are not supported.",\r
1669                             ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))\r
1670         else:\r
1671             Target = Target[0].lower()\r
1672 \r
1673         if Target not in gSupportedTarget:\r
1674             EdkLogger.error("build", OPTION_NOT_SUPPORTED, "Not supported target [%s]." % Target,\r
1675                             ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))\r
1676 \r
1677         GlobalData.gGlobalDefines = ParseDefines(Option.Macros)\r
1678         #\r
1679         # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH\r
1680         #\r
1681         CheckEnvVariable()\r
1682         Workspace = os.getenv("WORKSPACE")\r
1683         #\r
1684         # Get files real name in workspace dir\r
1685         #\r
1686         GlobalData.gAllFiles = Utils.DirCache(Workspace)\r
1687 \r
1688         WorkingDirectory = os.getcwd()\r
1689         if not Option.ModuleFile:\r
1690             FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.inf')))\r
1691             FileNum = len(FileList)\r
1692             if FileNum >= 2:\r
1693                 EdkLogger.error("build", OPTION_NOT_SUPPORTED, "There are %d INF files in %s." % (FileNum, WorkingDirectory),\r
1694                                 ExtraData="Please use '-m <INF_FILE_PATH>' switch to choose one.")\r
1695             elif FileNum == 1:\r
1696                 Option.ModuleFile = NormFile(FileList[0], Workspace)\r
1697 \r
1698         if Option.ModuleFile:\r
1699             if os.path.isabs (Option.ModuleFile):\r
1700                 if os.path.normcase (os.path.normpath(Option.ModuleFile)).find (Workspace) == 0:\r
1701                     Option.ModuleFile = NormFile(os.path.normpath(Option.ModuleFile), Workspace)\r
1702             Option.ModuleFile = PathClass(Option.ModuleFile, Workspace)\r
1703             ErrorCode, ErrorInfo = Option.ModuleFile.Validate(".inf", False)\r
1704             if ErrorCode != 0:\r
1705                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
1706 \r
1707         if Option.PlatformFile != None:\r
1708             if os.path.isabs (Option.PlatformFile):\r
1709                 if os.path.normcase (os.path.normpath(Option.PlatformFile)).find (Workspace) == 0:\r
1710                     Option.PlatformFile = NormFile(os.path.normpath(Option.PlatformFile), Workspace)\r
1711             Option.PlatformFile = PathClass(Option.PlatformFile, Workspace)\r
1712             ErrorCode, ErrorInfo = Option.PlatformFile.Validate(".dsc", False)\r
1713             if ErrorCode != 0:\r
1714                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
1715 \r
1716         if Option.FdfFile != None:\r
1717             if os.path.isabs (Option.FdfFile):\r
1718                 if os.path.normcase (os.path.normpath(Option.FdfFile)).find (Workspace) == 0:\r
1719                     Option.FdfFile = NormFile(os.path.normpath(Option.FdfFile), Workspace)\r
1720             Option.FdfFile = PathClass(Option.FdfFile, Workspace)\r
1721             ErrorCode, ErrorInfo = Option.FdfFile.Validate(".fdf", False)\r
1722             if ErrorCode != 0:\r
1723                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
1724 \r
1725         MyBuild = Build(Target, Workspace, Option.PlatformFile, Option.ModuleFile,\r
1726                         Option.TargetArch, Option.ToolChain, Option.BuildTarget,\r
1727                         Option.FdfFile, Option.RomImage, Option.FvImage,\r
1728                         None, Option.SilentMode, Option.ThreadNumber,\r
1729                         Option.SkipAutoGen, Option.Reparse, Option.SkuId, \r
1730                         Option.ReportFile, Option.ReportType)\r
1731         MyBuild.Launch()\r
1732         #MyBuild.DumpBuildData()\r
1733     except FatalError, X:\r
1734         if MyBuild != None:\r
1735             # for multi-thread build exits safely\r
1736             MyBuild.Relinquish()\r
1737         if Option != None and Option.debug != None:\r
1738             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1739         ReturnCode = X.args[0]\r
1740     except Warning, X:\r
1741         # error from Fdf parser\r
1742         if MyBuild != None:\r
1743             # for multi-thread build exits safely\r
1744             MyBuild.Relinquish()\r
1745         if Option != None and Option.debug != None:\r
1746             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1747         else:\r
1748             EdkLogger.error(X.ToolName, FORMAT_INVALID, File=X.FileName, Line=X.LineNumber, ExtraData=X.Message, RaiseError = False)\r
1749         ReturnCode = FORMAT_INVALID\r
1750     except KeyboardInterrupt:\r
1751         ReturnCode = ABORT_ERROR\r
1752         if Option != None and Option.debug != None:\r
1753             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1754     except:\r
1755         if MyBuild != None:\r
1756             # for multi-thread build exits safely\r
1757             MyBuild.Relinquish()\r
1758 \r
1759         # try to get the meta-file from the object causing exception\r
1760         Tb = sys.exc_info()[-1]\r
1761         MetaFile = GlobalData.gProcessingFile\r
1762         while Tb != None:\r
1763             if 'self' in Tb.tb_frame.f_locals and hasattr(Tb.tb_frame.f_locals['self'], 'MetaFile'):\r
1764                 MetaFile = Tb.tb_frame.f_locals['self'].MetaFile\r
1765             Tb = Tb.tb_next\r
1766         EdkLogger.error(\r
1767                     "\nbuild",\r
1768                     CODE_ERROR,\r
1769                     "Unknown fatal error when processing [%s]" % MetaFile,\r
1770                     ExtraData="\n(Please send email to dev@buildtools.tianocore.org for help, attaching following call stack trace!)\n",\r
1771                     RaiseError=False\r
1772                     )\r
1773         EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
1774         ReturnCode = CODE_ERROR\r
1775     finally:\r
1776         Utils.Progressor.Abort()\r
1777 \r
1778     if ReturnCode == 0:\r
1779         Conclusion = "Done"\r
1780     elif ReturnCode == ABORT_ERROR:\r
1781         Conclusion = "Aborted"\r
1782     else:\r
1783         Conclusion = "Failed"\r
1784     FinishTime = time.time()\r
1785     BuildDuration = time.strftime("%M:%S", time.gmtime(int(round(FinishTime - StartTime))))\r
1786     if MyBuild != None:\r
1787         MyBuild.BuildReport.GenerateReport(BuildDuration)\r
1788         MyBuild.Db.Close()\r
1789     EdkLogger.SetLevel(EdkLogger.QUIET)\r
1790     EdkLogger.quiet("\n- %s -\n%s [%s]" % (Conclusion, time.strftime("%H:%M:%S, %b.%d %Y", time.localtime()), BuildDuration))\r
1791 \r
1792     return ReturnCode\r
1793 \r
1794 if __name__ == '__main__':\r
1795     r = Main()\r
1796     ## 0-127 is a safe return range, and 1 is a standard default error\r
1797     if r < 0 or r > 127: r = 1\r
1798     sys.exit(r)\r
1799 \r