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