a8b4ce73751fc881a4262d989eff7edb0cc38f5c
[mirror_edk2.git] / BaseTools / Source / Python / build / build.py
1 ## @file\r
2 # build a platform or a module\r
3 #\r
4 #  Copyright (c) 2014, Hewlett-Packard Development Company, L.P.<BR>\r
5 #  Copyright (c) 2007 - 2019, Intel Corporation. All rights reserved.<BR>\r
6 #  Copyright (c) 2018, Hewlett Packard Enterprise Development, L.P.<BR>\r
7 #\r
8 #  SPDX-License-Identifier: BSD-2-Clause-Patent\r
9 #\r
10 \r
11 ##\r
12 # Import Modules\r
13 #\r
14 from __future__ import print_function\r
15 import Common.LongFilePathOs as os\r
16 import re\r
17 import sys\r
18 import glob\r
19 import time\r
20 import platform\r
21 import traceback\r
22 import encodings.ascii\r
23 import multiprocessing\r
24 \r
25 from struct import *\r
26 from threading import *\r
27 import threading\r
28 from optparse import OptionParser\r
29 from subprocess import *\r
30 from Common import Misc as Utils\r
31 \r
32 from Common.LongFilePathSupport import OpenLongFilePath as open\r
33 from Common.TargetTxtClassObject import TargetTxtClassObject\r
34 from Common.ToolDefClassObject import ToolDefClassObject\r
35 from Common.DataType import *\r
36 from Common.BuildVersion import gBUILD_VERSION\r
37 from AutoGen.AutoGen import *\r
38 from Common.BuildToolError import *\r
39 from Workspace.WorkspaceDatabase import WorkspaceDatabase\r
40 from Common.MultipleWorkspace import MultipleWorkspace as mws\r
41 \r
42 from BuildReport import BuildReport\r
43 from GenPatchPcdTable.GenPatchPcdTable import *\r
44 from PatchPcdValue.PatchPcdValue import *\r
45 \r
46 import Common.EdkLogger\r
47 import Common.GlobalData as GlobalData\r
48 from GenFds.GenFds import GenFds, GenFdsApi\r
49 \r
50 from collections import OrderedDict, defaultdict\r
51 \r
52 # Version and Copyright\r
53 VersionNumber = "0.60" + ' ' + gBUILD_VERSION\r
54 __version__ = "%prog Version " + VersionNumber\r
55 __copyright__ = "Copyright (c) 2007 - 2018, Intel Corporation  All rights reserved."\r
56 \r
57 ## standard targets of build command\r
58 gSupportedTarget = ['all', 'genc', 'genmake', 'modules', 'libraries', 'fds', 'clean', 'cleanall', 'cleanlib', 'run']\r
59 \r
60 ## build configuration file\r
61 gBuildConfiguration = "target.txt"\r
62 gToolsDefinition = "tools_def.txt"\r
63 \r
64 TemporaryTablePattern = re.compile(r'^_\d+_\d+_[a-fA-F0-9]+$')\r
65 TmpTableDict = {}\r
66 \r
67 ## Check environment PATH variable to make sure the specified tool is found\r
68 #\r
69 #   If the tool is found in the PATH, then True is returned\r
70 #   Otherwise, False is returned\r
71 #\r
72 def IsToolInPath(tool):\r
73     if 'PATHEXT' in os.environ:\r
74         extns = os.environ['PATHEXT'].split(os.path.pathsep)\r
75     else:\r
76         extns = ('',)\r
77     for pathDir in os.environ['PATH'].split(os.path.pathsep):\r
78         for ext in extns:\r
79             if os.path.exists(os.path.join(pathDir, tool + ext)):\r
80                 return True\r
81     return False\r
82 \r
83 ## Check environment variables\r
84 #\r
85 #  Check environment variables that must be set for build. Currently they are\r
86 #\r
87 #   WORKSPACE           The directory all packages/platforms start from\r
88 #   EDK_TOOLS_PATH      The directory contains all tools needed by the build\r
89 #   PATH                $(EDK_TOOLS_PATH)/Bin/<sys> must be set in PATH\r
90 #\r
91 #   If any of above environment variable is not set or has error, the build\r
92 #   will be broken.\r
93 #\r
94 def CheckEnvVariable():\r
95     # check WORKSPACE\r
96     if "WORKSPACE" not in os.environ:\r
97         EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",\r
98                         ExtraData="WORKSPACE")\r
99 \r
100     WorkspaceDir = os.path.normcase(os.path.normpath(os.environ["WORKSPACE"]))\r
101     if not os.path.exists(WorkspaceDir):\r
102         EdkLogger.error("build", FILE_NOT_FOUND, "WORKSPACE doesn't exist", ExtraData=WorkspaceDir)\r
103     elif ' ' in WorkspaceDir:\r
104         EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in WORKSPACE path",\r
105                         ExtraData=WorkspaceDir)\r
106     os.environ["WORKSPACE"] = WorkspaceDir\r
107 \r
108     # set multiple workspace\r
109     PackagesPath = os.getenv("PACKAGES_PATH")\r
110     mws.setWs(WorkspaceDir, PackagesPath)\r
111     if mws.PACKAGES_PATH:\r
112         for Path in mws.PACKAGES_PATH:\r
113             if not os.path.exists(Path):\r
114                 EdkLogger.error("build", FILE_NOT_FOUND, "One Path in PACKAGES_PATH doesn't exist", ExtraData=Path)\r
115             elif ' ' in Path:\r
116                 EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in PACKAGES_PATH", ExtraData=Path)\r
117 \r
118 \r
119     os.environ["EDK_TOOLS_PATH"] = os.path.normcase(os.environ["EDK_TOOLS_PATH"])\r
120 \r
121     # check EDK_TOOLS_PATH\r
122     if "EDK_TOOLS_PATH" not in os.environ:\r
123         EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",\r
124                         ExtraData="EDK_TOOLS_PATH")\r
125 \r
126     # check PATH\r
127     if "PATH" not in os.environ:\r
128         EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",\r
129                         ExtraData="PATH")\r
130 \r
131     GlobalData.gWorkspace = WorkspaceDir\r
132 \r
133     GlobalData.gGlobalDefines["WORKSPACE"]  = WorkspaceDir\r
134     GlobalData.gGlobalDefines["EDK_TOOLS_PATH"] = os.environ["EDK_TOOLS_PATH"]\r
135 \r
136 ## Get normalized file path\r
137 #\r
138 # Convert the path to be local format, and remove the WORKSPACE path at the\r
139 # beginning if the file path is given in full path.\r
140 #\r
141 # @param  FilePath      File path to be normalized\r
142 # @param  Workspace     Workspace path which the FilePath will be checked against\r
143 #\r
144 # @retval string        The normalized file path\r
145 #\r
146 def NormFile(FilePath, Workspace):\r
147     # check if the path is absolute or relative\r
148     if os.path.isabs(FilePath):\r
149         FileFullPath = os.path.normpath(FilePath)\r
150     else:\r
151         FileFullPath = os.path.normpath(mws.join(Workspace, FilePath))\r
152         Workspace = mws.getWs(Workspace, FilePath)\r
153 \r
154     # check if the file path exists or not\r
155     if not os.path.isfile(FileFullPath):\r
156         EdkLogger.error("build", FILE_NOT_FOUND, ExtraData="\t%s (Please give file in absolute path or relative to WORKSPACE)" % FileFullPath)\r
157 \r
158     # remove workspace directory from the beginning part of the file path\r
159     if Workspace[-1] in ["\\", "/"]:\r
160         return FileFullPath[len(Workspace):]\r
161     else:\r
162         return FileFullPath[(len(Workspace) + 1):]\r
163 \r
164 ## Get the output of an external program\r
165 #\r
166 # This is the entrance method of thread reading output of an external program and\r
167 # putting them in STDOUT/STDERR of current program.\r
168 #\r
169 # @param  From      The stream message read from\r
170 # @param  To        The stream message put on\r
171 # @param  ExitFlag  The flag used to indicate stopping reading\r
172 #\r
173 def ReadMessage(From, To, ExitFlag):\r
174     while True:\r
175         # read one line a time\r
176         Line = From.readline()\r
177         # empty string means "end"\r
178         if Line is not None and Line != b"":\r
179             To(Line.rstrip().decode(encoding='utf-8', errors='ignore'))\r
180         else:\r
181             break\r
182         if ExitFlag.isSet():\r
183             break\r
184 \r
185 ## Launch an external program\r
186 #\r
187 # This method will call subprocess.Popen to execute an external program with\r
188 # given options in specified directory. Because of the dead-lock issue during\r
189 # redirecting output of the external program, threads are used to to do the\r
190 # redirection work.\r
191 #\r
192 # @param  Command               A list or string containing the call of the program\r
193 # @param  WorkingDir            The directory in which the program will be running\r
194 #\r
195 def LaunchCommand(Command, WorkingDir):\r
196     BeginTime = time.time()\r
197     # if working directory doesn't exist, Popen() will raise an exception\r
198     if not os.path.isdir(WorkingDir):\r
199         EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=WorkingDir)\r
200 \r
201     # Command is used as the first Argument in following Popen().\r
202     # It could be a string or sequence. We find that if command is a string in following Popen(),\r
203     # ubuntu may fail with an error message that the command is not found.\r
204     # So here we may need convert command from string to list instance.\r
205     if platform.system() != 'Windows':\r
206         if not isinstance(Command, list):\r
207             Command = Command.split()\r
208         Command = ' '.join(Command)\r
209 \r
210     Proc = None\r
211     EndOfProcedure = None\r
212     try:\r
213         # launch the command\r
214         Proc = Popen(Command, stdout=PIPE, stderr=PIPE, env=os.environ, cwd=WorkingDir, bufsize=-1, shell=True)\r
215 \r
216         # launch two threads to read the STDOUT and STDERR\r
217         EndOfProcedure = Event()\r
218         EndOfProcedure.clear()\r
219         if Proc.stdout:\r
220             StdOutThread = Thread(target=ReadMessage, args=(Proc.stdout, EdkLogger.info, EndOfProcedure))\r
221             StdOutThread.setName("STDOUT-Redirector")\r
222             StdOutThread.setDaemon(False)\r
223             StdOutThread.start()\r
224 \r
225         if Proc.stderr:\r
226             StdErrThread = Thread(target=ReadMessage, args=(Proc.stderr, EdkLogger.quiet, EndOfProcedure))\r
227             StdErrThread.setName("STDERR-Redirector")\r
228             StdErrThread.setDaemon(False)\r
229             StdErrThread.start()\r
230 \r
231         # waiting for program exit\r
232         Proc.wait()\r
233     except: # in case of aborting\r
234         # terminate the threads redirecting the program output\r
235         EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
236         if EndOfProcedure is not None:\r
237             EndOfProcedure.set()\r
238         if Proc is None:\r
239             if not isinstance(Command, type("")):\r
240                 Command = " ".join(Command)\r
241             EdkLogger.error("build", COMMAND_FAILURE, "Failed to start command", ExtraData="%s [%s]" % (Command, WorkingDir))\r
242 \r
243     if Proc.stdout:\r
244         StdOutThread.join()\r
245     if Proc.stderr:\r
246         StdErrThread.join()\r
247 \r
248     # check the return code of the program\r
249     if Proc.returncode != 0:\r
250         if not isinstance(Command, type("")):\r
251             Command = " ".join(Command)\r
252         # print out the Response file and its content when make failure\r
253         RespFile = os.path.join(WorkingDir, 'OUTPUT', 'respfilelist.txt')\r
254         if os.path.isfile(RespFile):\r
255             f = open(RespFile)\r
256             RespContent = f.read()\r
257             f.close()\r
258             EdkLogger.info(RespContent)\r
259 \r
260         EdkLogger.error("build", COMMAND_FAILURE, ExtraData="%s [%s]" % (Command, WorkingDir))\r
261     return "%dms" % (int(round((time.time() - BeginTime) * 1000)))\r
262 \r
263 ## The smallest unit that can be built in multi-thread build mode\r
264 #\r
265 # This is the base class of build unit. The "Obj" parameter must provide\r
266 # __str__(), __eq__() and __hash__() methods. Otherwise there could be build units\r
267 # missing build.\r
268 #\r
269 # Currently the "Obj" should be only ModuleAutoGen or PlatformAutoGen objects.\r
270 #\r
271 class BuildUnit:\r
272     ## The constructor\r
273     #\r
274     #   @param  self        The object pointer\r
275     #   @param  Obj         The object the build is working on\r
276     #   @param  Target      The build target name, one of gSupportedTarget\r
277     #   @param  Dependency  The BuildUnit(s) which must be completed in advance\r
278     #   @param  WorkingDir  The directory build command starts in\r
279     #\r
280     def __init__(self, Obj, BuildCommand, Target, Dependency, WorkingDir="."):\r
281         self.BuildObject = Obj\r
282         self.Dependency = Dependency\r
283         self.WorkingDir = WorkingDir\r
284         self.Target = Target\r
285         self.BuildCommand = BuildCommand\r
286         if not BuildCommand:\r
287             EdkLogger.error("build", OPTION_MISSING,\r
288                             "No build command found for this module. "\r
289                             "Please check your setting of %s_%s_%s_MAKE_PATH in Conf/tools_def.txt file." %\r
290                                 (Obj.BuildTarget, Obj.ToolChain, Obj.Arch),\r
291                             ExtraData=str(Obj))\r
292 \r
293 \r
294     ## str() method\r
295     #\r
296     #   It just returns the string representation of self.BuildObject\r
297     #\r
298     #   @param  self        The object pointer\r
299     #\r
300     def __str__(self):\r
301         return str(self.BuildObject)\r
302 \r
303     ## "==" operator method\r
304     #\r
305     #   It just compares self.BuildObject with "Other". So self.BuildObject must\r
306     #   provide its own __eq__() method.\r
307     #\r
308     #   @param  self        The object pointer\r
309     #   @param  Other       The other BuildUnit object compared to\r
310     #\r
311     def __eq__(self, Other):\r
312         return Other and self.BuildObject == Other.BuildObject \\r
313                 and Other.BuildObject \\r
314                 and self.BuildObject.Arch == Other.BuildObject.Arch\r
315 \r
316     ## hash() method\r
317     #\r
318     #   It just returns the hash value of self.BuildObject which must be hashable.\r
319     #\r
320     #   @param  self        The object pointer\r
321     #\r
322     def __hash__(self):\r
323         return hash(self.BuildObject) + hash(self.BuildObject.Arch)\r
324 \r
325     def __repr__(self):\r
326         return repr(self.BuildObject)\r
327 \r
328 ## The smallest module unit that can be built by nmake/make command in multi-thread build mode\r
329 #\r
330 # This class is for module build by nmake/make build system. The "Obj" parameter\r
331 # must provide __str__(), __eq__() and __hash__() methods. Otherwise there could\r
332 # be make units missing build.\r
333 #\r
334 # Currently the "Obj" should be only ModuleAutoGen object.\r
335 #\r
336 class ModuleMakeUnit(BuildUnit):\r
337     ## The constructor\r
338     #\r
339     #   @param  self        The object pointer\r
340     #   @param  Obj         The ModuleAutoGen object the build is working on\r
341     #   @param  Target      The build target name, one of gSupportedTarget\r
342     #\r
343     def __init__(self, Obj, Target):\r
344         Dependency = [ModuleMakeUnit(La, Target) for La in Obj.LibraryAutoGenList]\r
345         BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir)\r
346         if Target in [None, "", "all"]:\r
347             self.Target = "tbuild"\r
348 \r
349 ## The smallest platform unit that can be built by nmake/make command in multi-thread build mode\r
350 #\r
351 # This class is for platform build by nmake/make build system. The "Obj" parameter\r
352 # must provide __str__(), __eq__() and __hash__() methods. Otherwise there could\r
353 # be make units missing build.\r
354 #\r
355 # Currently the "Obj" should be only PlatformAutoGen object.\r
356 #\r
357 class PlatformMakeUnit(BuildUnit):\r
358     ## The constructor\r
359     #\r
360     #   @param  self        The object pointer\r
361     #   @param  Obj         The PlatformAutoGen object the build is working on\r
362     #   @param  Target      The build target name, one of gSupportedTarget\r
363     #\r
364     def __init__(self, Obj, Target):\r
365         Dependency = [ModuleMakeUnit(Lib, Target) for Lib in self.BuildObject.LibraryAutoGenList]\r
366         Dependency.extend([ModuleMakeUnit(Mod, Target) for Mod in self.BuildObject.ModuleAutoGenList])\r
367         BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir)\r
368 \r
369 ## The class representing the task of a module build or platform build\r
370 #\r
371 # This class manages the build tasks in multi-thread build mode. Its jobs include\r
372 # scheduling thread running, catching thread error, monitor the thread status, etc.\r
373 #\r
374 class BuildTask:\r
375     # queue for tasks waiting for schedule\r
376     _PendingQueue = OrderedDict()\r
377     _PendingQueueLock = threading.Lock()\r
378 \r
379     # queue for tasks ready for running\r
380     _ReadyQueue = OrderedDict()\r
381     _ReadyQueueLock = threading.Lock()\r
382 \r
383     # queue for run tasks\r
384     _RunningQueue = OrderedDict()\r
385     _RunningQueueLock = threading.Lock()\r
386 \r
387     # queue containing all build tasks, in case duplicate build\r
388     _TaskQueue = OrderedDict()\r
389 \r
390     # flag indicating error occurs in a running thread\r
391     _ErrorFlag = threading.Event()\r
392     _ErrorFlag.clear()\r
393     _ErrorMessage = ""\r
394 \r
395     # BoundedSemaphore object used to control the number of running threads\r
396     _Thread = None\r
397 \r
398     # flag indicating if the scheduler is started or not\r
399     _SchedulerStopped = threading.Event()\r
400     _SchedulerStopped.set()\r
401 \r
402     ## Start the task scheduler thread\r
403     #\r
404     #   @param  MaxThreadNumber     The maximum thread number\r
405     #   @param  ExitFlag            Flag used to end the scheduler\r
406     #\r
407     @staticmethod\r
408     def StartScheduler(MaxThreadNumber, ExitFlag):\r
409         SchedulerThread = Thread(target=BuildTask.Scheduler, args=(MaxThreadNumber, ExitFlag))\r
410         SchedulerThread.setName("Build-Task-Scheduler")\r
411         SchedulerThread.setDaemon(False)\r
412         SchedulerThread.start()\r
413         # wait for the scheduler to be started, especially useful in Linux\r
414         while not BuildTask.IsOnGoing():\r
415             time.sleep(0.01)\r
416 \r
417     ## Scheduler method\r
418     #\r
419     #   @param  MaxThreadNumber     The maximum thread number\r
420     #   @param  ExitFlag            Flag used to end the scheduler\r
421     #\r
422     @staticmethod\r
423     def Scheduler(MaxThreadNumber, ExitFlag):\r
424         BuildTask._SchedulerStopped.clear()\r
425         try:\r
426             # use BoundedSemaphore to control the maximum running threads\r
427             BuildTask._Thread = BoundedSemaphore(MaxThreadNumber)\r
428             #\r
429             # scheduling loop, which will exits when no pending/ready task and\r
430             # indicated to do so, or there's error in running thread\r
431             #\r
432             while (len(BuildTask._PendingQueue) > 0 or len(BuildTask._ReadyQueue) > 0 \\r
433                    or not ExitFlag.isSet()) and not BuildTask._ErrorFlag.isSet():\r
434                 EdkLogger.debug(EdkLogger.DEBUG_8, "Pending Queue (%d), Ready Queue (%d)"\r
435                                 % (len(BuildTask._PendingQueue), len(BuildTask._ReadyQueue)))\r
436 \r
437                 # get all pending tasks\r
438                 BuildTask._PendingQueueLock.acquire()\r
439                 BuildObjectList = list(BuildTask._PendingQueue.keys())\r
440                 #\r
441                 # check if their dependency is resolved, and if true, move them\r
442                 # into ready queue\r
443                 #\r
444                 for BuildObject in BuildObjectList:\r
445                     Bt = BuildTask._PendingQueue[BuildObject]\r
446                     if Bt.IsReady():\r
447                         BuildTask._ReadyQueue[BuildObject] = BuildTask._PendingQueue.pop(BuildObject)\r
448                 BuildTask._PendingQueueLock.release()\r
449 \r
450                 # launch build thread until the maximum number of threads is reached\r
451                 while not BuildTask._ErrorFlag.isSet():\r
452                     # empty ready queue, do nothing further\r
453                     if len(BuildTask._ReadyQueue) == 0:\r
454                         break\r
455 \r
456                     # wait for active thread(s) exit\r
457                     BuildTask._Thread.acquire(True)\r
458 \r
459                     # start a new build thread\r
460                     Bo, Bt = BuildTask._ReadyQueue.popitem()\r
461 \r
462                     # move into running queue\r
463                     BuildTask._RunningQueueLock.acquire()\r
464                     BuildTask._RunningQueue[Bo] = Bt\r
465                     BuildTask._RunningQueueLock.release()\r
466 \r
467                     Bt.Start()\r
468                     # avoid tense loop\r
469                     time.sleep(0.01)\r
470 \r
471                 # avoid tense loop\r
472                 time.sleep(0.01)\r
473 \r
474             # wait for all running threads exit\r
475             if BuildTask._ErrorFlag.isSet():\r
476                 EdkLogger.quiet("\nWaiting for all build threads exit...")\r
477             # while not BuildTask._ErrorFlag.isSet() and \\r
478             while len(BuildTask._RunningQueue) > 0:\r
479                 EdkLogger.verbose("Waiting for thread ending...(%d)" % len(BuildTask._RunningQueue))\r
480                 EdkLogger.debug(EdkLogger.DEBUG_8, "Threads [%s]" % ", ".join(Th.getName() for Th in threading.enumerate()))\r
481                 # avoid tense loop\r
482                 time.sleep(0.1)\r
483         except BaseException as X:\r
484             #\r
485             # TRICK: hide the output of threads left running, so that the user can\r
486             #        catch the error message easily\r
487             #\r
488             EdkLogger.SetLevel(EdkLogger.ERROR)\r
489             BuildTask._ErrorFlag.set()\r
490             BuildTask._ErrorMessage = "build thread scheduler error\n\t%s" % str(X)\r
491 \r
492         BuildTask._PendingQueue.clear()\r
493         BuildTask._ReadyQueue.clear()\r
494         BuildTask._RunningQueue.clear()\r
495         BuildTask._TaskQueue.clear()\r
496         BuildTask._SchedulerStopped.set()\r
497 \r
498     ## Wait for all running method exit\r
499     #\r
500     @staticmethod\r
501     def WaitForComplete():\r
502         BuildTask._SchedulerStopped.wait()\r
503 \r
504     ## Check if the scheduler is running or not\r
505     #\r
506     @staticmethod\r
507     def IsOnGoing():\r
508         return not BuildTask._SchedulerStopped.isSet()\r
509 \r
510     ## Abort the build\r
511     @staticmethod\r
512     def Abort():\r
513         if BuildTask.IsOnGoing():\r
514             BuildTask._ErrorFlag.set()\r
515             BuildTask.WaitForComplete()\r
516 \r
517     ## Check if there's error in running thread\r
518     #\r
519     #   Since the main thread cannot catch exceptions in other thread, we have to\r
520     #   use threading.Event to communicate this formation to main thread.\r
521     #\r
522     @staticmethod\r
523     def HasError():\r
524         return BuildTask._ErrorFlag.isSet()\r
525 \r
526     ## Get error message in running thread\r
527     #\r
528     #   Since the main thread cannot catch exceptions in other thread, we have to\r
529     #   use a static variable to communicate this message to main thread.\r
530     #\r
531     @staticmethod\r
532     def GetErrorMessage():\r
533         return BuildTask._ErrorMessage\r
534 \r
535     ## Factory method to create a BuildTask object\r
536     #\r
537     #   This method will check if a module is building or has been built. And if\r
538     #   true, just return the associated BuildTask object in the _TaskQueue. If\r
539     #   not, create and return a new BuildTask object. The new BuildTask object\r
540     #   will be appended to the _PendingQueue for scheduling later.\r
541     #\r
542     #   @param  BuildItem       A BuildUnit object representing a build object\r
543     #   @param  Dependency      The dependent build object of BuildItem\r
544     #\r
545     @staticmethod\r
546     def New(BuildItem, Dependency=None):\r
547         if BuildItem in BuildTask._TaskQueue:\r
548             Bt = BuildTask._TaskQueue[BuildItem]\r
549             return Bt\r
550 \r
551         Bt = BuildTask()\r
552         Bt._Init(BuildItem, Dependency)\r
553         BuildTask._TaskQueue[BuildItem] = Bt\r
554 \r
555         BuildTask._PendingQueueLock.acquire()\r
556         BuildTask._PendingQueue[BuildItem] = Bt\r
557         BuildTask._PendingQueueLock.release()\r
558 \r
559         return Bt\r
560 \r
561     ## The real constructor of BuildTask\r
562     #\r
563     #   @param  BuildItem       A BuildUnit object representing a build object\r
564     #   @param  Dependency      The dependent build object of BuildItem\r
565     #\r
566     def _Init(self, BuildItem, Dependency=None):\r
567         self.BuildItem = BuildItem\r
568 \r
569         self.DependencyList = []\r
570         if Dependency is None:\r
571             Dependency = BuildItem.Dependency\r
572         else:\r
573             Dependency.extend(BuildItem.Dependency)\r
574         self.AddDependency(Dependency)\r
575         # flag indicating build completes, used to avoid unnecessary re-build\r
576         self.CompleteFlag = False\r
577 \r
578     ## Check if all dependent build tasks are completed or not\r
579     #\r
580     def IsReady(self):\r
581         ReadyFlag = True\r
582         for Dep in self.DependencyList:\r
583             if Dep.CompleteFlag == True:\r
584                 continue\r
585             ReadyFlag = False\r
586             break\r
587 \r
588         return ReadyFlag\r
589 \r
590     ## Add dependent build task\r
591     #\r
592     #   @param  Dependency      The list of dependent build objects\r
593     #\r
594     def AddDependency(self, Dependency):\r
595         for Dep in Dependency:\r
596             if not Dep.BuildObject.IsBinaryModule and not Dep.BuildObject.CanSkipbyHash():\r
597                 self.DependencyList.append(BuildTask.New(Dep))    # BuildTask list\r
598 \r
599     ## The thread wrapper of LaunchCommand function\r
600     #\r
601     # @param  Command               A list or string contains the call of the command\r
602     # @param  WorkingDir            The directory in which the program will be running\r
603     #\r
604     def _CommandThread(self, Command, WorkingDir):\r
605         try:\r
606             self.BuildItem.BuildObject.BuildTime = LaunchCommand(Command, WorkingDir)\r
607             self.CompleteFlag = True\r
608 \r
609             # Run hash operation post dependency, to account for libs\r
610             if GlobalData.gUseHashCache and self.BuildItem.BuildObject.IsLibrary:\r
611                 HashFile = path.join(self.BuildItem.BuildObject.BuildDir, self.BuildItem.BuildObject.Name + ".hash")\r
612                 SaveFileOnChange(HashFile, self.BuildItem.BuildObject.GenModuleHash(), True)\r
613         except:\r
614             #\r
615             # TRICK: hide the output of threads left running, so that the user can\r
616             #        catch the error message easily\r
617             #\r
618             if not BuildTask._ErrorFlag.isSet():\r
619                 GlobalData.gBuildingModule = "%s [%s, %s, %s]" % (str(self.BuildItem.BuildObject),\r
620                                                                   self.BuildItem.BuildObject.Arch,\r
621                                                                   self.BuildItem.BuildObject.ToolChain,\r
622                                                                   self.BuildItem.BuildObject.BuildTarget\r
623                                                                  )\r
624             EdkLogger.SetLevel(EdkLogger.ERROR)\r
625             BuildTask._ErrorFlag.set()\r
626             BuildTask._ErrorMessage = "%s broken\n    %s [%s]" % \\r
627                                       (threading.currentThread().getName(), Command, WorkingDir)\r
628         if self.BuildItem.BuildObject in GlobalData.gModuleBuildTracking and not BuildTask._ErrorFlag.isSet():\r
629             GlobalData.gModuleBuildTracking[self.BuildItem.BuildObject] = True\r
630         # indicate there's a thread is available for another build task\r
631         BuildTask._RunningQueueLock.acquire()\r
632         BuildTask._RunningQueue.pop(self.BuildItem)\r
633         BuildTask._RunningQueueLock.release()\r
634         BuildTask._Thread.release()\r
635 \r
636     ## Start build task thread\r
637     #\r
638     def Start(self):\r
639         EdkLogger.quiet("Building ... %s" % repr(self.BuildItem))\r
640         Command = self.BuildItem.BuildCommand + [self.BuildItem.Target]\r
641         self.BuildTread = Thread(target=self._CommandThread, args=(Command, self.BuildItem.WorkingDir))\r
642         self.BuildTread.setName("build thread")\r
643         self.BuildTread.setDaemon(False)\r
644         self.BuildTread.start()\r
645 \r
646 ## The class contains the information related to EFI image\r
647 #\r
648 class PeImageInfo():\r
649     ## Constructor\r
650     #\r
651     # Constructor will load all required image information.\r
652     #\r
653     #   @param  BaseName          The full file path of image.\r
654     #   @param  Guid              The GUID for image.\r
655     #   @param  Arch              Arch of this image.\r
656     #   @param  OutputDir         The output directory for image.\r
657     #   @param  DebugDir          The debug directory for image.\r
658     #   @param  ImageClass        PeImage Information\r
659     #\r
660     def __init__(self, BaseName, Guid, Arch, OutputDir, DebugDir, ImageClass):\r
661         self.BaseName         = BaseName\r
662         self.Guid             = Guid\r
663         self.Arch             = Arch\r
664         self.OutputDir        = OutputDir\r
665         self.DebugDir         = DebugDir\r
666         self.Image            = ImageClass\r
667         self.Image.Size       = (self.Image.Size // 0x1000 + 1) * 0x1000\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  BuildOptions        Build options passed from command line\r
688     #\r
689     def __init__(self, Target, WorkspaceDir, BuildOptions):\r
690         self.WorkspaceDir   = WorkspaceDir\r
691         self.Target         = Target\r
692         self.PlatformFile   = BuildOptions.PlatformFile\r
693         self.ModuleFile     = BuildOptions.ModuleFile\r
694         self.ArchList       = BuildOptions.TargetArch\r
695         self.ToolChainList  = BuildOptions.ToolChain\r
696         self.BuildTargetList= BuildOptions.BuildTarget\r
697         self.Fdf            = BuildOptions.FdfFile\r
698         self.FdList         = BuildOptions.RomImage\r
699         self.FvList         = BuildOptions.FvImage\r
700         self.CapList        = BuildOptions.CapName\r
701         self.SilentMode     = BuildOptions.SilentMode\r
702         self.ThreadNumber   = BuildOptions.ThreadNumber\r
703         self.SkipAutoGen    = BuildOptions.SkipAutoGen\r
704         self.Reparse        = BuildOptions.Reparse\r
705         self.SkuId          = BuildOptions.SkuId\r
706         if self.SkuId:\r
707             GlobalData.gSKUID_CMD = self.SkuId\r
708         self.ConfDirectory = BuildOptions.ConfDirectory\r
709         self.SpawnMode      = True\r
710         self.BuildReport    = BuildReport(BuildOptions.ReportFile, BuildOptions.ReportType)\r
711         self.TargetTxt      = TargetTxtClassObject()\r
712         self.ToolDef        = ToolDefClassObject()\r
713         self.AutoGenTime    = 0\r
714         self.MakeTime       = 0\r
715         self.GenFdsTime     = 0\r
716         GlobalData.BuildOptionPcd     = BuildOptions.OptionPcd if BuildOptions.OptionPcd else []\r
717         #Set global flag for build mode\r
718         GlobalData.gIgnoreSource = BuildOptions.IgnoreSources\r
719         GlobalData.gUseHashCache = BuildOptions.UseHashCache\r
720         GlobalData.gBinCacheDest   = BuildOptions.BinCacheDest\r
721         GlobalData.gBinCacheSource = BuildOptions.BinCacheSource\r
722         GlobalData.gEnableGenfdsMultiThread = BuildOptions.GenfdsMultiThread\r
723         GlobalData.gDisableIncludePathCheck = BuildOptions.DisableIncludePathCheck\r
724 \r
725         if GlobalData.gBinCacheDest and not GlobalData.gUseHashCache:\r
726             EdkLogger.error("build", OPTION_NOT_SUPPORTED, ExtraData="--binary-destination must be used together with --hash.")\r
727 \r
728         if GlobalData.gBinCacheSource and not GlobalData.gUseHashCache:\r
729             EdkLogger.error("build", OPTION_NOT_SUPPORTED, ExtraData="--binary-source must be used together with --hash.")\r
730 \r
731         if GlobalData.gBinCacheDest and GlobalData.gBinCacheSource:\r
732             EdkLogger.error("build", OPTION_NOT_SUPPORTED, ExtraData="--binary-destination can not be used together with --binary-source.")\r
733 \r
734         if GlobalData.gBinCacheSource:\r
735             BinCacheSource = os.path.normpath(GlobalData.gBinCacheSource)\r
736             if not os.path.isabs(BinCacheSource):\r
737                 BinCacheSource = mws.join(self.WorkspaceDir, BinCacheSource)\r
738             GlobalData.gBinCacheSource = BinCacheSource\r
739         else:\r
740             if GlobalData.gBinCacheSource is not None:\r
741                 EdkLogger.error("build", OPTION_VALUE_INVALID, ExtraData="Invalid value of option --binary-source.")\r
742 \r
743         if GlobalData.gBinCacheDest:\r
744             BinCacheDest = os.path.normpath(GlobalData.gBinCacheDest)\r
745             if not os.path.isabs(BinCacheDest):\r
746                 BinCacheDest = mws.join(self.WorkspaceDir, BinCacheDest)\r
747             GlobalData.gBinCacheDest = BinCacheDest\r
748         else:\r
749             if GlobalData.gBinCacheDest is not None:\r
750                 EdkLogger.error("build", OPTION_VALUE_INVALID, ExtraData="Invalid value of option --binary-destination.")\r
751 \r
752         if self.ConfDirectory:\r
753             # Get alternate Conf location, if it is absolute, then just use the absolute directory name\r
754             ConfDirectoryPath = os.path.normpath(self.ConfDirectory)\r
755 \r
756             if not os.path.isabs(ConfDirectoryPath):\r
757                 # Since alternate directory name is not absolute, the alternate directory is located within the WORKSPACE\r
758                 # This also handles someone specifying the Conf directory in the workspace. Using --conf=Conf\r
759                 ConfDirectoryPath = mws.join(self.WorkspaceDir, ConfDirectoryPath)\r
760         else:\r
761             if "CONF_PATH" in os.environ:\r
762                 ConfDirectoryPath = os.path.normcase(os.path.normpath(os.environ["CONF_PATH"]))\r
763             else:\r
764                 # Get standard WORKSPACE/Conf use the absolute path to the WORKSPACE/Conf\r
765                 ConfDirectoryPath = mws.join(self.WorkspaceDir, 'Conf')\r
766         GlobalData.gConfDirectory = ConfDirectoryPath\r
767         GlobalData.gDatabasePath = os.path.normpath(os.path.join(ConfDirectoryPath, GlobalData.gDatabasePath))\r
768 \r
769         self.Db = WorkspaceDatabase()\r
770         self.BuildDatabase = self.Db.BuildObject\r
771         self.Platform = None\r
772         self.ToolChainFamily = None\r
773         self.LoadFixAddress = 0\r
774         self.UniFlag        = BuildOptions.Flag\r
775         self.BuildModules = []\r
776         self.HashSkipModules = []\r
777         self.Db_Flag = False\r
778         self.LaunchPrebuildFlag = False\r
779         self.PlatformBuildPath = os.path.join(GlobalData.gConfDirectory, '.cache', '.PlatformBuild')\r
780         if BuildOptions.CommandLength:\r
781             GlobalData.gCommandMaxLength = BuildOptions.CommandLength\r
782 \r
783         # print dot character during doing some time-consuming work\r
784         self.Progress = Utils.Progressor()\r
785         # print current build environment and configuration\r
786         EdkLogger.quiet("%-16s = %s" % ("WORKSPACE", os.environ["WORKSPACE"]))\r
787         if "PACKAGES_PATH" in os.environ:\r
788             # WORKSPACE env has been converted before. Print the same path style with WORKSPACE env.\r
789             EdkLogger.quiet("%-16s = %s" % ("PACKAGES_PATH", os.path.normcase(os.path.normpath(os.environ["PACKAGES_PATH"]))))\r
790         EdkLogger.quiet("%-16s = %s" % ("EDK_TOOLS_PATH", os.environ["EDK_TOOLS_PATH"]))\r
791         if "EDK_TOOLS_BIN" in os.environ:\r
792             # Print the same path style with WORKSPACE env.\r
793             EdkLogger.quiet("%-16s = %s" % ("EDK_TOOLS_BIN", os.path.normcase(os.path.normpath(os.environ["EDK_TOOLS_BIN"]))))\r
794         EdkLogger.quiet("%-16s = %s" % ("CONF_PATH", GlobalData.gConfDirectory))\r
795         if "PYTHON3_ENABLE" in os.environ:\r
796             PYTHON3_ENABLE = os.environ["PYTHON3_ENABLE"]\r
797             if PYTHON3_ENABLE != "TRUE":\r
798                 PYTHON3_ENABLE = "FALSE"\r
799             EdkLogger.quiet("%-16s = %s" % ("PYTHON3_ENABLE", PYTHON3_ENABLE))\r
800         if "PYTHON_COMMAND" in os.environ:\r
801             EdkLogger.quiet("%-16s = %s" % ("PYTHON_COMMAND", os.environ["PYTHON_COMMAND"]))\r
802         self.InitPreBuild()\r
803         self.InitPostBuild()\r
804         if self.Prebuild:\r
805             EdkLogger.quiet("%-16s = %s" % ("PREBUILD", self.Prebuild))\r
806         if self.Postbuild:\r
807             EdkLogger.quiet("%-16s = %s" % ("POSTBUILD", self.Postbuild))\r
808         if self.Prebuild:\r
809             self.LaunchPrebuild()\r
810             self.TargetTxt = TargetTxtClassObject()\r
811             self.ToolDef   = ToolDefClassObject()\r
812         if not (self.LaunchPrebuildFlag and os.path.exists(self.PlatformBuildPath)):\r
813             self.InitBuild()\r
814 \r
815         EdkLogger.info("")\r
816         os.chdir(self.WorkspaceDir)\r
817 \r
818     ## Load configuration\r
819     #\r
820     #   This method will parse target.txt and get the build configurations.\r
821     #\r
822     def LoadConfiguration(self):\r
823         #\r
824         # Check target.txt and tools_def.txt and Init them\r
825         #\r
826         BuildConfigurationFile = os.path.normpath(os.path.join(GlobalData.gConfDirectory, gBuildConfiguration))\r
827         if os.path.isfile(BuildConfigurationFile) == True:\r
828             StatusCode = self.TargetTxt.LoadTargetTxtFile(BuildConfigurationFile)\r
829 \r
830             ToolDefinitionFile = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_TOOL_CHAIN_CONF]\r
831             if ToolDefinitionFile == '':\r
832                 ToolDefinitionFile = gToolsDefinition\r
833                 ToolDefinitionFile = os.path.normpath(mws.join(self.WorkspaceDir, 'Conf', ToolDefinitionFile))\r
834             if os.path.isfile(ToolDefinitionFile) == True:\r
835                 StatusCode = self.ToolDef.LoadToolDefFile(ToolDefinitionFile)\r
836             else:\r
837                 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=ToolDefinitionFile)\r
838         else:\r
839             EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=BuildConfigurationFile)\r
840 \r
841         # if no ARCH given in command line, get it from target.txt\r
842         if not self.ArchList:\r
843             self.ArchList = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_TARGET_ARCH]\r
844         self.ArchList = tuple(self.ArchList)\r
845 \r
846         # if no build target given in command line, get it from target.txt\r
847         if not self.BuildTargetList:\r
848             self.BuildTargetList = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_TARGET]\r
849 \r
850         # if no tool chain given in command line, get it from target.txt\r
851         if not self.ToolChainList:\r
852             self.ToolChainList = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_TOOL_CHAIN_TAG]\r
853             if self.ToolChainList is None or len(self.ToolChainList) == 0:\r
854                 EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, ExtraData="No toolchain given. Don't know how to build.\n")\r
855 \r
856         # check if the tool chains are defined or not\r
857         NewToolChainList = []\r
858         for ToolChain in self.ToolChainList:\r
859             if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG]:\r
860                 EdkLogger.warn("build", "Tool chain [%s] is not defined" % ToolChain)\r
861             else:\r
862                 NewToolChainList.append(ToolChain)\r
863         # if no tool chain available, break the build\r
864         if len(NewToolChainList) == 0:\r
865             EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,\r
866                             ExtraData="[%s] not defined. No toolchain available for build!\n" % ", ".join(self.ToolChainList))\r
867         else:\r
868             self.ToolChainList = NewToolChainList\r
869 \r
870         ToolChainFamily = []\r
871         ToolDefinition = self.ToolDef.ToolsDefTxtDatabase\r
872         for Tool in self.ToolChainList:\r
873             if TAB_TOD_DEFINES_FAMILY not in ToolDefinition or Tool not in ToolDefinition[TAB_TOD_DEFINES_FAMILY] \\r
874                or not ToolDefinition[TAB_TOD_DEFINES_FAMILY][Tool]:\r
875                 EdkLogger.warn("build", "No tool chain family found in configuration for %s. Default to MSFT." % Tool)\r
876                 ToolChainFamily.append(TAB_COMPILER_MSFT)\r
877             else:\r
878                 ToolChainFamily.append(ToolDefinition[TAB_TOD_DEFINES_FAMILY][Tool])\r
879         self.ToolChainFamily = ToolChainFamily\r
880 \r
881         if self.ThreadNumber is None:\r
882             self.ThreadNumber = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER]\r
883             if self.ThreadNumber == '':\r
884                 self.ThreadNumber = 0\r
885             else:\r
886                 self.ThreadNumber = int(self.ThreadNumber, 0)\r
887 \r
888         if self.ThreadNumber == 0:\r
889             try:\r
890                 self.ThreadNumber = multiprocessing.cpu_count()\r
891             except (ImportError, NotImplementedError):\r
892                 self.ThreadNumber = 1\r
893 \r
894         if not self.PlatformFile:\r
895             PlatformFile = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_ACTIVE_PLATFORM]\r
896             if not PlatformFile:\r
897                 # Try to find one in current directory\r
898                 WorkingDirectory = os.getcwd()\r
899                 FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.dsc')))\r
900                 FileNum = len(FileList)\r
901                 if FileNum >= 2:\r
902                     EdkLogger.error("build", OPTION_MISSING,\r
903                                     ExtraData="There are %d DSC files in %s. Use '-p' to specify one.\n" % (FileNum, WorkingDirectory))\r
904                 elif FileNum == 1:\r
905                     PlatformFile = FileList[0]\r
906                 else:\r
907                     EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,\r
908                                     ExtraData="No active platform specified in target.txt or command line! Nothing can be built.\n")\r
909 \r
910             self.PlatformFile = PathClass(NormFile(PlatformFile, self.WorkspaceDir), self.WorkspaceDir)\r
911 \r
912     ## Initialize build configuration\r
913     #\r
914     #   This method will parse DSC file and merge the configurations from\r
915     #   command line and target.txt, then get the final build configurations.\r
916     #\r
917     def InitBuild(self):\r
918         # parse target.txt, tools_def.txt, and platform file\r
919         self.LoadConfiguration()\r
920 \r
921         # Allow case-insensitive for those from command line or configuration file\r
922         ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False)\r
923         if ErrorCode != 0:\r
924             EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
925 \r
926 \r
927     def InitPreBuild(self):\r
928         self.LoadConfiguration()\r
929         ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False)\r
930         if ErrorCode != 0:\r
931             EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
932         if self.BuildTargetList:\r
933             GlobalData.gGlobalDefines['TARGET'] = self.BuildTargetList[0]\r
934         if self.ArchList:\r
935             GlobalData.gGlobalDefines['ARCH'] = self.ArchList[0]\r
936         if self.ToolChainList:\r
937             GlobalData.gGlobalDefines['TOOLCHAIN'] = self.ToolChainList[0]\r
938             GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = self.ToolChainList[0]\r
939         if self.ToolChainFamily:\r
940             GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[0]\r
941         if 'PREBUILD' in GlobalData.gCommandLineDefines:\r
942             self.Prebuild   = GlobalData.gCommandLineDefines.get('PREBUILD')\r
943         else:\r
944             self.Db_Flag = True\r
945             Platform = self.Db.MapPlatform(str(self.PlatformFile))\r
946             self.Prebuild = str(Platform.Prebuild)\r
947         if self.Prebuild:\r
948             PrebuildList = []\r
949             #\r
950             # Evaluate all arguments and convert arguments that are WORKSPACE\r
951             # relative paths to absolute paths.  Filter arguments that look like\r
952             # flags or do not follow the file/dir naming rules to avoid false\r
953             # positives on this conversion.\r
954             #\r
955             for Arg in self.Prebuild.split():\r
956                 #\r
957                 # Do not modify Arg if it looks like a flag or an absolute file path\r
958                 #\r
959                 if Arg.startswith('-')  or os.path.isabs(Arg):\r
960                     PrebuildList.append(Arg)\r
961                     continue\r
962                 #\r
963                 # Do not modify Arg if it does not look like a Workspace relative\r
964                 # path that starts with a valid package directory name\r
965                 #\r
966                 if not Arg[0].isalpha() or os.path.dirname(Arg) == '':\r
967                     PrebuildList.append(Arg)\r
968                     continue\r
969                 #\r
970                 # If Arg looks like a WORKSPACE relative path, then convert to an\r
971                 # absolute path and check to see if the file exists.\r
972                 #\r
973                 Temp = mws.join(self.WorkspaceDir, Arg)\r
974                 if os.path.isfile(Temp):\r
975                     Arg = Temp\r
976                 PrebuildList.append(Arg)\r
977             self.Prebuild       = ' '.join(PrebuildList)\r
978             self.Prebuild += self.PassCommandOption(self.BuildTargetList, self.ArchList, self.ToolChainList, self.PlatformFile, self.Target)\r
979 \r
980     def InitPostBuild(self):\r
981         if 'POSTBUILD' in GlobalData.gCommandLineDefines:\r
982             self.Postbuild = GlobalData.gCommandLineDefines.get('POSTBUILD')\r
983         else:\r
984             Platform = self.Db.MapPlatform(str(self.PlatformFile))\r
985             self.Postbuild = str(Platform.Postbuild)\r
986         if self.Postbuild:\r
987             PostbuildList = []\r
988             #\r
989             # Evaluate all arguments and convert arguments that are WORKSPACE\r
990             # relative paths to absolute paths.  Filter arguments that look like\r
991             # flags or do not follow the file/dir naming rules to avoid false\r
992             # positives on this conversion.\r
993             #\r
994             for Arg in self.Postbuild.split():\r
995                 #\r
996                 # Do not modify Arg if it looks like a flag or an absolute file path\r
997                 #\r
998                 if Arg.startswith('-')  or os.path.isabs(Arg):\r
999                     PostbuildList.append(Arg)\r
1000                     continue\r
1001                 #\r
1002                 # Do not modify Arg if it does not look like a Workspace relative\r
1003                 # path that starts with a valid package directory name\r
1004                 #\r
1005                 if not Arg[0].isalpha() or os.path.dirname(Arg) == '':\r
1006                     PostbuildList.append(Arg)\r
1007                     continue\r
1008                 #\r
1009                 # If Arg looks like a WORKSPACE relative path, then convert to an\r
1010                 # absolute path and check to see if the file exists.\r
1011                 #\r
1012                 Temp = mws.join(self.WorkspaceDir, Arg)\r
1013                 if os.path.isfile(Temp):\r
1014                     Arg = Temp\r
1015                 PostbuildList.append(Arg)\r
1016             self.Postbuild       = ' '.join(PostbuildList)\r
1017             self.Postbuild += self.PassCommandOption(self.BuildTargetList, self.ArchList, self.ToolChainList, self.PlatformFile, self.Target)\r
1018 \r
1019     def PassCommandOption(self, BuildTarget, TargetArch, ToolChain, PlatformFile, Target):\r
1020         BuildStr = ''\r
1021         if GlobalData.gCommand and isinstance(GlobalData.gCommand, list):\r
1022             BuildStr += ' ' + ' '.join(GlobalData.gCommand)\r
1023         TargetFlag = False\r
1024         ArchFlag = False\r
1025         ToolChainFlag = False\r
1026         PlatformFileFlag = False\r
1027 \r
1028         if GlobalData.gOptions and not GlobalData.gOptions.BuildTarget:\r
1029             TargetFlag = True\r
1030         if GlobalData.gOptions and not GlobalData.gOptions.TargetArch:\r
1031             ArchFlag = True\r
1032         if GlobalData.gOptions and not GlobalData.gOptions.ToolChain:\r
1033             ToolChainFlag = True\r
1034         if GlobalData.gOptions and not GlobalData.gOptions.PlatformFile:\r
1035             PlatformFileFlag = True\r
1036 \r
1037         if TargetFlag and BuildTarget:\r
1038             if isinstance(BuildTarget, list) or isinstance(BuildTarget, tuple):\r
1039                 BuildStr += ' -b ' + ' -b '.join(BuildTarget)\r
1040             elif isinstance(BuildTarget, str):\r
1041                 BuildStr += ' -b ' + BuildTarget\r
1042         if ArchFlag and TargetArch:\r
1043             if isinstance(TargetArch, list) or isinstance(TargetArch, tuple):\r
1044                 BuildStr += ' -a ' + ' -a '.join(TargetArch)\r
1045             elif isinstance(TargetArch, str):\r
1046                 BuildStr += ' -a ' + TargetArch\r
1047         if ToolChainFlag and ToolChain:\r
1048             if isinstance(ToolChain, list) or isinstance(ToolChain, tuple):\r
1049                 BuildStr += ' -t ' + ' -t '.join(ToolChain)\r
1050             elif isinstance(ToolChain, str):\r
1051                 BuildStr += ' -t ' + ToolChain\r
1052         if PlatformFileFlag and PlatformFile:\r
1053             if isinstance(PlatformFile, list) or isinstance(PlatformFile, tuple):\r
1054                 BuildStr += ' -p ' + ' -p '.join(PlatformFile)\r
1055             elif isinstance(PlatformFile, str):\r
1056                 BuildStr += ' -p' + PlatformFile\r
1057         BuildStr += ' --conf=' + GlobalData.gConfDirectory\r
1058         if Target:\r
1059             BuildStr += ' ' + Target\r
1060 \r
1061         return BuildStr\r
1062 \r
1063     def LaunchPrebuild(self):\r
1064         if self.Prebuild:\r
1065             EdkLogger.info("\n- Prebuild Start -\n")\r
1066             self.LaunchPrebuildFlag = True\r
1067             #\r
1068             # The purpose of .PrebuildEnv file is capture environment variable settings set by the prebuild script\r
1069             # and preserve them for the rest of the main build step, because the child process environment will\r
1070             # evaporate as soon as it exits, we cannot get it in build step.\r
1071             #\r
1072             PrebuildEnvFile = os.path.join(GlobalData.gConfDirectory, '.cache', '.PrebuildEnv')\r
1073             if os.path.isfile(PrebuildEnvFile):\r
1074                 os.remove(PrebuildEnvFile)\r
1075             if os.path.isfile(self.PlatformBuildPath):\r
1076                 os.remove(self.PlatformBuildPath)\r
1077             if sys.platform == "win32":\r
1078                 args = ' && '.join((self.Prebuild, 'set > ' + PrebuildEnvFile))\r
1079                 Process = Popen(args, stdout=PIPE, stderr=PIPE, shell=True)\r
1080             else:\r
1081                 args = ' && '.join((self.Prebuild, 'env > ' + PrebuildEnvFile))\r
1082                 Process = Popen(args, stdout=PIPE, stderr=PIPE, shell=True)\r
1083 \r
1084             # launch two threads to read the STDOUT and STDERR\r
1085             EndOfProcedure = Event()\r
1086             EndOfProcedure.clear()\r
1087             if Process.stdout:\r
1088                 StdOutThread = Thread(target=ReadMessage, args=(Process.stdout, EdkLogger.info, EndOfProcedure))\r
1089                 StdOutThread.setName("STDOUT-Redirector")\r
1090                 StdOutThread.setDaemon(False)\r
1091                 StdOutThread.start()\r
1092 \r
1093             if Process.stderr:\r
1094                 StdErrThread = Thread(target=ReadMessage, args=(Process.stderr, EdkLogger.quiet, EndOfProcedure))\r
1095                 StdErrThread.setName("STDERR-Redirector")\r
1096                 StdErrThread.setDaemon(False)\r
1097                 StdErrThread.start()\r
1098             # waiting for program exit\r
1099             Process.wait()\r
1100 \r
1101             if Process.stdout:\r
1102                 StdOutThread.join()\r
1103             if Process.stderr:\r
1104                 StdErrThread.join()\r
1105             if Process.returncode != 0 :\r
1106                 EdkLogger.error("Prebuild", PREBUILD_ERROR, 'Prebuild process is not success!')\r
1107 \r
1108             if os.path.exists(PrebuildEnvFile):\r
1109                 f = open(PrebuildEnvFile)\r
1110                 envs = f.readlines()\r
1111                 f.close()\r
1112                 envs = [l.split("=", 1) for l in envs ]\r
1113                 envs = [[I.strip() for I in item] for item in envs if len(item) == 2]\r
1114                 os.environ.update(dict(envs))\r
1115             EdkLogger.info("\n- Prebuild Done -\n")\r
1116 \r
1117     def LaunchPostbuild(self):\r
1118         if self.Postbuild:\r
1119             EdkLogger.info("\n- Postbuild Start -\n")\r
1120             if sys.platform == "win32":\r
1121                 Process = Popen(self.Postbuild, stdout=PIPE, stderr=PIPE, shell=True)\r
1122             else:\r
1123                 Process = Popen(self.Postbuild, stdout=PIPE, stderr=PIPE, shell=True)\r
1124             # launch two threads to read the STDOUT and STDERR\r
1125             EndOfProcedure = Event()\r
1126             EndOfProcedure.clear()\r
1127             if Process.stdout:\r
1128                 StdOutThread = Thread(target=ReadMessage, args=(Process.stdout, EdkLogger.info, EndOfProcedure))\r
1129                 StdOutThread.setName("STDOUT-Redirector")\r
1130                 StdOutThread.setDaemon(False)\r
1131                 StdOutThread.start()\r
1132 \r
1133             if Process.stderr:\r
1134                 StdErrThread = Thread(target=ReadMessage, args=(Process.stderr, EdkLogger.quiet, EndOfProcedure))\r
1135                 StdErrThread.setName("STDERR-Redirector")\r
1136                 StdErrThread.setDaemon(False)\r
1137                 StdErrThread.start()\r
1138             # waiting for program exit\r
1139             Process.wait()\r
1140 \r
1141             if Process.stdout:\r
1142                 StdOutThread.join()\r
1143             if Process.stderr:\r
1144                 StdErrThread.join()\r
1145             if Process.returncode != 0 :\r
1146                 EdkLogger.error("Postbuild", POSTBUILD_ERROR, 'Postbuild process is not success!')\r
1147             EdkLogger.info("\n- Postbuild Done -\n")\r
1148 \r
1149     ## Error handling for hash feature\r
1150     #\r
1151     # On BuildTask error, iterate through the Module Build tracking\r
1152     # dictionary to determine wheather a module failed to build. Invalidate\r
1153     # the hash associated with that module by removing it from storage.\r
1154     #\r
1155     #\r
1156     def invalidateHash(self):\r
1157         # GlobalData.gModuleBuildTracking contains only modules that cannot be skipped by hash\r
1158         for moduleAutoGenObj in GlobalData.gModuleBuildTracking.keys():\r
1159             # False == FAIL : True == Success\r
1160             # Skip invalidating for Successful module builds\r
1161             if GlobalData.gModuleBuildTracking[moduleAutoGenObj] == True:\r
1162                 continue\r
1163 \r
1164             # The module failed to build or failed to start building, from this point on\r
1165 \r
1166             # Remove .hash from build\r
1167             if GlobalData.gUseHashCache:\r
1168                 ModuleHashFile = path.join(moduleAutoGenObj.BuildDir, moduleAutoGenObj.Name + ".hash")\r
1169                 if os.path.exists(ModuleHashFile):\r
1170                     os.remove(ModuleHashFile)\r
1171 \r
1172             # Remove .hash file from cache\r
1173             if GlobalData.gBinCacheDest:\r
1174                 FileDir = path.join(GlobalData.gBinCacheDest, moduleAutoGenObj.Arch, moduleAutoGenObj.SourceDir, moduleAutoGenObj.MetaFile.BaseName)\r
1175                 HashFile = path.join(FileDir, moduleAutoGenObj.Name + '.hash')\r
1176                 if os.path.exists(HashFile):\r
1177                     os.remove(HashFile)\r
1178 \r
1179     ## Build a module or platform\r
1180     #\r
1181     # Create autogen code and makefile for a module or platform, and the launch\r
1182     # "make" command to build it\r
1183     #\r
1184     #   @param  Target                      The target of build command\r
1185     #   @param  Platform                    The platform file\r
1186     #   @param  Module                      The module file\r
1187     #   @param  BuildTarget                 The name of build target, one of "DEBUG", "RELEASE"\r
1188     #   @param  ToolChain                   The name of toolchain to build\r
1189     #   @param  Arch                        The arch of the module/platform\r
1190     #   @param  CreateDepModuleCodeFile     Flag used to indicate creating code\r
1191     #                                       for dependent modules/Libraries\r
1192     #   @param  CreateDepModuleMakeFile     Flag used to indicate creating makefile\r
1193     #                                       for dependent modules/Libraries\r
1194     #\r
1195     def _BuildPa(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True, BuildModule=False, FfsCommand={}):\r
1196         if AutoGenObject is None:\r
1197             return False\r
1198 \r
1199         # skip file generation for cleanxxx targets, run and fds target\r
1200         if Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
1201             # for target which must generate AutoGen code and makefile\r
1202             if not self.SkipAutoGen or Target == 'genc':\r
1203                 self.Progress.Start("Generating code")\r
1204                 AutoGenObject.CreateCodeFile(CreateDepsCodeFile)\r
1205                 self.Progress.Stop("done!")\r
1206             if Target == "genc":\r
1207                 return True\r
1208 \r
1209             if not self.SkipAutoGen or Target == 'genmake':\r
1210                 self.Progress.Start("Generating makefile")\r
1211                 AutoGenObject.CreateMakeFile(CreateDepsMakeFile, FfsCommand)\r
1212                 self.Progress.Stop("done!")\r
1213             if Target == "genmake":\r
1214                 return True\r
1215         else:\r
1216             # always recreate top/platform makefile when clean, just in case of inconsistency\r
1217             AutoGenObject.CreateCodeFile(False)\r
1218             AutoGenObject.CreateMakeFile(False)\r
1219 \r
1220         if EdkLogger.GetLevel() == EdkLogger.QUIET:\r
1221             EdkLogger.quiet("Building ... %s" % repr(AutoGenObject))\r
1222 \r
1223         BuildCommand = AutoGenObject.BuildCommand\r
1224         if BuildCommand is None or len(BuildCommand) == 0:\r
1225             EdkLogger.error("build", OPTION_MISSING,\r
1226                             "No build command found for this module. "\r
1227                             "Please check your setting of %s_%s_%s_MAKE_PATH in Conf/tools_def.txt file." %\r
1228                                 (AutoGenObject.BuildTarget, AutoGenObject.ToolChain, AutoGenObject.Arch),\r
1229                             ExtraData=str(AutoGenObject))\r
1230 \r
1231         makefile = GenMake.BuildFile(AutoGenObject)._FILE_NAME_[GenMake.gMakeType]\r
1232 \r
1233         # run\r
1234         if Target == 'run':\r
1235             return True\r
1236 \r
1237         # build modules\r
1238         if BuildModule:\r
1239             BuildCommand = BuildCommand + [Target]\r
1240             LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir)\r
1241             self.CreateAsBuiltInf()\r
1242             return True\r
1243 \r
1244         # build library\r
1245         if Target == 'libraries':\r
1246             for Lib in AutoGenObject.LibraryBuildDirectoryList:\r
1247                 NewBuildCommand = BuildCommand + ['-f', os.path.normpath(os.path.join(Lib, makefile)), 'pbuild']\r
1248                 LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)\r
1249             return True\r
1250 \r
1251         # build module\r
1252         if Target == 'modules':\r
1253             for Lib in AutoGenObject.LibraryBuildDirectoryList:\r
1254                 NewBuildCommand = BuildCommand + ['-f', os.path.normpath(os.path.join(Lib, makefile)), 'pbuild']\r
1255                 LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)\r
1256             for Mod in AutoGenObject.ModuleBuildDirectoryList:\r
1257                 NewBuildCommand = BuildCommand + ['-f', os.path.normpath(os.path.join(Mod, makefile)), 'pbuild']\r
1258                 LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)\r
1259             self.CreateAsBuiltInf()\r
1260             return True\r
1261 \r
1262         # cleanlib\r
1263         if Target == 'cleanlib':\r
1264             for Lib in AutoGenObject.LibraryBuildDirectoryList:\r
1265                 LibMakefile = os.path.normpath(os.path.join(Lib, makefile))\r
1266                 if os.path.exists(LibMakefile):\r
1267                     NewBuildCommand = BuildCommand + ['-f', LibMakefile, 'cleanall']\r
1268                     LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)\r
1269             return True\r
1270 \r
1271         # clean\r
1272         if Target == 'clean':\r
1273             for Mod in AutoGenObject.ModuleBuildDirectoryList:\r
1274                 ModMakefile = os.path.normpath(os.path.join(Mod, makefile))\r
1275                 if os.path.exists(ModMakefile):\r
1276                     NewBuildCommand = BuildCommand + ['-f', ModMakefile, 'cleanall']\r
1277                     LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)\r
1278             for Lib in AutoGenObject.LibraryBuildDirectoryList:\r
1279                 LibMakefile = os.path.normpath(os.path.join(Lib, makefile))\r
1280                 if os.path.exists(LibMakefile):\r
1281                     NewBuildCommand = BuildCommand + ['-f', LibMakefile, 'cleanall']\r
1282                     LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)\r
1283             return True\r
1284 \r
1285         # cleanall\r
1286         if Target == 'cleanall':\r
1287             try:\r
1288                 #os.rmdir(AutoGenObject.BuildDir)\r
1289                 RemoveDirectory(AutoGenObject.BuildDir, True)\r
1290             except WindowsError as X:\r
1291                 EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X))\r
1292         return True\r
1293 \r
1294     ## Build a module or platform\r
1295     #\r
1296     # Create autogen code and makefile for a module or platform, and the launch\r
1297     # "make" command to build it\r
1298     #\r
1299     #   @param  Target                      The target of build command\r
1300     #   @param  Platform                    The platform file\r
1301     #   @param  Module                      The module file\r
1302     #   @param  BuildTarget                 The name of build target, one of "DEBUG", "RELEASE"\r
1303     #   @param  ToolChain                   The name of toolchain to build\r
1304     #   @param  Arch                        The arch of the module/platform\r
1305     #   @param  CreateDepModuleCodeFile     Flag used to indicate creating code\r
1306     #                                       for dependent modules/Libraries\r
1307     #   @param  CreateDepModuleMakeFile     Flag used to indicate creating makefile\r
1308     #                                       for dependent modules/Libraries\r
1309     #\r
1310     def _Build(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True, BuildModule=False):\r
1311         if AutoGenObject is None:\r
1312             return False\r
1313 \r
1314         # skip file generation for cleanxxx targets, run and fds target\r
1315         if Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
1316             # for target which must generate AutoGen code and makefile\r
1317             if not self.SkipAutoGen or Target == 'genc':\r
1318                 self.Progress.Start("Generating code")\r
1319                 AutoGenObject.CreateCodeFile(CreateDepsCodeFile)\r
1320                 self.Progress.Stop("done!")\r
1321             if Target == "genc":\r
1322                 return True\r
1323 \r
1324             if not self.SkipAutoGen or Target == 'genmake':\r
1325                 self.Progress.Start("Generating makefile")\r
1326                 AutoGenObject.CreateMakeFile(CreateDepsMakeFile)\r
1327                 #AutoGenObject.CreateAsBuiltInf()\r
1328                 self.Progress.Stop("done!")\r
1329             if Target == "genmake":\r
1330                 return True\r
1331         else:\r
1332             # always recreate top/platform makefile when clean, just in case of inconsistency\r
1333             AutoGenObject.CreateCodeFile(False)\r
1334             AutoGenObject.CreateMakeFile(False)\r
1335 \r
1336         if EdkLogger.GetLevel() == EdkLogger.QUIET:\r
1337             EdkLogger.quiet("Building ... %s" % repr(AutoGenObject))\r
1338 \r
1339         BuildCommand = AutoGenObject.BuildCommand\r
1340         if BuildCommand is None or len(BuildCommand) == 0:\r
1341             EdkLogger.error("build", OPTION_MISSING,\r
1342                             "No build command found for this module. "\r
1343                             "Please check your setting of %s_%s_%s_MAKE_PATH in Conf/tools_def.txt file." %\r
1344                                 (AutoGenObject.BuildTarget, AutoGenObject.ToolChain, AutoGenObject.Arch),\r
1345                             ExtraData=str(AutoGenObject))\r
1346 \r
1347         # build modules\r
1348         if BuildModule:\r
1349             if Target != 'fds':\r
1350                 BuildCommand = BuildCommand + [Target]\r
1351             AutoGenObject.BuildTime = LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir)\r
1352             self.CreateAsBuiltInf()\r
1353             return True\r
1354 \r
1355         # genfds\r
1356         if Target == 'fds':\r
1357             if GenFdsApi(AutoGenObject.GenFdsCommandDict, self.Db):\r
1358                 EdkLogger.error("build", COMMAND_FAILURE)\r
1359             return True\r
1360 \r
1361         # run\r
1362         if Target == 'run':\r
1363             RunDir = os.path.normpath(os.path.join(AutoGenObject.BuildDir, GlobalData.gGlobalDefines['ARCH']))\r
1364             Command = '.\SecMain'\r
1365             os.chdir(RunDir)\r
1366             LaunchCommand(Command, RunDir)\r
1367             return True\r
1368 \r
1369         # build library\r
1370         if Target == 'libraries':\r
1371             pass\r
1372 \r
1373         # not build modules\r
1374 \r
1375 \r
1376         # cleanall\r
1377         if Target == 'cleanall':\r
1378             try:\r
1379                 #os.rmdir(AutoGenObject.BuildDir)\r
1380                 RemoveDirectory(AutoGenObject.BuildDir, True)\r
1381             except WindowsError as X:\r
1382                 EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X))\r
1383         return True\r
1384 \r
1385     ## Rebase module image and Get function address for the input module list.\r
1386     #\r
1387     def _RebaseModule (self, MapBuffer, BaseAddress, ModuleList, AddrIsOffset = True, ModeIsSmm = False):\r
1388         if ModeIsSmm:\r
1389             AddrIsOffset = False\r
1390         for InfFile in ModuleList:\r
1391             sys.stdout.write (".")\r
1392             sys.stdout.flush()\r
1393             ModuleInfo = ModuleList[InfFile]\r
1394             ModuleName = ModuleInfo.BaseName\r
1395             ModuleOutputImage = ModuleInfo.Image.FileName\r
1396             ModuleDebugImage  = os.path.join(ModuleInfo.DebugDir, ModuleInfo.BaseName + '.efi')\r
1397             ## for SMM module in SMRAM, the SMRAM will be allocated from base to top.\r
1398             if not ModeIsSmm:\r
1399                 BaseAddress = BaseAddress - ModuleInfo.Image.Size\r
1400                 #\r
1401                 # Update Image to new BaseAddress by GenFw tool\r
1402                 #\r
1403                 LaunchCommand(["GenFw", "--rebase", str(BaseAddress), "-r", ModuleOutputImage], ModuleInfo.OutputDir)\r
1404                 LaunchCommand(["GenFw", "--rebase", str(BaseAddress), "-r", ModuleDebugImage], ModuleInfo.DebugDir)\r
1405             else:\r
1406                 #\r
1407                 # Set new address to the section header only for SMM driver.\r
1408                 #\r
1409                 LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleOutputImage], ModuleInfo.OutputDir)\r
1410                 LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleDebugImage], ModuleInfo.DebugDir)\r
1411             #\r
1412             # Collect function address from Map file\r
1413             #\r
1414             ImageMapTable = ModuleOutputImage.replace('.efi', '.map')\r
1415             FunctionList = []\r
1416             if os.path.exists(ImageMapTable):\r
1417                 OrigImageBaseAddress = 0\r
1418                 ImageMap = open(ImageMapTable, 'r')\r
1419                 for LinStr in ImageMap:\r
1420                     if len (LinStr.strip()) == 0:\r
1421                         continue\r
1422                     #\r
1423                     # Get the preferred address set on link time.\r
1424                     #\r
1425                     if LinStr.find ('Preferred load address is') != -1:\r
1426                         StrList = LinStr.split()\r
1427                         OrigImageBaseAddress = int (StrList[len(StrList) - 1], 16)\r
1428 \r
1429                     StrList = LinStr.split()\r
1430                     if len (StrList) > 4:\r
1431                         if StrList[3] == 'f' or StrList[3] == 'F':\r
1432                             Name = StrList[1]\r
1433                             RelativeAddress = int (StrList[2], 16) - OrigImageBaseAddress\r
1434                             FunctionList.append ((Name, RelativeAddress))\r
1435 \r
1436                 ImageMap.close()\r
1437             #\r
1438             # Add general information.\r
1439             #\r
1440             if ModeIsSmm:\r
1441                 MapBuffer.append('\n\n%s (Fixed SMRAM Offset,   BaseAddress=0x%010X,  EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint))\r
1442             elif AddrIsOffset:\r
1443                 MapBuffer.append('\n\n%s (Fixed Memory Offset,  BaseAddress=-0x%010X, EntryPoint=-0x%010X)\n' % (ModuleName, 0 - BaseAddress, 0 - (BaseAddress + ModuleInfo.Image.EntryPoint)))\r
1444             else:\r
1445                 MapBuffer.append('\n\n%s (Fixed Memory Address, BaseAddress=0x%010X,  EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint))\r
1446             #\r
1447             # Add guid and general seciton section.\r
1448             #\r
1449             TextSectionAddress = 0\r
1450             DataSectionAddress = 0\r
1451             for SectionHeader in ModuleInfo.Image.SectionHeaderList:\r
1452                 if SectionHeader[0] == '.text':\r
1453                     TextSectionAddress = SectionHeader[1]\r
1454                 elif SectionHeader[0] in ['.data', '.sdata']:\r
1455                     DataSectionAddress = SectionHeader[1]\r
1456             if AddrIsOffset:\r
1457                 MapBuffer.append('(GUID=%s, .textbaseaddress=-0x%010X, .databaseaddress=-0x%010X)\n' % (ModuleInfo.Guid, 0 - (BaseAddress + TextSectionAddress), 0 - (BaseAddress + DataSectionAddress)))\r
1458             else:\r
1459                 MapBuffer.append('(GUID=%s, .textbaseaddress=0x%010X, .databaseaddress=0x%010X)\n' % (ModuleInfo.Guid, BaseAddress + TextSectionAddress, BaseAddress + DataSectionAddress))\r
1460             #\r
1461             # Add debug image full path.\r
1462             #\r
1463             MapBuffer.append('(IMAGE=%s)\n\n' % (ModuleDebugImage))\r
1464             #\r
1465             # Add function address\r
1466             #\r
1467             for Function in FunctionList:\r
1468                 if AddrIsOffset:\r
1469                     MapBuffer.append('  -0x%010X    %s\n' % (0 - (BaseAddress + Function[1]), Function[0]))\r
1470                 else:\r
1471                     MapBuffer.append('  0x%010X    %s\n' % (BaseAddress + Function[1], Function[0]))\r
1472             ImageMap.close()\r
1473 \r
1474             #\r
1475             # for SMM module in SMRAM, the SMRAM will be allocated from base to top.\r
1476             #\r
1477             if ModeIsSmm:\r
1478                 BaseAddress = BaseAddress + ModuleInfo.Image.Size\r
1479 \r
1480     ## Collect MAP information of all FVs\r
1481     #\r
1482     def _CollectFvMapBuffer (self, MapBuffer, Wa, ModuleList):\r
1483         if self.Fdf:\r
1484             # First get the XIP base address for FV map file.\r
1485             GuidPattern = re.compile("[-a-fA-F0-9]+")\r
1486             GuidName = re.compile("\(GUID=[-a-fA-F0-9]+")\r
1487             for FvName in Wa.FdfProfile.FvDict:\r
1488                 FvMapBuffer = os.path.join(Wa.FvDir, FvName + '.Fv.map')\r
1489                 if not os.path.exists(FvMapBuffer):\r
1490                     continue\r
1491                 FvMap = open(FvMapBuffer, 'r')\r
1492                 #skip FV size information\r
1493                 FvMap.readline()\r
1494                 FvMap.readline()\r
1495                 FvMap.readline()\r
1496                 FvMap.readline()\r
1497                 for Line in FvMap:\r
1498                     MatchGuid = GuidPattern.match(Line)\r
1499                     if MatchGuid is not None:\r
1500                         #\r
1501                         # Replace GUID with module name\r
1502                         #\r
1503                         GuidString = MatchGuid.group()\r
1504                         if GuidString.upper() in ModuleList:\r
1505                             Line = Line.replace(GuidString, ModuleList[GuidString.upper()].Name)\r
1506                     MapBuffer.append(Line)\r
1507                     #\r
1508                     # Add the debug image full path.\r
1509                     #\r
1510                     MatchGuid = GuidName.match(Line)\r
1511                     if MatchGuid is not None:\r
1512                         GuidString = MatchGuid.group().split("=")[1]\r
1513                         if GuidString.upper() in ModuleList:\r
1514                             MapBuffer.append('(IMAGE=%s)\n' % (os.path.join(ModuleList[GuidString.upper()].DebugDir, ModuleList[GuidString.upper()].Name + '.efi')))\r
1515 \r
1516                 FvMap.close()\r
1517 \r
1518     ## Collect MAP information of all modules\r
1519     #\r
1520     def _CollectModuleMapBuffer (self, MapBuffer, ModuleList):\r
1521         sys.stdout.write ("Generate Load Module At Fix Address Map")\r
1522         sys.stdout.flush()\r
1523         PatchEfiImageList = []\r
1524         PeiModuleList  = {}\r
1525         BtModuleList   = {}\r
1526         RtModuleList   = {}\r
1527         SmmModuleList  = {}\r
1528         PeiSize = 0\r
1529         BtSize  = 0\r
1530         RtSize  = 0\r
1531         # reserve 4K size in SMRAM to make SMM module address not from 0.\r
1532         SmmSize = 0x1000\r
1533         for ModuleGuid in ModuleList:\r
1534             Module = ModuleList[ModuleGuid]\r
1535             GlobalData.gProcessingFile = "%s [%s, %s, %s]" % (Module.MetaFile, Module.Arch, Module.ToolChain, Module.BuildTarget)\r
1536 \r
1537             OutputImageFile = ''\r
1538             for ResultFile in Module.CodaTargetList:\r
1539                 if str(ResultFile.Target).endswith('.efi'):\r
1540                     #\r
1541                     # module list for PEI, DXE, RUNTIME and SMM\r
1542                     #\r
1543                     OutputImageFile = os.path.join(Module.OutputDir, Module.Name + '.efi')\r
1544                     ImageClass = PeImageClass (OutputImageFile)\r
1545                     if not ImageClass.IsValid:\r
1546                         EdkLogger.error("build", FILE_PARSE_FAILURE, ExtraData=ImageClass.ErrorInfo)\r
1547                     ImageInfo = PeImageInfo(Module.Name, Module.Guid, Module.Arch, Module.OutputDir, Module.DebugDir, ImageClass)\r
1548                     if Module.ModuleType in [SUP_MODULE_PEI_CORE, SUP_MODULE_PEIM, EDK_COMPONENT_TYPE_COMBINED_PEIM_DRIVER, EDK_COMPONENT_TYPE_PIC_PEIM, EDK_COMPONENT_TYPE_RELOCATABLE_PEIM, SUP_MODULE_DXE_CORE]:\r
1549                         PeiModuleList[Module.MetaFile] = ImageInfo\r
1550                         PeiSize += ImageInfo.Image.Size\r
1551                     elif Module.ModuleType in [EDK_COMPONENT_TYPE_BS_DRIVER, SUP_MODULE_DXE_DRIVER, SUP_MODULE_UEFI_DRIVER]:\r
1552                         BtModuleList[Module.MetaFile] = ImageInfo\r
1553                         BtSize += ImageInfo.Image.Size\r
1554                     elif Module.ModuleType in [SUP_MODULE_DXE_RUNTIME_DRIVER, EDK_COMPONENT_TYPE_RT_DRIVER, SUP_MODULE_DXE_SAL_DRIVER, EDK_COMPONENT_TYPE_SAL_RT_DRIVER]:\r
1555                         RtModuleList[Module.MetaFile] = ImageInfo\r
1556                         RtSize += ImageInfo.Image.Size\r
1557                     elif Module.ModuleType in [SUP_MODULE_SMM_CORE, SUP_MODULE_DXE_SMM_DRIVER, SUP_MODULE_MM_STANDALONE, SUP_MODULE_MM_CORE_STANDALONE]:\r
1558                         SmmModuleList[Module.MetaFile] = ImageInfo\r
1559                         SmmSize += ImageInfo.Image.Size\r
1560                         if Module.ModuleType == SUP_MODULE_DXE_SMM_DRIVER:\r
1561                             PiSpecVersion = Module.Module.Specification.get('PI_SPECIFICATION_VERSION', '0x00000000')\r
1562                             # for PI specification < PI1.1, DXE_SMM_DRIVER also runs as BOOT time driver.\r
1563                             if int(PiSpecVersion, 16) < 0x0001000A:\r
1564                                 BtModuleList[Module.MetaFile] = ImageInfo\r
1565                                 BtSize += ImageInfo.Image.Size\r
1566                     break\r
1567             #\r
1568             # EFI image is final target.\r
1569             # Check EFI image contains patchable FixAddress related PCDs.\r
1570             #\r
1571             if OutputImageFile != '':\r
1572                 ModuleIsPatch = False\r
1573                 for Pcd in Module.ModulePcdList:\r
1574                     if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SET:\r
1575                         ModuleIsPatch = True\r
1576                         break\r
1577                 if not ModuleIsPatch:\r
1578                     for Pcd in Module.LibraryPcdList:\r
1579                         if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SET:\r
1580                             ModuleIsPatch = True\r
1581                             break\r
1582 \r
1583                 if not ModuleIsPatch:\r
1584                     continue\r
1585                 #\r
1586                 # Module includes the patchable load fix address PCDs.\r
1587                 # It will be fixed up later.\r
1588                 #\r
1589                 PatchEfiImageList.append (OutputImageFile)\r
1590 \r
1591         #\r
1592         # Get Top Memory address\r
1593         #\r
1594         ReservedRuntimeMemorySize = 0\r
1595         TopMemoryAddress = 0\r
1596         if self.LoadFixAddress == 0xFFFFFFFFFFFFFFFF:\r
1597             TopMemoryAddress = 0\r
1598         else:\r
1599             TopMemoryAddress = self.LoadFixAddress\r
1600             if TopMemoryAddress < RtSize + BtSize + PeiSize:\r
1601                 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS is too low to load driver")\r
1602 \r
1603         #\r
1604         # Patch FixAddress related PCDs into EFI image\r
1605         #\r
1606         for EfiImage in PatchEfiImageList:\r
1607             EfiImageMap = EfiImage.replace('.efi', '.map')\r
1608             if not os.path.exists(EfiImageMap):\r
1609                 continue\r
1610             #\r
1611             # Get PCD offset in EFI image by GenPatchPcdTable function\r
1612             #\r
1613             PcdTable = parsePcdInfoFromMapFile(EfiImageMap, EfiImage)\r
1614             #\r
1615             # Patch real PCD value by PatchPcdValue tool\r
1616             #\r
1617             for PcdInfo in PcdTable:\r
1618                 ReturnValue = 0\r
1619                 if PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE:\r
1620                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE_DATA_TYPE, str (PeiSize // 0x1000))\r
1621                 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE:\r
1622                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE_DATA_TYPE, str (BtSize // 0x1000))\r
1623                 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE:\r
1624                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE_DATA_TYPE, str (RtSize // 0x1000))\r
1625                 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE and len (SmmModuleList) > 0:\r
1626                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE_DATA_TYPE, str (SmmSize // 0x1000))\r
1627                 if ReturnValue != 0:\r
1628                     EdkLogger.error("build", PARAMETER_INVALID, "Patch PCD value failed", ExtraData=ErrorInfo)\r
1629 \r
1630         MapBuffer.append('PEI_CODE_PAGE_NUMBER      = 0x%x\n' % (PeiSize // 0x1000))\r
1631         MapBuffer.append('BOOT_CODE_PAGE_NUMBER     = 0x%x\n' % (BtSize // 0x1000))\r
1632         MapBuffer.append('RUNTIME_CODE_PAGE_NUMBER  = 0x%x\n' % (RtSize // 0x1000))\r
1633         if len (SmmModuleList) > 0:\r
1634             MapBuffer.append('SMM_CODE_PAGE_NUMBER      = 0x%x\n' % (SmmSize // 0x1000))\r
1635 \r
1636         PeiBaseAddr = TopMemoryAddress - RtSize - BtSize\r
1637         BtBaseAddr  = TopMemoryAddress - RtSize\r
1638         RtBaseAddr  = TopMemoryAddress - ReservedRuntimeMemorySize\r
1639 \r
1640         self._RebaseModule (MapBuffer, PeiBaseAddr, PeiModuleList, TopMemoryAddress == 0)\r
1641         self._RebaseModule (MapBuffer, BtBaseAddr, BtModuleList, TopMemoryAddress == 0)\r
1642         self._RebaseModule (MapBuffer, RtBaseAddr, RtModuleList, TopMemoryAddress == 0)\r
1643         self._RebaseModule (MapBuffer, 0x1000, SmmModuleList, AddrIsOffset=False, ModeIsSmm=True)\r
1644         MapBuffer.append('\n\n')\r
1645         sys.stdout.write ("\n")\r
1646         sys.stdout.flush()\r
1647 \r
1648     ## Save platform Map file\r
1649     #\r
1650     def _SaveMapFile (self, MapBuffer, Wa):\r
1651         #\r
1652         # Map file path is got.\r
1653         #\r
1654         MapFilePath = os.path.join(Wa.BuildDir, Wa.Name + '.map')\r
1655         #\r
1656         # Save address map into MAP file.\r
1657         #\r
1658         SaveFileOnChange(MapFilePath, ''.join(MapBuffer), False)\r
1659         if self.LoadFixAddress != 0:\r
1660             sys.stdout.write ("\nLoad Module At Fix Address Map file can be found at %s\n" % (MapFilePath))\r
1661         sys.stdout.flush()\r
1662 \r
1663     ## Build active platform for different build targets and different tool chains\r
1664     #\r
1665     def _BuildPlatform(self):\r
1666         SaveFileOnChange(self.PlatformBuildPath, '# DO NOT EDIT \n# FILE auto-generated\n', False)\r
1667         for BuildTarget in self.BuildTargetList:\r
1668             GlobalData.gGlobalDefines['TARGET'] = BuildTarget\r
1669             index = 0\r
1670             for ToolChain in self.ToolChainList:\r
1671                 GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain\r
1672                 GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain\r
1673                 GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index]\r
1674                 index += 1\r
1675                 Wa = WorkspaceAutoGen(\r
1676                         self.WorkspaceDir,\r
1677                         self.PlatformFile,\r
1678                         BuildTarget,\r
1679                         ToolChain,\r
1680                         self.ArchList,\r
1681                         self.BuildDatabase,\r
1682                         self.TargetTxt,\r
1683                         self.ToolDef,\r
1684                         self.Fdf,\r
1685                         self.FdList,\r
1686                         self.FvList,\r
1687                         self.CapList,\r
1688                         self.SkuId,\r
1689                         self.UniFlag,\r
1690                         self.Progress\r
1691                         )\r
1692                 self.Fdf = Wa.FdfFile\r
1693                 self.LoadFixAddress = Wa.Platform.LoadFixAddress\r
1694                 self.BuildReport.AddPlatformReport(Wa)\r
1695                 self.Progress.Stop("done!")\r
1696 \r
1697                 # Add ffs build to makefile\r
1698                 CmdListDict = {}\r
1699                 if GlobalData.gEnableGenfdsMultiThread and self.Fdf:\r
1700                     CmdListDict = self._GenFfsCmd(Wa.ArchList)\r
1701 \r
1702                 for Arch in Wa.ArchList:\r
1703                     GlobalData.gGlobalDefines['ARCH'] = Arch\r
1704                     Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)\r
1705                     for Module in Pa.Platform.Modules:\r
1706                         # Get ModuleAutoGen object to generate C code file and makefile\r
1707                         Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
1708                         if Ma is None:\r
1709                             continue\r
1710                         self.BuildModules.append(Ma)\r
1711                     self._BuildPa(self.Target, Pa, FfsCommand=CmdListDict)\r
1712 \r
1713                 # Create MAP file when Load Fix Address is enabled.\r
1714                 if self.Target in ["", "all", "fds"]:\r
1715                     for Arch in Wa.ArchList:\r
1716                         GlobalData.gGlobalDefines['ARCH'] = Arch\r
1717                         #\r
1718                         # Check whether the set fix address is above 4G for 32bit image.\r
1719                         #\r
1720                         if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
1721                             EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS can't be set to larger than or equal to 4G for the platform with IA32 or ARM arch modules")\r
1722                     #\r
1723                     # Get Module List\r
1724                     #\r
1725                     ModuleList = {}\r
1726                     for Pa in Wa.AutoGenObjectList:\r
1727                         for Ma in Pa.ModuleAutoGenList:\r
1728                             if Ma is None:\r
1729                                 continue\r
1730                             if not Ma.IsLibrary:\r
1731                                 ModuleList[Ma.Guid.upper()] = Ma\r
1732 \r
1733                     MapBuffer = []\r
1734                     if self.LoadFixAddress != 0:\r
1735                         #\r
1736                         # Rebase module to the preferred memory address before GenFds\r
1737                         #\r
1738                         self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
1739                     if self.Fdf:\r
1740                         #\r
1741                         # create FDS again for the updated EFI image\r
1742                         #\r
1743                         self._Build("fds", Wa)\r
1744                         #\r
1745                         # Create MAP file for all platform FVs after GenFds.\r
1746                         #\r
1747                         self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)\r
1748                     #\r
1749                     # Save MAP buffer into MAP file.\r
1750                     #\r
1751                     self._SaveMapFile (MapBuffer, Wa)\r
1752 \r
1753     ## Build active module for different build targets, different tool chains and different archs\r
1754     #\r
1755     def _BuildModule(self):\r
1756         for BuildTarget in self.BuildTargetList:\r
1757             GlobalData.gGlobalDefines['TARGET'] = BuildTarget\r
1758             index = 0\r
1759             for ToolChain in self.ToolChainList:\r
1760                 WorkspaceAutoGenTime = time.time()\r
1761                 GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain\r
1762                 GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain\r
1763                 GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index]\r
1764                 index += 1\r
1765                 #\r
1766                 # module build needs platform build information, so get platform\r
1767                 # AutoGen first\r
1768                 #\r
1769                 Wa = WorkspaceAutoGen(\r
1770                         self.WorkspaceDir,\r
1771                         self.PlatformFile,\r
1772                         BuildTarget,\r
1773                         ToolChain,\r
1774                         self.ArchList,\r
1775                         self.BuildDatabase,\r
1776                         self.TargetTxt,\r
1777                         self.ToolDef,\r
1778                         self.Fdf,\r
1779                         self.FdList,\r
1780                         self.FvList,\r
1781                         self.CapList,\r
1782                         self.SkuId,\r
1783                         self.UniFlag,\r
1784                         self.Progress,\r
1785                         self.ModuleFile\r
1786                         )\r
1787                 self.Fdf = Wa.FdfFile\r
1788                 self.LoadFixAddress = Wa.Platform.LoadFixAddress\r
1789                 Wa.CreateMakeFile(False)\r
1790                 # Add ffs build to makefile\r
1791                 CmdListDict = None\r
1792                 if GlobalData.gEnableGenfdsMultiThread and self.Fdf:\r
1793                     CmdListDict = self._GenFfsCmd(Wa.ArchList)\r
1794                 self.Progress.Stop("done!")\r
1795                 MaList = []\r
1796                 ExitFlag = threading.Event()\r
1797                 ExitFlag.clear()\r
1798                 self.AutoGenTime += int(round((time.time() - WorkspaceAutoGenTime)))\r
1799                 for Arch in Wa.ArchList:\r
1800                     AutoGenStart = time.time()\r
1801                     GlobalData.gGlobalDefines['ARCH'] = Arch\r
1802                     Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)\r
1803                     for Module in Pa.Platform.Modules:\r
1804                         if self.ModuleFile.Dir == Module.Dir and self.ModuleFile.Name == Module.Name:\r
1805                             Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
1806                             if Ma is None:\r
1807                                 continue\r
1808                             MaList.append(Ma)\r
1809                             if Ma.CanSkipbyHash():\r
1810                                 self.HashSkipModules.append(Ma)\r
1811                                 continue\r
1812                             # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'\r
1813                             if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
1814                                 # for target which must generate AutoGen code and makefile\r
1815                                 if not self.SkipAutoGen or self.Target == 'genc':\r
1816                                     self.Progress.Start("Generating code")\r
1817                                     Ma.CreateCodeFile(True)\r
1818                                     self.Progress.Stop("done!")\r
1819                                 if self.Target == "genc":\r
1820                                     return True\r
1821                                 if not self.SkipAutoGen or self.Target == 'genmake':\r
1822                                     self.Progress.Start("Generating makefile")\r
1823                                     if CmdListDict and self.Fdf and (Module.File, Arch) in CmdListDict:\r
1824                                         Ma.CreateMakeFile(True, CmdListDict[Module.File, Arch])\r
1825                                         del CmdListDict[Module.File, Arch]\r
1826                                     else:\r
1827                                         Ma.CreateMakeFile(True)\r
1828                                     self.Progress.Stop("done!")\r
1829                                 if self.Target == "genmake":\r
1830                                     return True\r
1831                             self.BuildModules.append(Ma)\r
1832                             # Initialize all modules in tracking to False (FAIL)\r
1833                             if Ma not in GlobalData.gModuleBuildTracking:\r
1834                                 GlobalData.gModuleBuildTracking[Ma] = False\r
1835                     self.AutoGenTime += int(round((time.time() - AutoGenStart)))\r
1836                     MakeStart = time.time()\r
1837                     for Ma in self.BuildModules:\r
1838                         if not Ma.IsBinaryModule:\r
1839                             Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))\r
1840                         # Break build if any build thread has error\r
1841                         if BuildTask.HasError():\r
1842                             # we need a full version of makefile for platform\r
1843                             ExitFlag.set()\r
1844                             BuildTask.WaitForComplete()\r
1845                             self.invalidateHash()\r
1846                             Pa.CreateMakeFile(False)\r
1847                             EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1848                         # Start task scheduler\r
1849                         if not BuildTask.IsOnGoing():\r
1850                             BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)\r
1851 \r
1852                     # in case there's an interruption. we need a full version of makefile for platform\r
1853                     Pa.CreateMakeFile(False)\r
1854                     if BuildTask.HasError():\r
1855                         self.invalidateHash()\r
1856                         EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1857                     self.MakeTime += int(round((time.time() - MakeStart)))\r
1858 \r
1859                 MakeContiue = time.time()\r
1860                 ExitFlag.set()\r
1861                 BuildTask.WaitForComplete()\r
1862                 self.CreateAsBuiltInf()\r
1863                 self.MakeTime += int(round((time.time() - MakeContiue)))\r
1864                 if BuildTask.HasError():\r
1865                     self.invalidateHash()\r
1866                     EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1867 \r
1868                 self.BuildReport.AddPlatformReport(Wa, MaList)\r
1869                 if MaList == []:\r
1870                     EdkLogger.error(\r
1871                                 'build',\r
1872                                 BUILD_ERROR,\r
1873                                 "Module for [%s] is not a component of active platform."\\r
1874                                 " Please make sure that the ARCH and inf file path are"\\r
1875                                 " given in the same as in [%s]" % \\r
1876                                     (', '.join(Wa.ArchList), self.PlatformFile),\r
1877                                 ExtraData=self.ModuleFile\r
1878                                 )\r
1879                 # Create MAP file when Load Fix Address is enabled.\r
1880                 if self.Target == "fds" and self.Fdf:\r
1881                     for Arch in Wa.ArchList:\r
1882                         #\r
1883                         # Check whether the set fix address is above 4G for 32bit image.\r
1884                         #\r
1885                         if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
1886                             EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS can't be set to larger than or equal to 4G for the platorm with IA32 or ARM arch modules")\r
1887                     #\r
1888                     # Get Module List\r
1889                     #\r
1890                     ModuleList = {}\r
1891                     for Pa in Wa.AutoGenObjectList:\r
1892                         for Ma in Pa.ModuleAutoGenList:\r
1893                             if Ma is None:\r
1894                                 continue\r
1895                             if not Ma.IsLibrary:\r
1896                                 ModuleList[Ma.Guid.upper()] = Ma\r
1897 \r
1898                     MapBuffer = []\r
1899                     if self.LoadFixAddress != 0:\r
1900                         #\r
1901                         # Rebase module to the preferred memory address before GenFds\r
1902                         #\r
1903                         self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
1904                     #\r
1905                     # create FDS again for the updated EFI image\r
1906                     #\r
1907                     GenFdsStart = time.time()\r
1908                     self._Build("fds", Wa)\r
1909                     self.GenFdsTime += int(round((time.time() - GenFdsStart)))\r
1910                     #\r
1911                     # Create MAP file for all platform FVs after GenFds.\r
1912                     #\r
1913                     self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)\r
1914                     #\r
1915                     # Save MAP buffer into MAP file.\r
1916                     #\r
1917                     self._SaveMapFile (MapBuffer, Wa)\r
1918 \r
1919     def _GenFfsCmd(self,ArchList):\r
1920         # convert dictionary of Cmd:(Inf,Arch)\r
1921         # to a new dictionary of (Inf,Arch):Cmd,Cmd,Cmd...\r
1922         CmdSetDict = defaultdict(set)\r
1923         GenFfsDict = GenFds.GenFfsMakefile('', GlobalData.gFdfParser, self, ArchList, GlobalData)\r
1924         for Cmd in GenFfsDict:\r
1925             tmpInf, tmpArch = GenFfsDict[Cmd]\r
1926             CmdSetDict[tmpInf, tmpArch].add(Cmd)\r
1927         return CmdSetDict\r
1928 \r
1929     ## Build a platform in multi-thread mode\r
1930     #\r
1931     def _MultiThreadBuildPlatform(self):\r
1932         SaveFileOnChange(self.PlatformBuildPath, '# DO NOT EDIT \n# FILE auto-generated\n', False)\r
1933         for BuildTarget in self.BuildTargetList:\r
1934             GlobalData.gGlobalDefines['TARGET'] = BuildTarget\r
1935             index = 0\r
1936             for ToolChain in self.ToolChainList:\r
1937                 WorkspaceAutoGenTime = time.time()\r
1938                 GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain\r
1939                 GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain\r
1940                 GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index]\r
1941                 index += 1\r
1942                 Wa = WorkspaceAutoGen(\r
1943                         self.WorkspaceDir,\r
1944                         self.PlatformFile,\r
1945                         BuildTarget,\r
1946                         ToolChain,\r
1947                         self.ArchList,\r
1948                         self.BuildDatabase,\r
1949                         self.TargetTxt,\r
1950                         self.ToolDef,\r
1951                         self.Fdf,\r
1952                         self.FdList,\r
1953                         self.FvList,\r
1954                         self.CapList,\r
1955                         self.SkuId,\r
1956                         self.UniFlag,\r
1957                         self.Progress\r
1958                         )\r
1959                 self.Fdf = Wa.FdfFile\r
1960                 self.LoadFixAddress = Wa.Platform.LoadFixAddress\r
1961                 self.BuildReport.AddPlatformReport(Wa)\r
1962                 Wa.CreateMakeFile(False)\r
1963 \r
1964                 # Add ffs build to makefile\r
1965                 CmdListDict = None\r
1966                 if GlobalData.gEnableGenfdsMultiThread and self.Fdf:\r
1967                     CmdListDict = self._GenFfsCmd(Wa.ArchList)\r
1968 \r
1969                 # multi-thread exit flag\r
1970                 ExitFlag = threading.Event()\r
1971                 ExitFlag.clear()\r
1972                 self.AutoGenTime += int(round((time.time() - WorkspaceAutoGenTime)))\r
1973                 for Arch in Wa.ArchList:\r
1974                     AutoGenStart = time.time()\r
1975                     GlobalData.gGlobalDefines['ARCH'] = Arch\r
1976                     Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)\r
1977                     if Pa is None:\r
1978                         continue\r
1979                     ModuleList = []\r
1980                     for Inf in Pa.Platform.Modules:\r
1981                         ModuleList.append(Inf)\r
1982                     # Add the INF only list in FDF\r
1983                     if GlobalData.gFdfParser is not None:\r
1984                         for InfName in GlobalData.gFdfParser.Profile.InfList:\r
1985                             Inf = PathClass(NormPath(InfName), self.WorkspaceDir, Arch)\r
1986                             if Inf in Pa.Platform.Modules:\r
1987                                 continue\r
1988                             ModuleList.append(Inf)\r
1989                     for Module in ModuleList:\r
1990                         # Get ModuleAutoGen object to generate C code file and makefile\r
1991                         Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
1992 \r
1993                         if Ma is None:\r
1994                             continue\r
1995                         if Ma.CanSkipbyHash():\r
1996                             self.HashSkipModules.append(Ma)\r
1997                             continue\r
1998 \r
1999                         # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'\r
2000                         if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
2001                             # for target which must generate AutoGen code and makefile\r
2002                             if not self.SkipAutoGen or self.Target == 'genc':\r
2003                                 Ma.CreateCodeFile(True)\r
2004                             if self.Target == "genc":\r
2005                                 continue\r
2006 \r
2007                             if not self.SkipAutoGen or self.Target == 'genmake':\r
2008                                 if CmdListDict and self.Fdf and (Module.File, Arch) in CmdListDict:\r
2009                                     Ma.CreateMakeFile(True, CmdListDict[Module.File, Arch])\r
2010                                     del CmdListDict[Module.File, Arch]\r
2011                                 else:\r
2012                                     Ma.CreateMakeFile(True)\r
2013                             if self.Target == "genmake":\r
2014                                 continue\r
2015                         self.BuildModules.append(Ma)\r
2016                         # Initialize all modules in tracking to False (FAIL)\r
2017                         if Ma not in GlobalData.gModuleBuildTracking:\r
2018                             GlobalData.gModuleBuildTracking[Ma] = False\r
2019                     self.Progress.Stop("done!")\r
2020                     self.AutoGenTime += int(round((time.time() - AutoGenStart)))\r
2021                     MakeStart = time.time()\r
2022                     for Ma in self.BuildModules:\r
2023                         # Generate build task for the module\r
2024                         if not Ma.IsBinaryModule:\r
2025                             Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))\r
2026                         # Break build if any build thread has error\r
2027                         if BuildTask.HasError():\r
2028                             # we need a full version of makefile for platform\r
2029                             ExitFlag.set()\r
2030                             BuildTask.WaitForComplete()\r
2031                             self.invalidateHash()\r
2032                             Pa.CreateMakeFile(False)\r
2033                             EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
2034                         # Start task scheduler\r
2035                         if not BuildTask.IsOnGoing():\r
2036                             BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)\r
2037 \r
2038                     # in case there's an interruption. we need a full version of makefile for platform\r
2039                     Pa.CreateMakeFile(False)\r
2040                     if BuildTask.HasError():\r
2041                         self.invalidateHash()\r
2042                         EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
2043                     self.MakeTime += int(round((time.time() - MakeStart)))\r
2044 \r
2045                 MakeContiue = time.time()\r
2046 \r
2047                 #\r
2048                 #\r
2049                 # All modules have been put in build tasks queue. Tell task scheduler\r
2050                 # to exit if all tasks are completed\r
2051                 #\r
2052                 ExitFlag.set()\r
2053                 BuildTask.WaitForComplete()\r
2054                 self.CreateAsBuiltInf()\r
2055                 self.MakeTime += int(round((time.time() - MakeContiue)))\r
2056                 #\r
2057                 # Check for build error, and raise exception if one\r
2058                 # has been signaled.\r
2059                 #\r
2060                 if BuildTask.HasError():\r
2061                     self.invalidateHash()\r
2062                     EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
2063 \r
2064                 # Create MAP file when Load Fix Address is enabled.\r
2065                 if self.Target in ["", "all", "fds"]:\r
2066                     for Arch in Wa.ArchList:\r
2067                         #\r
2068                         # Check whether the set fix address is above 4G for 32bit image.\r
2069                         #\r
2070                         if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
2071                             EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS can't be set to larger than or equal to 4G for the platorm with IA32 or ARM arch modules")\r
2072                     #\r
2073                     # Get Module List\r
2074                     #\r
2075                     ModuleList = {}\r
2076                     for Pa in Wa.AutoGenObjectList:\r
2077                         for Ma in Pa.ModuleAutoGenList:\r
2078                             if Ma is None:\r
2079                                 continue\r
2080                             if not Ma.IsLibrary:\r
2081                                 ModuleList[Ma.Guid.upper()] = Ma\r
2082                     #\r
2083                     # Rebase module to the preferred memory address before GenFds\r
2084                     #\r
2085                     MapBuffer = []\r
2086                     if self.LoadFixAddress != 0:\r
2087                         self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
2088 \r
2089                     if self.Fdf:\r
2090                         #\r
2091                         # Generate FD image if there's a FDF file found\r
2092                         #\r
2093                         GenFdsStart = time.time()\r
2094                         if GenFdsApi(Wa.GenFdsCommandDict, self.Db):\r
2095                             EdkLogger.error("build", COMMAND_FAILURE)\r
2096 \r
2097                         #\r
2098                         # Create MAP file for all platform FVs after GenFds.\r
2099                         #\r
2100                         self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)\r
2101                         self.GenFdsTime += int(round((time.time() - GenFdsStart)))\r
2102                     #\r
2103                     # Save MAP buffer into MAP file.\r
2104                     #\r
2105                     self._SaveMapFile(MapBuffer, Wa)\r
2106 \r
2107     ## Generate GuidedSectionTools.txt in the FV directories.\r
2108     #\r
2109     def CreateGuidedSectionToolsFile(self):\r
2110         for BuildTarget in self.BuildTargetList:\r
2111             for ToolChain in self.ToolChainList:\r
2112                 Wa = WorkspaceAutoGen(\r
2113                         self.WorkspaceDir,\r
2114                         self.PlatformFile,\r
2115                         BuildTarget,\r
2116                         ToolChain,\r
2117                         self.ArchList,\r
2118                         self.BuildDatabase,\r
2119                         self.TargetTxt,\r
2120                         self.ToolDef,\r
2121                         self.Fdf,\r
2122                         self.FdList,\r
2123                         self.FvList,\r
2124                         self.CapList,\r
2125                         self.SkuId,\r
2126                         self.UniFlag\r
2127                         )\r
2128                 FvDir = Wa.FvDir\r
2129                 if not os.path.exists(FvDir):\r
2130                     continue\r
2131 \r
2132                 for Arch in self.ArchList:\r
2133                     # Build up the list of supported architectures for this build\r
2134                     prefix = '%s_%s_%s_' % (BuildTarget, ToolChain, Arch)\r
2135 \r
2136                     # Look through the tool definitions for GUIDed tools\r
2137                     guidAttribs = []\r
2138                     for (attrib, value) in self.ToolDef.ToolsDefTxtDictionary.items():\r
2139                         if attrib.upper().endswith('_GUID'):\r
2140                             split = attrib.split('_')\r
2141                             thisPrefix = '_'.join(split[0:3]) + '_'\r
2142                             if thisPrefix == prefix:\r
2143                                 guid = self.ToolDef.ToolsDefTxtDictionary[attrib]\r
2144                                 guid = guid.lower()\r
2145                                 toolName = split[3]\r
2146                                 path = '_'.join(split[0:4]) + '_PATH'\r
2147                                 path = self.ToolDef.ToolsDefTxtDictionary[path]\r
2148                                 path = self.GetFullPathOfTool(path)\r
2149                                 guidAttribs.append((guid, toolName, path))\r
2150 \r
2151                     # Write out GuidedSecTools.txt\r
2152                     toolsFile = os.path.join(FvDir, 'GuidedSectionTools.txt')\r
2153                     toolsFile = open(toolsFile, 'wt')\r
2154                     for guidedSectionTool in guidAttribs:\r
2155                         print(' '.join(guidedSectionTool), file=toolsFile)\r
2156                     toolsFile.close()\r
2157 \r
2158     ## Returns the full path of the tool.\r
2159     #\r
2160     def GetFullPathOfTool (self, tool):\r
2161         if os.path.exists(tool):\r
2162             return os.path.realpath(tool)\r
2163         else:\r
2164             # We need to search for the tool using the\r
2165             # PATH environment variable.\r
2166             for dirInPath in os.environ['PATH'].split(os.pathsep):\r
2167                 foundPath = os.path.join(dirInPath, tool)\r
2168                 if os.path.exists(foundPath):\r
2169                     return os.path.realpath(foundPath)\r
2170 \r
2171         # If the tool was not found in the path then we just return\r
2172         # the input tool.\r
2173         return tool\r
2174 \r
2175     ## Launch the module or platform build\r
2176     #\r
2177     def Launch(self):\r
2178         if not self.ModuleFile:\r
2179             if not self.SpawnMode or self.Target not in ["", "all"]:\r
2180                 self.SpawnMode = False\r
2181                 self._BuildPlatform()\r
2182             else:\r
2183                 self._MultiThreadBuildPlatform()\r
2184             self.CreateGuidedSectionToolsFile()\r
2185         else:\r
2186             self.SpawnMode = False\r
2187             self._BuildModule()\r
2188 \r
2189         if self.Target == 'cleanall':\r
2190             RemoveDirectory(os.path.dirname(GlobalData.gDatabasePath), True)\r
2191 \r
2192     def CreateAsBuiltInf(self):\r
2193         all_lib_set = set()\r
2194         all_mod_set = set()\r
2195         for Module in self.BuildModules:\r
2196             Module.CreateAsBuiltInf()\r
2197             all_mod_set.add(Module)\r
2198         for Module in self.HashSkipModules:\r
2199             Module.CreateAsBuiltInf(True)\r
2200             all_mod_set.add(Module)\r
2201         for Module in all_mod_set:\r
2202             for lib in Module.LibraryAutoGenList:\r
2203                 all_lib_set.add(lib)\r
2204         for lib in all_lib_set:\r
2205             lib.CreateAsBuiltInf(True)\r
2206         all_lib_set.clear()\r
2207         all_mod_set.clear()\r
2208         self.BuildModules = []\r
2209         self.HashSkipModules = []\r
2210     ## Do some clean-up works when error occurred\r
2211     def Relinquish(self):\r
2212         OldLogLevel = EdkLogger.GetLevel()\r
2213         EdkLogger.SetLevel(EdkLogger.ERROR)\r
2214         Utils.Progressor.Abort()\r
2215         if self.SpawnMode == True:\r
2216             BuildTask.Abort()\r
2217         EdkLogger.SetLevel(OldLogLevel)\r
2218 \r
2219 def ParseDefines(DefineList=[]):\r
2220     DefineDict = {}\r
2221     if DefineList is not None:\r
2222         for Define in DefineList:\r
2223             DefineTokenList = Define.split("=", 1)\r
2224             if not GlobalData.gMacroNamePattern.match(DefineTokenList[0]):\r
2225                 EdkLogger.error('build', FORMAT_INVALID,\r
2226                                 "The macro name must be in the pattern [A-Z][A-Z0-9_]*",\r
2227                                 ExtraData=DefineTokenList[0])\r
2228 \r
2229             if len(DefineTokenList) == 1:\r
2230                 DefineDict[DefineTokenList[0]] = "TRUE"\r
2231             else:\r
2232                 DefineDict[DefineTokenList[0]] = DefineTokenList[1].strip()\r
2233     return DefineDict\r
2234 \r
2235 gParamCheck = []\r
2236 def SingleCheckCallback(option, opt_str, value, parser):\r
2237     if option not in gParamCheck:\r
2238         setattr(parser.values, option.dest, value)\r
2239         gParamCheck.append(option)\r
2240     else:\r
2241         parser.error("Option %s only allows one instance in command line!" % option)\r
2242 \r
2243 def LogBuildTime(Time):\r
2244     if Time:\r
2245         TimeDurStr = ''\r
2246         TimeDur = time.gmtime(Time)\r
2247         if TimeDur.tm_yday > 1:\r
2248             TimeDurStr = time.strftime("%H:%M:%S", TimeDur) + ", %d day(s)" % (TimeDur.tm_yday - 1)\r
2249         else:\r
2250             TimeDurStr = time.strftime("%H:%M:%S", TimeDur)\r
2251         return TimeDurStr\r
2252     else:\r
2253         return None\r
2254 \r
2255 ## Parse command line options\r
2256 #\r
2257 # Using standard Python module optparse to parse command line option of this tool.\r
2258 #\r
2259 #   @retval Opt   A optparse.Values object containing the parsed options\r
2260 #   @retval Args  Target of build command\r
2261 #\r
2262 def MyOptionParser():\r
2263     Parser = OptionParser(description=__copyright__, version=__version__, prog="build.exe", usage="%prog [options] [all|fds|genc|genmake|clean|cleanall|cleanlib|modules|libraries|run]")\r
2264     Parser.add_option("-a", "--arch", action="append", type="choice", choices=['IA32', 'X64', 'EBC', 'ARM', 'AARCH64'], dest="TargetArch",\r
2265         help="ARCHS is one of list: IA32, X64, ARM, AARCH64 or EBC, which overrides target.txt's TARGET_ARCH definition. To specify more archs, please repeat this option.")\r
2266     Parser.add_option("-p", "--platform", action="callback", type="string", dest="PlatformFile", callback=SingleCheckCallback,\r
2267         help="Build the platform specified by the DSC file name argument, overriding target.txt's ACTIVE_PLATFORM definition.")\r
2268     Parser.add_option("-m", "--module", action="callback", type="string", dest="ModuleFile", callback=SingleCheckCallback,\r
2269         help="Build the module specified by the INF file name argument.")\r
2270     Parser.add_option("-b", "--buildtarget", type="string", dest="BuildTarget", help="Using the TARGET to build the platform, overriding target.txt's TARGET definition.",\r
2271                       action="append")\r
2272     Parser.add_option("-t", "--tagname", action="append", type="string", dest="ToolChain",\r
2273         help="Using the Tool Chain Tagname to build the platform, overriding target.txt's TOOL_CHAIN_TAG definition.")\r
2274     Parser.add_option("-x", "--sku-id", action="callback", type="string", dest="SkuId", callback=SingleCheckCallback,\r
2275         help="Using this name of SKU ID to build the platform, overriding SKUID_IDENTIFIER in DSC file.")\r
2276 \r
2277     Parser.add_option("-n", action="callback", type="int", dest="ThreadNumber", callback=SingleCheckCallback,\r
2278         help="Build the platform using multi-threaded compiler. The value overrides target.txt's MAX_CONCURRENT_THREAD_NUMBER. When value is set to 0, tool automatically detect number of "\\r
2279              "processor threads, set value to 1 means disable multi-thread build, and set value to more than 1 means user specify the threads number to build.")\r
2280 \r
2281     Parser.add_option("-f", "--fdf", action="callback", type="string", dest="FdfFile", callback=SingleCheckCallback,\r
2282         help="The name of the FDF file to use, which overrides the setting in the DSC file.")\r
2283     Parser.add_option("-r", "--rom-image", action="append", type="string", dest="RomImage", default=[],\r
2284         help="The name of FD to be generated. The name must be from [FD] section in FDF file.")\r
2285     Parser.add_option("-i", "--fv-image", action="append", type="string", dest="FvImage", default=[],\r
2286         help="The name of FV to be generated. The name must be from [FV] section in FDF file.")\r
2287     Parser.add_option("-C", "--capsule-image", action="append", type="string", dest="CapName", default=[],\r
2288         help="The name of Capsule to be generated. The name must be from [Capsule] section in FDF file.")\r
2289     Parser.add_option("-u", "--skip-autogen", action="store_true", dest="SkipAutoGen", help="Skip AutoGen step.")\r
2290     Parser.add_option("-e", "--re-parse", action="store_true", dest="Reparse", help="Re-parse all meta-data files.")\r
2291 \r
2292     Parser.add_option("-c", "--case-insensitive", action="store_true", dest="CaseInsensitive", default=False, help="Don't check case of file name.")\r
2293 \r
2294     Parser.add_option("-w", "--warning-as-error", action="store_true", dest="WarningAsError", help="Treat warning in tools as error.")\r
2295     Parser.add_option("-j", "--log", action="store", dest="LogFile", help="Put log in specified file as well as on console.")\r
2296 \r
2297     Parser.add_option("-s", "--silent", action="store_true", type=None, dest="SilentMode",\r
2298         help="Make use of silent mode of (n)make.")\r
2299     Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")\r
2300     Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\\r
2301                                                                                "including library instances selected, final dependency expression, "\\r
2302                                                                                "and warning messages, etc.")\r
2303     Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")\r
2304     Parser.add_option("-D", "--define", action="append", type="string", dest="Macros", help="Macro: \"Name [= Value]\".")\r
2305 \r
2306     Parser.add_option("-y", "--report-file", action="store", dest="ReportFile", help="Create/overwrite the report to the specified filename.")\r
2307     Parser.add_option("-Y", "--report-type", action="append", type="choice", choices=['PCD', 'LIBRARY', 'FLASH', 'DEPEX', 'BUILD_FLAGS', 'FIXED_ADDRESS', 'HASH', 'EXECUTION_ORDER'], dest="ReportType", default=[],\r
2308         help="Flags that control the type of build report to generate.  Must be one of: [PCD, LIBRARY, FLASH, DEPEX, BUILD_FLAGS, FIXED_ADDRESS, HASH, EXECUTION_ORDER].  "\\r
2309              "To specify more than one flag, repeat this option on the command line and the default flag set is [PCD, LIBRARY, FLASH, DEPEX, HASH, BUILD_FLAGS, FIXED_ADDRESS]")\r
2310     Parser.add_option("-F", "--flag", action="store", type="string", dest="Flag",\r
2311         help="Specify the specific option to parse EDK UNI file. Must be one of: [-c, -s]. -c is for EDK framework UNI file, and -s is for EDK UEFI UNI file. "\\r
2312              "This option can also be specified by setting *_*_*_BUILD_FLAGS in [BuildOptions] section of platform DSC. If they are both specified, this value "\\r
2313              "will override the setting in [BuildOptions] section of platform DSC.")\r
2314     Parser.add_option("-N", "--no-cache", action="store_true", dest="DisableCache", default=False, help="Disable build cache mechanism")\r
2315     Parser.add_option("--conf", action="store", type="string", dest="ConfDirectory", help="Specify the customized Conf directory.")\r
2316     Parser.add_option("--check-usage", action="store_true", dest="CheckUsage", default=False, help="Check usage content of entries listed in INF file.")\r
2317     Parser.add_option("--ignore-sources", action="store_true", dest="IgnoreSources", default=False, help="Focus to a binary build and ignore all source files")\r
2318     Parser.add_option("--pcd", action="append", dest="OptionPcd", help="Set PCD value by command line. Format: \"PcdName=Value\" ")\r
2319     Parser.add_option("-l", "--cmd-len", action="store", type="int", dest="CommandLength", help="Specify the maximum line length of build command. Default is 4096.")\r
2320     Parser.add_option("--hash", action="store_true", dest="UseHashCache", default=False, help="Enable hash-based caching during build process.")\r
2321     Parser.add_option("--binary-destination", action="store", type="string", dest="BinCacheDest", help="Generate a cache of binary files in the specified directory.")\r
2322     Parser.add_option("--binary-source", action="store", type="string", dest="BinCacheSource", help="Consume a cache of binary files from the specified directory.")\r
2323     Parser.add_option("--genfds-multi-thread", action="store_true", dest="GenfdsMultiThread", default=False, help="Enable GenFds multi thread to generate ffs file.")\r
2324     Parser.add_option("--disable-include-path-check", action="store_true", dest="DisableIncludePathCheck", default=False, help="Disable the include path check for outside of package.")\r
2325     (Opt, Args) = Parser.parse_args()\r
2326     return (Opt, Args)\r
2327 \r
2328 ## Tool entrance method\r
2329 #\r
2330 # This method mainly dispatch specific methods per the command line options.\r
2331 # If no error found, return zero value so the caller of this tool can know\r
2332 # if it's executed successfully or not.\r
2333 #\r
2334 #   @retval 0     Tool was successful\r
2335 #   @retval 1     Tool failed\r
2336 #\r
2337 def Main():\r
2338     StartTime = time.time()\r
2339 \r
2340     # Initialize log system\r
2341     EdkLogger.Initialize()\r
2342     GlobalData.gCommand = sys.argv[1:]\r
2343     #\r
2344     # Parse the options and args\r
2345     #\r
2346     (Option, Target) = MyOptionParser()\r
2347     GlobalData.gOptions = Option\r
2348     GlobalData.gCaseInsensitive = Option.CaseInsensitive\r
2349 \r
2350     # Set log level\r
2351     if Option.verbose is not None:\r
2352         EdkLogger.SetLevel(EdkLogger.VERBOSE)\r
2353     elif Option.quiet is not None:\r
2354         EdkLogger.SetLevel(EdkLogger.QUIET)\r
2355     elif Option.debug is not None:\r
2356         EdkLogger.SetLevel(Option.debug + 1)\r
2357     else:\r
2358         EdkLogger.SetLevel(EdkLogger.INFO)\r
2359 \r
2360     if Option.LogFile is not None:\r
2361         EdkLogger.SetLogFile(Option.LogFile)\r
2362 \r
2363     if Option.WarningAsError == True:\r
2364         EdkLogger.SetWarningAsError()\r
2365 \r
2366     if platform.platform().find("Windows") >= 0:\r
2367         GlobalData.gIsWindows = True\r
2368     else:\r
2369         GlobalData.gIsWindows = False\r
2370 \r
2371     EdkLogger.quiet("Build environment: %s" % platform.platform())\r
2372     EdkLogger.quiet(time.strftime("Build start time: %H:%M:%S, %b.%d %Y\n", time.localtime()));\r
2373     ReturnCode = 0\r
2374     MyBuild = None\r
2375     BuildError = True\r
2376     try:\r
2377         if len(Target) == 0:\r
2378             Target = "all"\r
2379         elif len(Target) >= 2:\r
2380             EdkLogger.error("build", OPTION_NOT_SUPPORTED, "More than one targets are not supported.",\r
2381                             ExtraData="Please select one of: %s" % (' '.join(gSupportedTarget)))\r
2382         else:\r
2383             Target = Target[0].lower()\r
2384 \r
2385         if Target not in gSupportedTarget:\r
2386             EdkLogger.error("build", OPTION_NOT_SUPPORTED, "Not supported target [%s]." % Target,\r
2387                             ExtraData="Please select one of: %s" % (' '.join(gSupportedTarget)))\r
2388 \r
2389         #\r
2390         # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH\r
2391         #\r
2392         CheckEnvVariable()\r
2393         GlobalData.gCommandLineDefines.update(ParseDefines(Option.Macros))\r
2394 \r
2395         Workspace = os.getenv("WORKSPACE")\r
2396         #\r
2397         # Get files real name in workspace dir\r
2398         #\r
2399         GlobalData.gAllFiles = Utils.DirCache(Workspace)\r
2400 \r
2401         WorkingDirectory = os.getcwd()\r
2402         if not Option.ModuleFile:\r
2403             FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.inf')))\r
2404             FileNum = len(FileList)\r
2405             if FileNum >= 2:\r
2406                 EdkLogger.error("build", OPTION_NOT_SUPPORTED, "There are %d INF files in %s." % (FileNum, WorkingDirectory),\r
2407                                 ExtraData="Please use '-m <INF_FILE_PATH>' switch to choose one.")\r
2408             elif FileNum == 1:\r
2409                 Option.ModuleFile = NormFile(FileList[0], Workspace)\r
2410 \r
2411         if Option.ModuleFile:\r
2412             if os.path.isabs (Option.ModuleFile):\r
2413                 if os.path.normcase (os.path.normpath(Option.ModuleFile)).find (Workspace) == 0:\r
2414                     Option.ModuleFile = NormFile(os.path.normpath(Option.ModuleFile), Workspace)\r
2415             Option.ModuleFile = PathClass(Option.ModuleFile, Workspace)\r
2416             ErrorCode, ErrorInfo = Option.ModuleFile.Validate(".inf", False)\r
2417             if ErrorCode != 0:\r
2418                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
2419 \r
2420         if Option.PlatformFile is not None:\r
2421             if os.path.isabs (Option.PlatformFile):\r
2422                 if os.path.normcase (os.path.normpath(Option.PlatformFile)).find (Workspace) == 0:\r
2423                     Option.PlatformFile = NormFile(os.path.normpath(Option.PlatformFile), Workspace)\r
2424             Option.PlatformFile = PathClass(Option.PlatformFile, Workspace)\r
2425 \r
2426         if Option.FdfFile is not None:\r
2427             if os.path.isabs (Option.FdfFile):\r
2428                 if os.path.normcase (os.path.normpath(Option.FdfFile)).find (Workspace) == 0:\r
2429                     Option.FdfFile = NormFile(os.path.normpath(Option.FdfFile), Workspace)\r
2430             Option.FdfFile = PathClass(Option.FdfFile, Workspace)\r
2431             ErrorCode, ErrorInfo = Option.FdfFile.Validate(".fdf", False)\r
2432             if ErrorCode != 0:\r
2433                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
2434 \r
2435         if Option.Flag is not None and Option.Flag not in ['-c', '-s']:\r
2436             EdkLogger.error("build", OPTION_VALUE_INVALID, "UNI flag must be one of -c or -s")\r
2437 \r
2438         MyBuild = Build(Target, Workspace, Option)\r
2439         GlobalData.gCommandLineDefines['ARCH'] = ' '.join(MyBuild.ArchList)\r
2440         if not (MyBuild.LaunchPrebuildFlag and os.path.exists(MyBuild.PlatformBuildPath)):\r
2441             MyBuild.Launch()\r
2442 \r
2443         #\r
2444         # All job done, no error found and no exception raised\r
2445         #\r
2446         BuildError = False\r
2447     except FatalError as X:\r
2448         if MyBuild is not None:\r
2449             # for multi-thread build exits safely\r
2450             MyBuild.Relinquish()\r
2451         if Option is not None and Option.debug is not None:\r
2452             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
2453         ReturnCode = X.args[0]\r
2454     except Warning as X:\r
2455         # error from Fdf parser\r
2456         if MyBuild is not None:\r
2457             # for multi-thread build exits safely\r
2458             MyBuild.Relinquish()\r
2459         if Option is not None and Option.debug is not None:\r
2460             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
2461         else:\r
2462             EdkLogger.error(X.ToolName, FORMAT_INVALID, File=X.FileName, Line=X.LineNumber, ExtraData=X.Message, RaiseError=False)\r
2463         ReturnCode = FORMAT_INVALID\r
2464     except KeyboardInterrupt:\r
2465         ReturnCode = ABORT_ERROR\r
2466         if Option is not None and Option.debug is not None:\r
2467             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
2468     except:\r
2469         if MyBuild is not None:\r
2470             # for multi-thread build exits safely\r
2471             MyBuild.Relinquish()\r
2472 \r
2473         # try to get the meta-file from the object causing exception\r
2474         Tb = sys.exc_info()[-1]\r
2475         MetaFile = GlobalData.gProcessingFile\r
2476         while Tb is not None:\r
2477             if 'self' in Tb.tb_frame.f_locals and hasattr(Tb.tb_frame.f_locals['self'], 'MetaFile'):\r
2478                 MetaFile = Tb.tb_frame.f_locals['self'].MetaFile\r
2479             Tb = Tb.tb_next\r
2480         EdkLogger.error(\r
2481                     "\nbuild",\r
2482                     CODE_ERROR,\r
2483                     "Unknown fatal error when processing [%s]" % MetaFile,\r
2484                     ExtraData="\n(Please send email to %s for help, attaching following call stack trace!)\n" % MSG_EDKII_MAIL_ADDR,\r
2485                     RaiseError=False\r
2486                     )\r
2487         EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
2488         ReturnCode = CODE_ERROR\r
2489     finally:\r
2490         Utils.Progressor.Abort()\r
2491         Utils.ClearDuplicatedInf()\r
2492 \r
2493     if ReturnCode == 0:\r
2494         try:\r
2495             MyBuild.LaunchPostbuild()\r
2496             Conclusion = "Done"\r
2497         except:\r
2498             Conclusion = "Failed"\r
2499     elif ReturnCode == ABORT_ERROR:\r
2500         Conclusion = "Aborted"\r
2501     else:\r
2502         Conclusion = "Failed"\r
2503     FinishTime = time.time()\r
2504     BuildDuration = time.gmtime(int(round(FinishTime - StartTime)))\r
2505     BuildDurationStr = ""\r
2506     if BuildDuration.tm_yday > 1:\r
2507         BuildDurationStr = time.strftime("%H:%M:%S", BuildDuration) + ", %d day(s)" % (BuildDuration.tm_yday - 1)\r
2508     else:\r
2509         BuildDurationStr = time.strftime("%H:%M:%S", BuildDuration)\r
2510     if MyBuild is not None:\r
2511         if not BuildError:\r
2512             MyBuild.BuildReport.GenerateReport(BuildDurationStr, LogBuildTime(MyBuild.AutoGenTime), LogBuildTime(MyBuild.MakeTime), LogBuildTime(MyBuild.GenFdsTime))\r
2513 \r
2514     EdkLogger.SetLevel(EdkLogger.QUIET)\r
2515     EdkLogger.quiet("\n- %s -" % Conclusion)\r
2516     EdkLogger.quiet(time.strftime("Build end time: %H:%M:%S, %b.%d %Y", time.localtime()))\r
2517     EdkLogger.quiet("Build total time: %s\n" % BuildDurationStr)\r
2518     return ReturnCode\r
2519 \r
2520 if __name__ == '__main__':\r
2521     r = Main()\r
2522     ## 0-127 is a safe return range, and 1 is a standard default error\r
2523     if r < 0 or r > 127: r = 1\r
2524     sys.exit(r)\r
2525 \r