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