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