7271570d2956db80e50e6667b1aa2f6d0d6baf07
[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:\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         except:\r
609             #\r
610             # TRICK: hide the output of threads left running, so that the user can\r
611             #        catch the error message easily\r
612             #\r
613             if not BuildTask._ErrorFlag.isSet():\r
614                 GlobalData.gBuildingModule = "%s [%s, %s, %s]" % (str(self.BuildItem.BuildObject),\r
615                                                                   self.BuildItem.BuildObject.Arch,\r
616                                                                   self.BuildItem.BuildObject.ToolChain,\r
617                                                                   self.BuildItem.BuildObject.BuildTarget\r
618                                                                  )\r
619             EdkLogger.SetLevel(EdkLogger.ERROR)\r
620             BuildTask._ErrorFlag.set()\r
621             BuildTask._ErrorMessage = "%s broken\n    %s [%s]" % \\r
622                                       (threading.currentThread().getName(), Command, WorkingDir)\r
623         if self.BuildItem.BuildObject in GlobalData.gModuleBuildTracking and not BuildTask._ErrorFlag.isSet():\r
624             GlobalData.gModuleBuildTracking[self.BuildItem.BuildObject] = True\r
625         # indicate there's a thread is available for another build task\r
626         BuildTask._RunningQueueLock.acquire()\r
627         BuildTask._RunningQueue.pop(self.BuildItem)\r
628         BuildTask._RunningQueueLock.release()\r
629         BuildTask._Thread.release()\r
630 \r
631     ## Start build task thread\r
632     #\r
633     def Start(self):\r
634         EdkLogger.quiet("Building ... %s" % repr(self.BuildItem))\r
635         Command = self.BuildItem.BuildCommand + [self.BuildItem.Target]\r
636         self.BuildTread = Thread(target=self._CommandThread, args=(Command, self.BuildItem.WorkingDir))\r
637         self.BuildTread.setName("build thread")\r
638         self.BuildTread.setDaemon(False)\r
639         self.BuildTread.start()\r
640 \r
641 ## The class contains the information related to EFI image\r
642 #\r
643 class PeImageInfo():\r
644     ## Constructor\r
645     #\r
646     # Constructor will load all required image information.\r
647     #\r
648     #   @param  BaseName          The full file path of image.\r
649     #   @param  Guid              The GUID for image.\r
650     #   @param  Arch              Arch of this image.\r
651     #   @param  OutputDir         The output directory for image.\r
652     #   @param  DebugDir          The debug directory for image.\r
653     #   @param  ImageClass        PeImage Information\r
654     #\r
655     def __init__(self, BaseName, Guid, Arch, OutputDir, DebugDir, ImageClass):\r
656         self.BaseName         = BaseName\r
657         self.Guid             = Guid\r
658         self.Arch             = Arch\r
659         self.OutputDir        = OutputDir\r
660         self.DebugDir         = DebugDir\r
661         self.Image            = ImageClass\r
662         self.Image.Size       = (self.Image.Size // 0x1000 + 1) * 0x1000\r
663 \r
664 ## The class implementing the EDK2 build process\r
665 #\r
666 #   The build process includes:\r
667 #       1. Load configuration from target.txt and tools_def.txt in $(WORKSPACE)/Conf\r
668 #       2. Parse DSC file of active platform\r
669 #       3. Parse FDF file if any\r
670 #       4. Establish build database, including parse all other files (module, package)\r
671 #       5. Create AutoGen files (C code file, depex file, makefile) if necessary\r
672 #       6. Call build command\r
673 #\r
674 class Build():\r
675     ## Constructor\r
676     #\r
677     # Constructor will load all necessary configurations, parse platform, modules\r
678     # and packages and the establish a database for AutoGen.\r
679     #\r
680     #   @param  Target              The build command target, one of gSupportedTarget\r
681     #   @param  WorkspaceDir        The directory of workspace\r
682     #   @param  BuildOptions        Build options passed from command line\r
683     #\r
684     def __init__(self, Target, WorkspaceDir, BuildOptions):\r
685         self.WorkspaceDir   = WorkspaceDir\r
686         self.Target         = Target\r
687         self.PlatformFile   = BuildOptions.PlatformFile\r
688         self.ModuleFile     = BuildOptions.ModuleFile\r
689         self.ArchList       = BuildOptions.TargetArch\r
690         self.ToolChainList  = BuildOptions.ToolChain\r
691         self.BuildTargetList= BuildOptions.BuildTarget\r
692         self.Fdf            = BuildOptions.FdfFile\r
693         self.FdList         = BuildOptions.RomImage\r
694         self.FvList         = BuildOptions.FvImage\r
695         self.CapList        = BuildOptions.CapName\r
696         self.SilentMode     = BuildOptions.SilentMode\r
697         self.ThreadNumber   = BuildOptions.ThreadNumber\r
698         self.SkipAutoGen    = BuildOptions.SkipAutoGen\r
699         self.Reparse        = BuildOptions.Reparse\r
700         self.SkuId          = BuildOptions.SkuId\r
701         if self.SkuId:\r
702             GlobalData.gSKUID_CMD = self.SkuId\r
703         self.ConfDirectory = BuildOptions.ConfDirectory\r
704         self.SpawnMode      = True\r
705         self.BuildReport    = BuildReport(BuildOptions.ReportFile, BuildOptions.ReportType)\r
706         self.TargetTxt      = TargetTxtClassObject()\r
707         self.ToolDef        = ToolDefClassObject()\r
708         self.AutoGenTime    = 0\r
709         self.MakeTime       = 0\r
710         self.GenFdsTime     = 0\r
711         GlobalData.BuildOptionPcd     = BuildOptions.OptionPcd if BuildOptions.OptionPcd else []\r
712         #Set global flag for build mode\r
713         GlobalData.gIgnoreSource = BuildOptions.IgnoreSources\r
714         GlobalData.gUseHashCache = BuildOptions.UseHashCache\r
715         GlobalData.gBinCacheDest   = BuildOptions.BinCacheDest\r
716         GlobalData.gBinCacheSource = BuildOptions.BinCacheSource\r
717         GlobalData.gEnableGenfdsMultiThread = BuildOptions.GenfdsMultiThread\r
718         GlobalData.gDisableIncludePathCheck = BuildOptions.DisableIncludePathCheck\r
719 \r
720         if GlobalData.gBinCacheDest and not GlobalData.gUseHashCache:\r
721             EdkLogger.error("build", OPTION_NOT_SUPPORTED, ExtraData="--binary-destination must be used together with --hash.")\r
722 \r
723         if GlobalData.gBinCacheSource and not GlobalData.gUseHashCache:\r
724             EdkLogger.error("build", OPTION_NOT_SUPPORTED, ExtraData="--binary-source must be used together with --hash.")\r
725 \r
726         if GlobalData.gBinCacheDest and GlobalData.gBinCacheSource:\r
727             EdkLogger.error("build", OPTION_NOT_SUPPORTED, ExtraData="--binary-destination can not be used together with --binary-source.")\r
728 \r
729         if GlobalData.gBinCacheSource:\r
730             BinCacheSource = os.path.normpath(GlobalData.gBinCacheSource)\r
731             if not os.path.isabs(BinCacheSource):\r
732                 BinCacheSource = mws.join(self.WorkspaceDir, BinCacheSource)\r
733             GlobalData.gBinCacheSource = BinCacheSource\r
734         else:\r
735             if GlobalData.gBinCacheSource is not None:\r
736                 EdkLogger.error("build", OPTION_VALUE_INVALID, ExtraData="Invalid value of option --binary-source.")\r
737 \r
738         if GlobalData.gBinCacheDest:\r
739             BinCacheDest = os.path.normpath(GlobalData.gBinCacheDest)\r
740             if not os.path.isabs(BinCacheDest):\r
741                 BinCacheDest = mws.join(self.WorkspaceDir, BinCacheDest)\r
742             GlobalData.gBinCacheDest = BinCacheDest\r
743         else:\r
744             if GlobalData.gBinCacheDest is not None:\r
745                 EdkLogger.error("build", OPTION_VALUE_INVALID, ExtraData="Invalid value of option --binary-destination.")\r
746 \r
747         if self.ConfDirectory:\r
748             # Get alternate Conf location, if it is absolute, then just use the absolute directory name\r
749             ConfDirectoryPath = os.path.normpath(self.ConfDirectory)\r
750 \r
751             if not os.path.isabs(ConfDirectoryPath):\r
752                 # Since alternate directory name is not absolute, the alternate directory is located within the WORKSPACE\r
753                 # This also handles someone specifying the Conf directory in the workspace. Using --conf=Conf\r
754                 ConfDirectoryPath = mws.join(self.WorkspaceDir, ConfDirectoryPath)\r
755         else:\r
756             if "CONF_PATH" in os.environ:\r
757                 ConfDirectoryPath = os.path.normcase(os.path.normpath(os.environ["CONF_PATH"]))\r
758             else:\r
759                 # Get standard WORKSPACE/Conf use the absolute path to the WORKSPACE/Conf\r
760                 ConfDirectoryPath = mws.join(self.WorkspaceDir, 'Conf')\r
761         GlobalData.gConfDirectory = ConfDirectoryPath\r
762         GlobalData.gDatabasePath = os.path.normpath(os.path.join(ConfDirectoryPath, GlobalData.gDatabasePath))\r
763 \r
764         self.Db = WorkspaceDatabase()\r
765         self.BuildDatabase = self.Db.BuildObject\r
766         self.Platform = None\r
767         self.ToolChainFamily = None\r
768         self.LoadFixAddress = 0\r
769         self.UniFlag        = BuildOptions.Flag\r
770         self.BuildModules = []\r
771         self.HashSkipModules = []\r
772         self.Db_Flag = False\r
773         self.LaunchPrebuildFlag = False\r
774         self.PlatformBuildPath = os.path.join(GlobalData.gConfDirectory, '.cache', '.PlatformBuild')\r
775         if BuildOptions.CommandLength:\r
776             GlobalData.gCommandMaxLength = BuildOptions.CommandLength\r
777 \r
778         # print dot character during doing some time-consuming work\r
779         self.Progress = Utils.Progressor()\r
780         # print current build environment and configuration\r
781         EdkLogger.quiet("%-16s = %s" % ("WORKSPACE", os.environ["WORKSPACE"]))\r
782         if "PACKAGES_PATH" in os.environ:\r
783             # WORKSPACE env has been converted before. Print the same path style with WORKSPACE env.\r
784             EdkLogger.quiet("%-16s = %s" % ("PACKAGES_PATH", os.path.normcase(os.path.normpath(os.environ["PACKAGES_PATH"]))))\r
785         EdkLogger.quiet("%-16s = %s" % ("EDK_TOOLS_PATH", os.environ["EDK_TOOLS_PATH"]))\r
786         if "EDK_TOOLS_BIN" in os.environ:\r
787             # Print the same path style with WORKSPACE env.\r
788             EdkLogger.quiet("%-16s = %s" % ("EDK_TOOLS_BIN", os.path.normcase(os.path.normpath(os.environ["EDK_TOOLS_BIN"]))))\r
789         EdkLogger.quiet("%-16s = %s" % ("CONF_PATH", GlobalData.gConfDirectory))\r
790         if "PYTHON3_ENABLE" in os.environ:\r
791             PYTHON3_ENABLE = os.environ["PYTHON3_ENABLE"]\r
792             if PYTHON3_ENABLE != "TRUE":\r
793                 PYTHON3_ENABLE = "FALSE"\r
794             EdkLogger.quiet("%-16s = %s" % ("PYTHON3_ENABLE", PYTHON3_ENABLE))\r
795         if "PYTHON_COMMAND" in os.environ:\r
796             EdkLogger.quiet("%-16s = %s" % ("PYTHON_COMMAND", os.environ["PYTHON_COMMAND"]))\r
797         self.InitPreBuild()\r
798         self.InitPostBuild()\r
799         if self.Prebuild:\r
800             EdkLogger.quiet("%-16s = %s" % ("PREBUILD", self.Prebuild))\r
801         if self.Postbuild:\r
802             EdkLogger.quiet("%-16s = %s" % ("POSTBUILD", self.Postbuild))\r
803         if self.Prebuild:\r
804             self.LaunchPrebuild()\r
805             self.TargetTxt = TargetTxtClassObject()\r
806             self.ToolDef   = ToolDefClassObject()\r
807         if not (self.LaunchPrebuildFlag and os.path.exists(self.PlatformBuildPath)):\r
808             self.InitBuild()\r
809 \r
810         EdkLogger.info("")\r
811         os.chdir(self.WorkspaceDir)\r
812 \r
813     ## Load configuration\r
814     #\r
815     #   This method will parse target.txt and get the build configurations.\r
816     #\r
817     def LoadConfiguration(self):\r
818         #\r
819         # Check target.txt and tools_def.txt and Init them\r
820         #\r
821         BuildConfigurationFile = os.path.normpath(os.path.join(GlobalData.gConfDirectory, gBuildConfiguration))\r
822         if os.path.isfile(BuildConfigurationFile) == True:\r
823             StatusCode = self.TargetTxt.LoadTargetTxtFile(BuildConfigurationFile)\r
824 \r
825             ToolDefinitionFile = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_TOOL_CHAIN_CONF]\r
826             if ToolDefinitionFile == '':\r
827                 ToolDefinitionFile = gToolsDefinition\r
828                 ToolDefinitionFile = os.path.normpath(mws.join(self.WorkspaceDir, 'Conf', ToolDefinitionFile))\r
829             if os.path.isfile(ToolDefinitionFile) == True:\r
830                 StatusCode = self.ToolDef.LoadToolDefFile(ToolDefinitionFile)\r
831             else:\r
832                 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=ToolDefinitionFile)\r
833         else:\r
834             EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=BuildConfigurationFile)\r
835 \r
836         # if no ARCH given in command line, get it from target.txt\r
837         if not self.ArchList:\r
838             self.ArchList = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_TARGET_ARCH]\r
839         self.ArchList = tuple(self.ArchList)\r
840 \r
841         # if no build target given in command line, get it from target.txt\r
842         if not self.BuildTargetList:\r
843             self.BuildTargetList = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_TARGET]\r
844 \r
845         # if no tool chain given in command line, get it from target.txt\r
846         if not self.ToolChainList:\r
847             self.ToolChainList = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_TOOL_CHAIN_TAG]\r
848             if self.ToolChainList is None or len(self.ToolChainList) == 0:\r
849                 EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, ExtraData="No toolchain given. Don't know how to build.\n")\r
850 \r
851         # check if the tool chains are defined or not\r
852         NewToolChainList = []\r
853         for ToolChain in self.ToolChainList:\r
854             if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG]:\r
855                 EdkLogger.warn("build", "Tool chain [%s] is not defined" % ToolChain)\r
856             else:\r
857                 NewToolChainList.append(ToolChain)\r
858         # if no tool chain available, break the build\r
859         if len(NewToolChainList) == 0:\r
860             EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,\r
861                             ExtraData="[%s] not defined. No toolchain available for build!\n" % ", ".join(self.ToolChainList))\r
862         else:\r
863             self.ToolChainList = NewToolChainList\r
864 \r
865         ToolChainFamily = []\r
866         ToolDefinition = self.ToolDef.ToolsDefTxtDatabase\r
867         for Tool in self.ToolChainList:\r
868             if TAB_TOD_DEFINES_FAMILY not in ToolDefinition or Tool not in ToolDefinition[TAB_TOD_DEFINES_FAMILY] \\r
869                or not ToolDefinition[TAB_TOD_DEFINES_FAMILY][Tool]:\r
870                 EdkLogger.warn("build", "No tool chain family found in configuration for %s. Default to MSFT." % Tool)\r
871                 ToolChainFamily.append(TAB_COMPILER_MSFT)\r
872             else:\r
873                 ToolChainFamily.append(ToolDefinition[TAB_TOD_DEFINES_FAMILY][Tool])\r
874         self.ToolChainFamily = ToolChainFamily\r
875 \r
876         if self.ThreadNumber is None:\r
877             self.ThreadNumber = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER]\r
878             if self.ThreadNumber == '':\r
879                 self.ThreadNumber = 0\r
880             else:\r
881                 self.ThreadNumber = int(self.ThreadNumber, 0)\r
882 \r
883         if self.ThreadNumber == 0:\r
884             try:\r
885                 self.ThreadNumber = multiprocessing.cpu_count()\r
886             except (ImportError, NotImplementedError):\r
887                 self.ThreadNumber = 1\r
888 \r
889         if not self.PlatformFile:\r
890             PlatformFile = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_ACTIVE_PLATFORM]\r
891             if not PlatformFile:\r
892                 # Try to find one in current directory\r
893                 WorkingDirectory = os.getcwd()\r
894                 FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.dsc')))\r
895                 FileNum = len(FileList)\r
896                 if FileNum >= 2:\r
897                     EdkLogger.error("build", OPTION_MISSING,\r
898                                     ExtraData="There are %d DSC files in %s. Use '-p' to specify one.\n" % (FileNum, WorkingDirectory))\r
899                 elif FileNum == 1:\r
900                     PlatformFile = FileList[0]\r
901                 else:\r
902                     EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,\r
903                                     ExtraData="No active platform specified in target.txt or command line! Nothing can be built.\n")\r
904 \r
905             self.PlatformFile = PathClass(NormFile(PlatformFile, self.WorkspaceDir), self.WorkspaceDir)\r
906 \r
907     ## Initialize build configuration\r
908     #\r
909     #   This method will parse DSC file and merge the configurations from\r
910     #   command line and target.txt, then get the final build configurations.\r
911     #\r
912     def InitBuild(self):\r
913         # parse target.txt, tools_def.txt, and platform file\r
914         self.LoadConfiguration()\r
915 \r
916         # Allow case-insensitive for those from command line or configuration file\r
917         ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False)\r
918         if ErrorCode != 0:\r
919             EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
920 \r
921 \r
922     def InitPreBuild(self):\r
923         self.LoadConfiguration()\r
924         ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False)\r
925         if ErrorCode != 0:\r
926             EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
927         if self.BuildTargetList:\r
928             GlobalData.gGlobalDefines['TARGET'] = self.BuildTargetList[0]\r
929         if self.ArchList:\r
930             GlobalData.gGlobalDefines['ARCH'] = self.ArchList[0]\r
931         if self.ToolChainList:\r
932             GlobalData.gGlobalDefines['TOOLCHAIN'] = self.ToolChainList[0]\r
933             GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = self.ToolChainList[0]\r
934         if self.ToolChainFamily:\r
935             GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[0]\r
936         if 'PREBUILD' in GlobalData.gCommandLineDefines:\r
937             self.Prebuild   = GlobalData.gCommandLineDefines.get('PREBUILD')\r
938         else:\r
939             self.Db_Flag = True\r
940             Platform = self.Db.MapPlatform(str(self.PlatformFile))\r
941             self.Prebuild = str(Platform.Prebuild)\r
942         if self.Prebuild:\r
943             PrebuildList = []\r
944             #\r
945             # Evaluate all arguments and convert arguments that are WORKSPACE\r
946             # relative paths to absolute paths.  Filter arguments that look like\r
947             # flags or do not follow the file/dir naming rules to avoid false\r
948             # positives on this conversion.\r
949             #\r
950             for Arg in self.Prebuild.split():\r
951                 #\r
952                 # Do not modify Arg if it looks like a flag or an absolute file path\r
953                 #\r
954                 if Arg.startswith('-')  or os.path.isabs(Arg):\r
955                     PrebuildList.append(Arg)\r
956                     continue\r
957                 #\r
958                 # Do not modify Arg if it does not look like a Workspace relative\r
959                 # path that starts with a valid package directory name\r
960                 #\r
961                 if not Arg[0].isalpha() or os.path.dirname(Arg) == '':\r
962                     PrebuildList.append(Arg)\r
963                     continue\r
964                 #\r
965                 # If Arg looks like a WORKSPACE relative path, then convert to an\r
966                 # absolute path and check to see if the file exists.\r
967                 #\r
968                 Temp = mws.join(self.WorkspaceDir, Arg)\r
969                 if os.path.isfile(Temp):\r
970                     Arg = Temp\r
971                 PrebuildList.append(Arg)\r
972             self.Prebuild       = ' '.join(PrebuildList)\r
973             self.Prebuild += self.PassCommandOption(self.BuildTargetList, self.ArchList, self.ToolChainList, self.PlatformFile, self.Target)\r
974 \r
975     def InitPostBuild(self):\r
976         if 'POSTBUILD' in GlobalData.gCommandLineDefines:\r
977             self.Postbuild = GlobalData.gCommandLineDefines.get('POSTBUILD')\r
978         else:\r
979             Platform = self.Db.MapPlatform(str(self.PlatformFile))\r
980             self.Postbuild = str(Platform.Postbuild)\r
981         if self.Postbuild:\r
982             PostbuildList = []\r
983             #\r
984             # Evaluate all arguments and convert arguments that are WORKSPACE\r
985             # relative paths to absolute paths.  Filter arguments that look like\r
986             # flags or do not follow the file/dir naming rules to avoid false\r
987             # positives on this conversion.\r
988             #\r
989             for Arg in self.Postbuild.split():\r
990                 #\r
991                 # Do not modify Arg if it looks like a flag or an absolute file path\r
992                 #\r
993                 if Arg.startswith('-')  or os.path.isabs(Arg):\r
994                     PostbuildList.append(Arg)\r
995                     continue\r
996                 #\r
997                 # Do not modify Arg if it does not look like a Workspace relative\r
998                 # path that starts with a valid package directory name\r
999                 #\r
1000                 if not Arg[0].isalpha() or os.path.dirname(Arg) == '':\r
1001                     PostbuildList.append(Arg)\r
1002                     continue\r
1003                 #\r
1004                 # If Arg looks like a WORKSPACE relative path, then convert to an\r
1005                 # absolute path and check to see if the file exists.\r
1006                 #\r
1007                 Temp = mws.join(self.WorkspaceDir, Arg)\r
1008                 if os.path.isfile(Temp):\r
1009                     Arg = Temp\r
1010                 PostbuildList.append(Arg)\r
1011             self.Postbuild       = ' '.join(PostbuildList)\r
1012             self.Postbuild += self.PassCommandOption(self.BuildTargetList, self.ArchList, self.ToolChainList, self.PlatformFile, self.Target)\r
1013 \r
1014     def PassCommandOption(self, BuildTarget, TargetArch, ToolChain, PlatformFile, Target):\r
1015         BuildStr = ''\r
1016         if GlobalData.gCommand and isinstance(GlobalData.gCommand, list):\r
1017             BuildStr += ' ' + ' '.join(GlobalData.gCommand)\r
1018         TargetFlag = False\r
1019         ArchFlag = False\r
1020         ToolChainFlag = False\r
1021         PlatformFileFlag = False\r
1022 \r
1023         if GlobalData.gOptions and not GlobalData.gOptions.BuildTarget:\r
1024             TargetFlag = True\r
1025         if GlobalData.gOptions and not GlobalData.gOptions.TargetArch:\r
1026             ArchFlag = True\r
1027         if GlobalData.gOptions and not GlobalData.gOptions.ToolChain:\r
1028             ToolChainFlag = True\r
1029         if GlobalData.gOptions and not GlobalData.gOptions.PlatformFile:\r
1030             PlatformFileFlag = True\r
1031 \r
1032         if TargetFlag and BuildTarget:\r
1033             if isinstance(BuildTarget, list) or isinstance(BuildTarget, tuple):\r
1034                 BuildStr += ' -b ' + ' -b '.join(BuildTarget)\r
1035             elif isinstance(BuildTarget, str):\r
1036                 BuildStr += ' -b ' + BuildTarget\r
1037         if ArchFlag and TargetArch:\r
1038             if isinstance(TargetArch, list) or isinstance(TargetArch, tuple):\r
1039                 BuildStr += ' -a ' + ' -a '.join(TargetArch)\r
1040             elif isinstance(TargetArch, str):\r
1041                 BuildStr += ' -a ' + TargetArch\r
1042         if ToolChainFlag and ToolChain:\r
1043             if isinstance(ToolChain, list) or isinstance(ToolChain, tuple):\r
1044                 BuildStr += ' -t ' + ' -t '.join(ToolChain)\r
1045             elif isinstance(ToolChain, str):\r
1046                 BuildStr += ' -t ' + ToolChain\r
1047         if PlatformFileFlag and PlatformFile:\r
1048             if isinstance(PlatformFile, list) or isinstance(PlatformFile, tuple):\r
1049                 BuildStr += ' -p ' + ' -p '.join(PlatformFile)\r
1050             elif isinstance(PlatformFile, str):\r
1051                 BuildStr += ' -p' + PlatformFile\r
1052         BuildStr += ' --conf=' + GlobalData.gConfDirectory\r
1053         if Target:\r
1054             BuildStr += ' ' + Target\r
1055 \r
1056         return BuildStr\r
1057 \r
1058     def LaunchPrebuild(self):\r
1059         if self.Prebuild:\r
1060             EdkLogger.info("\n- Prebuild Start -\n")\r
1061             self.LaunchPrebuildFlag = True\r
1062             #\r
1063             # The purpose of .PrebuildEnv file is capture environment variable settings set by the prebuild script\r
1064             # and preserve them for the rest of the main build step, because the child process environment will\r
1065             # evaporate as soon as it exits, we cannot get it in build step.\r
1066             #\r
1067             PrebuildEnvFile = os.path.join(GlobalData.gConfDirectory, '.cache', '.PrebuildEnv')\r
1068             if os.path.isfile(PrebuildEnvFile):\r
1069                 os.remove(PrebuildEnvFile)\r
1070             if os.path.isfile(self.PlatformBuildPath):\r
1071                 os.remove(self.PlatformBuildPath)\r
1072             if sys.platform == "win32":\r
1073                 args = ' && '.join((self.Prebuild, 'set > ' + PrebuildEnvFile))\r
1074                 Process = Popen(args, stdout=PIPE, stderr=PIPE, shell=True)\r
1075             else:\r
1076                 args = ' && '.join((self.Prebuild, 'env > ' + PrebuildEnvFile))\r
1077                 Process = Popen(args, stdout=PIPE, stderr=PIPE, shell=True)\r
1078 \r
1079             # launch two threads to read the STDOUT and STDERR\r
1080             EndOfProcedure = Event()\r
1081             EndOfProcedure.clear()\r
1082             if Process.stdout:\r
1083                 StdOutThread = Thread(target=ReadMessage, args=(Process.stdout, EdkLogger.info, EndOfProcedure))\r
1084                 StdOutThread.setName("STDOUT-Redirector")\r
1085                 StdOutThread.setDaemon(False)\r
1086                 StdOutThread.start()\r
1087 \r
1088             if Process.stderr:\r
1089                 StdErrThread = Thread(target=ReadMessage, args=(Process.stderr, EdkLogger.quiet, EndOfProcedure))\r
1090                 StdErrThread.setName("STDERR-Redirector")\r
1091                 StdErrThread.setDaemon(False)\r
1092                 StdErrThread.start()\r
1093             # waiting for program exit\r
1094             Process.wait()\r
1095 \r
1096             if Process.stdout:\r
1097                 StdOutThread.join()\r
1098             if Process.stderr:\r
1099                 StdErrThread.join()\r
1100             if Process.returncode != 0 :\r
1101                 EdkLogger.error("Prebuild", PREBUILD_ERROR, 'Prebuild process is not success!')\r
1102 \r
1103             if os.path.exists(PrebuildEnvFile):\r
1104                 f = open(PrebuildEnvFile)\r
1105                 envs = f.readlines()\r
1106                 f.close()\r
1107                 envs = [l.split("=", 1) for l in envs ]\r
1108                 envs = [[I.strip() for I in item] for item in envs if len(item) == 2]\r
1109                 os.environ.update(dict(envs))\r
1110             EdkLogger.info("\n- Prebuild Done -\n")\r
1111 \r
1112     def LaunchPostbuild(self):\r
1113         if self.Postbuild:\r
1114             EdkLogger.info("\n- Postbuild Start -\n")\r
1115             if sys.platform == "win32":\r
1116                 Process = Popen(self.Postbuild, stdout=PIPE, stderr=PIPE, shell=True)\r
1117             else:\r
1118                 Process = Popen(self.Postbuild, stdout=PIPE, stderr=PIPE, shell=True)\r
1119             # launch two threads to read the STDOUT and STDERR\r
1120             EndOfProcedure = Event()\r
1121             EndOfProcedure.clear()\r
1122             if Process.stdout:\r
1123                 StdOutThread = Thread(target=ReadMessage, args=(Process.stdout, EdkLogger.info, EndOfProcedure))\r
1124                 StdOutThread.setName("STDOUT-Redirector")\r
1125                 StdOutThread.setDaemon(False)\r
1126                 StdOutThread.start()\r
1127 \r
1128             if Process.stderr:\r
1129                 StdErrThread = Thread(target=ReadMessage, args=(Process.stderr, EdkLogger.quiet, EndOfProcedure))\r
1130                 StdErrThread.setName("STDERR-Redirector")\r
1131                 StdErrThread.setDaemon(False)\r
1132                 StdErrThread.start()\r
1133             # waiting for program exit\r
1134             Process.wait()\r
1135 \r
1136             if Process.stdout:\r
1137                 StdOutThread.join()\r
1138             if Process.stderr:\r
1139                 StdErrThread.join()\r
1140             if Process.returncode != 0 :\r
1141                 EdkLogger.error("Postbuild", POSTBUILD_ERROR, 'Postbuild process is not success!')\r
1142             EdkLogger.info("\n- Postbuild Done -\n")\r
1143 \r
1144     ## Error handling for hash feature\r
1145     #\r
1146     # On BuildTask error, iterate through the Module Build tracking\r
1147     # dictionary to determine wheather a module failed to build. Invalidate\r
1148     # the hash associated with that module by removing it from storage.\r
1149     #\r
1150     #\r
1151     def invalidateHash(self):\r
1152         # GlobalData.gModuleBuildTracking contains only modules that cannot be skipped by hash\r
1153         for moduleAutoGenObj in GlobalData.gModuleBuildTracking.keys():\r
1154             # False == FAIL : True == Success\r
1155             # Skip invalidating for Successful module builds\r
1156             if GlobalData.gModuleBuildTracking[moduleAutoGenObj] == True:\r
1157                 continue\r
1158 \r
1159             # The module failed to build or failed to start building, from this point on\r
1160 \r
1161             # Remove .hash from build\r
1162             if GlobalData.gUseHashCache:\r
1163                 ModuleHashFile = path.join(moduleAutoGenObj.BuildDir, moduleAutoGenObj.Name + ".hash")\r
1164                 if os.path.exists(ModuleHashFile):\r
1165                     os.remove(ModuleHashFile)\r
1166 \r
1167             # Remove .hash file from cache\r
1168             if GlobalData.gBinCacheDest:\r
1169                 FileDir = path.join(GlobalData.gBinCacheDest, moduleAutoGenObj.Arch, moduleAutoGenObj.SourceDir, moduleAutoGenObj.MetaFile.BaseName)\r
1170                 HashFile = path.join(FileDir, moduleAutoGenObj.Name + '.hash')\r
1171                 if os.path.exists(HashFile):\r
1172                     os.remove(HashFile)\r
1173 \r
1174     ## Build a module or platform\r
1175     #\r
1176     # Create autogen code and makefile for a module or platform, and the launch\r
1177     # "make" command to build it\r
1178     #\r
1179     #   @param  Target                      The target of build command\r
1180     #   @param  Platform                    The platform file\r
1181     #   @param  Module                      The module file\r
1182     #   @param  BuildTarget                 The name of build target, one of "DEBUG", "RELEASE"\r
1183     #   @param  ToolChain                   The name of toolchain to build\r
1184     #   @param  Arch                        The arch of the module/platform\r
1185     #   @param  CreateDepModuleCodeFile     Flag used to indicate creating code\r
1186     #                                       for dependent modules/Libraries\r
1187     #   @param  CreateDepModuleMakeFile     Flag used to indicate creating makefile\r
1188     #                                       for dependent modules/Libraries\r
1189     #\r
1190     def _BuildPa(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True, BuildModule=False, FfsCommand={}):\r
1191         if AutoGenObject is None:\r
1192             return False\r
1193 \r
1194         # skip file generation for cleanxxx targets, run and fds target\r
1195         if Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
1196             # for target which must generate AutoGen code and makefile\r
1197             if not self.SkipAutoGen or Target == 'genc':\r
1198                 self.Progress.Start("Generating code")\r
1199                 AutoGenObject.CreateCodeFile(CreateDepsCodeFile)\r
1200                 self.Progress.Stop("done!")\r
1201             if Target == "genc":\r
1202                 return True\r
1203 \r
1204             if not self.SkipAutoGen or Target == 'genmake':\r
1205                 self.Progress.Start("Generating makefile")\r
1206                 AutoGenObject.CreateMakeFile(CreateDepsMakeFile, FfsCommand)\r
1207                 self.Progress.Stop("done!")\r
1208             if Target == "genmake":\r
1209                 return True\r
1210         else:\r
1211             # always recreate top/platform makefile when clean, just in case of inconsistency\r
1212             AutoGenObject.CreateCodeFile(False)\r
1213             AutoGenObject.CreateMakeFile(False)\r
1214 \r
1215         if EdkLogger.GetLevel() == EdkLogger.QUIET:\r
1216             EdkLogger.quiet("Building ... %s" % repr(AutoGenObject))\r
1217 \r
1218         BuildCommand = AutoGenObject.BuildCommand\r
1219         if BuildCommand is None or len(BuildCommand) == 0:\r
1220             EdkLogger.error("build", OPTION_MISSING,\r
1221                             "No build command found for this module. "\r
1222                             "Please check your setting of %s_%s_%s_MAKE_PATH in Conf/tools_def.txt file." %\r
1223                                 (AutoGenObject.BuildTarget, AutoGenObject.ToolChain, AutoGenObject.Arch),\r
1224                             ExtraData=str(AutoGenObject))\r
1225 \r
1226         makefile = GenMake.BuildFile(AutoGenObject)._FILE_NAME_[GenMake.gMakeType]\r
1227 \r
1228         # run\r
1229         if Target == 'run':\r
1230             RunDir = os.path.normpath(os.path.join(AutoGenObject.BuildDir, GlobalData.gGlobalDefines['ARCH']))\r
1231             Command = '.\SecMain'\r
1232             os.chdir(RunDir)\r
1233             LaunchCommand(Command, RunDir)\r
1234             return True\r
1235 \r
1236         # build modules\r
1237         if BuildModule:\r
1238             BuildCommand = BuildCommand + [Target]\r
1239             LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir)\r
1240             self.CreateAsBuiltInf()\r
1241             return True\r
1242 \r
1243         # build library\r
1244         if Target == 'libraries':\r
1245             for Lib in AutoGenObject.LibraryBuildDirectoryList:\r
1246                 NewBuildCommand = BuildCommand + ['-f', os.path.normpath(os.path.join(Lib, makefile)), 'pbuild']\r
1247                 LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)\r
1248             return True\r
1249 \r
1250         # build module\r
1251         if Target == 'modules':\r
1252             for Lib in AutoGenObject.LibraryBuildDirectoryList:\r
1253                 NewBuildCommand = BuildCommand + ['-f', os.path.normpath(os.path.join(Lib, makefile)), 'pbuild']\r
1254                 LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)\r
1255             for Mod in AutoGenObject.ModuleBuildDirectoryList:\r
1256                 NewBuildCommand = BuildCommand + ['-f', os.path.normpath(os.path.join(Mod, makefile)), 'pbuild']\r
1257                 LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)\r
1258             self.CreateAsBuiltInf()\r
1259             return True\r
1260 \r
1261         # cleanlib\r
1262         if Target == 'cleanlib':\r
1263             for Lib in AutoGenObject.LibraryBuildDirectoryList:\r
1264                 LibMakefile = os.path.normpath(os.path.join(Lib, makefile))\r
1265                 if os.path.exists(LibMakefile):\r
1266                     NewBuildCommand = BuildCommand + ['-f', LibMakefile, 'cleanall']\r
1267                     LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)\r
1268             return True\r
1269 \r
1270         # clean\r
1271         if Target == 'clean':\r
1272             for Mod in AutoGenObject.ModuleBuildDirectoryList:\r
1273                 ModMakefile = os.path.normpath(os.path.join(Mod, makefile))\r
1274                 if os.path.exists(ModMakefile):\r
1275                     NewBuildCommand = BuildCommand + ['-f', ModMakefile, 'cleanall']\r
1276                     LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)\r
1277             for Lib in AutoGenObject.LibraryBuildDirectoryList:\r
1278                 LibMakefile = os.path.normpath(os.path.join(Lib, makefile))\r
1279                 if os.path.exists(LibMakefile):\r
1280                     NewBuildCommand = BuildCommand + ['-f', LibMakefile, 'cleanall']\r
1281                     LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)\r
1282             return True\r
1283 \r
1284         # cleanall\r
1285         if Target == 'cleanall':\r
1286             try:\r
1287                 #os.rmdir(AutoGenObject.BuildDir)\r
1288                 RemoveDirectory(AutoGenObject.BuildDir, True)\r
1289             except WindowsError as X:\r
1290                 EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X))\r
1291         return True\r
1292 \r
1293     ## Build a module or platform\r
1294     #\r
1295     # Create autogen code and makefile for a module or platform, and the launch\r
1296     # "make" command to build it\r
1297     #\r
1298     #   @param  Target                      The target of build command\r
1299     #   @param  Platform                    The platform file\r
1300     #   @param  Module                      The module file\r
1301     #   @param  BuildTarget                 The name of build target, one of "DEBUG", "RELEASE"\r
1302     #   @param  ToolChain                   The name of toolchain to build\r
1303     #   @param  Arch                        The arch of the module/platform\r
1304     #   @param  CreateDepModuleCodeFile     Flag used to indicate creating code\r
1305     #                                       for dependent modules/Libraries\r
1306     #   @param  CreateDepModuleMakeFile     Flag used to indicate creating makefile\r
1307     #                                       for dependent modules/Libraries\r
1308     #\r
1309     def _Build(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True, BuildModule=False):\r
1310         if AutoGenObject is None:\r
1311             return False\r
1312 \r
1313         # skip file generation for cleanxxx targets, run and fds target\r
1314         if Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
1315             # for target which must generate AutoGen code and makefile\r
1316             if not self.SkipAutoGen or Target == 'genc':\r
1317                 self.Progress.Start("Generating code")\r
1318                 AutoGenObject.CreateCodeFile(CreateDepsCodeFile)\r
1319                 self.Progress.Stop("done!")\r
1320             if Target == "genc":\r
1321                 return True\r
1322 \r
1323             if not self.SkipAutoGen or Target == 'genmake':\r
1324                 self.Progress.Start("Generating makefile")\r
1325                 AutoGenObject.CreateMakeFile(CreateDepsMakeFile)\r
1326                 #AutoGenObject.CreateAsBuiltInf()\r
1327                 self.Progress.Stop("done!")\r
1328             if Target == "genmake":\r
1329                 return True\r
1330         else:\r
1331             # always recreate top/platform makefile when clean, just in case of inconsistency\r
1332             AutoGenObject.CreateCodeFile(False)\r
1333             AutoGenObject.CreateMakeFile(False)\r
1334 \r
1335         if EdkLogger.GetLevel() == EdkLogger.QUIET:\r
1336             EdkLogger.quiet("Building ... %s" % repr(AutoGenObject))\r
1337 \r
1338         BuildCommand = AutoGenObject.BuildCommand\r
1339         if BuildCommand is None or len(BuildCommand) == 0:\r
1340             EdkLogger.error("build", OPTION_MISSING,\r
1341                             "No build command found for this module. "\r
1342                             "Please check your setting of %s_%s_%s_MAKE_PATH in Conf/tools_def.txt file." %\r
1343                                 (AutoGenObject.BuildTarget, AutoGenObject.ToolChain, AutoGenObject.Arch),\r
1344                             ExtraData=str(AutoGenObject))\r
1345 \r
1346         # build modules\r
1347         if BuildModule:\r
1348             if Target != 'fds':\r
1349                 BuildCommand = BuildCommand + [Target]\r
1350             AutoGenObject.BuildTime = LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir)\r
1351             self.CreateAsBuiltInf()\r
1352             return True\r
1353 \r
1354         # genfds\r
1355         if Target == 'fds':\r
1356             if GenFdsApi(AutoGenObject.GenFdsCommandDict, self.Db):\r
1357                 EdkLogger.error("build", COMMAND_FAILURE)\r
1358             return True\r
1359 \r
1360         # run\r
1361         if Target == 'run':\r
1362             RunDir = os.path.normpath(os.path.join(AutoGenObject.BuildDir, GlobalData.gGlobalDefines['ARCH']))\r
1363             Command = '.\SecMain'\r
1364             os.chdir(RunDir)\r
1365             LaunchCommand(Command, RunDir)\r
1366             return True\r
1367 \r
1368         # build library\r
1369         if Target == 'libraries':\r
1370             pass\r
1371 \r
1372         # not build modules\r
1373 \r
1374 \r
1375         # cleanall\r
1376         if Target == 'cleanall':\r
1377             try:\r
1378                 #os.rmdir(AutoGenObject.BuildDir)\r
1379                 RemoveDirectory(AutoGenObject.BuildDir, True)\r
1380             except WindowsError as X:\r
1381                 EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X))\r
1382         return True\r
1383 \r
1384     ## Rebase module image and Get function address for the input module list.\r
1385     #\r
1386     def _RebaseModule (self, MapBuffer, BaseAddress, ModuleList, AddrIsOffset = True, ModeIsSmm = False):\r
1387         if ModeIsSmm:\r
1388             AddrIsOffset = False\r
1389         for InfFile in ModuleList:\r
1390             sys.stdout.write (".")\r
1391             sys.stdout.flush()\r
1392             ModuleInfo = ModuleList[InfFile]\r
1393             ModuleName = ModuleInfo.BaseName\r
1394             ModuleOutputImage = ModuleInfo.Image.FileName\r
1395             ModuleDebugImage  = os.path.join(ModuleInfo.DebugDir, ModuleInfo.BaseName + '.efi')\r
1396             ## for SMM module in SMRAM, the SMRAM will be allocated from base to top.\r
1397             if not ModeIsSmm:\r
1398                 BaseAddress = BaseAddress - ModuleInfo.Image.Size\r
1399                 #\r
1400                 # Update Image to new BaseAddress by GenFw tool\r
1401                 #\r
1402                 LaunchCommand(["GenFw", "--rebase", str(BaseAddress), "-r", ModuleOutputImage], ModuleInfo.OutputDir)\r
1403                 LaunchCommand(["GenFw", "--rebase", str(BaseAddress), "-r", ModuleDebugImage], ModuleInfo.DebugDir)\r
1404             else:\r
1405                 #\r
1406                 # Set new address to the section header only for SMM driver.\r
1407                 #\r
1408                 LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleOutputImage], ModuleInfo.OutputDir)\r
1409                 LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleDebugImage], ModuleInfo.DebugDir)\r
1410             #\r
1411             # Collect function address from Map file\r
1412             #\r
1413             ImageMapTable = ModuleOutputImage.replace('.efi', '.map')\r
1414             FunctionList = []\r
1415             if os.path.exists(ImageMapTable):\r
1416                 OrigImageBaseAddress = 0\r
1417                 ImageMap = open(ImageMapTable, 'r')\r
1418                 for LinStr in ImageMap:\r
1419                     if len (LinStr.strip()) == 0:\r
1420                         continue\r
1421                     #\r
1422                     # Get the preferred address set on link time.\r
1423                     #\r
1424                     if LinStr.find ('Preferred load address is') != -1:\r
1425                         StrList = LinStr.split()\r
1426                         OrigImageBaseAddress = int (StrList[len(StrList) - 1], 16)\r
1427 \r
1428                     StrList = LinStr.split()\r
1429                     if len (StrList) > 4:\r
1430                         if StrList[3] == 'f' or StrList[3] == 'F':\r
1431                             Name = StrList[1]\r
1432                             RelativeAddress = int (StrList[2], 16) - OrigImageBaseAddress\r
1433                             FunctionList.append ((Name, RelativeAddress))\r
1434 \r
1435                 ImageMap.close()\r
1436             #\r
1437             # Add general information.\r
1438             #\r
1439             if ModeIsSmm:\r
1440                 MapBuffer.append('\n\n%s (Fixed SMRAM Offset,   BaseAddress=0x%010X,  EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint))\r
1441             elif AddrIsOffset:\r
1442                 MapBuffer.append('\n\n%s (Fixed Memory Offset,  BaseAddress=-0x%010X, EntryPoint=-0x%010X)\n' % (ModuleName, 0 - BaseAddress, 0 - (BaseAddress + ModuleInfo.Image.EntryPoint)))\r
1443             else:\r
1444                 MapBuffer.append('\n\n%s (Fixed Memory Address, BaseAddress=0x%010X,  EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint))\r
1445             #\r
1446             # Add guid and general seciton section.\r
1447             #\r
1448             TextSectionAddress = 0\r
1449             DataSectionAddress = 0\r
1450             for SectionHeader in ModuleInfo.Image.SectionHeaderList:\r
1451                 if SectionHeader[0] == '.text':\r
1452                     TextSectionAddress = SectionHeader[1]\r
1453                 elif SectionHeader[0] in ['.data', '.sdata']:\r
1454                     DataSectionAddress = SectionHeader[1]\r
1455             if AddrIsOffset:\r
1456                 MapBuffer.append('(GUID=%s, .textbaseaddress=-0x%010X, .databaseaddress=-0x%010X)\n' % (ModuleInfo.Guid, 0 - (BaseAddress + TextSectionAddress), 0 - (BaseAddress + DataSectionAddress)))\r
1457             else:\r
1458                 MapBuffer.append('(GUID=%s, .textbaseaddress=0x%010X, .databaseaddress=0x%010X)\n' % (ModuleInfo.Guid, BaseAddress + TextSectionAddress, BaseAddress + DataSectionAddress))\r
1459             #\r
1460             # Add debug image full path.\r
1461             #\r
1462             MapBuffer.append('(IMAGE=%s)\n\n' % (ModuleDebugImage))\r
1463             #\r
1464             # Add function address\r
1465             #\r
1466             for Function in FunctionList:\r
1467                 if AddrIsOffset:\r
1468                     MapBuffer.append('  -0x%010X    %s\n' % (0 - (BaseAddress + Function[1]), Function[0]))\r
1469                 else:\r
1470                     MapBuffer.append('  0x%010X    %s\n' % (BaseAddress + Function[1], Function[0]))\r
1471             ImageMap.close()\r
1472 \r
1473             #\r
1474             # for SMM module in SMRAM, the SMRAM will be allocated from base to top.\r
1475             #\r
1476             if ModeIsSmm:\r
1477                 BaseAddress = BaseAddress + ModuleInfo.Image.Size\r
1478 \r
1479     ## Collect MAP information of all FVs\r
1480     #\r
1481     def _CollectFvMapBuffer (self, MapBuffer, Wa, ModuleList):\r
1482         if self.Fdf:\r
1483             # First get the XIP base address for FV map file.\r
1484             GuidPattern = re.compile("[-a-fA-F0-9]+")\r
1485             GuidName = re.compile("\(GUID=[-a-fA-F0-9]+")\r
1486             for FvName in Wa.FdfProfile.FvDict:\r
1487                 FvMapBuffer = os.path.join(Wa.FvDir, FvName + '.Fv.map')\r
1488                 if not os.path.exists(FvMapBuffer):\r
1489                     continue\r
1490                 FvMap = open(FvMapBuffer, 'r')\r
1491                 #skip FV size information\r
1492                 FvMap.readline()\r
1493                 FvMap.readline()\r
1494                 FvMap.readline()\r
1495                 FvMap.readline()\r
1496                 for Line in FvMap:\r
1497                     MatchGuid = GuidPattern.match(Line)\r
1498                     if MatchGuid is not None:\r
1499                         #\r
1500                         # Replace GUID with module name\r
1501                         #\r
1502                         GuidString = MatchGuid.group()\r
1503                         if GuidString.upper() in ModuleList:\r
1504                             Line = Line.replace(GuidString, ModuleList[GuidString.upper()].Name)\r
1505                     MapBuffer.append(Line)\r
1506                     #\r
1507                     # Add the debug image full path.\r
1508                     #\r
1509                     MatchGuid = GuidName.match(Line)\r
1510                     if MatchGuid is not None:\r
1511                         GuidString = MatchGuid.group().split("=")[1]\r
1512                         if GuidString.upper() in ModuleList:\r
1513                             MapBuffer.append('(IMAGE=%s)\n' % (os.path.join(ModuleList[GuidString.upper()].DebugDir, ModuleList[GuidString.upper()].Name + '.efi')))\r
1514 \r
1515                 FvMap.close()\r
1516 \r
1517     ## Collect MAP information of all modules\r
1518     #\r
1519     def _CollectModuleMapBuffer (self, MapBuffer, ModuleList):\r
1520         sys.stdout.write ("Generate Load Module At Fix Address Map")\r
1521         sys.stdout.flush()\r
1522         PatchEfiImageList = []\r
1523         PeiModuleList  = {}\r
1524         BtModuleList   = {}\r
1525         RtModuleList   = {}\r
1526         SmmModuleList  = {}\r
1527         PeiSize = 0\r
1528         BtSize  = 0\r
1529         RtSize  = 0\r
1530         # reserve 4K size in SMRAM to make SMM module address not from 0.\r
1531         SmmSize = 0x1000\r
1532         for ModuleGuid in ModuleList:\r
1533             Module = ModuleList[ModuleGuid]\r
1534             GlobalData.gProcessingFile = "%s [%s, %s, %s]" % (Module.MetaFile, Module.Arch, Module.ToolChain, Module.BuildTarget)\r
1535 \r
1536             OutputImageFile = ''\r
1537             for ResultFile in Module.CodaTargetList:\r
1538                 if str(ResultFile.Target).endswith('.efi'):\r
1539                     #\r
1540                     # module list for PEI, DXE, RUNTIME and SMM\r
1541                     #\r
1542                     OutputImageFile = os.path.join(Module.OutputDir, Module.Name + '.efi')\r
1543                     ImageClass = PeImageClass (OutputImageFile)\r
1544                     if not ImageClass.IsValid:\r
1545                         EdkLogger.error("build", FILE_PARSE_FAILURE, ExtraData=ImageClass.ErrorInfo)\r
1546                     ImageInfo = PeImageInfo(Module.Name, Module.Guid, Module.Arch, Module.OutputDir, Module.DebugDir, ImageClass)\r
1547                     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
1548                         PeiModuleList[Module.MetaFile] = ImageInfo\r
1549                         PeiSize += ImageInfo.Image.Size\r
1550                     elif Module.ModuleType in [EDK_COMPONENT_TYPE_BS_DRIVER, SUP_MODULE_DXE_DRIVER, SUP_MODULE_UEFI_DRIVER]:\r
1551                         BtModuleList[Module.MetaFile] = ImageInfo\r
1552                         BtSize += ImageInfo.Image.Size\r
1553                     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
1554                         RtModuleList[Module.MetaFile] = ImageInfo\r
1555                         RtSize += ImageInfo.Image.Size\r
1556                     elif Module.ModuleType in [SUP_MODULE_SMM_CORE, SUP_MODULE_DXE_SMM_DRIVER, SUP_MODULE_MM_STANDALONE, SUP_MODULE_MM_CORE_STANDALONE]:\r
1557                         SmmModuleList[Module.MetaFile] = ImageInfo\r
1558                         SmmSize += ImageInfo.Image.Size\r
1559                         if Module.ModuleType == SUP_MODULE_DXE_SMM_DRIVER:\r
1560                             PiSpecVersion = Module.Module.Specification.get('PI_SPECIFICATION_VERSION', '0x00000000')\r
1561                             # for PI specification < PI1.1, DXE_SMM_DRIVER also runs as BOOT time driver.\r
1562                             if int(PiSpecVersion, 16) < 0x0001000A:\r
1563                                 BtModuleList[Module.MetaFile] = ImageInfo\r
1564                                 BtSize += ImageInfo.Image.Size\r
1565                     break\r
1566             #\r
1567             # EFI image is final target.\r
1568             # Check EFI image contains patchable FixAddress related PCDs.\r
1569             #\r
1570             if OutputImageFile != '':\r
1571                 ModuleIsPatch = False\r
1572                 for Pcd in Module.ModulePcdList:\r
1573                     if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SET:\r
1574                         ModuleIsPatch = True\r
1575                         break\r
1576                 if not ModuleIsPatch:\r
1577                     for Pcd in Module.LibraryPcdList:\r
1578                         if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SET:\r
1579                             ModuleIsPatch = True\r
1580                             break\r
1581 \r
1582                 if not ModuleIsPatch:\r
1583                     continue\r
1584                 #\r
1585                 # Module includes the patchable load fix address PCDs.\r
1586                 # It will be fixed up later.\r
1587                 #\r
1588                 PatchEfiImageList.append (OutputImageFile)\r
1589 \r
1590         #\r
1591         # Get Top Memory address\r
1592         #\r
1593         ReservedRuntimeMemorySize = 0\r
1594         TopMemoryAddress = 0\r
1595         if self.LoadFixAddress == 0xFFFFFFFFFFFFFFFF:\r
1596             TopMemoryAddress = 0\r
1597         else:\r
1598             TopMemoryAddress = self.LoadFixAddress\r
1599             if TopMemoryAddress < RtSize + BtSize + PeiSize:\r
1600                 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS is too low to load driver")\r
1601 \r
1602         #\r
1603         # Patch FixAddress related PCDs into EFI image\r
1604         #\r
1605         for EfiImage in PatchEfiImageList:\r
1606             EfiImageMap = EfiImage.replace('.efi', '.map')\r
1607             if not os.path.exists(EfiImageMap):\r
1608                 continue\r
1609             #\r
1610             # Get PCD offset in EFI image by GenPatchPcdTable function\r
1611             #\r
1612             PcdTable = parsePcdInfoFromMapFile(EfiImageMap, EfiImage)\r
1613             #\r
1614             # Patch real PCD value by PatchPcdValue tool\r
1615             #\r
1616             for PcdInfo in PcdTable:\r
1617                 ReturnValue = 0\r
1618                 if PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE:\r
1619                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE_DATA_TYPE, str (PeiSize // 0x1000))\r
1620                 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE:\r
1621                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE_DATA_TYPE, str (BtSize // 0x1000))\r
1622                 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE:\r
1623                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE_DATA_TYPE, str (RtSize // 0x1000))\r
1624                 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE and len (SmmModuleList) > 0:\r
1625                     ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE_DATA_TYPE, str (SmmSize // 0x1000))\r
1626                 if ReturnValue != 0:\r
1627                     EdkLogger.error("build", PARAMETER_INVALID, "Patch PCD value failed", ExtraData=ErrorInfo)\r
1628 \r
1629         MapBuffer.append('PEI_CODE_PAGE_NUMBER      = 0x%x\n' % (PeiSize // 0x1000))\r
1630         MapBuffer.append('BOOT_CODE_PAGE_NUMBER     = 0x%x\n' % (BtSize // 0x1000))\r
1631         MapBuffer.append('RUNTIME_CODE_PAGE_NUMBER  = 0x%x\n' % (RtSize // 0x1000))\r
1632         if len (SmmModuleList) > 0:\r
1633             MapBuffer.append('SMM_CODE_PAGE_NUMBER      = 0x%x\n' % (SmmSize // 0x1000))\r
1634 \r
1635         PeiBaseAddr = TopMemoryAddress - RtSize - BtSize\r
1636         BtBaseAddr  = TopMemoryAddress - RtSize\r
1637         RtBaseAddr  = TopMemoryAddress - ReservedRuntimeMemorySize\r
1638 \r
1639         self._RebaseModule (MapBuffer, PeiBaseAddr, PeiModuleList, TopMemoryAddress == 0)\r
1640         self._RebaseModule (MapBuffer, BtBaseAddr, BtModuleList, TopMemoryAddress == 0)\r
1641         self._RebaseModule (MapBuffer, RtBaseAddr, RtModuleList, TopMemoryAddress == 0)\r
1642         self._RebaseModule (MapBuffer, 0x1000, SmmModuleList, AddrIsOffset=False, ModeIsSmm=True)\r
1643         MapBuffer.append('\n\n')\r
1644         sys.stdout.write ("\n")\r
1645         sys.stdout.flush()\r
1646 \r
1647     ## Save platform Map file\r
1648     #\r
1649     def _SaveMapFile (self, MapBuffer, Wa):\r
1650         #\r
1651         # Map file path is got.\r
1652         #\r
1653         MapFilePath = os.path.join(Wa.BuildDir, Wa.Name + '.map')\r
1654         #\r
1655         # Save address map into MAP file.\r
1656         #\r
1657         SaveFileOnChange(MapFilePath, ''.join(MapBuffer), False)\r
1658         if self.LoadFixAddress != 0:\r
1659             sys.stdout.write ("\nLoad Module At Fix Address Map file can be found at %s\n" % (MapFilePath))\r
1660         sys.stdout.flush()\r
1661 \r
1662     ## Build active platform for different build targets and different tool chains\r
1663     #\r
1664     def _BuildPlatform(self):\r
1665         SaveFileOnChange(self.PlatformBuildPath, '# DO NOT EDIT \n# FILE auto-generated\n', False)\r
1666         for BuildTarget in self.BuildTargetList:\r
1667             GlobalData.gGlobalDefines['TARGET'] = BuildTarget\r
1668             index = 0\r
1669             for ToolChain in self.ToolChainList:\r
1670                 GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain\r
1671                 GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain\r
1672                 GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index]\r
1673                 index += 1\r
1674                 Wa = WorkspaceAutoGen(\r
1675                         self.WorkspaceDir,\r
1676                         self.PlatformFile,\r
1677                         BuildTarget,\r
1678                         ToolChain,\r
1679                         self.ArchList,\r
1680                         self.BuildDatabase,\r
1681                         self.TargetTxt,\r
1682                         self.ToolDef,\r
1683                         self.Fdf,\r
1684                         self.FdList,\r
1685                         self.FvList,\r
1686                         self.CapList,\r
1687                         self.SkuId,\r
1688                         self.UniFlag,\r
1689                         self.Progress\r
1690                         )\r
1691                 self.Fdf = Wa.FdfFile\r
1692                 self.LoadFixAddress = Wa.Platform.LoadFixAddress\r
1693                 self.BuildReport.AddPlatformReport(Wa)\r
1694                 self.Progress.Stop("done!")\r
1695 \r
1696                 # Add ffs build to makefile\r
1697                 CmdListDict = {}\r
1698                 if GlobalData.gEnableGenfdsMultiThread and self.Fdf:\r
1699                     CmdListDict = self._GenFfsCmd(Wa.ArchList)\r
1700 \r
1701                 for Arch in Wa.ArchList:\r
1702                     GlobalData.gGlobalDefines['ARCH'] = Arch\r
1703                     Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)\r
1704                     for Module in Pa.Platform.Modules:\r
1705                         # Get ModuleAutoGen object to generate C code file and makefile\r
1706                         Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
1707                         if Ma is None:\r
1708                             continue\r
1709                         self.BuildModules.append(Ma)\r
1710                     self._BuildPa(self.Target, Pa, FfsCommand=CmdListDict)\r
1711 \r
1712                 # Create MAP file when Load Fix Address is enabled.\r
1713                 if self.Target in ["", "all", "fds"]:\r
1714                     for Arch in Wa.ArchList:\r
1715                         GlobalData.gGlobalDefines['ARCH'] = Arch\r
1716                         #\r
1717                         # Check whether the set fix address is above 4G for 32bit image.\r
1718                         #\r
1719                         if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
1720                             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
1721                     #\r
1722                     # Get Module List\r
1723                     #\r
1724                     ModuleList = {}\r
1725                     for Pa in Wa.AutoGenObjectList:\r
1726                         for Ma in Pa.ModuleAutoGenList:\r
1727                             if Ma is None:\r
1728                                 continue\r
1729                             if not Ma.IsLibrary:\r
1730                                 ModuleList[Ma.Guid.upper()] = Ma\r
1731 \r
1732                     MapBuffer = []\r
1733                     if self.LoadFixAddress != 0:\r
1734                         #\r
1735                         # Rebase module to the preferred memory address before GenFds\r
1736                         #\r
1737                         self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
1738                     if self.Fdf:\r
1739                         #\r
1740                         # create FDS again for the updated EFI image\r
1741                         #\r
1742                         self._Build("fds", Wa)\r
1743                         #\r
1744                         # Create MAP file for all platform FVs after GenFds.\r
1745                         #\r
1746                         self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)\r
1747                     #\r
1748                     # Save MAP buffer into MAP file.\r
1749                     #\r
1750                     self._SaveMapFile (MapBuffer, Wa)\r
1751 \r
1752     ## Build active module for different build targets, different tool chains and different archs\r
1753     #\r
1754     def _BuildModule(self):\r
1755         for BuildTarget in self.BuildTargetList:\r
1756             GlobalData.gGlobalDefines['TARGET'] = BuildTarget\r
1757             index = 0\r
1758             for ToolChain in self.ToolChainList:\r
1759                 WorkspaceAutoGenTime = time.time()\r
1760                 GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain\r
1761                 GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain\r
1762                 GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index]\r
1763                 index += 1\r
1764                 #\r
1765                 # module build needs platform build information, so get platform\r
1766                 # AutoGen first\r
1767                 #\r
1768                 Wa = WorkspaceAutoGen(\r
1769                         self.WorkspaceDir,\r
1770                         self.PlatformFile,\r
1771                         BuildTarget,\r
1772                         ToolChain,\r
1773                         self.ArchList,\r
1774                         self.BuildDatabase,\r
1775                         self.TargetTxt,\r
1776                         self.ToolDef,\r
1777                         self.Fdf,\r
1778                         self.FdList,\r
1779                         self.FvList,\r
1780                         self.CapList,\r
1781                         self.SkuId,\r
1782                         self.UniFlag,\r
1783                         self.Progress,\r
1784                         self.ModuleFile\r
1785                         )\r
1786                 self.Fdf = Wa.FdfFile\r
1787                 self.LoadFixAddress = Wa.Platform.LoadFixAddress\r
1788                 Wa.CreateMakeFile(False)\r
1789                 # Add ffs build to makefile\r
1790                 CmdListDict = None\r
1791                 if GlobalData.gEnableGenfdsMultiThread and self.Fdf:\r
1792                     CmdListDict = self._GenFfsCmd(Wa.ArchList)\r
1793                 self.Progress.Stop("done!")\r
1794                 MaList = []\r
1795                 ExitFlag = threading.Event()\r
1796                 ExitFlag.clear()\r
1797                 self.AutoGenTime += int(round((time.time() - WorkspaceAutoGenTime)))\r
1798                 for Arch in Wa.ArchList:\r
1799                     AutoGenStart = time.time()\r
1800                     GlobalData.gGlobalDefines['ARCH'] = Arch\r
1801                     Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)\r
1802                     for Module in Pa.Platform.Modules:\r
1803                         if self.ModuleFile.Dir == Module.Dir and self.ModuleFile.Name == Module.Name:\r
1804                             Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
1805                             if Ma is None:\r
1806                                 continue\r
1807                             MaList.append(Ma)\r
1808                             if Ma.CanSkipbyHash():\r
1809                                 self.HashSkipModules.append(Ma)\r
1810                                 continue\r
1811                             # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'\r
1812                             if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
1813                                 # for target which must generate AutoGen code and makefile\r
1814                                 if not self.SkipAutoGen or self.Target == 'genc':\r
1815                                     self.Progress.Start("Generating code")\r
1816                                     Ma.CreateCodeFile(True)\r
1817                                     self.Progress.Stop("done!")\r
1818                                 if self.Target == "genc":\r
1819                                     return True\r
1820                                 if not self.SkipAutoGen or self.Target == 'genmake':\r
1821                                     self.Progress.Start("Generating makefile")\r
1822                                     if CmdListDict and self.Fdf and (Module.File, Arch) in CmdListDict:\r
1823                                         Ma.CreateMakeFile(True, CmdListDict[Module.File, Arch])\r
1824                                         del CmdListDict[Module.File, Arch]\r
1825                                     else:\r
1826                                         Ma.CreateMakeFile(True)\r
1827                                     self.Progress.Stop("done!")\r
1828                                 if self.Target == "genmake":\r
1829                                     return True\r
1830                             self.BuildModules.append(Ma)\r
1831                             # Initialize all modules in tracking to False (FAIL)\r
1832                             if Ma not in GlobalData.gModuleBuildTracking:\r
1833                                 GlobalData.gModuleBuildTracking[Ma] = False\r
1834                     self.AutoGenTime += int(round((time.time() - AutoGenStart)))\r
1835                     MakeStart = time.time()\r
1836                     for Ma in self.BuildModules:\r
1837                         if not Ma.IsBinaryModule:\r
1838                             Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))\r
1839                         # Break build if any build thread has error\r
1840                         if BuildTask.HasError():\r
1841                             # we need a full version of makefile for platform\r
1842                             ExitFlag.set()\r
1843                             BuildTask.WaitForComplete()\r
1844                             self.invalidateHash()\r
1845                             Pa.CreateMakeFile(False)\r
1846                             EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1847                         # Start task scheduler\r
1848                         if not BuildTask.IsOnGoing():\r
1849                             BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)\r
1850 \r
1851                     # in case there's an interruption. we need a full version of makefile for platform\r
1852                     Pa.CreateMakeFile(False)\r
1853                     if BuildTask.HasError():\r
1854                         self.invalidateHash()\r
1855                         EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1856                     self.MakeTime += int(round((time.time() - MakeStart)))\r
1857 \r
1858                 MakeContiue = time.time()\r
1859                 ExitFlag.set()\r
1860                 BuildTask.WaitForComplete()\r
1861                 self.CreateAsBuiltInf()\r
1862                 self.MakeTime += int(round((time.time() - MakeContiue)))\r
1863                 if BuildTask.HasError():\r
1864                     self.invalidateHash()\r
1865                     EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
1866 \r
1867                 self.BuildReport.AddPlatformReport(Wa, MaList)\r
1868                 if MaList == []:\r
1869                     EdkLogger.error(\r
1870                                 'build',\r
1871                                 BUILD_ERROR,\r
1872                                 "Module for [%s] is not a component of active platform."\\r
1873                                 " Please make sure that the ARCH and inf file path are"\\r
1874                                 " given in the same as in [%s]" % \\r
1875                                     (', '.join(Wa.ArchList), self.PlatformFile),\r
1876                                 ExtraData=self.ModuleFile\r
1877                                 )\r
1878                 # Create MAP file when Load Fix Address is enabled.\r
1879                 if self.Target == "fds" and self.Fdf:\r
1880                     for Arch in Wa.ArchList:\r
1881                         #\r
1882                         # Check whether the set fix address is above 4G for 32bit image.\r
1883                         #\r
1884                         if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
1885                             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
1886                     #\r
1887                     # Get Module List\r
1888                     #\r
1889                     ModuleList = {}\r
1890                     for Pa in Wa.AutoGenObjectList:\r
1891                         for Ma in Pa.ModuleAutoGenList:\r
1892                             if Ma is None:\r
1893                                 continue\r
1894                             if not Ma.IsLibrary:\r
1895                                 ModuleList[Ma.Guid.upper()] = Ma\r
1896 \r
1897                     MapBuffer = []\r
1898                     if self.LoadFixAddress != 0:\r
1899                         #\r
1900                         # Rebase module to the preferred memory address before GenFds\r
1901                         #\r
1902                         self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
1903                     #\r
1904                     # create FDS again for the updated EFI image\r
1905                     #\r
1906                     GenFdsStart = time.time()\r
1907                     self._Build("fds", Wa)\r
1908                     self.GenFdsTime += int(round((time.time() - GenFdsStart)))\r
1909                     #\r
1910                     # Create MAP file for all platform FVs after GenFds.\r
1911                     #\r
1912                     self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)\r
1913                     #\r
1914                     # Save MAP buffer into MAP file.\r
1915                     #\r
1916                     self._SaveMapFile (MapBuffer, Wa)\r
1917 \r
1918     def _GenFfsCmd(self,ArchList):\r
1919         # convert dictionary of Cmd:(Inf,Arch)\r
1920         # to a new dictionary of (Inf,Arch):Cmd,Cmd,Cmd...\r
1921         CmdSetDict = defaultdict(set)\r
1922         GenFfsDict = GenFds.GenFfsMakefile('', GlobalData.gFdfParser, self, ArchList, GlobalData)\r
1923         for Cmd in GenFfsDict:\r
1924             tmpInf, tmpArch = GenFfsDict[Cmd]\r
1925             CmdSetDict[tmpInf, tmpArch].add(Cmd)\r
1926         return CmdSetDict\r
1927 \r
1928     ## Build a platform in multi-thread mode\r
1929     #\r
1930     def _MultiThreadBuildPlatform(self):\r
1931         SaveFileOnChange(self.PlatformBuildPath, '# DO NOT EDIT \n# FILE auto-generated\n', False)\r
1932         for BuildTarget in self.BuildTargetList:\r
1933             GlobalData.gGlobalDefines['TARGET'] = BuildTarget\r
1934             index = 0\r
1935             for ToolChain in self.ToolChainList:\r
1936                 WorkspaceAutoGenTime = time.time()\r
1937                 GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain\r
1938                 GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain\r
1939                 GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index]\r
1940                 index += 1\r
1941                 Wa = WorkspaceAutoGen(\r
1942                         self.WorkspaceDir,\r
1943                         self.PlatformFile,\r
1944                         BuildTarget,\r
1945                         ToolChain,\r
1946                         self.ArchList,\r
1947                         self.BuildDatabase,\r
1948                         self.TargetTxt,\r
1949                         self.ToolDef,\r
1950                         self.Fdf,\r
1951                         self.FdList,\r
1952                         self.FvList,\r
1953                         self.CapList,\r
1954                         self.SkuId,\r
1955                         self.UniFlag,\r
1956                         self.Progress\r
1957                         )\r
1958                 self.Fdf = Wa.FdfFile\r
1959                 self.LoadFixAddress = Wa.Platform.LoadFixAddress\r
1960                 self.BuildReport.AddPlatformReport(Wa)\r
1961                 Wa.CreateMakeFile(False)\r
1962 \r
1963                 # Add ffs build to makefile\r
1964                 CmdListDict = None\r
1965                 if GlobalData.gEnableGenfdsMultiThread and self.Fdf:\r
1966                     CmdListDict = self._GenFfsCmd(Wa.ArchList)\r
1967 \r
1968                 # multi-thread exit flag\r
1969                 ExitFlag = threading.Event()\r
1970                 ExitFlag.clear()\r
1971                 self.AutoGenTime += int(round((time.time() - WorkspaceAutoGenTime)))\r
1972                 for Arch in Wa.ArchList:\r
1973                     AutoGenStart = time.time()\r
1974                     GlobalData.gGlobalDefines['ARCH'] = Arch\r
1975                     Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)\r
1976                     if Pa is None:\r
1977                         continue\r
1978                     ModuleList = []\r
1979                     for Inf in Pa.Platform.Modules:\r
1980                         ModuleList.append(Inf)\r
1981                     # Add the INF only list in FDF\r
1982                     if GlobalData.gFdfParser is not None:\r
1983                         for InfName in GlobalData.gFdfParser.Profile.InfList:\r
1984                             Inf = PathClass(NormPath(InfName), self.WorkspaceDir, Arch)\r
1985                             if Inf in Pa.Platform.Modules:\r
1986                                 continue\r
1987                             ModuleList.append(Inf)\r
1988                     for Module in ModuleList:\r
1989                         # Get ModuleAutoGen object to generate C code file and makefile\r
1990                         Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
1991 \r
1992                         if Ma is None:\r
1993                             continue\r
1994                         if Ma.CanSkipbyHash():\r
1995                             self.HashSkipModules.append(Ma)\r
1996                             continue\r
1997 \r
1998                         # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'\r
1999                         if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
2000                             # for target which must generate AutoGen code and makefile\r
2001                             if not self.SkipAutoGen or self.Target == 'genc':\r
2002                                 Ma.CreateCodeFile(True)\r
2003                             if self.Target == "genc":\r
2004                                 continue\r
2005 \r
2006                             if not self.SkipAutoGen or self.Target == 'genmake':\r
2007                                 if CmdListDict and self.Fdf and (Module.File, Arch) in CmdListDict:\r
2008                                     Ma.CreateMakeFile(True, CmdListDict[Module.File, Arch])\r
2009                                     del CmdListDict[Module.File, Arch]\r
2010                                 else:\r
2011                                     Ma.CreateMakeFile(True)\r
2012                             if self.Target == "genmake":\r
2013                                 continue\r
2014                         self.BuildModules.append(Ma)\r
2015                         # Initialize all modules in tracking to False (FAIL)\r
2016                         if Ma not in GlobalData.gModuleBuildTracking:\r
2017                             GlobalData.gModuleBuildTracking[Ma] = False\r
2018                     self.Progress.Stop("done!")\r
2019                     self.AutoGenTime += int(round((time.time() - AutoGenStart)))\r
2020                     MakeStart = time.time()\r
2021                     for Ma in self.BuildModules:\r
2022                         # Generate build task for the module\r
2023                         if not Ma.IsBinaryModule:\r
2024                             Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))\r
2025                         # Break build if any build thread has error\r
2026                         if BuildTask.HasError():\r
2027                             # we need a full version of makefile for platform\r
2028                             ExitFlag.set()\r
2029                             BuildTask.WaitForComplete()\r
2030                             self.invalidateHash()\r
2031                             Pa.CreateMakeFile(False)\r
2032                             EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
2033                         # Start task scheduler\r
2034                         if not BuildTask.IsOnGoing():\r
2035                             BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)\r
2036 \r
2037                     # in case there's an interruption. we need a full version of makefile for platform\r
2038                     Pa.CreateMakeFile(False)\r
2039                     if BuildTask.HasError():\r
2040                         self.invalidateHash()\r
2041                         EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
2042                     self.MakeTime += int(round((time.time() - MakeStart)))\r
2043 \r
2044                 MakeContiue = time.time()\r
2045 \r
2046                 #\r
2047                 #\r
2048                 # All modules have been put in build tasks queue. Tell task scheduler\r
2049                 # to exit if all tasks are completed\r
2050                 #\r
2051                 ExitFlag.set()\r
2052                 BuildTask.WaitForComplete()\r
2053                 self.CreateAsBuiltInf()\r
2054                 self.MakeTime += int(round((time.time() - MakeContiue)))\r
2055                 #\r
2056                 # Check for build error, and raise exception if one\r
2057                 # has been signaled.\r
2058                 #\r
2059                 if BuildTask.HasError():\r
2060                     self.invalidateHash()\r
2061                     EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
2062 \r
2063                 # Create MAP file when Load Fix Address is enabled.\r
2064                 if self.Target in ["", "all", "fds"]:\r
2065                     for Arch in Wa.ArchList:\r
2066                         #\r
2067                         # Check whether the set fix address is above 4G for 32bit image.\r
2068                         #\r
2069                         if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
2070                             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
2071                     #\r
2072                     # Get Module List\r
2073                     #\r
2074                     ModuleList = {}\r
2075                     for Pa in Wa.AutoGenObjectList:\r
2076                         for Ma in Pa.ModuleAutoGenList:\r
2077                             if Ma is None:\r
2078                                 continue\r
2079                             if not Ma.IsLibrary:\r
2080                                 ModuleList[Ma.Guid.upper()] = Ma\r
2081                     #\r
2082                     # Rebase module to the preferred memory address before GenFds\r
2083                     #\r
2084                     MapBuffer = []\r
2085                     if self.LoadFixAddress != 0:\r
2086                         self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
2087 \r
2088                     if self.Fdf:\r
2089                         #\r
2090                         # Generate FD image if there's a FDF file found\r
2091                         #\r
2092                         GenFdsStart = time.time()\r
2093                         if GenFdsApi(Wa.GenFdsCommandDict, self.Db):\r
2094                             EdkLogger.error("build", COMMAND_FAILURE)\r
2095 \r
2096                         #\r
2097                         # Create MAP file for all platform FVs after GenFds.\r
2098                         #\r
2099                         self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)\r
2100                         self.GenFdsTime += int(round((time.time() - GenFdsStart)))\r
2101                     #\r
2102                     # Save MAP buffer into MAP file.\r
2103                     #\r
2104                     self._SaveMapFile(MapBuffer, Wa)\r
2105 \r
2106     ## Generate GuidedSectionTools.txt in the FV directories.\r
2107     #\r
2108     def CreateGuidedSectionToolsFile(self):\r
2109         for BuildTarget in self.BuildTargetList:\r
2110             for ToolChain in self.ToolChainList:\r
2111                 Wa = WorkspaceAutoGen(\r
2112                         self.WorkspaceDir,\r
2113                         self.PlatformFile,\r
2114                         BuildTarget,\r
2115                         ToolChain,\r
2116                         self.ArchList,\r
2117                         self.BuildDatabase,\r
2118                         self.TargetTxt,\r
2119                         self.ToolDef,\r
2120                         self.Fdf,\r
2121                         self.FdList,\r
2122                         self.FvList,\r
2123                         self.CapList,\r
2124                         self.SkuId,\r
2125                         self.UniFlag\r
2126                         )\r
2127                 FvDir = Wa.FvDir\r
2128                 if not os.path.exists(FvDir):\r
2129                     continue\r
2130 \r
2131                 for Arch in self.ArchList:\r
2132                     # Build up the list of supported architectures for this build\r
2133                     prefix = '%s_%s_%s_' % (BuildTarget, ToolChain, Arch)\r
2134 \r
2135                     # Look through the tool definitions for GUIDed tools\r
2136                     guidAttribs = []\r
2137                     for (attrib, value) in self.ToolDef.ToolsDefTxtDictionary.items():\r
2138                         if attrib.upper().endswith('_GUID'):\r
2139                             split = attrib.split('_')\r
2140                             thisPrefix = '_'.join(split[0:3]) + '_'\r
2141                             if thisPrefix == prefix:\r
2142                                 guid = self.ToolDef.ToolsDefTxtDictionary[attrib]\r
2143                                 guid = guid.lower()\r
2144                                 toolName = split[3]\r
2145                                 path = '_'.join(split[0:4]) + '_PATH'\r
2146                                 path = self.ToolDef.ToolsDefTxtDictionary[path]\r
2147                                 path = self.GetFullPathOfTool(path)\r
2148                                 guidAttribs.append((guid, toolName, path))\r
2149 \r
2150                     # Write out GuidedSecTools.txt\r
2151                     toolsFile = os.path.join(FvDir, 'GuidedSectionTools.txt')\r
2152                     toolsFile = open(toolsFile, 'wt')\r
2153                     for guidedSectionTool in guidAttribs:\r
2154                         print(' '.join(guidedSectionTool), file=toolsFile)\r
2155                     toolsFile.close()\r
2156 \r
2157     ## Returns the full path of the tool.\r
2158     #\r
2159     def GetFullPathOfTool (self, tool):\r
2160         if os.path.exists(tool):\r
2161             return os.path.realpath(tool)\r
2162         else:\r
2163             # We need to search for the tool using the\r
2164             # PATH environment variable.\r
2165             for dirInPath in os.environ['PATH'].split(os.pathsep):\r
2166                 foundPath = os.path.join(dirInPath, tool)\r
2167                 if os.path.exists(foundPath):\r
2168                     return os.path.realpath(foundPath)\r
2169 \r
2170         # If the tool was not found in the path then we just return\r
2171         # the input tool.\r
2172         return tool\r
2173 \r
2174     ## Launch the module or platform build\r
2175     #\r
2176     def Launch(self):\r
2177         if not self.ModuleFile:\r
2178             if not self.SpawnMode or self.Target not in ["", "all"]:\r
2179                 self.SpawnMode = False\r
2180                 self._BuildPlatform()\r
2181             else:\r
2182                 self._MultiThreadBuildPlatform()\r
2183             self.CreateGuidedSectionToolsFile()\r
2184         else:\r
2185             self.SpawnMode = False\r
2186             self._BuildModule()\r
2187 \r
2188         if self.Target == 'cleanall':\r
2189             RemoveDirectory(os.path.dirname(GlobalData.gDatabasePath), True)\r
2190 \r
2191     def CreateAsBuiltInf(self):\r
2192         all_lib_set = set()\r
2193         all_mod_set = set()\r
2194         for Module in self.BuildModules:\r
2195             Module.CreateAsBuiltInf()\r
2196             all_mod_set.add(Module)\r
2197         for Module in self.HashSkipModules:\r
2198             Module.CreateAsBuiltInf(True)\r
2199             all_mod_set.add(Module)\r
2200         for Module in all_mod_set:\r
2201             for lib in Module.LibraryAutoGenList:\r
2202                 all_lib_set.add(lib)\r
2203         for lib in all_lib_set:\r
2204             lib.CreateAsBuiltInf(True)\r
2205         all_lib_set.clear()\r
2206         all_mod_set.clear()\r
2207         self.BuildModules = []\r
2208         self.HashSkipModules = []\r
2209     ## Do some clean-up works when error occurred\r
2210     def Relinquish(self):\r
2211         OldLogLevel = EdkLogger.GetLevel()\r
2212         EdkLogger.SetLevel(EdkLogger.ERROR)\r
2213         Utils.Progressor.Abort()\r
2214         if self.SpawnMode == True:\r
2215             BuildTask.Abort()\r
2216         EdkLogger.SetLevel(OldLogLevel)\r
2217 \r
2218 def ParseDefines(DefineList=[]):\r
2219     DefineDict = {}\r
2220     if DefineList is not None:\r
2221         for Define in DefineList:\r
2222             DefineTokenList = Define.split("=", 1)\r
2223             if not GlobalData.gMacroNamePattern.match(DefineTokenList[0]):\r
2224                 EdkLogger.error('build', FORMAT_INVALID,\r
2225                                 "The macro name must be in the pattern [A-Z][A-Z0-9_]*",\r
2226                                 ExtraData=DefineTokenList[0])\r
2227 \r
2228             if len(DefineTokenList) == 1:\r
2229                 DefineDict[DefineTokenList[0]] = "TRUE"\r
2230             else:\r
2231                 DefineDict[DefineTokenList[0]] = DefineTokenList[1].strip()\r
2232     return DefineDict\r
2233 \r
2234 gParamCheck = []\r
2235 def SingleCheckCallback(option, opt_str, value, parser):\r
2236     if option not in gParamCheck:\r
2237         setattr(parser.values, option.dest, value)\r
2238         gParamCheck.append(option)\r
2239     else:\r
2240         parser.error("Option %s only allows one instance in command line!" % option)\r
2241 \r
2242 def LogBuildTime(Time):\r
2243     if Time:\r
2244         TimeDurStr = ''\r
2245         TimeDur = time.gmtime(Time)\r
2246         if TimeDur.tm_yday > 1:\r
2247             TimeDurStr = time.strftime("%H:%M:%S", TimeDur) + ", %d day(s)" % (TimeDur.tm_yday - 1)\r
2248         else:\r
2249             TimeDurStr = time.strftime("%H:%M:%S", TimeDur)\r
2250         return TimeDurStr\r
2251     else:\r
2252         return None\r
2253 \r
2254 ## Parse command line options\r
2255 #\r
2256 # Using standard Python module optparse to parse command line option of this tool.\r
2257 #\r
2258 #   @retval Opt   A optparse.Values object containing the parsed options\r
2259 #   @retval Args  Target of build command\r
2260 #\r
2261 def MyOptionParser():\r
2262     Parser = OptionParser(description=__copyright__, version=__version__, prog="build.exe", usage="%prog [options] [all|fds|genc|genmake|clean|cleanall|cleanlib|modules|libraries|run]")\r
2263     Parser.add_option("-a", "--arch", action="append", type="choice", choices=['IA32', 'X64', 'EBC', 'ARM', 'AARCH64'], dest="TargetArch",\r
2264         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
2265     Parser.add_option("-p", "--platform", action="callback", type="string", dest="PlatformFile", callback=SingleCheckCallback,\r
2266         help="Build the platform specified by the DSC file name argument, overriding target.txt's ACTIVE_PLATFORM definition.")\r
2267     Parser.add_option("-m", "--module", action="callback", type="string", dest="ModuleFile", callback=SingleCheckCallback,\r
2268         help="Build the module specified by the INF file name argument.")\r
2269     Parser.add_option("-b", "--buildtarget", type="string", dest="BuildTarget", help="Using the TARGET to build the platform, overriding target.txt's TARGET definition.",\r
2270                       action="append")\r
2271     Parser.add_option("-t", "--tagname", action="append", type="string", dest="ToolChain",\r
2272         help="Using the Tool Chain Tagname to build the platform, overriding target.txt's TOOL_CHAIN_TAG definition.")\r
2273     Parser.add_option("-x", "--sku-id", action="callback", type="string", dest="SkuId", callback=SingleCheckCallback,\r
2274         help="Using this name of SKU ID to build the platform, overriding SKUID_IDENTIFIER in DSC file.")\r
2275 \r
2276     Parser.add_option("-n", action="callback", type="int", dest="ThreadNumber", callback=SingleCheckCallback,\r
2277         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
2278              "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
2279 \r
2280     Parser.add_option("-f", "--fdf", action="callback", type="string", dest="FdfFile", callback=SingleCheckCallback,\r
2281         help="The name of the FDF file to use, which overrides the setting in the DSC file.")\r
2282     Parser.add_option("-r", "--rom-image", action="append", type="string", dest="RomImage", default=[],\r
2283         help="The name of FD to be generated. The name must be from [FD] section in FDF file.")\r
2284     Parser.add_option("-i", "--fv-image", action="append", type="string", dest="FvImage", default=[],\r
2285         help="The name of FV to be generated. The name must be from [FV] section in FDF file.")\r
2286     Parser.add_option("-C", "--capsule-image", action="append", type="string", dest="CapName", default=[],\r
2287         help="The name of Capsule to be generated. The name must be from [Capsule] section in FDF file.")\r
2288     Parser.add_option("-u", "--skip-autogen", action="store_true", dest="SkipAutoGen", help="Skip AutoGen step.")\r
2289     Parser.add_option("-e", "--re-parse", action="store_true", dest="Reparse", help="Re-parse all meta-data files.")\r
2290 \r
2291     Parser.add_option("-c", "--case-insensitive", action="store_true", dest="CaseInsensitive", default=False, help="Don't check case of file name.")\r
2292 \r
2293     Parser.add_option("-w", "--warning-as-error", action="store_true", dest="WarningAsError", help="Treat warning in tools as error.")\r
2294     Parser.add_option("-j", "--log", action="store", dest="LogFile", help="Put log in specified file as well as on console.")\r
2295 \r
2296     Parser.add_option("-s", "--silent", action="store_true", type=None, dest="SilentMode",\r
2297         help="Make use of silent mode of (n)make.")\r
2298     Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")\r
2299     Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\\r
2300                                                                                "including library instances selected, final dependency expression, "\\r
2301                                                                                "and warning messages, etc.")\r
2302     Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")\r
2303     Parser.add_option("-D", "--define", action="append", type="string", dest="Macros", help="Macro: \"Name [= Value]\".")\r
2304 \r
2305     Parser.add_option("-y", "--report-file", action="store", dest="ReportFile", help="Create/overwrite the report to the specified filename.")\r
2306     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
2307         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
2308              "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
2309     Parser.add_option("-F", "--flag", action="store", type="string", dest="Flag",\r
2310         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
2311              "This option can also be specified by setting *_*_*_BUILD_FLAGS in [BuildOptions] section of platform DSC. If they are both specified, this value "\\r
2312              "will override the setting in [BuildOptions] section of platform DSC.")\r
2313     Parser.add_option("-N", "--no-cache", action="store_true", dest="DisableCache", default=False, help="Disable build cache mechanism")\r
2314     Parser.add_option("--conf", action="store", type="string", dest="ConfDirectory", help="Specify the customized Conf directory.")\r
2315     Parser.add_option("--check-usage", action="store_true", dest="CheckUsage", default=False, help="Check usage content of entries listed in INF file.")\r
2316     Parser.add_option("--ignore-sources", action="store_true", dest="IgnoreSources", default=False, help="Focus to a binary build and ignore all source files")\r
2317     Parser.add_option("--pcd", action="append", dest="OptionPcd", help="Set PCD value by command line. Format: \"PcdName=Value\" ")\r
2318     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
2319     Parser.add_option("--hash", action="store_true", dest="UseHashCache", default=False, help="Enable hash-based caching during build process.")\r
2320     Parser.add_option("--binary-destination", action="store", type="string", dest="BinCacheDest", help="Generate a cache of binary files in the specified directory.")\r
2321     Parser.add_option("--binary-source", action="store", type="string", dest="BinCacheSource", help="Consume a cache of binary files from the specified directory.")\r
2322     Parser.add_option("--genfds-multi-thread", action="store_true", dest="GenfdsMultiThread", default=False, help="Enable GenFds multi thread to generate ffs file.")\r
2323     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
2324     (Opt, Args) = Parser.parse_args()\r
2325     return (Opt, Args)\r
2326 \r
2327 ## Tool entrance method\r
2328 #\r
2329 # This method mainly dispatch specific methods per the command line options.\r
2330 # If no error found, return zero value so the caller of this tool can know\r
2331 # if it's executed successfully or not.\r
2332 #\r
2333 #   @retval 0     Tool was successful\r
2334 #   @retval 1     Tool failed\r
2335 #\r
2336 def Main():\r
2337     StartTime = time.time()\r
2338 \r
2339     # Initialize log system\r
2340     EdkLogger.Initialize()\r
2341     GlobalData.gCommand = sys.argv[1:]\r
2342     #\r
2343     # Parse the options and args\r
2344     #\r
2345     (Option, Target) = MyOptionParser()\r
2346     GlobalData.gOptions = Option\r
2347     GlobalData.gCaseInsensitive = Option.CaseInsensitive\r
2348 \r
2349     # Set log level\r
2350     if Option.verbose is not None:\r
2351         EdkLogger.SetLevel(EdkLogger.VERBOSE)\r
2352     elif Option.quiet is not None:\r
2353         EdkLogger.SetLevel(EdkLogger.QUIET)\r
2354     elif Option.debug is not None:\r
2355         EdkLogger.SetLevel(Option.debug + 1)\r
2356     else:\r
2357         EdkLogger.SetLevel(EdkLogger.INFO)\r
2358 \r
2359     if Option.LogFile is not None:\r
2360         EdkLogger.SetLogFile(Option.LogFile)\r
2361 \r
2362     if Option.WarningAsError == True:\r
2363         EdkLogger.SetWarningAsError()\r
2364 \r
2365     if platform.platform().find("Windows") >= 0:\r
2366         GlobalData.gIsWindows = True\r
2367     else:\r
2368         GlobalData.gIsWindows = False\r
2369 \r
2370     EdkLogger.quiet("Build environment: %s" % platform.platform())\r
2371     EdkLogger.quiet(time.strftime("Build start time: %H:%M:%S, %b.%d %Y\n", time.localtime()));\r
2372     ReturnCode = 0\r
2373     MyBuild = None\r
2374     BuildError = True\r
2375     try:\r
2376         if len(Target) == 0:\r
2377             Target = "all"\r
2378         elif len(Target) >= 2:\r
2379             EdkLogger.error("build", OPTION_NOT_SUPPORTED, "More than one targets are not supported.",\r
2380                             ExtraData="Please select one of: %s" % (' '.join(gSupportedTarget)))\r
2381         else:\r
2382             Target = Target[0].lower()\r
2383 \r
2384         if Target not in gSupportedTarget:\r
2385             EdkLogger.error("build", OPTION_NOT_SUPPORTED, "Not supported target [%s]." % Target,\r
2386                             ExtraData="Please select one of: %s" % (' '.join(gSupportedTarget)))\r
2387 \r
2388         #\r
2389         # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH\r
2390         #\r
2391         CheckEnvVariable()\r
2392         GlobalData.gCommandLineDefines.update(ParseDefines(Option.Macros))\r
2393 \r
2394         Workspace = os.getenv("WORKSPACE")\r
2395         #\r
2396         # Get files real name in workspace dir\r
2397         #\r
2398         GlobalData.gAllFiles = Utils.DirCache(Workspace)\r
2399 \r
2400         WorkingDirectory = os.getcwd()\r
2401         if not Option.ModuleFile:\r
2402             FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.inf')))\r
2403             FileNum = len(FileList)\r
2404             if FileNum >= 2:\r
2405                 EdkLogger.error("build", OPTION_NOT_SUPPORTED, "There are %d INF files in %s." % (FileNum, WorkingDirectory),\r
2406                                 ExtraData="Please use '-m <INF_FILE_PATH>' switch to choose one.")\r
2407             elif FileNum == 1:\r
2408                 Option.ModuleFile = NormFile(FileList[0], Workspace)\r
2409 \r
2410         if Option.ModuleFile:\r
2411             if os.path.isabs (Option.ModuleFile):\r
2412                 if os.path.normcase (os.path.normpath(Option.ModuleFile)).find (Workspace) == 0:\r
2413                     Option.ModuleFile = NormFile(os.path.normpath(Option.ModuleFile), Workspace)\r
2414             Option.ModuleFile = PathClass(Option.ModuleFile, Workspace)\r
2415             ErrorCode, ErrorInfo = Option.ModuleFile.Validate(".inf", False)\r
2416             if ErrorCode != 0:\r
2417                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
2418 \r
2419         if Option.PlatformFile is not None:\r
2420             if os.path.isabs (Option.PlatformFile):\r
2421                 if os.path.normcase (os.path.normpath(Option.PlatformFile)).find (Workspace) == 0:\r
2422                     Option.PlatformFile = NormFile(os.path.normpath(Option.PlatformFile), Workspace)\r
2423             Option.PlatformFile = PathClass(Option.PlatformFile, Workspace)\r
2424 \r
2425         if Option.FdfFile is not None:\r
2426             if os.path.isabs (Option.FdfFile):\r
2427                 if os.path.normcase (os.path.normpath(Option.FdfFile)).find (Workspace) == 0:\r
2428                     Option.FdfFile = NormFile(os.path.normpath(Option.FdfFile), Workspace)\r
2429             Option.FdfFile = PathClass(Option.FdfFile, Workspace)\r
2430             ErrorCode, ErrorInfo = Option.FdfFile.Validate(".fdf", False)\r
2431             if ErrorCode != 0:\r
2432                 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
2433 \r
2434         if Option.Flag is not None and Option.Flag not in ['-c', '-s']:\r
2435             EdkLogger.error("build", OPTION_VALUE_INVALID, "UNI flag must be one of -c or -s")\r
2436 \r
2437         MyBuild = Build(Target, Workspace, Option)\r
2438         GlobalData.gCommandLineDefines['ARCH'] = ' '.join(MyBuild.ArchList)\r
2439         if not (MyBuild.LaunchPrebuildFlag and os.path.exists(MyBuild.PlatformBuildPath)):\r
2440             MyBuild.Launch()\r
2441 \r
2442         #\r
2443         # All job done, no error found and no exception raised\r
2444         #\r
2445         BuildError = False\r
2446     except FatalError as X:\r
2447         if MyBuild is not None:\r
2448             # for multi-thread build exits safely\r
2449             MyBuild.Relinquish()\r
2450         if Option is not None and Option.debug is not None:\r
2451             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
2452         ReturnCode = X.args[0]\r
2453     except Warning as X:\r
2454         # error from Fdf parser\r
2455         if MyBuild is not None:\r
2456             # for multi-thread build exits safely\r
2457             MyBuild.Relinquish()\r
2458         if Option is not None and Option.debug is not None:\r
2459             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
2460         else:\r
2461             EdkLogger.error(X.ToolName, FORMAT_INVALID, File=X.FileName, Line=X.LineNumber, ExtraData=X.Message, RaiseError=False)\r
2462         ReturnCode = FORMAT_INVALID\r
2463     except KeyboardInterrupt:\r
2464         ReturnCode = ABORT_ERROR\r
2465         if Option is not None and Option.debug is not None:\r
2466             EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
2467     except:\r
2468         if MyBuild is not None:\r
2469             # for multi-thread build exits safely\r
2470             MyBuild.Relinquish()\r
2471 \r
2472         # try to get the meta-file from the object causing exception\r
2473         Tb = sys.exc_info()[-1]\r
2474         MetaFile = GlobalData.gProcessingFile\r
2475         while Tb is not None:\r
2476             if 'self' in Tb.tb_frame.f_locals and hasattr(Tb.tb_frame.f_locals['self'], 'MetaFile'):\r
2477                 MetaFile = Tb.tb_frame.f_locals['self'].MetaFile\r
2478             Tb = Tb.tb_next\r
2479         EdkLogger.error(\r
2480                     "\nbuild",\r
2481                     CODE_ERROR,\r
2482                     "Unknown fatal error when processing [%s]" % MetaFile,\r
2483                     ExtraData="\n(Please send email to edk2-devel@lists.01.org for help, attaching following call stack trace!)\n",\r
2484                     RaiseError=False\r
2485                     )\r
2486         EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
2487         ReturnCode = CODE_ERROR\r
2488     finally:\r
2489         Utils.Progressor.Abort()\r
2490         Utils.ClearDuplicatedInf()\r
2491 \r
2492     if ReturnCode == 0:\r
2493         try:\r
2494             MyBuild.LaunchPostbuild()\r
2495             Conclusion = "Done"\r
2496         except:\r
2497             Conclusion = "Failed"\r
2498     elif ReturnCode == ABORT_ERROR:\r
2499         Conclusion = "Aborted"\r
2500     else:\r
2501         Conclusion = "Failed"\r
2502     FinishTime = time.time()\r
2503     BuildDuration = time.gmtime(int(round(FinishTime - StartTime)))\r
2504     BuildDurationStr = ""\r
2505     if BuildDuration.tm_yday > 1:\r
2506         BuildDurationStr = time.strftime("%H:%M:%S", BuildDuration) + ", %d day(s)" % (BuildDuration.tm_yday - 1)\r
2507     else:\r
2508         BuildDurationStr = time.strftime("%H:%M:%S", BuildDuration)\r
2509     if MyBuild is not None:\r
2510         if not BuildError:\r
2511             MyBuild.BuildReport.GenerateReport(BuildDurationStr, LogBuildTime(MyBuild.AutoGenTime), LogBuildTime(MyBuild.MakeTime), LogBuildTime(MyBuild.GenFdsTime))\r
2512 \r
2513     EdkLogger.SetLevel(EdkLogger.QUIET)\r
2514     EdkLogger.quiet("\n- %s -" % Conclusion)\r
2515     EdkLogger.quiet(time.strftime("Build end time: %H:%M:%S, %b.%d %Y", time.localtime()))\r
2516     EdkLogger.quiet("Build total time: %s\n" % BuildDurationStr)\r
2517     return ReturnCode\r
2518 \r
2519 if __name__ == '__main__':\r
2520     r = Main()\r
2521     ## 0-127 is a safe return range, and 1 is a standard default error\r
2522     if r < 0 or r > 127: r = 1\r
2523     sys.exit(r)\r
2524 \r