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