]> git.proxmox.com Git - mirror_edk2.git/blob - BaseTools/Source/Python/build/build.py
BaseTools: Enable block queue log agent.
[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 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.CanSkipbyHash():
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.Arch in GlobalData.gModuleBuildTracking and
634 self.BuildItem.BuildObject in GlobalData.gModuleBuildTracking[self.BuildItem.BuildObject.Arch] and
635 GlobalData.gModuleBuildTracking[self.BuildItem.BuildObject.Arch][self.BuildItem.BuildObject] != 'FAIL_METAFILE' and
636 not BuildTask._ErrorFlag.isSet()
637 ):
638 GlobalData.gModuleBuildTracking[self.BuildItem.BuildObject.Arch][self.BuildItem.BuildObject] = 'SUCCESS'
639
640 # indicate there's a thread is available for another build task
641 BuildTask._RunningQueueLock.acquire()
642 BuildTask._RunningQueue.pop(self.BuildItem)
643 BuildTask._RunningQueueLock.release()
644 BuildTask._Thread.release()
645
646 ## Start build task thread
647 #
648 def Start(self):
649 EdkLogger.quiet("Building ... %s" % repr(self.BuildItem))
650 Command = self.BuildItem.BuildCommand + [self.BuildItem.Target]
651 self.BuildTread = Thread(target=self._CommandThread, args=(Command, self.BuildItem.WorkingDir))
652 self.BuildTread.setName("build thread")
653 self.BuildTread.setDaemon(False)
654 self.BuildTread.start()
655
656 ## The class contains the information related to EFI image
657 #
658 class PeImageInfo():
659 ## Constructor
660 #
661 # Constructor will load all required image information.
662 #
663 # @param BaseName The full file path of image.
664 # @param Guid The GUID for image.
665 # @param Arch Arch of this image.
666 # @param OutputDir The output directory for image.
667 # @param DebugDir The debug directory for image.
668 # @param ImageClass PeImage Information
669 #
670 def __init__(self, BaseName, Guid, Arch, OutputDir, DebugDir, ImageClass):
671 self.BaseName = BaseName
672 self.Guid = Guid
673 self.Arch = Arch
674 self.OutputDir = OutputDir
675 self.DebugDir = DebugDir
676 self.Image = ImageClass
677 self.Image.Size = (self.Image.Size // 0x1000 + 1) * 0x1000
678
679 ## The class implementing the EDK2 build process
680 #
681 # The build process includes:
682 # 1. Load configuration from target.txt and tools_def.txt in $(WORKSPACE)/Conf
683 # 2. Parse DSC file of active platform
684 # 3. Parse FDF file if any
685 # 4. Establish build database, including parse all other files (module, package)
686 # 5. Create AutoGen files (C code file, depex file, makefile) if necessary
687 # 6. Call build command
688 #
689 class Build():
690 ## Constructor
691 #
692 # Constructor will load all necessary configurations, parse platform, modules
693 # and packages and the establish a database for AutoGen.
694 #
695 # @param Target The build command target, one of gSupportedTarget
696 # @param WorkspaceDir The directory of workspace
697 # @param BuildOptions Build options passed from command line
698 #
699 def __init__(self, Target, WorkspaceDir, BuildOptions,log_q):
700 self.WorkspaceDir = WorkspaceDir
701 self.Target = Target
702 self.PlatformFile = BuildOptions.PlatformFile
703 self.ModuleFile = BuildOptions.ModuleFile
704 self.ArchList = BuildOptions.TargetArch
705 self.ToolChainList = BuildOptions.ToolChain
706 self.BuildTargetList= BuildOptions.BuildTarget
707 self.Fdf = BuildOptions.FdfFile
708 self.FdList = BuildOptions.RomImage
709 self.FvList = BuildOptions.FvImage
710 self.CapList = BuildOptions.CapName
711 self.SilentMode = BuildOptions.SilentMode
712 self.ThreadNumber = 1
713 self.SkipAutoGen = BuildOptions.SkipAutoGen
714 self.Reparse = BuildOptions.Reparse
715 self.SkuId = BuildOptions.SkuId
716 if self.SkuId:
717 GlobalData.gSKUID_CMD = self.SkuId
718 self.ConfDirectory = BuildOptions.ConfDirectory
719 self.SpawnMode = True
720 self.BuildReport = BuildReport(BuildOptions.ReportFile, BuildOptions.ReportType)
721 self.TargetTxt = TargetTxt
722 self.ToolDef = ToolDef
723 self.AutoGenTime = 0
724 self.MakeTime = 0
725 self.GenFdsTime = 0
726 GlobalData.BuildOptionPcd = BuildOptions.OptionPcd if BuildOptions.OptionPcd else []
727 #Set global flag for build mode
728 GlobalData.gIgnoreSource = BuildOptions.IgnoreSources
729 GlobalData.gUseHashCache = BuildOptions.UseHashCache
730 GlobalData.gBinCacheDest = BuildOptions.BinCacheDest
731 GlobalData.gBinCacheSource = BuildOptions.BinCacheSource
732 GlobalData.gEnableGenfdsMultiThread = BuildOptions.GenfdsMultiThread
733 GlobalData.gDisableIncludePathCheck = BuildOptions.DisableIncludePathCheck
734
735 if GlobalData.gBinCacheDest and not GlobalData.gUseHashCache:
736 EdkLogger.error("build", OPTION_NOT_SUPPORTED, ExtraData="--binary-destination must be used together with --hash.")
737
738 if GlobalData.gBinCacheSource and not GlobalData.gUseHashCache:
739 EdkLogger.error("build", OPTION_NOT_SUPPORTED, ExtraData="--binary-source must be used together with --hash.")
740
741 if GlobalData.gBinCacheDest and GlobalData.gBinCacheSource:
742 EdkLogger.error("build", OPTION_NOT_SUPPORTED, ExtraData="--binary-destination can not be used together with --binary-source.")
743
744 if GlobalData.gBinCacheSource:
745 BinCacheSource = os.path.normpath(GlobalData.gBinCacheSource)
746 if not os.path.isabs(BinCacheSource):
747 BinCacheSource = mws.join(self.WorkspaceDir, BinCacheSource)
748 GlobalData.gBinCacheSource = BinCacheSource
749 else:
750 if GlobalData.gBinCacheSource is not None:
751 EdkLogger.error("build", OPTION_VALUE_INVALID, ExtraData="Invalid value of option --binary-source.")
752
753 if GlobalData.gBinCacheDest:
754 BinCacheDest = os.path.normpath(GlobalData.gBinCacheDest)
755 if not os.path.isabs(BinCacheDest):
756 BinCacheDest = mws.join(self.WorkspaceDir, BinCacheDest)
757 GlobalData.gBinCacheDest = BinCacheDest
758 else:
759 if GlobalData.gBinCacheDest is not None:
760 EdkLogger.error("build", OPTION_VALUE_INVALID, ExtraData="Invalid value of option --binary-destination.")
761
762 GlobalData.gDatabasePath = os.path.normpath(os.path.join(GlobalData.gConfDirectory, GlobalData.gDatabasePath))
763 if not os.path.exists(os.path.join(GlobalData.gConfDirectory, '.cache')):
764 os.makedirs(os.path.join(GlobalData.gConfDirectory, '.cache'))
765 self.Db = BuildDB
766 self.BuildDatabase = self.Db.BuildObject
767 self.Platform = None
768 self.ToolChainFamily = None
769 self.LoadFixAddress = 0
770 self.UniFlag = BuildOptions.Flag
771 self.BuildModules = []
772 self.HashSkipModules = []
773 self.Db_Flag = False
774 self.LaunchPrebuildFlag = False
775 self.PlatformBuildPath = os.path.join(GlobalData.gConfDirectory, '.cache', '.PlatformBuild')
776 if BuildOptions.CommandLength:
777 GlobalData.gCommandMaxLength = BuildOptions.CommandLength
778
779 # print dot character during doing some time-consuming work
780 self.Progress = Utils.Progressor()
781 # print current build environment and configuration
782 EdkLogger.quiet("%-16s = %s" % ("WORKSPACE", os.environ["WORKSPACE"]))
783 if "PACKAGES_PATH" in os.environ:
784 # WORKSPACE env has been converted before. Print the same path style with WORKSPACE env.
785 EdkLogger.quiet("%-16s = %s" % ("PACKAGES_PATH", os.path.normcase(os.path.normpath(os.environ["PACKAGES_PATH"]))))
786 EdkLogger.quiet("%-16s = %s" % ("EDK_TOOLS_PATH", os.environ["EDK_TOOLS_PATH"]))
787 if "EDK_TOOLS_BIN" in os.environ:
788 # Print the same path style with WORKSPACE env.
789 EdkLogger.quiet("%-16s = %s" % ("EDK_TOOLS_BIN", os.path.normcase(os.path.normpath(os.environ["EDK_TOOLS_BIN"]))))
790 EdkLogger.quiet("%-16s = %s" % ("CONF_PATH", GlobalData.gConfDirectory))
791 if "PYTHON3_ENABLE" in os.environ:
792 PYTHON3_ENABLE = os.environ["PYTHON3_ENABLE"]
793 if PYTHON3_ENABLE != "TRUE":
794 PYTHON3_ENABLE = "FALSE"
795 EdkLogger.quiet("%-16s = %s" % ("PYTHON3_ENABLE", PYTHON3_ENABLE))
796 if "PYTHON_COMMAND" in os.environ:
797 EdkLogger.quiet("%-16s = %s" % ("PYTHON_COMMAND", os.environ["PYTHON_COMMAND"]))
798 self.InitPreBuild()
799 self.InitPostBuild()
800 if self.Prebuild:
801 EdkLogger.quiet("%-16s = %s" % ("PREBUILD", self.Prebuild))
802 if self.Postbuild:
803 EdkLogger.quiet("%-16s = %s" % ("POSTBUILD", self.Postbuild))
804 if self.Prebuild:
805 self.LaunchPrebuild()
806 self.TargetTxt = TargetTxt
807 self.ToolDef = ToolDef
808 if not (self.LaunchPrebuildFlag and os.path.exists(self.PlatformBuildPath)):
809 self.InitBuild()
810
811 self.AutoGenMgr = None
812 EdkLogger.info("")
813 os.chdir(self.WorkspaceDir)
814 self.share_data = Manager().dict()
815 self.log_q = log_q
816 def StartAutoGen(self,mqueue, DataPipe,SkipAutoGen,PcdMaList,share_data):
817 try:
818 if SkipAutoGen:
819 return True,0
820 feedback_q = mp.Queue()
821 file_lock = mp.Lock()
822 error_event = mp.Event()
823 auto_workers = [AutoGenWorkerInProcess(mqueue,DataPipe.dump_file,feedback_q,file_lock,share_data,self.log_q,error_event) for _ in range(self.ThreadNumber)]
824 self.AutoGenMgr = AutoGenManager(auto_workers,feedback_q,error_event)
825 self.AutoGenMgr.start()
826 for w in auto_workers:
827 w.start()
828 if PcdMaList is not None:
829 for PcdMa in PcdMaList:
830 PcdMa.CreateCodeFile(False)
831 PcdMa.CreateMakeFile(False,GenFfsList = DataPipe.Get("FfsCommand").get((PcdMa.MetaFile.File, PcdMa.Arch),[]))
832
833 self.AutoGenMgr.join()
834 rt = self.AutoGenMgr.Status
835 return rt, 0
836 except Exception as e:
837 return False,e.errcode
838
839 ## Load configuration
840 #
841 # This method will parse target.txt and get the build configurations.
842 #
843 def LoadConfiguration(self):
844
845 # if no ARCH given in command line, get it from target.txt
846 if not self.ArchList:
847 self.ArchList = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_TARGET_ARCH]
848 self.ArchList = tuple(self.ArchList)
849
850 # if no build target given in command line, get it from target.txt
851 if not self.BuildTargetList:
852 self.BuildTargetList = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_TARGET]
853
854 # if no tool chain given in command line, get it from target.txt
855 if not self.ToolChainList:
856 self.ToolChainList = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_TOOL_CHAIN_TAG]
857 if self.ToolChainList is None or len(self.ToolChainList) == 0:
858 EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, ExtraData="No toolchain given. Don't know how to build.\n")
859
860 # check if the tool chains are defined or not
861 NewToolChainList = []
862 for ToolChain in self.ToolChainList:
863 if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG]:
864 EdkLogger.warn("build", "Tool chain [%s] is not defined" % ToolChain)
865 else:
866 NewToolChainList.append(ToolChain)
867 # if no tool chain available, break the build
868 if len(NewToolChainList) == 0:
869 EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,
870 ExtraData="[%s] not defined. No toolchain available for build!\n" % ", ".join(self.ToolChainList))
871 else:
872 self.ToolChainList = NewToolChainList
873
874 ToolChainFamily = []
875 ToolDefinition = self.ToolDef.ToolsDefTxtDatabase
876 for Tool in self.ToolChainList:
877 if TAB_TOD_DEFINES_FAMILY not in ToolDefinition or Tool not in ToolDefinition[TAB_TOD_DEFINES_FAMILY] \
878 or not ToolDefinition[TAB_TOD_DEFINES_FAMILY][Tool]:
879 EdkLogger.warn("build", "No tool chain family found in configuration for %s. Default to MSFT." % Tool)
880 ToolChainFamily.append(TAB_COMPILER_MSFT)
881 else:
882 ToolChainFamily.append(ToolDefinition[TAB_TOD_DEFINES_FAMILY][Tool])
883 self.ToolChainFamily = ToolChainFamily
884
885 if not self.PlatformFile:
886 PlatformFile = self.TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_ACTIVE_PLATFORM]
887 if not PlatformFile:
888 # Try to find one in current directory
889 WorkingDirectory = os.getcwd()
890 FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.dsc')))
891 FileNum = len(FileList)
892 if FileNum >= 2:
893 EdkLogger.error("build", OPTION_MISSING,
894 ExtraData="There are %d DSC files in %s. Use '-p' to specify one.\n" % (FileNum, WorkingDirectory))
895 elif FileNum == 1:
896 PlatformFile = FileList[0]
897 else:
898 EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,
899 ExtraData="No active platform specified in target.txt or command line! Nothing can be built.\n")
900
901 self.PlatformFile = PathClass(NormFile(PlatformFile, self.WorkspaceDir), self.WorkspaceDir)
902 self.ThreadNumber = ThreadNum()
903 ## Initialize build configuration
904 #
905 # This method will parse DSC file and merge the configurations from
906 # command line and target.txt, then get the final build configurations.
907 #
908 def InitBuild(self):
909 # parse target.txt, tools_def.txt, and platform file
910 self.LoadConfiguration()
911
912 # Allow case-insensitive for those from command line or configuration file
913 ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False)
914 if ErrorCode != 0:
915 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)
916
917
918 def InitPreBuild(self):
919 self.LoadConfiguration()
920 ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False)
921 if ErrorCode != 0:
922 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)
923 if self.BuildTargetList:
924 GlobalData.gGlobalDefines['TARGET'] = self.BuildTargetList[0]
925 if self.ArchList:
926 GlobalData.gGlobalDefines['ARCH'] = self.ArchList[0]
927 if self.ToolChainList:
928 GlobalData.gGlobalDefines['TOOLCHAIN'] = self.ToolChainList[0]
929 GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = self.ToolChainList[0]
930 if self.ToolChainFamily:
931 GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[0]
932 if 'PREBUILD' in GlobalData.gCommandLineDefines:
933 self.Prebuild = GlobalData.gCommandLineDefines.get('PREBUILD')
934 else:
935 self.Db_Flag = True
936 Platform = self.Db.MapPlatform(str(self.PlatformFile))
937 self.Prebuild = str(Platform.Prebuild)
938 if self.Prebuild:
939 PrebuildList = []
940 #
941 # Evaluate all arguments and convert arguments that are WORKSPACE
942 # relative paths to absolute paths. Filter arguments that look like
943 # flags or do not follow the file/dir naming rules to avoid false
944 # positives on this conversion.
945 #
946 for Arg in self.Prebuild.split():
947 #
948 # Do not modify Arg if it looks like a flag or an absolute file path
949 #
950 if Arg.startswith('-') or os.path.isabs(Arg):
951 PrebuildList.append(Arg)
952 continue
953 #
954 # Do not modify Arg if it does not look like a Workspace relative
955 # path that starts with a valid package directory name
956 #
957 if not Arg[0].isalpha() or os.path.dirname(Arg) == '':
958 PrebuildList.append(Arg)
959 continue
960 #
961 # If Arg looks like a WORKSPACE relative path, then convert to an
962 # absolute path and check to see if the file exists.
963 #
964 Temp = mws.join(self.WorkspaceDir, Arg)
965 if os.path.isfile(Temp):
966 Arg = Temp
967 PrebuildList.append(Arg)
968 self.Prebuild = ' '.join(PrebuildList)
969 self.Prebuild += self.PassCommandOption(self.BuildTargetList, self.ArchList, self.ToolChainList, self.PlatformFile, self.Target)
970
971 def InitPostBuild(self):
972 if 'POSTBUILD' in GlobalData.gCommandLineDefines:
973 self.Postbuild = GlobalData.gCommandLineDefines.get('POSTBUILD')
974 else:
975 Platform = self.Db.MapPlatform(str(self.PlatformFile))
976 self.Postbuild = str(Platform.Postbuild)
977 if self.Postbuild:
978 PostbuildList = []
979 #
980 # Evaluate all arguments and convert arguments that are WORKSPACE
981 # relative paths to absolute paths. Filter arguments that look like
982 # flags or do not follow the file/dir naming rules to avoid false
983 # positives on this conversion.
984 #
985 for Arg in self.Postbuild.split():
986 #
987 # Do not modify Arg if it looks like a flag or an absolute file path
988 #
989 if Arg.startswith('-') or os.path.isabs(Arg):
990 PostbuildList.append(Arg)
991 continue
992 #
993 # Do not modify Arg if it does not look like a Workspace relative
994 # path that starts with a valid package directory name
995 #
996 if not Arg[0].isalpha() or os.path.dirname(Arg) == '':
997 PostbuildList.append(Arg)
998 continue
999 #
1000 # If Arg looks like a WORKSPACE relative path, then convert to an
1001 # absolute path and check to see if the file exists.
1002 #
1003 Temp = mws.join(self.WorkspaceDir, Arg)
1004 if os.path.isfile(Temp):
1005 Arg = Temp
1006 PostbuildList.append(Arg)
1007 self.Postbuild = ' '.join(PostbuildList)
1008 self.Postbuild += self.PassCommandOption(self.BuildTargetList, self.ArchList, self.ToolChainList, self.PlatformFile, self.Target)
1009
1010 def PassCommandOption(self, BuildTarget, TargetArch, ToolChain, PlatformFile, Target):
1011 BuildStr = ''
1012 if GlobalData.gCommand and isinstance(GlobalData.gCommand, list):
1013 BuildStr += ' ' + ' '.join(GlobalData.gCommand)
1014 TargetFlag = False
1015 ArchFlag = False
1016 ToolChainFlag = False
1017 PlatformFileFlag = False
1018
1019 if GlobalData.gOptions and not GlobalData.gOptions.BuildTarget:
1020 TargetFlag = True
1021 if GlobalData.gOptions and not GlobalData.gOptions.TargetArch:
1022 ArchFlag = True
1023 if GlobalData.gOptions and not GlobalData.gOptions.ToolChain:
1024 ToolChainFlag = True
1025 if GlobalData.gOptions and not GlobalData.gOptions.PlatformFile:
1026 PlatformFileFlag = True
1027
1028 if TargetFlag and BuildTarget:
1029 if isinstance(BuildTarget, list) or isinstance(BuildTarget, tuple):
1030 BuildStr += ' -b ' + ' -b '.join(BuildTarget)
1031 elif isinstance(BuildTarget, str):
1032 BuildStr += ' -b ' + BuildTarget
1033 if ArchFlag and TargetArch:
1034 if isinstance(TargetArch, list) or isinstance(TargetArch, tuple):
1035 BuildStr += ' -a ' + ' -a '.join(TargetArch)
1036 elif isinstance(TargetArch, str):
1037 BuildStr += ' -a ' + TargetArch
1038 if ToolChainFlag and ToolChain:
1039 if isinstance(ToolChain, list) or isinstance(ToolChain, tuple):
1040 BuildStr += ' -t ' + ' -t '.join(ToolChain)
1041 elif isinstance(ToolChain, str):
1042 BuildStr += ' -t ' + ToolChain
1043 if PlatformFileFlag and PlatformFile:
1044 if isinstance(PlatformFile, list) or isinstance(PlatformFile, tuple):
1045 BuildStr += ' -p ' + ' -p '.join(PlatformFile)
1046 elif isinstance(PlatformFile, str):
1047 BuildStr += ' -p' + PlatformFile
1048 BuildStr += ' --conf=' + GlobalData.gConfDirectory
1049 if Target:
1050 BuildStr += ' ' + Target
1051
1052 return BuildStr
1053
1054 def LaunchPrebuild(self):
1055 if self.Prebuild:
1056 EdkLogger.info("\n- Prebuild Start -\n")
1057 self.LaunchPrebuildFlag = True
1058 #
1059 # The purpose of .PrebuildEnv file is capture environment variable settings set by the prebuild script
1060 # and preserve them for the rest of the main build step, because the child process environment will
1061 # evaporate as soon as it exits, we cannot get it in build step.
1062 #
1063 PrebuildEnvFile = os.path.join(GlobalData.gConfDirectory, '.cache', '.PrebuildEnv')
1064 if os.path.isfile(PrebuildEnvFile):
1065 os.remove(PrebuildEnvFile)
1066 if os.path.isfile(self.PlatformBuildPath):
1067 os.remove(self.PlatformBuildPath)
1068 if sys.platform == "win32":
1069 args = ' && '.join((self.Prebuild, 'set > ' + PrebuildEnvFile))
1070 Process = Popen(args, stdout=PIPE, stderr=PIPE, shell=True)
1071 else:
1072 args = ' && '.join((self.Prebuild, 'env > ' + PrebuildEnvFile))
1073 Process = Popen(args, stdout=PIPE, stderr=PIPE, shell=True)
1074
1075 # launch two threads to read the STDOUT and STDERR
1076 EndOfProcedure = Event()
1077 EndOfProcedure.clear()
1078 if Process.stdout:
1079 StdOutThread = Thread(target=ReadMessage, args=(Process.stdout, EdkLogger.info, EndOfProcedure))
1080 StdOutThread.setName("STDOUT-Redirector")
1081 StdOutThread.setDaemon(False)
1082 StdOutThread.start()
1083
1084 if Process.stderr:
1085 StdErrThread = Thread(target=ReadMessage, args=(Process.stderr, EdkLogger.quiet, EndOfProcedure))
1086 StdErrThread.setName("STDERR-Redirector")
1087 StdErrThread.setDaemon(False)
1088 StdErrThread.start()
1089 # waiting for program exit
1090 Process.wait()
1091
1092 if Process.stdout:
1093 StdOutThread.join()
1094 if Process.stderr:
1095 StdErrThread.join()
1096 if Process.returncode != 0 :
1097 EdkLogger.error("Prebuild", PREBUILD_ERROR, 'Prebuild process is not success!')
1098
1099 if os.path.exists(PrebuildEnvFile):
1100 f = open(PrebuildEnvFile)
1101 envs = f.readlines()
1102 f.close()
1103 envs = [l.split("=", 1) for l in envs ]
1104 envs = [[I.strip() for I in item] for item in envs if len(item) == 2]
1105 os.environ.update(dict(envs))
1106 EdkLogger.info("\n- Prebuild Done -\n")
1107
1108 def LaunchPostbuild(self):
1109 if self.Postbuild:
1110 EdkLogger.info("\n- Postbuild Start -\n")
1111 if sys.platform == "win32":
1112 Process = Popen(self.Postbuild, stdout=PIPE, stderr=PIPE, shell=True)
1113 else:
1114 Process = Popen(self.Postbuild, stdout=PIPE, stderr=PIPE, shell=True)
1115 # launch two threads to read the STDOUT and STDERR
1116 EndOfProcedure = Event()
1117 EndOfProcedure.clear()
1118 if Process.stdout:
1119 StdOutThread = Thread(target=ReadMessage, args=(Process.stdout, EdkLogger.info, EndOfProcedure))
1120 StdOutThread.setName("STDOUT-Redirector")
1121 StdOutThread.setDaemon(False)
1122 StdOutThread.start()
1123
1124 if Process.stderr:
1125 StdErrThread = Thread(target=ReadMessage, args=(Process.stderr, EdkLogger.quiet, EndOfProcedure))
1126 StdErrThread.setName("STDERR-Redirector")
1127 StdErrThread.setDaemon(False)
1128 StdErrThread.start()
1129 # waiting for program exit
1130 Process.wait()
1131
1132 if Process.stdout:
1133 StdOutThread.join()
1134 if Process.stderr:
1135 StdErrThread.join()
1136 if Process.returncode != 0 :
1137 EdkLogger.error("Postbuild", POSTBUILD_ERROR, 'Postbuild process is not success!')
1138 EdkLogger.info("\n- Postbuild Done -\n")
1139
1140 ## Error handling for hash feature
1141 #
1142 # On BuildTask error, iterate through the Module Build tracking
1143 # dictionary to determine wheather a module failed to build. Invalidate
1144 # the hash associated with that module by removing it from storage.
1145 #
1146 #
1147 def invalidateHash(self):
1148 # Only for hashing feature
1149 if not GlobalData.gUseHashCache:
1150 return
1151
1152 # GlobalData.gModuleBuildTracking contains only modules or libs that cannot be skipped by hash
1153 for moduleAutoGenObjArch in GlobalData.gModuleBuildTracking.keys():
1154 for moduleAutoGenObj in GlobalData.gModuleBuildTracking[moduleAutoGenObjArch].keys():
1155 # Skip invalidating for Successful Module/Lib builds
1156 if GlobalData.gModuleBuildTracking[moduleAutoGenObjArch][moduleAutoGenObj] == 'SUCCESS':
1157 continue
1158
1159 # The module failed to build, failed to start building, or failed the header check test from this point on
1160
1161 # Remove .hash from build
1162 ModuleHashFile = os.path.join(moduleAutoGenObj.BuildDir, moduleAutoGenObj.Name + ".hash")
1163 if os.path.exists(ModuleHashFile):
1164 os.remove(ModuleHashFile)
1165
1166 # Remove .hash file from cache
1167 if GlobalData.gBinCacheDest:
1168 FileDir = os.path.join(GlobalData.gBinCacheDest, moduleAutoGenObj.Arch, moduleAutoGenObj.SourceDir, moduleAutoGenObj.MetaFile.BaseName)
1169 HashFile = os.path.join(FileDir, moduleAutoGenObj.Name + '.hash')
1170 if os.path.exists(HashFile):
1171 os.remove(HashFile)
1172
1173 ## Build a module or platform
1174 #
1175 # Create autogen code and makefile for a module or platform, and the launch
1176 # "make" command to build it
1177 #
1178 # @param Target The target of build command
1179 # @param Platform The platform file
1180 # @param Module The module file
1181 # @param BuildTarget The name of build target, one of "DEBUG", "RELEASE"
1182 # @param ToolChain The name of toolchain to build
1183 # @param Arch The arch of the module/platform
1184 # @param CreateDepModuleCodeFile Flag used to indicate creating code
1185 # for dependent modules/Libraries
1186 # @param CreateDepModuleMakeFile Flag used to indicate creating makefile
1187 # for dependent modules/Libraries
1188 #
1189 def _BuildPa(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True, BuildModule=False, FfsCommand=None, PcdMaList=None):
1190 if AutoGenObject is None:
1191 return False
1192 if FfsCommand is None:
1193 FfsCommand = {}
1194 # skip file generation for cleanxxx targets, run and fds target
1195 if Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:
1196 # for target which must generate AutoGen code and makefile
1197 mqueue = mp.Queue()
1198 for m in AutoGenObject.GetAllModuleInfo:
1199 mqueue.put(m)
1200
1201 AutoGenObject.DataPipe.DataContainer = {"FfsCommand":FfsCommand}
1202 self.Progress.Start("Generating makefile and code")
1203 data_pipe_file = os.path.join(AutoGenObject.BuildDir, "GlobalVar_%s_%s.bin" % (str(AutoGenObject.Guid),AutoGenObject.Arch))
1204 AutoGenObject.DataPipe.dump(data_pipe_file)
1205 autogen_rt, errorcode = self.StartAutoGen(mqueue, AutoGenObject.DataPipe, self.SkipAutoGen, PcdMaList,self.share_data)
1206 self.Progress.Stop("done!")
1207 if not autogen_rt:
1208 self.AutoGenMgr.TerminateWorkers()
1209 self.AutoGenMgr.join(0.1)
1210 raise FatalError(errorcode)
1211 AutoGenObject.CreateCodeFile(False)
1212 AutoGenObject.CreateMakeFile(False)
1213 else:
1214 # always recreate top/platform makefile when clean, just in case of inconsistency
1215 AutoGenObject.CreateCodeFile(True)
1216 AutoGenObject.CreateMakeFile(True)
1217
1218 if EdkLogger.GetLevel() == EdkLogger.QUIET:
1219 EdkLogger.quiet("Building ... %s" % repr(AutoGenObject))
1220
1221 BuildCommand = AutoGenObject.BuildCommand
1222 if BuildCommand is None or len(BuildCommand) == 0:
1223 EdkLogger.error("build", OPTION_MISSING,
1224 "No build command found for this module. "
1225 "Please check your setting of %s_%s_%s_MAKE_PATH in Conf/tools_def.txt file." %
1226 (AutoGenObject.BuildTarget, AutoGenObject.ToolChain, AutoGenObject.Arch),
1227 ExtraData=str(AutoGenObject))
1228
1229 makefile = GenMake.BuildFile(AutoGenObject)._FILE_NAME_[GenMake.gMakeType]
1230
1231 # run
1232 if Target == 'run':
1233 return True
1234
1235 # build modules
1236 if BuildModule:
1237 BuildCommand = BuildCommand + [Target]
1238 LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir)
1239 self.CreateAsBuiltInf()
1240 if GlobalData.gBinCacheDest:
1241 self.UpdateBuildCache()
1242 self.BuildModules = []
1243 return True
1244
1245 # build library
1246 if Target == 'libraries':
1247 for Lib in AutoGenObject.LibraryBuildDirectoryList:
1248 NewBuildCommand = BuildCommand + ['-f', os.path.normpath(os.path.join(Lib, makefile)), 'pbuild']
1249 LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)
1250 return True
1251
1252 # build module
1253 if Target == 'modules':
1254 for Lib in AutoGenObject.LibraryBuildDirectoryList:
1255 NewBuildCommand = BuildCommand + ['-f', os.path.normpath(os.path.join(Lib, makefile)), 'pbuild']
1256 LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)
1257 for Mod in AutoGenObject.ModuleBuildDirectoryList:
1258 NewBuildCommand = BuildCommand + ['-f', os.path.normpath(os.path.join(Mod, makefile)), 'pbuild']
1259 LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)
1260 self.CreateAsBuiltInf()
1261 if GlobalData.gBinCacheDest:
1262 self.UpdateBuildCache()
1263 self.BuildModules = []
1264 return True
1265
1266 # cleanlib
1267 if Target == 'cleanlib':
1268 for Lib in AutoGenObject.LibraryBuildDirectoryList:
1269 LibMakefile = os.path.normpath(os.path.join(Lib, makefile))
1270 if os.path.exists(LibMakefile):
1271 NewBuildCommand = BuildCommand + ['-f', LibMakefile, 'cleanall']
1272 LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)
1273 return True
1274
1275 # clean
1276 if Target == 'clean':
1277 for Mod in AutoGenObject.ModuleBuildDirectoryList:
1278 ModMakefile = os.path.normpath(os.path.join(Mod, makefile))
1279 if os.path.exists(ModMakefile):
1280 NewBuildCommand = BuildCommand + ['-f', ModMakefile, 'cleanall']
1281 LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)
1282 for Lib in AutoGenObject.LibraryBuildDirectoryList:
1283 LibMakefile = os.path.normpath(os.path.join(Lib, makefile))
1284 if os.path.exists(LibMakefile):
1285 NewBuildCommand = BuildCommand + ['-f', LibMakefile, 'cleanall']
1286 LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)
1287 return True
1288
1289 # cleanall
1290 if Target == 'cleanall':
1291 try:
1292 #os.rmdir(AutoGenObject.BuildDir)
1293 RemoveDirectory(AutoGenObject.BuildDir, True)
1294 except WindowsError as X:
1295 EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X))
1296 return True
1297
1298 ## Build a module or platform
1299 #
1300 # Create autogen code and makefile for a module or platform, and the launch
1301 # "make" command to build it
1302 #
1303 # @param Target The target of build command
1304 # @param Platform The platform file
1305 # @param Module The module file
1306 # @param BuildTarget The name of build target, one of "DEBUG", "RELEASE"
1307 # @param ToolChain The name of toolchain to build
1308 # @param Arch The arch of the module/platform
1309 # @param CreateDepModuleCodeFile Flag used to indicate creating code
1310 # for dependent modules/Libraries
1311 # @param CreateDepModuleMakeFile Flag used to indicate creating makefile
1312 # for dependent modules/Libraries
1313 #
1314 def _Build(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True, BuildModule=False):
1315 if AutoGenObject is None:
1316 return False
1317
1318 # skip file generation for cleanxxx targets, run and fds target
1319 if Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:
1320 # for target which must generate AutoGen code and makefile
1321 if not self.SkipAutoGen or Target == 'genc':
1322 self.Progress.Start("Generating code")
1323 AutoGenObject.CreateCodeFile(CreateDepsCodeFile)
1324 self.Progress.Stop("done!")
1325 if Target == "genc":
1326 return True
1327
1328 if not self.SkipAutoGen or Target == 'genmake':
1329 self.Progress.Start("Generating makefile")
1330 AutoGenObject.CreateMakeFile(CreateDepsMakeFile)
1331 #AutoGenObject.CreateAsBuiltInf()
1332 self.Progress.Stop("done!")
1333 if Target == "genmake":
1334 return True
1335 else:
1336 # always recreate top/platform makefile when clean, just in case of inconsistency
1337 AutoGenObject.CreateCodeFile(True)
1338 AutoGenObject.CreateMakeFile(True)
1339
1340 if EdkLogger.GetLevel() == EdkLogger.QUIET:
1341 EdkLogger.quiet("Building ... %s" % repr(AutoGenObject))
1342
1343 BuildCommand = AutoGenObject.BuildCommand
1344 if BuildCommand is None or len(BuildCommand) == 0:
1345 EdkLogger.error("build", OPTION_MISSING,
1346 "No build command found for this module. "
1347 "Please check your setting of %s_%s_%s_MAKE_PATH in Conf/tools_def.txt file." %
1348 (AutoGenObject.BuildTarget, AutoGenObject.ToolChain, AutoGenObject.Arch),
1349 ExtraData=str(AutoGenObject))
1350
1351 # build modules
1352 if BuildModule:
1353 if Target != 'fds':
1354 BuildCommand = BuildCommand + [Target]
1355 AutoGenObject.BuildTime = LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir)
1356 self.CreateAsBuiltInf()
1357 if GlobalData.gBinCacheDest:
1358 self.UpdateBuildCache()
1359 self.BuildModules = []
1360 return True
1361
1362 # genfds
1363 if Target == 'fds':
1364 if GenFdsApi(AutoGenObject.GenFdsCommandDict, self.Db):
1365 EdkLogger.error("build", COMMAND_FAILURE)
1366 return True
1367
1368 # run
1369 if Target == 'run':
1370 return True
1371
1372 # build library
1373 if Target == 'libraries':
1374 pass
1375
1376 # not build modules
1377
1378
1379 # cleanall
1380 if Target == 'cleanall':
1381 try:
1382 #os.rmdir(AutoGenObject.BuildDir)
1383 RemoveDirectory(AutoGenObject.BuildDir, True)
1384 except WindowsError as X:
1385 EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X))
1386 return True
1387
1388 ## Rebase module image and Get function address for the input module list.
1389 #
1390 def _RebaseModule (self, MapBuffer, BaseAddress, ModuleList, AddrIsOffset = True, ModeIsSmm = False):
1391 if ModeIsSmm:
1392 AddrIsOffset = False
1393 for InfFile in ModuleList:
1394 sys.stdout.write (".")
1395 sys.stdout.flush()
1396 ModuleInfo = ModuleList[InfFile]
1397 ModuleName = ModuleInfo.BaseName
1398 ModuleOutputImage = ModuleInfo.Image.FileName
1399 ModuleDebugImage = os.path.join(ModuleInfo.DebugDir, ModuleInfo.BaseName + '.efi')
1400 ## for SMM module in SMRAM, the SMRAM will be allocated from base to top.
1401 if not ModeIsSmm:
1402 BaseAddress = BaseAddress - ModuleInfo.Image.Size
1403 #
1404 # Update Image to new BaseAddress by GenFw tool
1405 #
1406 LaunchCommand(["GenFw", "--rebase", str(BaseAddress), "-r", ModuleOutputImage], ModuleInfo.OutputDir)
1407 LaunchCommand(["GenFw", "--rebase", str(BaseAddress), "-r", ModuleDebugImage], ModuleInfo.DebugDir)
1408 else:
1409 #
1410 # Set new address to the section header only for SMM driver.
1411 #
1412 LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleOutputImage], ModuleInfo.OutputDir)
1413 LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleDebugImage], ModuleInfo.DebugDir)
1414 #
1415 # Collect function address from Map file
1416 #
1417 ImageMapTable = ModuleOutputImage.replace('.efi', '.map')
1418 FunctionList = []
1419 if os.path.exists(ImageMapTable):
1420 OrigImageBaseAddress = 0
1421 ImageMap = open(ImageMapTable, 'r')
1422 for LinStr in ImageMap:
1423 if len (LinStr.strip()) == 0:
1424 continue
1425 #
1426 # Get the preferred address set on link time.
1427 #
1428 if LinStr.find ('Preferred load address is') != -1:
1429 StrList = LinStr.split()
1430 OrigImageBaseAddress = int (StrList[len(StrList) - 1], 16)
1431
1432 StrList = LinStr.split()
1433 if len (StrList) > 4:
1434 if StrList[3] == 'f' or StrList[3] == 'F':
1435 Name = StrList[1]
1436 RelativeAddress = int (StrList[2], 16) - OrigImageBaseAddress
1437 FunctionList.append ((Name, RelativeAddress))
1438
1439 ImageMap.close()
1440 #
1441 # Add general information.
1442 #
1443 if ModeIsSmm:
1444 MapBuffer.append('\n\n%s (Fixed SMRAM Offset, BaseAddress=0x%010X, EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint))
1445 elif AddrIsOffset:
1446 MapBuffer.append('\n\n%s (Fixed Memory Offset, BaseAddress=-0x%010X, EntryPoint=-0x%010X)\n' % (ModuleName, 0 - BaseAddress, 0 - (BaseAddress + ModuleInfo.Image.EntryPoint)))
1447 else:
1448 MapBuffer.append('\n\n%s (Fixed Memory Address, BaseAddress=0x%010X, EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint))
1449 #
1450 # Add guid and general seciton section.
1451 #
1452 TextSectionAddress = 0
1453 DataSectionAddress = 0
1454 for SectionHeader in ModuleInfo.Image.SectionHeaderList:
1455 if SectionHeader[0] == '.text':
1456 TextSectionAddress = SectionHeader[1]
1457 elif SectionHeader[0] in ['.data', '.sdata']:
1458 DataSectionAddress = SectionHeader[1]
1459 if AddrIsOffset:
1460 MapBuffer.append('(GUID=%s, .textbaseaddress=-0x%010X, .databaseaddress=-0x%010X)\n' % (ModuleInfo.Guid, 0 - (BaseAddress + TextSectionAddress), 0 - (BaseAddress + DataSectionAddress)))
1461 else:
1462 MapBuffer.append('(GUID=%s, .textbaseaddress=0x%010X, .databaseaddress=0x%010X)\n' % (ModuleInfo.Guid, BaseAddress + TextSectionAddress, BaseAddress + DataSectionAddress))
1463 #
1464 # Add debug image full path.
1465 #
1466 MapBuffer.append('(IMAGE=%s)\n\n' % (ModuleDebugImage))
1467 #
1468 # Add function address
1469 #
1470 for Function in FunctionList:
1471 if AddrIsOffset:
1472 MapBuffer.append(' -0x%010X %s\n' % (0 - (BaseAddress + Function[1]), Function[0]))
1473 else:
1474 MapBuffer.append(' 0x%010X %s\n' % (BaseAddress + Function[1], Function[0]))
1475 ImageMap.close()
1476
1477 #
1478 # for SMM module in SMRAM, the SMRAM will be allocated from base to top.
1479 #
1480 if ModeIsSmm:
1481 BaseAddress = BaseAddress + ModuleInfo.Image.Size
1482
1483 ## Collect MAP information of all FVs
1484 #
1485 def _CollectFvMapBuffer (self, MapBuffer, Wa, ModuleList):
1486 if self.Fdf:
1487 # First get the XIP base address for FV map file.
1488 GuidPattern = re.compile("[-a-fA-F0-9]+")
1489 GuidName = re.compile(r"\(GUID=[-a-fA-F0-9]+")
1490 for FvName in Wa.FdfProfile.FvDict:
1491 FvMapBuffer = os.path.join(Wa.FvDir, FvName + '.Fv.map')
1492 if not os.path.exists(FvMapBuffer):
1493 continue
1494 FvMap = open(FvMapBuffer, 'r')
1495 #skip FV size information
1496 FvMap.readline()
1497 FvMap.readline()
1498 FvMap.readline()
1499 FvMap.readline()
1500 for Line in FvMap:
1501 MatchGuid = GuidPattern.match(Line)
1502 if MatchGuid is not None:
1503 #
1504 # Replace GUID with module name
1505 #
1506 GuidString = MatchGuid.group()
1507 if GuidString.upper() in ModuleList:
1508 Line = Line.replace(GuidString, ModuleList[GuidString.upper()].Name)
1509 MapBuffer.append(Line)
1510 #
1511 # Add the debug image full path.
1512 #
1513 MatchGuid = GuidName.match(Line)
1514 if MatchGuid is not None:
1515 GuidString = MatchGuid.group().split("=")[1]
1516 if GuidString.upper() in ModuleList:
1517 MapBuffer.append('(IMAGE=%s)\n' % (os.path.join(ModuleList[GuidString.upper()].DebugDir, ModuleList[GuidString.upper()].Name + '.efi')))
1518
1519 FvMap.close()
1520
1521 ## Collect MAP information of all modules
1522 #
1523 def _CollectModuleMapBuffer (self, MapBuffer, ModuleList):
1524 sys.stdout.write ("Generate Load Module At Fix Address Map")
1525 sys.stdout.flush()
1526 PatchEfiImageList = []
1527 PeiModuleList = {}
1528 BtModuleList = {}
1529 RtModuleList = {}
1530 SmmModuleList = {}
1531 PeiSize = 0
1532 BtSize = 0
1533 RtSize = 0
1534 # reserve 4K size in SMRAM to make SMM module address not from 0.
1535 SmmSize = 0x1000
1536 for ModuleGuid in ModuleList:
1537 Module = ModuleList[ModuleGuid]
1538 GlobalData.gProcessingFile = "%s [%s, %s, %s]" % (Module.MetaFile, Module.Arch, Module.ToolChain, Module.BuildTarget)
1539
1540 OutputImageFile = ''
1541 for ResultFile in Module.CodaTargetList:
1542 if str(ResultFile.Target).endswith('.efi'):
1543 #
1544 # module list for PEI, DXE, RUNTIME and SMM
1545 #
1546 OutputImageFile = os.path.join(Module.OutputDir, Module.Name + '.efi')
1547 ImageClass = PeImageClass (OutputImageFile)
1548 if not ImageClass.IsValid:
1549 EdkLogger.error("build", FILE_PARSE_FAILURE, ExtraData=ImageClass.ErrorInfo)
1550 ImageInfo = PeImageInfo(Module.Name, Module.Guid, Module.Arch, Module.OutputDir, Module.DebugDir, ImageClass)
1551 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]:
1552 PeiModuleList[Module.MetaFile] = ImageInfo
1553 PeiSize += ImageInfo.Image.Size
1554 elif Module.ModuleType in [EDK_COMPONENT_TYPE_BS_DRIVER, SUP_MODULE_DXE_DRIVER, SUP_MODULE_UEFI_DRIVER]:
1555 BtModuleList[Module.MetaFile] = ImageInfo
1556 BtSize += ImageInfo.Image.Size
1557 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]:
1558 RtModuleList[Module.MetaFile] = ImageInfo
1559 RtSize += ImageInfo.Image.Size
1560 elif Module.ModuleType in [SUP_MODULE_SMM_CORE, SUP_MODULE_DXE_SMM_DRIVER, SUP_MODULE_MM_STANDALONE, SUP_MODULE_MM_CORE_STANDALONE]:
1561 SmmModuleList[Module.MetaFile] = ImageInfo
1562 SmmSize += ImageInfo.Image.Size
1563 if Module.ModuleType == SUP_MODULE_DXE_SMM_DRIVER:
1564 PiSpecVersion = Module.Module.Specification.get('PI_SPECIFICATION_VERSION', '0x00000000')
1565 # for PI specification < PI1.1, DXE_SMM_DRIVER also runs as BOOT time driver.
1566 if int(PiSpecVersion, 16) < 0x0001000A:
1567 BtModuleList[Module.MetaFile] = ImageInfo
1568 BtSize += ImageInfo.Image.Size
1569 break
1570 #
1571 # EFI image is final target.
1572 # Check EFI image contains patchable FixAddress related PCDs.
1573 #
1574 if OutputImageFile != '':
1575 ModuleIsPatch = False
1576 for Pcd in Module.ModulePcdList:
1577 if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SET:
1578 ModuleIsPatch = True
1579 break
1580 if not ModuleIsPatch:
1581 for Pcd in Module.LibraryPcdList:
1582 if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SET:
1583 ModuleIsPatch = True
1584 break
1585
1586 if not ModuleIsPatch:
1587 continue
1588 #
1589 # Module includes the patchable load fix address PCDs.
1590 # It will be fixed up later.
1591 #
1592 PatchEfiImageList.append (OutputImageFile)
1593
1594 #
1595 # Get Top Memory address
1596 #
1597 ReservedRuntimeMemorySize = 0
1598 TopMemoryAddress = 0
1599 if self.LoadFixAddress == 0xFFFFFFFFFFFFFFFF:
1600 TopMemoryAddress = 0
1601 else:
1602 TopMemoryAddress = self.LoadFixAddress
1603 if TopMemoryAddress < RtSize + BtSize + PeiSize:
1604 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS is too low to load driver")
1605
1606 #
1607 # Patch FixAddress related PCDs into EFI image
1608 #
1609 for EfiImage in PatchEfiImageList:
1610 EfiImageMap = EfiImage.replace('.efi', '.map')
1611 if not os.path.exists(EfiImageMap):
1612 continue
1613 #
1614 # Get PCD offset in EFI image by GenPatchPcdTable function
1615 #
1616 PcdTable = parsePcdInfoFromMapFile(EfiImageMap, EfiImage)
1617 #
1618 # Patch real PCD value by PatchPcdValue tool
1619 #
1620 for PcdInfo in PcdTable:
1621 ReturnValue = 0
1622 if PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE:
1623 ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE_DATA_TYPE, str (PeiSize // 0x1000))
1624 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE:
1625 ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE_DATA_TYPE, str (BtSize // 0x1000))
1626 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE:
1627 ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE_DATA_TYPE, str (RtSize // 0x1000))
1628 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE and len (SmmModuleList) > 0:
1629 ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE_DATA_TYPE, str (SmmSize // 0x1000))
1630 if ReturnValue != 0:
1631 EdkLogger.error("build", PARAMETER_INVALID, "Patch PCD value failed", ExtraData=ErrorInfo)
1632
1633 MapBuffer.append('PEI_CODE_PAGE_NUMBER = 0x%x\n' % (PeiSize // 0x1000))
1634 MapBuffer.append('BOOT_CODE_PAGE_NUMBER = 0x%x\n' % (BtSize // 0x1000))
1635 MapBuffer.append('RUNTIME_CODE_PAGE_NUMBER = 0x%x\n' % (RtSize // 0x1000))
1636 if len (SmmModuleList) > 0:
1637 MapBuffer.append('SMM_CODE_PAGE_NUMBER = 0x%x\n' % (SmmSize // 0x1000))
1638
1639 PeiBaseAddr = TopMemoryAddress - RtSize - BtSize
1640 BtBaseAddr = TopMemoryAddress - RtSize
1641 RtBaseAddr = TopMemoryAddress - ReservedRuntimeMemorySize
1642
1643 self._RebaseModule (MapBuffer, PeiBaseAddr, PeiModuleList, TopMemoryAddress == 0)
1644 self._RebaseModule (MapBuffer, BtBaseAddr, BtModuleList, TopMemoryAddress == 0)
1645 self._RebaseModule (MapBuffer, RtBaseAddr, RtModuleList, TopMemoryAddress == 0)
1646 self._RebaseModule (MapBuffer, 0x1000, SmmModuleList, AddrIsOffset=False, ModeIsSmm=True)
1647 MapBuffer.append('\n\n')
1648 sys.stdout.write ("\n")
1649 sys.stdout.flush()
1650
1651 ## Save platform Map file
1652 #
1653 def _SaveMapFile (self, MapBuffer, Wa):
1654 #
1655 # Map file path is got.
1656 #
1657 MapFilePath = os.path.join(Wa.BuildDir, Wa.Name + '.map')
1658 #
1659 # Save address map into MAP file.
1660 #
1661 SaveFileOnChange(MapFilePath, ''.join(MapBuffer), False)
1662 if self.LoadFixAddress != 0:
1663 sys.stdout.write ("\nLoad Module At Fix Address Map file can be found at %s\n" % (MapFilePath))
1664 sys.stdout.flush()
1665
1666 ## Build active platform for different build targets and different tool chains
1667 #
1668 def _BuildPlatform(self):
1669 SaveFileOnChange(self.PlatformBuildPath, '# DO NOT EDIT \n# FILE auto-generated\n', False)
1670 for BuildTarget in self.BuildTargetList:
1671 GlobalData.gGlobalDefines['TARGET'] = BuildTarget
1672 index = 0
1673 for ToolChain in self.ToolChainList:
1674 GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain
1675 GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain
1676 GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index]
1677 index += 1
1678 Wa = WorkspaceAutoGen(
1679 self.WorkspaceDir,
1680 self.PlatformFile,
1681 BuildTarget,
1682 ToolChain,
1683 self.ArchList,
1684 self.BuildDatabase,
1685 self.TargetTxt,
1686 self.ToolDef,
1687 self.Fdf,
1688 self.FdList,
1689 self.FvList,
1690 self.CapList,
1691 self.SkuId,
1692 self.UniFlag,
1693 self.Progress
1694 )
1695 self.Fdf = Wa.FdfFile
1696 self.LoadFixAddress = Wa.Platform.LoadFixAddress
1697 self.BuildReport.AddPlatformReport(Wa)
1698 self.Progress.Stop("done!")
1699
1700 # Add ffs build to makefile
1701 CmdListDict = {}
1702 if GlobalData.gEnableGenfdsMultiThread and self.Fdf:
1703 CmdListDict = self._GenFfsCmd(Wa.ArchList)
1704
1705 for Arch in Wa.ArchList:
1706 PcdMaList = []
1707 GlobalData.gGlobalDefines['ARCH'] = Arch
1708 Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)
1709 for Module in Pa.Platform.Modules:
1710 # Get ModuleAutoGen object to generate C code file and makefile
1711 Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile,Pa.DataPipe)
1712 if Ma is None:
1713 continue
1714 if Ma.PcdIsDriver:
1715 Ma.PlatformInfo = Pa
1716 Ma.Workspace = Wa
1717 PcdMaList.append(Ma)
1718 self.BuildModules.append(Ma)
1719 self._BuildPa(self.Target, Pa, FfsCommand=CmdListDict,PcdMaList=PcdMaList)
1720
1721 # Create MAP file when Load Fix Address is enabled.
1722 if self.Target in ["", "all", "fds"]:
1723 for Arch in Wa.ArchList:
1724 GlobalData.gGlobalDefines['ARCH'] = Arch
1725 #
1726 # Check whether the set fix address is above 4G for 32bit image.
1727 #
1728 if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:
1729 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")
1730 #
1731 # Get Module List
1732 #
1733 ModuleList = {}
1734 for Pa in Wa.AutoGenObjectList:
1735 for Ma in Pa.ModuleAutoGenList:
1736 if Ma is None:
1737 continue
1738 if not Ma.IsLibrary:
1739 ModuleList[Ma.Guid.upper()] = Ma
1740
1741 MapBuffer = []
1742 if self.LoadFixAddress != 0:
1743 #
1744 # Rebase module to the preferred memory address before GenFds
1745 #
1746 self._CollectModuleMapBuffer(MapBuffer, ModuleList)
1747 if self.Fdf:
1748 #
1749 # create FDS again for the updated EFI image
1750 #
1751 self._Build("fds", Wa)
1752 #
1753 # Create MAP file for all platform FVs after GenFds.
1754 #
1755 self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)
1756 #
1757 # Save MAP buffer into MAP file.
1758 #
1759 self._SaveMapFile (MapBuffer, Wa)
1760
1761 ## Build active module for different build targets, different tool chains and different archs
1762 #
1763 def _BuildModule(self):
1764 for BuildTarget in self.BuildTargetList:
1765 GlobalData.gGlobalDefines['TARGET'] = BuildTarget
1766 index = 0
1767 for ToolChain in self.ToolChainList:
1768 WorkspaceAutoGenTime = time.time()
1769 GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain
1770 GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain
1771 GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index]
1772 index += 1
1773 #
1774 # module build needs platform build information, so get platform
1775 # AutoGen first
1776 #
1777 Wa = WorkspaceAutoGen(
1778 self.WorkspaceDir,
1779 self.PlatformFile,
1780 BuildTarget,
1781 ToolChain,
1782 self.ArchList,
1783 self.BuildDatabase,
1784 self.TargetTxt,
1785 self.ToolDef,
1786 self.Fdf,
1787 self.FdList,
1788 self.FvList,
1789 self.CapList,
1790 self.SkuId,
1791 self.UniFlag,
1792 self.Progress,
1793 self.ModuleFile
1794 )
1795 self.Fdf = Wa.FdfFile
1796 self.LoadFixAddress = Wa.Platform.LoadFixAddress
1797 Wa.CreateMakeFile(False)
1798 # Add ffs build to makefile
1799 CmdListDict = None
1800 if GlobalData.gEnableGenfdsMultiThread and self.Fdf:
1801 CmdListDict = self._GenFfsCmd(Wa.ArchList)
1802 self.Progress.Stop("done!")
1803 MaList = []
1804 ExitFlag = threading.Event()
1805 ExitFlag.clear()
1806 self.AutoGenTime += int(round((time.time() - WorkspaceAutoGenTime)))
1807 for Arch in Wa.ArchList:
1808 AutoGenStart = time.time()
1809 GlobalData.gGlobalDefines['ARCH'] = Arch
1810 Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)
1811 for Module in Pa.Platform.Modules:
1812 if self.ModuleFile.Dir == Module.Dir and self.ModuleFile.Name == Module.Name:
1813 Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile,Pa.DataPipe)
1814 if Ma is None:
1815 continue
1816 MaList.append(Ma)
1817 if Ma.CanSkipbyHash():
1818 self.HashSkipModules.append(Ma)
1819 if GlobalData.gBinCacheSource:
1820 EdkLogger.quiet("cache hit: %s[%s]" % (Ma.MetaFile.Path, Ma.Arch))
1821 continue
1822 else:
1823 if GlobalData.gBinCacheSource:
1824 EdkLogger.quiet("cache miss: %s[%s]" % (Ma.MetaFile.Path, Ma.Arch))
1825 # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'
1826 if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:
1827 # for target which must generate AutoGen code and makefile
1828 if not self.SkipAutoGen or self.Target == 'genc':
1829 self.Progress.Start("Generating code")
1830 Ma.CreateCodeFile(True)
1831 self.Progress.Stop("done!")
1832 if self.Target == "genc":
1833 return True
1834 if not self.SkipAutoGen or self.Target == 'genmake':
1835 self.Progress.Start("Generating makefile")
1836 if CmdListDict and self.Fdf and (Module.File, Arch) in CmdListDict:
1837 Ma.CreateMakeFile(True, CmdListDict[Module.File, Arch])
1838 del CmdListDict[Module.File, Arch]
1839 else:
1840 Ma.CreateMakeFile(True)
1841 self.Progress.Stop("done!")
1842 if self.Target == "genmake":
1843 return True
1844 self.BuildModules.append(Ma)
1845 # Initialize all modules in tracking to 'FAIL'
1846 if Ma.Arch not in GlobalData.gModuleBuildTracking:
1847 GlobalData.gModuleBuildTracking[Ma.Arch] = dict()
1848 if Ma not in GlobalData.gModuleBuildTracking[Ma.Arch]:
1849 GlobalData.gModuleBuildTracking[Ma.Arch][Ma] = 'FAIL'
1850 self.AutoGenTime += int(round((time.time() - AutoGenStart)))
1851 MakeStart = time.time()
1852 for Ma in self.BuildModules:
1853 if not Ma.IsBinaryModule:
1854 Bt = BuildTask.New(ModuleMakeUnit(Ma, Pa.BuildCommand,self.Target))
1855 # Break build if any build thread has error
1856 if BuildTask.HasError():
1857 # we need a full version of makefile for platform
1858 ExitFlag.set()
1859 BuildTask.WaitForComplete()
1860 self.invalidateHash()
1861 Pa.CreateMakeFile(False)
1862 EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)
1863 # Start task scheduler
1864 if not BuildTask.IsOnGoing():
1865 BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)
1866
1867 # in case there's an interruption. we need a full version of makefile for platform
1868 Pa.CreateMakeFile(False)
1869 if BuildTask.HasError():
1870 self.invalidateHash()
1871 EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)
1872 self.MakeTime += int(round((time.time() - MakeStart)))
1873
1874 MakeContiue = time.time()
1875 ExitFlag.set()
1876 BuildTask.WaitForComplete()
1877 self.CreateAsBuiltInf()
1878 if GlobalData.gBinCacheDest:
1879 self.UpdateBuildCache()
1880 self.BuildModules = []
1881 self.MakeTime += int(round((time.time() - MakeContiue)))
1882 if BuildTask.HasError():
1883 self.invalidateHash()
1884 EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)
1885
1886 self.BuildReport.AddPlatformReport(Wa, MaList)
1887 if MaList == []:
1888 EdkLogger.error(
1889 'build',
1890 BUILD_ERROR,
1891 "Module for [%s] is not a component of active platform."\
1892 " Please make sure that the ARCH and inf file path are"\
1893 " given in the same as in [%s]" % \
1894 (', '.join(Wa.ArchList), self.PlatformFile),
1895 ExtraData=self.ModuleFile
1896 )
1897 # Create MAP file when Load Fix Address is enabled.
1898 if self.Target == "fds" and self.Fdf:
1899 for Arch in Wa.ArchList:
1900 #
1901 # Check whether the set fix address is above 4G for 32bit image.
1902 #
1903 if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:
1904 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")
1905 #
1906 # Get Module List
1907 #
1908 ModuleList = {}
1909 for Pa in Wa.AutoGenObjectList:
1910 for Ma in Pa.ModuleAutoGenList:
1911 if Ma is None:
1912 continue
1913 if not Ma.IsLibrary:
1914 ModuleList[Ma.Guid.upper()] = Ma
1915
1916 MapBuffer = []
1917 if self.LoadFixAddress != 0:
1918 #
1919 # Rebase module to the preferred memory address before GenFds
1920 #
1921 self._CollectModuleMapBuffer(MapBuffer, ModuleList)
1922 #
1923 # create FDS again for the updated EFI image
1924 #
1925 GenFdsStart = time.time()
1926 self._Build("fds", Wa)
1927 self.GenFdsTime += int(round((time.time() - GenFdsStart)))
1928 #
1929 # Create MAP file for all platform FVs after GenFds.
1930 #
1931 self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)
1932 #
1933 # Save MAP buffer into MAP file.
1934 #
1935 self._SaveMapFile (MapBuffer, Wa)
1936 self.invalidateHash()
1937
1938 def _GenFfsCmd(self,ArchList):
1939 # convert dictionary of Cmd:(Inf,Arch)
1940 # to a new dictionary of (Inf,Arch):Cmd,Cmd,Cmd...
1941 CmdSetDict = defaultdict(set)
1942 GenFfsDict = GenFds.GenFfsMakefile('', GlobalData.gFdfParser, self, ArchList, GlobalData)
1943 for Cmd in GenFfsDict:
1944 tmpInf, tmpArch = GenFfsDict[Cmd]
1945 CmdSetDict[tmpInf, tmpArch].add(Cmd)
1946 return CmdSetDict
1947
1948 ## Build a platform in multi-thread mode
1949 #
1950 def _MultiThreadBuildPlatform(self):
1951 SaveFileOnChange(self.PlatformBuildPath, '# DO NOT EDIT \n# FILE auto-generated\n', False)
1952 for BuildTarget in self.BuildTargetList:
1953 GlobalData.gGlobalDefines['TARGET'] = BuildTarget
1954 index = 0
1955 for ToolChain in self.ToolChainList:
1956 WorkspaceAutoGenTime = time.time()
1957 GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain
1958 GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain
1959 GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index]
1960 index += 1
1961 Wa = WorkspaceAutoGen(
1962 self.WorkspaceDir,
1963 self.PlatformFile,
1964 BuildTarget,
1965 ToolChain,
1966 self.ArchList,
1967 self.BuildDatabase,
1968 self.TargetTxt,
1969 self.ToolDef,
1970 self.Fdf,
1971 self.FdList,
1972 self.FvList,
1973 self.CapList,
1974 self.SkuId,
1975 self.UniFlag,
1976 self.Progress
1977 )
1978 self.Fdf = Wa.FdfFile
1979 self.LoadFixAddress = Wa.Platform.LoadFixAddress
1980 self.BuildReport.AddPlatformReport(Wa)
1981 Wa.CreateMakeFile(False)
1982
1983 # Add ffs build to makefile
1984 CmdListDict = {}
1985 if GlobalData.gEnableGenfdsMultiThread and self.Fdf:
1986 CmdListDict = self._GenFfsCmd(Wa.ArchList)
1987
1988 # multi-thread exit flag
1989 ExitFlag = threading.Event()
1990 ExitFlag.clear()
1991 self.AutoGenTime += int(round((time.time() - WorkspaceAutoGenTime)))
1992 self.BuildModules = []
1993 for Arch in Wa.ArchList:
1994 PcdMaList = []
1995 AutoGenStart = time.time()
1996 GlobalData.gGlobalDefines['ARCH'] = Arch
1997 Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)
1998 if Pa is None:
1999 continue
2000 ModuleList = []
2001 for Inf in Pa.Platform.Modules:
2002 ModuleList.append(Inf)
2003 # Add the INF only list in FDF
2004 if GlobalData.gFdfParser is not None:
2005 for InfName in GlobalData.gFdfParser.Profile.InfList:
2006 Inf = PathClass(NormPath(InfName), self.WorkspaceDir, Arch)
2007 if Inf in Pa.Platform.Modules:
2008 continue
2009 ModuleList.append(Inf)
2010 Pa.DataPipe.DataContainer = {"FfsCommand":CmdListDict}
2011 Pa.DataPipe.DataContainer = {"Workspace_timestamp": Wa._SrcTimeStamp}
2012 for Module in ModuleList:
2013 # Get ModuleAutoGen object to generate C code file and makefile
2014 Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile,Pa.DataPipe)
2015
2016 if Ma is None:
2017 continue
2018 if Ma.PcdIsDriver:
2019 Ma.PlatformInfo = Pa
2020 Ma.Workspace = Wa
2021 PcdMaList.append(Ma)
2022 if Ma.CanSkipbyHash():
2023 self.HashSkipModules.append(Ma)
2024 if GlobalData.gBinCacheSource:
2025 EdkLogger.quiet("cache hit: %s[%s]" % (Ma.MetaFile.Path, Ma.Arch))
2026 continue
2027 else:
2028 if GlobalData.gBinCacheSource:
2029 EdkLogger.quiet("cache miss: %s[%s]" % (Ma.MetaFile.Path, Ma.Arch))
2030
2031 # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'
2032 # for target which must generate AutoGen code and makefile
2033
2034 self.BuildModules.append(Ma)
2035 # Initialize all modules in tracking to 'FAIL'
2036 if Ma.Arch not in GlobalData.gModuleBuildTracking:
2037 GlobalData.gModuleBuildTracking[Ma.Arch] = dict()
2038 if Ma not in GlobalData.gModuleBuildTracking[Ma.Arch]:
2039 GlobalData.gModuleBuildTracking[Ma.Arch][Ma] = 'FAIL'
2040 mqueue = mp.Queue()
2041 for m in Pa.GetAllModuleInfo:
2042 mqueue.put(m)
2043 data_pipe_file = os.path.join(Pa.BuildDir, "GlobalVar_%s_%s.bin" % (str(Pa.Guid),Pa.Arch))
2044 Pa.DataPipe.dump(data_pipe_file)
2045 autogen_rt, errorcode = self.StartAutoGen(mqueue, Pa.DataPipe, self.SkipAutoGen, PcdMaList,self.share_data)
2046
2047 if not autogen_rt:
2048 self.AutoGenMgr.TerminateWorkers()
2049 self.AutoGenMgr.join(0.1)
2050 raise FatalError(errorcode)
2051 self.AutoGenTime += int(round((time.time() - AutoGenStart)))
2052 self.Progress.Stop("done!")
2053 for Arch in Wa.ArchList:
2054 MakeStart = time.time()
2055 for Ma in self.BuildModules:
2056 # Generate build task for the module
2057 if not Ma.IsBinaryModule:
2058 Bt = BuildTask.New(ModuleMakeUnit(Ma, Pa.BuildCommand,self.Target))
2059 # Break build if any build thread has error
2060 if BuildTask.HasError():
2061 # we need a full version of makefile for platform
2062 ExitFlag.set()
2063 BuildTask.WaitForComplete()
2064 self.invalidateHash()
2065 Pa.CreateMakeFile(False)
2066 EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)
2067 # Start task scheduler
2068 if not BuildTask.IsOnGoing():
2069 BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)
2070
2071 # in case there's an interruption. we need a full version of makefile for platform
2072 Pa.CreateMakeFile(False)
2073 if BuildTask.HasError():
2074 self.invalidateHash()
2075 EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)
2076 self.MakeTime += int(round((time.time() - MakeStart)))
2077
2078 MakeContiue = time.time()
2079
2080 #
2081 #
2082 # All modules have been put in build tasks queue. Tell task scheduler
2083 # to exit if all tasks are completed
2084 #
2085 ExitFlag.set()
2086 BuildTask.WaitForComplete()
2087 self.CreateAsBuiltInf()
2088 if GlobalData.gBinCacheDest:
2089 self.UpdateBuildCache()
2090 self.BuildModules = []
2091 self.MakeTime += int(round((time.time() - MakeContiue)))
2092 #
2093 # Check for build error, and raise exception if one
2094 # has been signaled.
2095 #
2096 if BuildTask.HasError():
2097 self.invalidateHash()
2098 EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)
2099
2100 # Create MAP file when Load Fix Address is enabled.
2101 if self.Target in ["", "all", "fds"]:
2102 for Arch in Wa.ArchList:
2103 #
2104 # Check whether the set fix address is above 4G for 32bit image.
2105 #
2106 if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:
2107 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")
2108 #
2109 # Get Module List
2110 #
2111 ModuleList = {}
2112 for Pa in Wa.AutoGenObjectList:
2113 for Ma in Pa.ModuleAutoGenList:
2114 if Ma is None:
2115 continue
2116 if not Ma.IsLibrary:
2117 ModuleList[Ma.Guid.upper()] = Ma
2118 #
2119 # Rebase module to the preferred memory address before GenFds
2120 #
2121 MapBuffer = []
2122 if self.LoadFixAddress != 0:
2123 self._CollectModuleMapBuffer(MapBuffer, ModuleList)
2124
2125 if self.Fdf:
2126 #
2127 # Generate FD image if there's a FDF file found
2128 #
2129 GenFdsStart = time.time()
2130 if GenFdsApi(Wa.GenFdsCommandDict, self.Db):
2131 EdkLogger.error("build", COMMAND_FAILURE)
2132
2133 #
2134 # Create MAP file for all platform FVs after GenFds.
2135 #
2136 self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)
2137 self.GenFdsTime += int(round((time.time() - GenFdsStart)))
2138 #
2139 # Save MAP buffer into MAP file.
2140 #
2141 self._SaveMapFile(MapBuffer, Wa)
2142 self.invalidateHash()
2143
2144 ## Generate GuidedSectionTools.txt in the FV directories.
2145 #
2146 def CreateGuidedSectionToolsFile(self):
2147 for BuildTarget in self.BuildTargetList:
2148 for ToolChain in self.ToolChainList:
2149 Wa = WorkspaceAutoGen(
2150 self.WorkspaceDir,
2151 self.PlatformFile,
2152 BuildTarget,
2153 ToolChain,
2154 self.ArchList,
2155 self.BuildDatabase,
2156 self.TargetTxt,
2157 self.ToolDef,
2158 self.Fdf,
2159 self.FdList,
2160 self.FvList,
2161 self.CapList,
2162 self.SkuId,
2163 self.UniFlag
2164 )
2165 FvDir = Wa.FvDir
2166 if not os.path.exists(FvDir):
2167 continue
2168
2169 for Arch in self.ArchList:
2170 # Build up the list of supported architectures for this build
2171 prefix = '%s_%s_%s_' % (BuildTarget, ToolChain, Arch)
2172
2173 # Look through the tool definitions for GUIDed tools
2174 guidAttribs = []
2175 for (attrib, value) in self.ToolDef.ToolsDefTxtDictionary.items():
2176 if attrib.upper().endswith('_GUID'):
2177 split = attrib.split('_')
2178 thisPrefix = '_'.join(split[0:3]) + '_'
2179 if thisPrefix == prefix:
2180 guid = self.ToolDef.ToolsDefTxtDictionary[attrib]
2181 guid = guid.lower()
2182 toolName = split[3]
2183 path = '_'.join(split[0:4]) + '_PATH'
2184 path = self.ToolDef.ToolsDefTxtDictionary[path]
2185 path = self.GetFullPathOfTool(path)
2186 guidAttribs.append((guid, toolName, path))
2187
2188 # Write out GuidedSecTools.txt
2189 toolsFile = os.path.join(FvDir, 'GuidedSectionTools.txt')
2190 toolsFile = open(toolsFile, 'wt')
2191 for guidedSectionTool in guidAttribs:
2192 print(' '.join(guidedSectionTool), file=toolsFile)
2193 toolsFile.close()
2194
2195 ## Returns the full path of the tool.
2196 #
2197 def GetFullPathOfTool (self, tool):
2198 if os.path.exists(tool):
2199 return os.path.realpath(tool)
2200 else:
2201 # We need to search for the tool using the
2202 # PATH environment variable.
2203 for dirInPath in os.environ['PATH'].split(os.pathsep):
2204 foundPath = os.path.join(dirInPath, tool)
2205 if os.path.exists(foundPath):
2206 return os.path.realpath(foundPath)
2207
2208 # If the tool was not found in the path then we just return
2209 # the input tool.
2210 return tool
2211
2212 ## Launch the module or platform build
2213 #
2214 def Launch(self):
2215 if not self.ModuleFile:
2216 if not self.SpawnMode or self.Target not in ["", "all"]:
2217 self.SpawnMode = False
2218 self._BuildPlatform()
2219 else:
2220 self._MultiThreadBuildPlatform()
2221 self.CreateGuidedSectionToolsFile()
2222 else:
2223 self.SpawnMode = False
2224 self._BuildModule()
2225
2226 if self.Target == 'cleanall':
2227 RemoveDirectory(os.path.dirname(GlobalData.gDatabasePath), True)
2228
2229 def CreateAsBuiltInf(self):
2230 for Module in self.BuildModules:
2231 Module.CreateAsBuiltInf()
2232
2233 def UpdateBuildCache(self):
2234 all_lib_set = set()
2235 all_mod_set = set()
2236 for Module in self.BuildModules:
2237 Module.CopyModuleToCache()
2238 all_mod_set.add(Module)
2239 for Module in self.HashSkipModules:
2240 Module.CopyModuleToCache()
2241 all_mod_set.add(Module)
2242 for Module in all_mod_set:
2243 for lib in Module.LibraryAutoGenList:
2244 all_lib_set.add(lib)
2245 for lib in all_lib_set:
2246 lib.CopyModuleToCache()
2247 all_lib_set.clear()
2248 all_mod_set.clear()
2249 self.HashSkipModules = []
2250 ## Do some clean-up works when error occurred
2251 def Relinquish(self):
2252 OldLogLevel = EdkLogger.GetLevel()
2253 EdkLogger.SetLevel(EdkLogger.ERROR)
2254 Utils.Progressor.Abort()
2255 if self.SpawnMode == True:
2256 BuildTask.Abort()
2257 EdkLogger.SetLevel(OldLogLevel)
2258
2259 def ParseDefines(DefineList=[]):
2260 DefineDict = {}
2261 if DefineList is not None:
2262 for Define in DefineList:
2263 DefineTokenList = Define.split("=", 1)
2264 if not GlobalData.gMacroNamePattern.match(DefineTokenList[0]):
2265 EdkLogger.error('build', FORMAT_INVALID,
2266 "The macro name must be in the pattern [A-Z][A-Z0-9_]*",
2267 ExtraData=DefineTokenList[0])
2268
2269 if len(DefineTokenList) == 1:
2270 DefineDict[DefineTokenList[0]] = "TRUE"
2271 else:
2272 DefineDict[DefineTokenList[0]] = DefineTokenList[1].strip()
2273 return DefineDict
2274
2275
2276
2277 def LogBuildTime(Time):
2278 if Time:
2279 TimeDurStr = ''
2280 TimeDur = time.gmtime(Time)
2281 if TimeDur.tm_yday > 1:
2282 TimeDurStr = time.strftime("%H:%M:%S", TimeDur) + ", %d day(s)" % (TimeDur.tm_yday - 1)
2283 else:
2284 TimeDurStr = time.strftime("%H:%M:%S", TimeDur)
2285 return TimeDurStr
2286 else:
2287 return None
2288 def ThreadNum():
2289 ThreadNumber = BuildOption.ThreadNumber
2290 if ThreadNumber is None:
2291 ThreadNumber = TargetTxt.TargetTxtDictionary[TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER]
2292 if ThreadNumber == '':
2293 ThreadNumber = 0
2294 else:
2295 ThreadNumber = int(ThreadNumber, 0)
2296
2297 if ThreadNumber == 0:
2298 try:
2299 ThreadNumber = multiprocessing.cpu_count()
2300 except (ImportError, NotImplementedError):
2301 ThreadNumber = 1
2302 return ThreadNumber
2303 ## Tool entrance method
2304 #
2305 # This method mainly dispatch specific methods per the command line options.
2306 # If no error found, return zero value so the caller of this tool can know
2307 # if it's executed successfully or not.
2308 #
2309 # @retval 0 Tool was successful
2310 # @retval 1 Tool failed
2311 #
2312 LogQMaxSize = ThreadNum() * 10
2313 def Main():
2314 StartTime = time.time()
2315
2316 #
2317 # Create a log Queue
2318 #
2319 LogQ = mp.Queue(LogQMaxSize)
2320 # Initialize log system
2321 EdkLogger.LogClientInitialize(LogQ)
2322 GlobalData.gCommand = sys.argv[1:]
2323 #
2324 # Parse the options and args
2325 #
2326 Option, Target = BuildOption, BuildTarget
2327 GlobalData.gOptions = Option
2328 GlobalData.gCaseInsensitive = Option.CaseInsensitive
2329
2330 # Set log level
2331 LogLevel = EdkLogger.INFO
2332 if Option.verbose is not None:
2333 EdkLogger.SetLevel(EdkLogger.VERBOSE)
2334 LogLevel = EdkLogger.VERBOSE
2335 elif Option.quiet is not None:
2336 EdkLogger.SetLevel(EdkLogger.QUIET)
2337 LogLevel = EdkLogger.QUIET
2338 elif Option.debug is not None:
2339 EdkLogger.SetLevel(Option.debug + 1)
2340 LogLevel = Option.debug + 1
2341 else:
2342 EdkLogger.SetLevel(EdkLogger.INFO)
2343
2344 if Option.WarningAsError == True:
2345 EdkLogger.SetWarningAsError()
2346 Log_Agent = LogAgent(LogQ,LogLevel,Option.LogFile)
2347 Log_Agent.start()
2348
2349 if platform.platform().find("Windows") >= 0:
2350 GlobalData.gIsWindows = True
2351 else:
2352 GlobalData.gIsWindows = False
2353
2354 EdkLogger.quiet("Build environment: %s" % platform.platform())
2355 EdkLogger.quiet(time.strftime("Build start time: %H:%M:%S, %b.%d %Y\n", time.localtime()));
2356 ReturnCode = 0
2357 MyBuild = None
2358 BuildError = True
2359 try:
2360 if len(Target) == 0:
2361 Target = "all"
2362 elif len(Target) >= 2:
2363 EdkLogger.error("build", OPTION_NOT_SUPPORTED, "More than one targets are not supported.",
2364 ExtraData="Please select one of: %s" % (' '.join(gSupportedTarget)))
2365 else:
2366 Target = Target[0].lower()
2367
2368 if Target not in gSupportedTarget:
2369 EdkLogger.error("build", OPTION_NOT_SUPPORTED, "Not supported target [%s]." % Target,
2370 ExtraData="Please select one of: %s" % (' '.join(gSupportedTarget)))
2371
2372 #
2373 # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH
2374 #
2375 CheckEnvVariable()
2376 GlobalData.gCommandLineDefines.update(ParseDefines(Option.Macros))
2377
2378 Workspace = os.getenv("WORKSPACE")
2379 #
2380 # Get files real name in workspace dir
2381 #
2382 GlobalData.gAllFiles = Utils.DirCache(Workspace)
2383
2384 WorkingDirectory = os.getcwd()
2385 if not Option.ModuleFile:
2386 FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.inf')))
2387 FileNum = len(FileList)
2388 if FileNum >= 2:
2389 EdkLogger.error("build", OPTION_NOT_SUPPORTED, "There are %d INF files in %s." % (FileNum, WorkingDirectory),
2390 ExtraData="Please use '-m <INF_FILE_PATH>' switch to choose one.")
2391 elif FileNum == 1:
2392 Option.ModuleFile = NormFile(FileList[0], Workspace)
2393
2394 if Option.ModuleFile:
2395 if os.path.isabs (Option.ModuleFile):
2396 if os.path.normcase (os.path.normpath(Option.ModuleFile)).find (Workspace) == 0:
2397 Option.ModuleFile = NormFile(os.path.normpath(Option.ModuleFile), Workspace)
2398 Option.ModuleFile = PathClass(Option.ModuleFile, Workspace)
2399 ErrorCode, ErrorInfo = Option.ModuleFile.Validate(".inf", False)
2400 if ErrorCode != 0:
2401 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)
2402
2403 if Option.PlatformFile is not None:
2404 if os.path.isabs (Option.PlatformFile):
2405 if os.path.normcase (os.path.normpath(Option.PlatformFile)).find (Workspace) == 0:
2406 Option.PlatformFile = NormFile(os.path.normpath(Option.PlatformFile), Workspace)
2407 Option.PlatformFile = PathClass(Option.PlatformFile, Workspace)
2408
2409 if Option.FdfFile is not None:
2410 if os.path.isabs (Option.FdfFile):
2411 if os.path.normcase (os.path.normpath(Option.FdfFile)).find (Workspace) == 0:
2412 Option.FdfFile = NormFile(os.path.normpath(Option.FdfFile), Workspace)
2413 Option.FdfFile = PathClass(Option.FdfFile, Workspace)
2414 ErrorCode, ErrorInfo = Option.FdfFile.Validate(".fdf", False)
2415 if ErrorCode != 0:
2416 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)
2417
2418 if Option.Flag is not None and Option.Flag not in ['-c', '-s']:
2419 EdkLogger.error("build", OPTION_VALUE_INVALID, "UNI flag must be one of -c or -s")
2420
2421 MyBuild = Build(Target, Workspace, Option,LogQ)
2422 GlobalData.gCommandLineDefines['ARCH'] = ' '.join(MyBuild.ArchList)
2423 if not (MyBuild.LaunchPrebuildFlag and os.path.exists(MyBuild.PlatformBuildPath)):
2424 MyBuild.Launch()
2425
2426 #
2427 # All job done, no error found and no exception raised
2428 #
2429 BuildError = False
2430 except FatalError as X:
2431 if MyBuild is not None:
2432 # for multi-thread build exits safely
2433 MyBuild.Relinquish()
2434 if Option is not None and Option.debug is not None:
2435 EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())
2436 ReturnCode = X.args[0]
2437 except Warning as X:
2438 # error from Fdf parser
2439 if MyBuild is not None:
2440 # for multi-thread build exits safely
2441 MyBuild.Relinquish()
2442 if Option is not None and Option.debug is not None:
2443 EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())
2444 else:
2445 EdkLogger.error(X.ToolName, FORMAT_INVALID, File=X.FileName, Line=X.LineNumber, ExtraData=X.Message, RaiseError=False)
2446 ReturnCode = FORMAT_INVALID
2447 except KeyboardInterrupt:
2448 if MyBuild is not None:
2449
2450 # for multi-thread build exits safely
2451 MyBuild.Relinquish()
2452 ReturnCode = ABORT_ERROR
2453 if Option is not None and Option.debug is not None:
2454 EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())
2455 except:
2456 if MyBuild is not None:
2457 # for multi-thread build exits safely
2458 MyBuild.Relinquish()
2459
2460 # try to get the meta-file from the object causing exception
2461 Tb = sys.exc_info()[-1]
2462 MetaFile = GlobalData.gProcessingFile
2463 while Tb is not None:
2464 if 'self' in Tb.tb_frame.f_locals and hasattr(Tb.tb_frame.f_locals['self'], 'MetaFile'):
2465 MetaFile = Tb.tb_frame.f_locals['self'].MetaFile
2466 Tb = Tb.tb_next
2467 EdkLogger.error(
2468 "\nbuild",
2469 CODE_ERROR,
2470 "Unknown fatal error when processing [%s]" % MetaFile,
2471 ExtraData="\n(Please send email to %s for help, attaching following call stack trace!)\n" % MSG_EDKII_MAIL_ADDR,
2472 RaiseError=False
2473 )
2474 EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())
2475 ReturnCode = CODE_ERROR
2476 finally:
2477 Utils.Progressor.Abort()
2478 Utils.ClearDuplicatedInf()
2479
2480 if ReturnCode == 0:
2481 try:
2482 MyBuild.LaunchPostbuild()
2483 Conclusion = "Done"
2484 except:
2485 Conclusion = "Failed"
2486 elif ReturnCode == ABORT_ERROR:
2487 Conclusion = "Aborted"
2488 else:
2489 Conclusion = "Failed"
2490 FinishTime = time.time()
2491 BuildDuration = time.gmtime(int(round(FinishTime - StartTime)))
2492 BuildDurationStr = ""
2493 if BuildDuration.tm_yday > 1:
2494 BuildDurationStr = time.strftime("%H:%M:%S", BuildDuration) + ", %d day(s)" % (BuildDuration.tm_yday - 1)
2495 else:
2496 BuildDurationStr = time.strftime("%H:%M:%S", BuildDuration)
2497 if MyBuild is not None:
2498 if not BuildError:
2499 MyBuild.BuildReport.GenerateReport(BuildDurationStr, LogBuildTime(MyBuild.AutoGenTime), LogBuildTime(MyBuild.MakeTime), LogBuildTime(MyBuild.GenFdsTime))
2500
2501 EdkLogger.SetLevel(EdkLogger.QUIET)
2502 EdkLogger.quiet("\n- %s -" % Conclusion)
2503 EdkLogger.quiet(time.strftime("Build end time: %H:%M:%S, %b.%d %Y", time.localtime()))
2504 EdkLogger.quiet("Build total time: %s\n" % BuildDurationStr)
2505 Log_Agent.kill()
2506 Log_Agent.join()
2507 return ReturnCode
2508
2509 if __name__ == '__main__':
2510 try:
2511 mp.set_start_method('spawn')
2512 except:
2513 pass
2514 r = Main()
2515 ## 0-127 is a safe return range, and 1 is a standard default error
2516 if r < 0 or r > 127: r = 1
2517 sys.exit(r)
2518