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