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