80ceb983104f6ecfa660942fd4a0a904e797f60d
[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             return True\r
1364 \r
1365         # build library\r
1366         if Target == 'libraries':\r
1367             pass\r
1368 \r
1369         # not build modules\r
1370 \r
1371 \r
1372         # cleanall\r
1373         if Target == 'cleanall':\r
1374             try:\r
1375                 #os.rmdir(AutoGenObject.BuildDir)\r
1376                 RemoveDirectory(AutoGenObject.BuildDir, True)\r
1377             except WindowsError as X:\r
1378                 EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X))\r
1379         return True\r
1380 \r
1381     ## Rebase module image and Get function address for the input module list.\r
1382     #\r
1383     def _RebaseModule (self, MapBuffer, BaseAddress, ModuleList, AddrIsOffset = True, ModeIsSmm = False):\r
1384         if ModeIsSmm:\r
1385             AddrIsOffset = False\r
1386         for InfFile in ModuleList:\r
1387             sys.stdout.write (".")\r
1388             sys.stdout.flush()\r
1389             ModuleInfo = ModuleList[InfFile]\r
1390             ModuleName = ModuleInfo.BaseName\r
1391             ModuleOutputImage = ModuleInfo.Image.FileName\r
1392             ModuleDebugImage  = os.path.join(ModuleInfo.DebugDir, ModuleInfo.BaseName + '.efi')\r
1393             ## for SMM module in SMRAM, the SMRAM will be allocated from base to top.\r
1394             if not ModeIsSmm:\r
1395                 BaseAddress = BaseAddress - ModuleInfo.Image.Size\r
1396                 #\r
1397                 # Update Image to new BaseAddress by GenFw tool\r
1398                 #\r
1399                 LaunchCommand(["GenFw", "--rebase", str(BaseAddress), "-r", ModuleOutputImage], ModuleInfo.OutputDir)\r
1400                 LaunchCommand(["GenFw", "--rebase", str(BaseAddress), "-r", ModuleDebugImage], ModuleInfo.DebugDir)\r
1401             else:\r
1402                 #\r
1403                 # Set new address to the section header only for SMM driver.\r
1404                 #\r
1405                 LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleOutputImage], ModuleInfo.OutputDir)\r
1406                 LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleDebugImage], ModuleInfo.DebugDir)\r
1407             #\r
1408             # Collect function address from Map file\r
1409             #\r
1410             ImageMapTable = ModuleOutputImage.replace('.efi', '.map')\r
1411             FunctionList = []\r
1412             if os.path.exists(ImageMapTable):\r
1413                 OrigImageBaseAddress = 0\r
1414                 ImageMap = open(ImageMapTable, 'r')\r
1415                 for LinStr in ImageMap:\r
1416                     if len (LinStr.strip()) == 0:\r
1417                         continue\r
1418                     #\r
1419                     # Get the preferred address set on link time.\r
1420                     #\r
1421                     if LinStr.find ('Preferred load address is') != -1:\r
1422                         StrList = LinStr.split()\r
1423                         OrigImageBaseAddress = int (StrList[len(StrList) - 1], 16)\r
1424 \r
1425                     StrList = LinStr.split()\r
1426                     if len (StrList) > 4:\r
1427                         if StrList[3] == 'f' or StrList[3] == 'F':\r
1428                             Name = StrList[1]\r
1429                             RelativeAddress = int (StrList[2], 16) - OrigImageBaseAddress\r
1430                             FunctionList.append ((Name, RelativeAddress))\r
1431 \r
1432                 ImageMap.close()\r
1433             #\r
1434             # Add general information.\r
1435             #\r
1436             if ModeIsSmm:\r
1437                 MapBuffer.append('\n\n%s (Fixed SMRAM Offset,   BaseAddress=0x%010X,  EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint))\r
1438             elif AddrIsOffset:\r
1439                 MapBuffer.append('\n\n%s (Fixed Memory Offset,  BaseAddress=-0x%010X, EntryPoint=-0x%010X)\n' % (ModuleName, 0 - BaseAddress, 0 - (BaseAddress + ModuleInfo.Image.EntryPoint)))\r
1440             else:\r
1441                 MapBuffer.append('\n\n%s (Fixed Memory Address, BaseAddress=0x%010X,  EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint))\r
1442             #\r
1443             # Add guid and general seciton section.\r
1444             #\r
1445             TextSectionAddress = 0\r
1446             DataSectionAddress = 0\r
1447             for SectionHeader in ModuleInfo.Image.SectionHeaderList:\r
1448                 if SectionHeader[0] == '.text':\r
1449                     TextSectionAddress = SectionHeader[1]\r
1450                 elif SectionHeader[0] in ['.data', '.sdata']:\r
1451                     DataSectionAddress = SectionHeader[1]\r
1452             if AddrIsOffset:\r
1453                 MapBuffer.append('(GUID=%s, .textbaseaddress=-0x%010X, .databaseaddress=-0x%010X)\n' % (ModuleInfo.Guid, 0 - (BaseAddress + TextSectionAddress), 0 - (BaseAddress + DataSectionAddress)))\r
1454             else:\r
1455                 MapBuffer.append('(GUID=%s, .textbaseaddress=0x%010X, .databaseaddress=0x%010X)\n' % (ModuleInfo.Guid, BaseAddress + TextSectionAddress, BaseAddress + DataSectionAddress))\r
1456             #\r
1457             # Add debug image full path.\r
1458             #\r
1459             MapBuffer.append('(IMAGE=%s)\n\n' % (ModuleDebugImage))\r
1460             #\r
1461             # Add function address\r
1462             #\r
1463             for Function in FunctionList:\r
1464                 if AddrIsOffset:\r
1465                     MapBuffer.append('  -0x%010X    %s\n' % (0 - (BaseAddress + Function[1]), Function[0]))\r
1466                 else:\r
1467                     MapBuffer.append('  0x%010X    %s\n' % (BaseAddress + Function[1], Function[0]))\r
1468             ImageMap.close()\r
1469 \r
1470             #\r
1471             # for SMM module in SMRAM, the SMRAM will be allocated from base to top.\r
1472             #\r
1473             if ModeIsSmm:\r
1474                 BaseAddress = BaseAddress + ModuleInfo.Image.Size\r
1475 \r
1476     ## Collect MAP information of all FVs\r
1477     #\r
1478     def _CollectFvMapBuffer (self, MapBuffer, Wa, ModuleList):\r
1479         if self.Fdf:\r
1480             # First get the XIP base address for FV map file.\r
1481             GuidPattern = re.compile("[-a-fA-F0-9]+")\r
1482             GuidName = re.compile("\(GUID=[-a-fA-F0-9]+")\r
1483             for FvName in Wa.FdfProfile.FvDict:\r
1484                 FvMapBuffer = os.path.join(Wa.FvDir, FvName + '.Fv.map')\r
1485                 if not os.path.exists(FvMapBuffer):\r
1486                     continue\r
1487                 FvMap = open(FvMapBuffer, 'r')\r
1488                 #skip FV size information\r
1489                 FvMap.readline()\r
1490                 FvMap.readline()\r
1491                 FvMap.readline()\r
1492                 FvMap.readline()\r
1493                 for Line in FvMap:\r
1494                     MatchGuid = GuidPattern.match(Line)\r
1495                     if MatchGuid is not None:\r
1496                         #\r
1497                         # Replace GUID with module name\r
1498                         #\r
1499                         GuidString = MatchGuid.group()\r
1500                         if GuidString.upper() in ModuleList:\r
1501                             Line = Line.replace(GuidString, ModuleList[GuidString.upper()].Name)\r
1502                     MapBuffer.append(Line)\r
1503                     #\r
1504                     # Add the debug image full path.\r
1505                     #\r
1506                     MatchGuid = GuidName.match(Line)\r
1507                     if MatchGuid is not None:\r
1508                         GuidString = MatchGuid.group().split("=")[1]\r
1509                         if GuidString.upper() in ModuleList:\r
1510                             MapBuffer.append('(IMAGE=%s)\n' % (os.path.join(ModuleList[GuidString.upper()].DebugDir, ModuleList[GuidString.upper()].Name + '.efi')))\r
1511 \r
1512                 FvMap.close()\r
1513 \r
1514     ## Collect MAP information of all modules\r
1515     #\r
1516     def _CollectModuleMapBuffer (self, MapBuffer, ModuleList):\r
1517         sys.stdout.write ("Generate Load Module At Fix Address Map")\r
1518         sys.stdout.flush()\r
1519         PatchEfiImageList = []\r
1520         PeiModuleList  = {}\r
1521         BtModuleList   = {}\r
1522         RtModuleList   = {}\r
1523         SmmModuleList  = {}\r
1524         PeiSize = 0\r
1525         BtSize  = 0\r
1526         RtSize  = 0\r
1527         # reserve 4K size in SMRAM to make SMM module address not from 0.\r
1528         SmmSize = 0x1000\r
1529         for ModuleGuid in ModuleList:\r
1530             Module = ModuleList[ModuleGuid]\r
1531             GlobalData.gProcessingFile = "%s [%s, %s, %s]" % (Module.MetaFile, Module.Arch, Module.ToolChain, Module.BuildTarget)\r
1532 \r
1533             OutputImageFile = ''\r
1534             for ResultFile in Module.CodaTargetList:\r
1535                 if str(ResultFile.Target).endswith('.efi'):\r
1536                     #\r
1537                     # module list for PEI, DXE, RUNTIME and SMM\r
1538                     #\r
1539                     OutputImageFile = os.path.join(Module.OutputDir, Module.Name + '.efi')\r
1540                     ImageClass = PeImageClass (OutputImageFile)\r
1541                     if not ImageClass.IsValid:\r
1542                         EdkLogger.error("build", FILE_PARSE_FAILURE, ExtraData=ImageClass.ErrorInfo)\r
1543                     ImageInfo = PeImageInfo(Module.Name, Module.Guid, Module.Arch, Module.OutputDir, Module.DebugDir, ImageClass)\r
1544                     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
1545                         PeiModuleList[Module.MetaFile] = ImageInfo\r
1546                         PeiSize += ImageInfo.Image.Size\r
1547                     elif Module.ModuleType in [EDK_COMPONENT_TYPE_BS_DRIVER, SUP_MODULE_DXE_DRIVER, SUP_MODULE_UEFI_DRIVER]:\r
1548                         BtModuleList[Module.MetaFile] = ImageInfo\r
1549                         BtSize += ImageInfo.Image.Size\r
1550                     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
1551                         RtModuleList[Module.MetaFile] = ImageInfo\r
1552                         RtSize += ImageInfo.Image.Size\r
1553                     elif Module.ModuleType in [SUP_MODULE_SMM_CORE, SUP_MODULE_DXE_SMM_DRIVER, SUP_MODULE_MM_STANDALONE, SUP_MODULE_MM_CORE_STANDALONE]:\r
1554                         SmmModuleList[Module.MetaFile] = ImageInfo\r
1555                         SmmSize += ImageInfo.Image.Size\r
1556                         if Module.ModuleType == SUP_MODULE_DXE_SMM_DRIVER:\r
1557                             PiSpecVersion = Module.Module.Specification.get('PI_SPECIFICATION_VERSION', '0x00000000')\r
1558                             # for PI specification < PI1.1, DXE_SMM_DRIVER also runs as BOOT time driver.\r
1559                             if int(PiSpecVersion, 16) < 0x0001000A:\r
1560                                 BtModuleList[Module.MetaFile] = ImageInfo\r
1561                                 BtSize += ImageInfo.Image.Size\r
1562                     break\r
1563             #\r
1564             # EFI image is final target.\r
1565             # Check EFI image contains patchable FixAddress related PCDs.\r
1566             #\r
1567             if OutputImageFile != '':\r
1568                 ModuleIsPatch = False\r
1569                 for Pcd in Module.ModulePcdList:\r
1570                     if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SET:\r
1571                         ModuleIsPatch = True\r
1572                         break\r
1573                 if not ModuleIsPatch:\r
1574                     for Pcd in Module.LibraryPcdList:\r
1575                         if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SET:\r
1576                             ModuleIsPatch = True\r
1577                             break\r
1578 \r
1579                 if not ModuleIsPatch:\r
1580                     continue\r
1581                 #\r
1582                 # Module includes the patchable load fix address PCDs.\r
1583                 # It will be fixed up later.\r
1584                 #\r
1585                 PatchEfiImageList.append (OutputImageFile)\r
1586 \r
1587         #\r
1588         # Get Top Memory address\r
1589         #\r
1590         ReservedRuntimeMemorySize = 0\r
1591         TopMemoryAddress = 0\r
1592         if self.LoadFixAddress == 0xFFFFFFFFFFFFFFFF:\r
1593             TopMemoryAddress = 0\r
1594         else:\r
1595             TopMemoryAddress = self.LoadFixAddress\r
1596             if TopMemoryAddress < RtSize + BtSize + PeiSize:\r
1597                 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS is too low to load driver")\r
1598 \r
1599         #\r
1600         # Patch FixAddress related PCDs into EFI image\r
1601         #\r
1602         for EfiImage in PatchEfiImageList:\r
1603             EfiImageMap = EfiImage.replace('.efi', '.map')\r
1604             if not os.path.exists(EfiImageMap):\r
1605                 continue\r
1606             #\r
1607             # Get PCD offset in EFI image by GenPatchPcdTable function\r
1608             #\r
1609             PcdTable = parsePcdInfoFromMapFile(EfiImageMap, EfiImage)\r
1610             #\r
1611             # Patch real PCD value by PatchPcdValue tool\r
1612             #\r
1613             for PcdInfo in PcdTable:\r
1614                 ReturnValue = 0\r
1615                 if PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE:\r
1616                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE_DATA_TYPE, str (PeiSize // 0x1000))\r
1617                 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE:\r
1618                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE_DATA_TYPE, str (BtSize // 0x1000))\r
1619                 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE:\r
1620                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE_DATA_TYPE, str (RtSize // 0x1000))\r
1621                 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE and len (SmmModuleList) > 0:\r
1622                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE_DATA_TYPE, str (SmmSize // 0x1000))\r
1623                 if ReturnValue != 0:\r
1624                     EdkLogger.error("build", PARAMETER_INVALID, "Patch PCD value failed", ExtraData=ErrorInfo)\r
1625 \r
1626         MapBuffer.append('PEI_CODE_PAGE_NUMBER      = 0x%x\n' % (PeiSize // 0x1000))\r
1627         MapBuffer.append('BOOT_CODE_PAGE_NUMBER     = 0x%x\n' % (BtSize // 0x1000))\r
1628         MapBuffer.append('RUNTIME_CODE_PAGE_NUMBER  = 0x%x\n' % (RtSize // 0x1000))\r
1629         if len (SmmModuleList) > 0:\r
1630             MapBuffer.append('SMM_CODE_PAGE_NUMBER      = 0x%x\n' % (SmmSize // 0x1000))\r
1631 \r
1632         PeiBaseAddr = TopMemoryAddress - RtSize - BtSize\r
1633         BtBaseAddr  = TopMemoryAddress - RtSize\r
1634         RtBaseAddr  = TopMemoryAddress - ReservedRuntimeMemorySize\r
1635 \r
1636         self._RebaseModule (MapBuffer, PeiBaseAddr, PeiModuleList, TopMemoryAddress == 0)\r
1637         self._RebaseModule (MapBuffer, BtBaseAddr, BtModuleList, TopMemoryAddress == 0)\r
1638         self._RebaseModule (MapBuffer, RtBaseAddr, RtModuleList, TopMemoryAddress == 0)\r
1639         self._RebaseModule (MapBuffer, 0x1000, SmmModuleList, AddrIsOffset=False, ModeIsSmm=True)\r
1640         MapBuffer.append('\n\n')\r
1641         sys.stdout.write ("\n")\r
1642         sys.stdout.flush()\r
1643 \r
1644     ## Save platform Map file\r
1645     #\r
1646     def _SaveMapFile (self, MapBuffer, Wa):\r
1647         #\r
1648         # Map file path is got.\r
1649         #\r
1650         MapFilePath = os.path.join(Wa.BuildDir, Wa.Name + '.map')\r
1651         #\r
1652         # Save address map into MAP file.\r
1653         #\r
1654         SaveFileOnChange(MapFilePath, ''.join(MapBuffer), False)\r
1655         if self.LoadFixAddress != 0:\r
1656             sys.stdout.write ("\nLoad Module At Fix Address Map file can be found at %s\n" % (MapFilePath))\r
1657         sys.stdout.flush()\r
1658 \r
1659     ## Build active platform for different build targets and different tool chains\r
1660     #\r
1661     def _BuildPlatform(self):\r
1662         SaveFileOnChange(self.PlatformBuildPath, '# DO NOT EDIT \n# FILE auto-generated\n', False)\r
1663         for BuildTarget in self.BuildTargetList:\r
1664             GlobalData.gGlobalDefines['TARGET'] = BuildTarget\r
1665             index = 0\r
1666             for ToolChain in self.ToolChainList:\r
1667                 GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain\r
1668                 GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain\r
1669                 GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index]\r
1670                 index += 1\r
1671                 Wa = WorkspaceAutoGen(\r
1672                         self.WorkspaceDir,\r
1673                         self.PlatformFile,\r
1674                         BuildTarget,\r
1675                         ToolChain,\r
1676                         self.ArchList,\r
1677                         self.BuildDatabase,\r
1678                         self.TargetTxt,\r
1679                         self.ToolDef,\r
1680                         self.Fdf,\r
1681                         self.FdList,\r
1682                         self.FvList,\r
1683                         self.CapList,\r
1684                         self.SkuId,\r
1685                         self.UniFlag,\r
1686                         self.Progress\r
1687                         )\r
1688                 self.Fdf = Wa.FdfFile\r
1689                 self.LoadFixAddress = Wa.Platform.LoadFixAddress\r
1690                 self.BuildReport.AddPlatformReport(Wa)\r
1691                 self.Progress.Stop("done!")\r
1692 \r
1693                 # Add ffs build to makefile\r
1694                 CmdListDict = {}\r
1695                 if GlobalData.gEnableGenfdsMultiThread and self.Fdf:\r
1696                     CmdListDict = self._GenFfsCmd(Wa.ArchList)\r
1697 \r
1698                 for Arch in Wa.ArchList:\r
1699                     GlobalData.gGlobalDefines['ARCH'] = Arch\r
1700                     Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)\r
1701                     for Module in Pa.Platform.Modules:\r
1702                         # Get ModuleAutoGen object to generate C code file and makefile\r
1703                         Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
1704                         if Ma is None:\r
1705                             continue\r
1706                         self.BuildModules.append(Ma)\r
1707                     self._BuildPa(self.Target, Pa, FfsCommand=CmdListDict)\r
1708 \r
1709                 # Create MAP file when Load Fix Address is enabled.\r
1710                 if self.Target in ["", "all", "fds"]:\r
1711                     for Arch in Wa.ArchList:\r
1712                         GlobalData.gGlobalDefines['ARCH'] = Arch\r
1713                         #\r
1714                         # Check whether the set fix address is above 4G for 32bit image.\r
1715                         #\r
1716                         if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
1717                             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
1718                     #\r
1719                     # Get Module List\r
1720                     #\r
1721                     ModuleList = {}\r
1722                     for Pa in Wa.AutoGenObjectList:\r
1723                         for Ma in Pa.ModuleAutoGenList:\r
1724                             if Ma is None:\r
1725                                 continue\r
1726                             if not Ma.IsLibrary:\r
1727                                 ModuleList[Ma.Guid.upper()] = Ma\r
1728 \r
1729                     MapBuffer = []\r
1730                     if self.LoadFixAddress != 0:\r
1731                         #\r
1732                         # Rebase module to the preferred memory address before GenFds\r
1733                         #\r
1734                         self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
1735                     if self.Fdf:\r
1736                         #\r
1737                         # create FDS again for the updated EFI image\r
1738                         #\r
1739                         self._Build("fds", Wa)\r
1740                         #\r
1741                         # Create MAP file for all platform FVs after GenFds.\r
1742                         #\r
1743                         self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)\r
1744                     #\r
1745                     # Save MAP buffer into MAP file.\r
1746                     #\r
1747                     self._SaveMapFile (MapBuffer, Wa)\r
1748 \r
1749     ## Build active module for different build targets, different tool chains and different archs\r
1750     #\r
1751     def _BuildModule(self):\r
1752         for BuildTarget in self.BuildTargetList:\r
1753             GlobalData.gGlobalDefines['TARGET'] = BuildTarget\r
1754             index = 0\r
1755             for ToolChain in self.ToolChainList:\r
1756                 WorkspaceAutoGenTime = time.time()\r
1757                 GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain\r
1758                 GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain\r
1759                 GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index]\r
1760                 index += 1\r
1761                 #\r
1762                 # module build needs platform build information, so get platform\r
1763                 # AutoGen first\r
1764                 #\r
1765                 Wa = WorkspaceAutoGen(\r
1766                         self.WorkspaceDir,\r
1767                         self.PlatformFile,\r
1768                         BuildTarget,\r
1769                         ToolChain,\r
1770                         self.ArchList,\r
1771                         self.BuildDatabase,\r
1772                         self.TargetTxt,\r
1773                         self.ToolDef,\r
1774                         self.Fdf,\r
1775                         self.FdList,\r
1776                         self.FvList,\r
1777                         self.CapList,\r
1778                         self.SkuId,\r
1779                         self.UniFlag,\r
1780                         self.Progress,\r
1781                         self.ModuleFile\r
1782                         )\r
1783                 self.Fdf = Wa.FdfFile\r
1784                 self.LoadFixAddress = Wa.Platform.LoadFixAddress\r
1785                 Wa.CreateMakeFile(False)\r
1786                 # Add ffs build to makefile\r
1787                 CmdListDict = None\r
1788                 if GlobalData.gEnableGenfdsMultiThread and self.Fdf:\r
1789                     CmdListDict = self._GenFfsCmd(Wa.ArchList)\r
1790                 self.Progress.Stop("done!")\r
1791                 MaList = []\r
1792                 ExitFlag = threading.Event()\r
1793                 ExitFlag.clear()\r
1794                 self.AutoGenTime += int(round((time.time() - WorkspaceAutoGenTime)))\r
1795                 for Arch in Wa.ArchList:\r
1796                     AutoGenStart = time.time()\r
1797                     GlobalData.gGlobalDefines['ARCH'] = Arch\r
1798                     Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)\r
1799                     for Module in Pa.Platform.Modules:\r
1800                         if self.ModuleFile.Dir == Module.Dir and self.ModuleFile.Name == Module.Name:\r
1801                             Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
1802                             if Ma is None:\r
1803                                 continue\r
1804                             MaList.append(Ma)\r
1805                             if Ma.CanSkipbyHash():\r
1806                                 self.HashSkipModules.append(Ma)\r
1807                                 continue\r
1808                             # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'\r
1809                             if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
1810                                 # for target which must generate AutoGen code and makefile\r
1811                                 if not self.SkipAutoGen or self.Target == 'genc':\r
1812                                     self.Progress.Start("Generating code")\r
1813                                     Ma.CreateCodeFile(True)\r
1814                                     self.Progress.Stop("done!")\r
1815                                 if self.Target == "genc":\r
1816                                     return True\r
1817                                 if not self.SkipAutoGen or self.Target == 'genmake':\r
1818                                     self.Progress.Start("Generating makefile")\r
1819                                     if CmdListDict and self.Fdf and (Module.File, Arch) in CmdListDict:\r
1820                                         Ma.CreateMakeFile(True, CmdListDict[Module.File, Arch])\r
1821                                         del CmdListDict[Module.File, Arch]\r
1822                                     else:\r
1823                                         Ma.CreateMakeFile(True)\r
1824                                     self.Progress.Stop("done!")\r
1825                                 if self.Target == "genmake":\r
1826                                     return True\r
1827                             self.BuildModules.append(Ma)\r
1828                             # Initialize all modules in tracking to False (FAIL)\r
1829                             if Ma not in GlobalData.gModuleBuildTracking:\r
1830                                 GlobalData.gModuleBuildTracking[Ma] = False\r
1831                     self.AutoGenTime += int(round((time.time() - AutoGenStart)))\r
1832                     MakeStart = time.time()\r
1833                     for Ma in self.BuildModules:\r
1834                         if not Ma.IsBinaryModule:\r
1835                             Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))\r
1836                         # Break build if any build thread has error\r
1837                         if BuildTask.HasError():\r
1838                             # we need a full version of makefile for platform\r
1839                             ExitFlag.set()\r
1840                             BuildTask.WaitForComplete()\r
1841                             self.invalidateHash()\r
1842                             Pa.CreateMakeFile(False)\r
1843                             EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1844                         # Start task scheduler\r
1845                         if not BuildTask.IsOnGoing():\r
1846                             BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)\r
1847 \r
1848                     # in case there's an interruption. we need a full version of makefile for platform\r
1849                     Pa.CreateMakeFile(False)\r
1850                     if BuildTask.HasError():\r
1851                         self.invalidateHash()\r
1852                         EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1853                     self.MakeTime += int(round((time.time() - MakeStart)))\r
1854 \r
1855                 MakeContiue = time.time()\r
1856                 ExitFlag.set()\r
1857                 BuildTask.WaitForComplete()\r
1858                 self.CreateAsBuiltInf()\r
1859                 self.MakeTime += int(round((time.time() - MakeContiue)))\r
1860                 if BuildTask.HasError():\r
1861                     self.invalidateHash()\r
1862                     EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1863 \r
1864                 self.BuildReport.AddPlatformReport(Wa, MaList)\r
1865                 if MaList == []:\r
1866                     EdkLogger.error(\r
1867                                 'build',\r
1868                                 BUILD_ERROR,\r
1869                                 "Module for [%s] is not a component of active platform."\\r
1870                                 " Please make sure that the ARCH and inf file path are"\\r
1871                                 " given in the same as in [%s]" % \\r
1872                                     (', '.join(Wa.ArchList), self.PlatformFile),\r
1873                                 ExtraData=self.ModuleFile\r
1874                                 )\r
1875                 # Create MAP file when Load Fix Address is enabled.\r
1876                 if self.Target == "fds" and self.Fdf:\r
1877                     for Arch in Wa.ArchList:\r
1878                         #\r
1879                         # Check whether the set fix address is above 4G for 32bit image.\r
1880                         #\r
1881                         if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
1882                             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
1883                     #\r
1884                     # Get Module List\r
1885                     #\r
1886                     ModuleList = {}\r
1887                     for Pa in Wa.AutoGenObjectList:\r
1888                         for Ma in Pa.ModuleAutoGenList:\r
1889                             if Ma is None:\r
1890                                 continue\r
1891                             if not Ma.IsLibrary:\r
1892                                 ModuleList[Ma.Guid.upper()] = Ma\r
1893 \r
1894                     MapBuffer = []\r
1895                     if self.LoadFixAddress != 0:\r
1896                         #\r
1897                         # Rebase module to the preferred memory address before GenFds\r
1898                         #\r
1899                         self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
1900                     #\r
1901                     # create FDS again for the updated EFI image\r
1902                     #\r
1903                     GenFdsStart = time.time()\r
1904                     self._Build("fds", Wa)\r
1905                     self.GenFdsTime += int(round((time.time() - GenFdsStart)))\r
1906                     #\r
1907                     # Create MAP file for all platform FVs after GenFds.\r
1908                     #\r
1909                     self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)\r
1910                     #\r
1911                     # Save MAP buffer into MAP file.\r
1912                     #\r
1913                     self._SaveMapFile (MapBuffer, Wa)\r
1914 \r
1915     def _GenFfsCmd(self,ArchList):\r
1916         # convert dictionary of Cmd:(Inf,Arch)\r
1917         # to a new dictionary of (Inf,Arch):Cmd,Cmd,Cmd...\r
1918         CmdSetDict = defaultdict(set)\r
1919         GenFfsDict = GenFds.GenFfsMakefile('', GlobalData.gFdfParser, self, ArchList, GlobalData)\r
1920         for Cmd in GenFfsDict:\r
1921             tmpInf, tmpArch = GenFfsDict[Cmd]\r
1922             CmdSetDict[tmpInf, tmpArch].add(Cmd)\r
1923         return CmdSetDict\r
1924 \r
1925     ## Build a platform in multi-thread mode\r
1926     #\r
1927     def _MultiThreadBuildPlatform(self):\r
1928         SaveFileOnChange(self.PlatformBuildPath, '# DO NOT EDIT \n# FILE auto-generated\n', False)\r
1929         for BuildTarget in self.BuildTargetList:\r
1930             GlobalData.gGlobalDefines['TARGET'] = BuildTarget\r
1931             index = 0\r
1932             for ToolChain in self.ToolChainList:\r
1933                 WorkspaceAutoGenTime = time.time()\r
1934                 GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain\r
1935                 GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain\r
1936                 GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index]\r
1937                 index += 1\r
1938                 Wa = WorkspaceAutoGen(\r
1939                         self.WorkspaceDir,\r
1940                         self.PlatformFile,\r
1941                         BuildTarget,\r
1942                         ToolChain,\r
1943                         self.ArchList,\r
1944                         self.BuildDatabase,\r
1945                         self.TargetTxt,\r
1946                         self.ToolDef,\r
1947                         self.Fdf,\r
1948                         self.FdList,\r
1949                         self.FvList,\r
1950                         self.CapList,\r
1951                         self.SkuId,\r
1952                         self.UniFlag,\r
1953                         self.Progress\r
1954                         )\r
1955                 self.Fdf = Wa.FdfFile\r
1956                 self.LoadFixAddress = Wa.Platform.LoadFixAddress\r
1957                 self.BuildReport.AddPlatformReport(Wa)\r
1958                 Wa.CreateMakeFile(False)\r
1959 \r
1960                 # Add ffs build to makefile\r
1961                 CmdListDict = None\r
1962                 if GlobalData.gEnableGenfdsMultiThread and self.Fdf:\r
1963                     CmdListDict = self._GenFfsCmd(Wa.ArchList)\r
1964 \r
1965                 # multi-thread exit flag\r
1966                 ExitFlag = threading.Event()\r
1967                 ExitFlag.clear()\r
1968                 self.AutoGenTime += int(round((time.time() - WorkspaceAutoGenTime)))\r
1969                 for Arch in Wa.ArchList:\r
1970                     AutoGenStart = time.time()\r
1971                     GlobalData.gGlobalDefines['ARCH'] = Arch\r
1972                     Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)\r
1973                     if Pa is None:\r
1974                         continue\r
1975                     ModuleList = []\r
1976                     for Inf in Pa.Platform.Modules:\r
1977                         ModuleList.append(Inf)\r
1978                     # Add the INF only list in FDF\r
1979                     if GlobalData.gFdfParser is not None:\r
1980                         for InfName in GlobalData.gFdfParser.Profile.InfList:\r
1981                             Inf = PathClass(NormPath(InfName), self.WorkspaceDir, Arch)\r
1982                             if Inf in Pa.Platform.Modules:\r
1983                                 continue\r
1984                             ModuleList.append(Inf)\r
1985                     for Module in ModuleList:\r
1986                         # Get ModuleAutoGen object to generate C code file and makefile\r
1987                         Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
1988 \r
1989                         if Ma is None:\r
1990                             continue\r
1991                         if Ma.CanSkipbyHash():\r
1992                             self.HashSkipModules.append(Ma)\r
1993                             continue\r
1994 \r
1995                         # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'\r
1996                         if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
1997                             # for target which must generate AutoGen code and makefile\r
1998                             if not self.SkipAutoGen or self.Target == 'genc':\r
1999                                 Ma.CreateCodeFile(True)\r
2000                             if self.Target == "genc":\r
2001                                 continue\r
2002 \r
2003                             if not self.SkipAutoGen or self.Target == 'genmake':\r
2004                                 if CmdListDict and self.Fdf and (Module.File, Arch) in CmdListDict:\r
2005                                     Ma.CreateMakeFile(True, CmdListDict[Module.File, Arch])\r
2006                                     del CmdListDict[Module.File, Arch]\r
2007                                 else:\r
2008                                     Ma.CreateMakeFile(True)\r
2009                             if self.Target == "genmake":\r
2010                                 continue\r
2011                         self.BuildModules.append(Ma)\r
2012                         # Initialize all modules in tracking to False (FAIL)\r
2013                         if Ma not in GlobalData.gModuleBuildTracking:\r
2014                             GlobalData.gModuleBuildTracking[Ma] = False\r
2015                     self.Progress.Stop("done!")\r
2016                     self.AutoGenTime += int(round((time.time() - AutoGenStart)))\r
2017                     MakeStart = time.time()\r
2018                     for Ma in self.BuildModules:\r
2019                         # Generate build task for the module\r
2020                         if not Ma.IsBinaryModule:\r
2021                             Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))\r
2022                         # Break build if any build thread has error\r
2023                         if BuildTask.HasError():\r
2024                             # we need a full version of makefile for platform\r
2025                             ExitFlag.set()\r
2026                             BuildTask.WaitForComplete()\r
2027                             self.invalidateHash()\r
2028                             Pa.CreateMakeFile(False)\r
2029                             EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
2030                         # Start task scheduler\r
2031                         if not BuildTask.IsOnGoing():\r
2032                             BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)\r
2033 \r
2034                     # in case there's an interruption. we need a full version of makefile for platform\r
2035                     Pa.CreateMakeFile(False)\r
2036                     if BuildTask.HasError():\r
2037                         self.invalidateHash()\r
2038                         EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
2039                     self.MakeTime += int(round((time.time() - MakeStart)))\r
2040 \r
2041                 MakeContiue = time.time()\r
2042 \r
2043                 #\r
2044                 #\r
2045                 # All modules have been put in build tasks queue. Tell task scheduler\r
2046                 # to exit if all tasks are completed\r
2047                 #\r
2048                 ExitFlag.set()\r
2049                 BuildTask.WaitForComplete()\r
2050                 self.CreateAsBuiltInf()\r
2051                 self.MakeTime += int(round((time.time() - MakeContiue)))\r
2052                 #\r
2053                 # Check for build error, and raise exception if one\r
2054                 # has been signaled.\r
2055                 #\r
2056                 if BuildTask.HasError():\r
2057                     self.invalidateHash()\r
2058                     EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
2059 \r
2060                 # Create MAP file when Load Fix Address is enabled.\r
2061                 if self.Target in ["", "all", "fds"]:\r
2062                     for Arch in Wa.ArchList:\r
2063                         #\r
2064                         # Check whether the set fix address is above 4G for 32bit image.\r
2065                         #\r
2066                         if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
2067                             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
2068                     #\r
2069                     # Get Module List\r
2070                     #\r
2071                     ModuleList = {}\r
2072                     for Pa in Wa.AutoGenObjectList:\r
2073                         for Ma in Pa.ModuleAutoGenList:\r
2074                             if Ma is None:\r
2075                                 continue\r
2076                             if not Ma.IsLibrary:\r
2077                                 ModuleList[Ma.Guid.upper()] = Ma\r
2078                     #\r
2079                     # Rebase module to the preferred memory address before GenFds\r
2080                     #\r
2081                     MapBuffer = []\r
2082                     if self.LoadFixAddress != 0:\r
2083                         self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
2084 \r
2085                     if self.Fdf:\r
2086                         #\r
2087                         # Generate FD image if there's a FDF file found\r
2088                         #\r
2089                         GenFdsStart = time.time()\r
2090                         if GenFdsApi(Wa.GenFdsCommandDict, self.Db):\r
2091                             EdkLogger.error("build", COMMAND_FAILURE)\r
2092 \r
2093                         #\r
2094                         # Create MAP file for all platform FVs after GenFds.\r
2095                         #\r
2096                         self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)\r
2097                         self.GenFdsTime += int(round((time.time() - GenFdsStart)))\r
2098                     #\r
2099                     # Save MAP buffer into MAP file.\r
2100                     #\r
2101                     self._SaveMapFile(MapBuffer, Wa)\r
2102 \r
2103     ## Generate GuidedSectionTools.txt in the FV directories.\r
2104     #\r
2105     def CreateGuidedSectionToolsFile(self):\r
2106         for BuildTarget in self.BuildTargetList:\r
2107             for ToolChain in self.ToolChainList:\r
2108                 Wa = WorkspaceAutoGen(\r
2109                         self.WorkspaceDir,\r
2110                         self.PlatformFile,\r
2111                         BuildTarget,\r
2112                         ToolChain,\r
2113                         self.ArchList,\r
2114                         self.BuildDatabase,\r
2115                         self.TargetTxt,\r
2116                         self.ToolDef,\r
2117                         self.Fdf,\r
2118                         self.FdList,\r
2119                         self.FvList,\r
2120                         self.CapList,\r
2121                         self.SkuId,\r
2122                         self.UniFlag\r
2123                         )\r
2124                 FvDir = Wa.FvDir\r
2125                 if not os.path.exists(FvDir):\r
2126                     continue\r
2127 \r
2128                 for Arch in self.ArchList:\r
2129                     # Build up the list of supported architectures for this build\r
2130                     prefix = '%s_%s_%s_' % (BuildTarget, ToolChain, Arch)\r
2131 \r
2132                     # Look through the tool definitions for GUIDed tools\r
2133                     guidAttribs = []\r
2134                     for (attrib, value) in self.ToolDef.ToolsDefTxtDictionary.items():\r
2135                         if attrib.upper().endswith('_GUID'):\r
2136                             split = attrib.split('_')\r
2137                             thisPrefix = '_'.join(split[0:3]) + '_'\r
2138                             if thisPrefix == prefix:\r
2139                                 guid = self.ToolDef.ToolsDefTxtDictionary[attrib]\r
2140                                 guid = guid.lower()\r
2141                                 toolName = split[3]\r
2142                                 path = '_'.join(split[0:4]) + '_PATH'\r
2143                                 path = self.ToolDef.ToolsDefTxtDictionary[path]\r
2144                                 path = self.GetFullPathOfTool(path)\r
2145                                 guidAttribs.append((guid, toolName, path))\r
2146 \r
2147                     # Write out GuidedSecTools.txt\r
2148                     toolsFile = os.path.join(FvDir, 'GuidedSectionTools.txt')\r
2149                     toolsFile = open(toolsFile, 'wt')\r
2150                     for guidedSectionTool in guidAttribs:\r
2151                         print(' '.join(guidedSectionTool), file=toolsFile)\r
2152                     toolsFile.close()\r
2153 \r
2154     ## Returns the full path of the tool.\r
2155     #\r
2156     def GetFullPathOfTool (self, tool):\r
2157         if os.path.exists(tool):\r
2158             return os.path.realpath(tool)\r
2159         else:\r
2160             # We need to search for the tool using the\r
2161             # PATH environment variable.\r
2162             for dirInPath in os.environ['PATH'].split(os.pathsep):\r
2163                 foundPath = os.path.join(dirInPath, tool)\r
2164                 if os.path.exists(foundPath):\r
2165                     return os.path.realpath(foundPath)\r
2166 \r
2167         # If the tool was not found in the path then we just return\r
2168         # the input tool.\r
2169         return tool\r
2170 \r
2171     ## Launch the module or platform build\r
2172     #\r
2173     def Launch(self):\r
2174         if not self.ModuleFile:\r
2175             if not self.SpawnMode or self.Target not in ["", "all"]:\r
2176                 self.SpawnMode = False\r
2177                 self._BuildPlatform()\r
2178             else:\r
2179                 self._MultiThreadBuildPlatform()\r
2180             self.CreateGuidedSectionToolsFile()\r
2181         else:\r
2182             self.SpawnMode = False\r
2183             self._BuildModule()\r
2184 \r
2185         if self.Target == 'cleanall':\r
2186             RemoveDirectory(os.path.dirname(GlobalData.gDatabasePath), True)\r
2187 \r
2188     def CreateAsBuiltInf(self):\r
2189         all_lib_set = set()\r
2190         all_mod_set = set()\r
2191         for Module in self.BuildModules:\r
2192             Module.CreateAsBuiltInf()\r
2193             all_mod_set.add(Module)\r
2194         for Module in self.HashSkipModules:\r
2195             Module.CreateAsBuiltInf(True)\r
2196             all_mod_set.add(Module)\r
2197         for Module in all_mod_set:\r
2198             for lib in Module.LibraryAutoGenList:\r
2199                 all_lib_set.add(lib)\r
2200         for lib in all_lib_set:\r
2201             lib.CreateAsBuiltInf(True)\r
2202         all_lib_set.clear()\r
2203         all_mod_set.clear()\r
2204         self.BuildModules = []\r
2205         self.HashSkipModules = []\r
2206     ## Do some clean-up works when error occurred\r
2207     def Relinquish(self):\r
2208         OldLogLevel = EdkLogger.GetLevel()\r
2209         EdkLogger.SetLevel(EdkLogger.ERROR)\r
2210         Utils.Progressor.Abort()\r
2211         if self.SpawnMode == True:\r
2212             BuildTask.Abort()\r
2213         EdkLogger.SetLevel(OldLogLevel)\r
2214 \r
2215 def ParseDefines(DefineList=[]):\r
2216     DefineDict = {}\r
2217     if DefineList is not None:\r
2218         for Define in DefineList:\r
2219             DefineTokenList = Define.split("=", 1)\r
2220             if not GlobalData.gMacroNamePattern.match(DefineTokenList[0]):\r
2221                 EdkLogger.error('build', FORMAT_INVALID,\r
2222                                 "The macro name must be in the pattern [A-Z][A-Z0-9_]*",\r
2223                                 ExtraData=DefineTokenList[0])\r
2224 \r
2225             if len(DefineTokenList) == 1:\r
2226                 DefineDict[DefineTokenList[0]] = "TRUE"\r
2227             else:\r
2228                 DefineDict[DefineTokenList[0]] = DefineTokenList[1].strip()\r
2229     return DefineDict\r
2230 \r
2231 gParamCheck = []\r
2232 def SingleCheckCallback(option, opt_str, value, parser):\r
2233     if option not in gParamCheck:\r
2234         setattr(parser.values, option.dest, value)\r
2235         gParamCheck.append(option)\r
2236     else:\r
2237         parser.error("Option %s only allows one instance in command line!" % option)\r
2238 \r
2239 def LogBuildTime(Time):\r
2240     if Time:\r
2241         TimeDurStr = ''\r
2242         TimeDur = time.gmtime(Time)\r
2243         if TimeDur.tm_yday > 1:\r
2244             TimeDurStr = time.strftime("%H:%M:%S", TimeDur) + ", %d day(s)" % (TimeDur.tm_yday - 1)\r
2245         else:\r
2246             TimeDurStr = time.strftime("%H:%M:%S", TimeDur)\r
2247         return TimeDurStr\r
2248     else:\r
2249         return None\r
2250 \r
2251 ## Parse command line options\r
2252 #\r
2253 # Using standard Python module optparse to parse command line option of this tool.\r
2254 #\r
2255 #   @retval Opt   A optparse.Values object containing the parsed options\r
2256 #   @retval Args  Target of build command\r
2257 #\r
2258 def MyOptionParser():\r
2259     Parser = OptionParser(description=__copyright__, version=__version__, prog="build.exe", usage="%prog [options] [all|fds|genc|genmake|clean|cleanall|cleanlib|modules|libraries|run]")\r
2260     Parser.add_option("-a", "--arch", action="append", type="choice", choices=['IA32', 'X64', 'EBC', 'ARM', 'AARCH64'], dest="TargetArch",\r
2261         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
2262     Parser.add_option("-p", "--platform", action="callback", type="string", dest="PlatformFile", callback=SingleCheckCallback,\r
2263         help="Build the platform specified by the DSC file name argument, overriding target.txt's ACTIVE_PLATFORM definition.")\r
2264     Parser.add_option("-m", "--module", action="callback", type="string", dest="ModuleFile", callback=SingleCheckCallback,\r
2265         help="Build the module specified by the INF file name argument.")\r
2266     Parser.add_option("-b", "--buildtarget", type="string", dest="BuildTarget", help="Using the TARGET to build the platform, overriding target.txt's TARGET definition.",\r
2267                       action="append")\r
2268     Parser.add_option("-t", "--tagname", action="append", type="string", dest="ToolChain",\r
2269         help="Using the Tool Chain Tagname to build the platform, overriding target.txt's TOOL_CHAIN_TAG definition.")\r
2270     Parser.add_option("-x", "--sku-id", action="callback", type="string", dest="SkuId", callback=SingleCheckCallback,\r
2271         help="Using this name of SKU ID to build the platform, overriding SKUID_IDENTIFIER in DSC file.")\r
2272 \r
2273     Parser.add_option("-n", action="callback", type="int", dest="ThreadNumber", callback=SingleCheckCallback,\r
2274         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
2275              "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
2276 \r
2277     Parser.add_option("-f", "--fdf", action="callback", type="string", dest="FdfFile", callback=SingleCheckCallback,\r
2278         help="The name of the FDF file to use, which overrides the setting in the DSC file.")\r
2279     Parser.add_option("-r", "--rom-image", action="append", type="string", dest="RomImage", default=[],\r
2280         help="The name of FD to be generated. The name must be from [FD] section in FDF file.")\r
2281     Parser.add_option("-i", "--fv-image", action="append", type="string", dest="FvImage", default=[],\r
2282         help="The name of FV to be generated. The name must be from [FV] section in FDF file.")\r
2283     Parser.add_option("-C", "--capsule-image", action="append", type="string", dest="CapName", default=[],\r
2284         help="The name of Capsule to be generated. The name must be from [Capsule] section in FDF file.")\r
2285     Parser.add_option("-u", "--skip-autogen", action="store_true", dest="SkipAutoGen", help="Skip AutoGen step.")\r
2286     Parser.add_option("-e", "--re-parse", action="store_true", dest="Reparse", help="Re-parse all meta-data files.")\r
2287 \r
2288     Parser.add_option("-c", "--case-insensitive", action="store_true", dest="CaseInsensitive", default=False, help="Don't check case of file name.")\r
2289 \r
2290     Parser.add_option("-w", "--warning-as-error", action="store_true", dest="WarningAsError", help="Treat warning in tools as error.")\r
2291     Parser.add_option("-j", "--log", action="store", dest="LogFile", help="Put log in specified file as well as on console.")\r
2292 \r
2293     Parser.add_option("-s", "--silent", action="store_true", type=None, dest="SilentMode",\r
2294         help="Make use of silent mode of (n)make.")\r
2295     Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")\r
2296     Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\\r
2297                                                                                "including library instances selected, final dependency expression, "\\r
2298                                                                                "and warning messages, etc.")\r
2299     Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")\r
2300     Parser.add_option("-D", "--define", action="append", type="string", dest="Macros", help="Macro: \"Name [= Value]\".")\r
2301 \r
2302     Parser.add_option("-y", "--report-file", action="store", dest="ReportFile", help="Create/overwrite the report to the specified filename.")\r
2303     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
2304         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
2305              "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
2306     Parser.add_option("-F", "--flag", action="store", type="string", dest="Flag",\r
2307         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
2308              "This option can also be specified by setting *_*_*_BUILD_FLAGS in [BuildOptions] section of platform DSC. If they are both specified, this value "\\r
2309              "will override the setting in [BuildOptions] section of platform DSC.")\r
2310     Parser.add_option("-N", "--no-cache", action="store_true", dest="DisableCache", default=False, help="Disable build cache mechanism")\r
2311     Parser.add_option("--conf", action="store", type="string", dest="ConfDirectory", help="Specify the customized Conf directory.")\r
2312     Parser.add_option("--check-usage", action="store_true", dest="CheckUsage", default=False, help="Check usage content of entries listed in INF file.")\r
2313     Parser.add_option("--ignore-sources", action="store_true", dest="IgnoreSources", default=False, help="Focus to a binary build and ignore all source files")\r
2314     Parser.add_option("--pcd", action="append", dest="OptionPcd", help="Set PCD value by command line. Format: \"PcdName=Value\" ")\r
2315     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
2316     Parser.add_option("--hash", action="store_true", dest="UseHashCache", default=False, help="Enable hash-based caching during build process.")\r
2317     Parser.add_option("--binary-destination", action="store", type="string", dest="BinCacheDest", help="Generate a cache of binary files in the specified directory.")\r
2318     Parser.add_option("--binary-source", action="store", type="string", dest="BinCacheSource", help="Consume a cache of binary files from the specified directory.")\r
2319     Parser.add_option("--genfds-multi-thread", action="store_true", dest="GenfdsMultiThread", default=False, help="Enable GenFds multi thread to generate ffs file.")\r
2320     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
2321     (Opt, Args) = Parser.parse_args()\r
2322     return (Opt, Args)\r
2323 \r
2324 ## Tool entrance method\r
2325 #\r
2326 # This method mainly dispatch specific methods per the command line options.\r
2327 # If no error found, return zero value so the caller of this tool can know\r
2328 # if it's executed successfully or not.\r
2329 #\r
2330 #   @retval 0     Tool was successful\r
2331 #   @retval 1     Tool failed\r
2332 #\r
2333 def Main():\r
2334     StartTime = time.time()\r
2335 \r
2336     # Initialize log system\r
2337     EdkLogger.Initialize()\r
2338     GlobalData.gCommand = sys.argv[1:]\r
2339     #\r
2340     # Parse the options and args\r
2341     #\r
2342     (Option, Target) = MyOptionParser()\r
2343     GlobalData.gOptions = Option\r
2344     GlobalData.gCaseInsensitive = Option.CaseInsensitive\r
2345 \r
2346     # Set log level\r
2347     if Option.verbose is not None:\r
2348         EdkLogger.SetLevel(EdkLogger.VERBOSE)\r
2349     elif Option.quiet is not None:\r
2350         EdkLogger.SetLevel(EdkLogger.QUIET)\r
2351     elif Option.debug is not None:\r
2352         EdkLogger.SetLevel(Option.debug + 1)\r
2353     else:\r
2354         EdkLogger.SetLevel(EdkLogger.INFO)\r
2355 \r
2356     if Option.LogFile is not None:\r
2357         EdkLogger.SetLogFile(Option.LogFile)\r
2358 \r
2359     if Option.WarningAsError == True:\r
2360         EdkLogger.SetWarningAsError()\r
2361 \r
2362     if platform.platform().find("Windows") >= 0:\r
2363         GlobalData.gIsWindows = True\r
2364     else:\r
2365         GlobalData.gIsWindows = False\r
2366 \r
2367     EdkLogger.quiet("Build environment: %s" % platform.platform())\r
2368     EdkLogger.quiet(time.strftime("Build start time: %H:%M:%S, %b.%d %Y\n", time.localtime()));\r
2369     ReturnCode = 0\r
2370     MyBuild = None\r
2371     BuildError = True\r
2372     try:\r
2373         if len(Target) == 0:\r
2374             Target = "all"\r
2375         elif len(Target) >= 2:\r
2376             EdkLogger.error("build", OPTION_NOT_SUPPORTED, "More than one targets are not supported.",\r
2377                             ExtraData="Please select one of: %s" % (' '.join(gSupportedTarget)))\r
2378         else:\r
2379             Target = Target[0].lower()\r
2380 \r
2381         if Target not in gSupportedTarget:\r
2382             EdkLogger.error("build", OPTION_NOT_SUPPORTED, "Not supported target [%s]." % Target,\r
2383                             ExtraData="Please select one of: %s" % (' '.join(gSupportedTarget)))\r
2384 \r
2385         #\r
2386         # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH\r
2387         #\r
2388         CheckEnvVariable()\r
2389         GlobalData.gCommandLineDefines.update(ParseDefines(Option.Macros))\r
2390 \r
2391         Workspace = os.getenv("WORKSPACE")\r
2392         #\r
2393         # Get files real name in workspace dir\r
2394         #\r
2395         GlobalData.gAllFiles = Utils.DirCache(Workspace)\r
2396 \r
2397         WorkingDirectory = os.getcwd()\r
2398         if not Option.ModuleFile:\r
2399             FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.inf')))\r
2400             FileNum = len(FileList)\r
2401             if FileNum >= 2:\r
2402                 EdkLogger.error("build", OPTION_NOT_SUPPORTED, "There are %d INF files in %s." % (FileNum, WorkingDirectory),\r
2403                                 ExtraData="Please use '-m <INF_FILE_PATH>' switch to choose one.")\r
2404             elif FileNum == 1:\r
2405                 Option.ModuleFile = NormFile(FileList[0], Workspace)\r
2406 \r
2407         if Option.ModuleFile:\r
2408             if os.path.isabs (Option.ModuleFile):\r
2409                 if os.path.normcase (os.path.normpath(Option.ModuleFile)).find (Workspace) == 0:\r
2410                     Option.ModuleFile = NormFile(os.path.normpath(Option.ModuleFile), Workspace)\r
2411             Option.ModuleFile = PathClass(Option.ModuleFile, Workspace)\r
2412             ErrorCode, ErrorInfo = Option.ModuleFile.Validate(".inf", False)\r
2413             if ErrorCode != 0:\r
2414                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
2415 \r
2416         if Option.PlatformFile is not None:\r
2417             if os.path.isabs (Option.PlatformFile):\r
2418                 if os.path.normcase (os.path.normpath(Option.PlatformFile)).find (Workspace) == 0:\r
2419                     Option.PlatformFile = NormFile(os.path.normpath(Option.PlatformFile), Workspace)\r
2420             Option.PlatformFile = PathClass(Option.PlatformFile, Workspace)\r
2421 \r
2422         if Option.FdfFile is not None:\r
2423             if os.path.isabs (Option.FdfFile):\r
2424                 if os.path.normcase (os.path.normpath(Option.FdfFile)).find (Workspace) == 0:\r
2425                     Option.FdfFile = NormFile(os.path.normpath(Option.FdfFile), Workspace)\r
2426             Option.FdfFile = PathClass(Option.FdfFile, Workspace)\r
2427             ErrorCode, ErrorInfo = Option.FdfFile.Validate(".fdf", False)\r
2428             if ErrorCode != 0:\r
2429                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
2430 \r
2431         if Option.Flag is not None and Option.Flag not in ['-c', '-s']:\r
2432             EdkLogger.error("build", OPTION_VALUE_INVALID, "UNI flag must be one of -c or -s")\r
2433 \r
2434         MyBuild = Build(Target, Workspace, Option)\r
2435         GlobalData.gCommandLineDefines['ARCH'] = ' '.join(MyBuild.ArchList)\r
2436         if not (MyBuild.LaunchPrebuildFlag and os.path.exists(MyBuild.PlatformBuildPath)):\r
2437             MyBuild.Launch()\r
2438 \r
2439         #\r
2440         # All job done, no error found and no exception raised\r
2441         #\r
2442         BuildError = False\r
2443     except FatalError as X:\r
2444         if MyBuild is not None:\r
2445             # for multi-thread build exits safely\r
2446             MyBuild.Relinquish()\r
2447         if Option is not None and Option.debug is not None:\r
2448             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
2449         ReturnCode = X.args[0]\r
2450     except Warning as X:\r
2451         # error from Fdf parser\r
2452         if MyBuild is not None:\r
2453             # for multi-thread build exits safely\r
2454             MyBuild.Relinquish()\r
2455         if Option is not None and Option.debug is not None:\r
2456             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
2457         else:\r
2458             EdkLogger.error(X.ToolName, FORMAT_INVALID, File=X.FileName, Line=X.LineNumber, ExtraData=X.Message, RaiseError=False)\r
2459         ReturnCode = FORMAT_INVALID\r
2460     except KeyboardInterrupt:\r
2461         ReturnCode = ABORT_ERROR\r
2462         if Option is not None and Option.debug is not None:\r
2463             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
2464     except:\r
2465         if MyBuild is not None:\r
2466             # for multi-thread build exits safely\r
2467             MyBuild.Relinquish()\r
2468 \r
2469         # try to get the meta-file from the object causing exception\r
2470         Tb = sys.exc_info()[-1]\r
2471         MetaFile = GlobalData.gProcessingFile\r
2472         while Tb is not None:\r
2473             if 'self' in Tb.tb_frame.f_locals and hasattr(Tb.tb_frame.f_locals['self'], 'MetaFile'):\r
2474                 MetaFile = Tb.tb_frame.f_locals['self'].MetaFile\r
2475             Tb = Tb.tb_next\r
2476         EdkLogger.error(\r
2477                     "\nbuild",\r
2478                     CODE_ERROR,\r
2479                     "Unknown fatal error when processing [%s]" % MetaFile,\r
2480                     ExtraData="\n(Please send email to %s for help, attaching following call stack trace!)\n" % MSG_EDKII_MAIL_ADDR,\r
2481                     RaiseError=False\r
2482                     )\r
2483         EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
2484         ReturnCode = CODE_ERROR\r
2485     finally:\r
2486         Utils.Progressor.Abort()\r
2487         Utils.ClearDuplicatedInf()\r
2488 \r
2489     if ReturnCode == 0:\r
2490         try:\r
2491             MyBuild.LaunchPostbuild()\r
2492             Conclusion = "Done"\r
2493         except:\r
2494             Conclusion = "Failed"\r
2495     elif ReturnCode == ABORT_ERROR:\r
2496         Conclusion = "Aborted"\r
2497     else:\r
2498         Conclusion = "Failed"\r
2499     FinishTime = time.time()\r
2500     BuildDuration = time.gmtime(int(round(FinishTime - StartTime)))\r
2501     BuildDurationStr = ""\r
2502     if BuildDuration.tm_yday > 1:\r
2503         BuildDurationStr = time.strftime("%H:%M:%S", BuildDuration) + ", %d day(s)" % (BuildDuration.tm_yday - 1)\r
2504     else:\r
2505         BuildDurationStr = time.strftime("%H:%M:%S", BuildDuration)\r
2506     if MyBuild is not None:\r
2507         if not BuildError:\r
2508             MyBuild.BuildReport.GenerateReport(BuildDurationStr, LogBuildTime(MyBuild.AutoGenTime), LogBuildTime(MyBuild.MakeTime), LogBuildTime(MyBuild.GenFdsTime))\r
2509 \r
2510     EdkLogger.SetLevel(EdkLogger.QUIET)\r
2511     EdkLogger.quiet("\n- %s -" % Conclusion)\r
2512     EdkLogger.quiet(time.strftime("Build end time: %H:%M:%S, %b.%d %Y", time.localtime()))\r
2513     EdkLogger.quiet("Build total time: %s\n" % BuildDurationStr)\r
2514     return ReturnCode\r
2515 \r
2516 if __name__ == '__main__':\r
2517     r = Main()\r
2518     ## 0-127 is a safe return range, and 1 is a standard default error\r
2519     if r < 0 or r > 127: r = 1\r
2520     sys.exit(r)\r
2521 \r