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