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