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