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