]> git.proxmox.com Git - mirror_edk2.git/blob - BaseTools/Source/Python/build/build.py
Sync BaseTools Branch (version r2321) to EDKII main trunk.
[mirror_edk2.git] / BaseTools / Source / Python / build / build.py
1 ## @file
2 # build a platform or a module
3 #
4 # Copyright (c) 2007 - 2011, Intel Corporation. All rights reserved.<BR>
5 #
6 # This program and the accompanying materials
7 # are licensed and made available under the terms and conditions of the BSD License
8 # which accompanies this distribution. The full text of the license may be found at
9 # http://opensource.org/licenses/bsd-license.php
10 #
11 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 #
14
15 ##
16 # Import Modules
17 #
18 import os
19 import re
20 import StringIO
21 import sys
22 import glob
23 import time
24 import platform
25 import traceback
26 import encodings.ascii
27
28 from struct import *
29 from threading import *
30 from optparse import OptionParser
31 from subprocess import *
32 from Common import Misc as Utils
33
34 from Common.TargetTxtClassObject import *
35 from Common.ToolDefClassObject import *
36 from Common.DataType import *
37 from Common.BuildVersion import gBUILD_VERSION
38 from AutoGen.AutoGen import *
39 from Common.BuildToolError import *
40 from Workspace.WorkspaceDatabase import *
41
42 from BuildReport import BuildReport
43 from GenPatchPcdTable.GenPatchPcdTable import *
44 from PatchPcdValue.PatchPcdValue import *
45
46 import Common.EdkLogger
47 import Common.GlobalData as GlobalData
48
49 # Version and Copyright
50 VersionNumber = "0.5" + ' ' + gBUILD_VERSION
51 __version__ = "%prog Version " + VersionNumber
52 __copyright__ = "Copyright (c) 2007 - 2010, Intel Corporation All rights reserved."
53
54 ## standard targets of build command
55 gSupportedTarget = ['all', 'genc', 'genmake', 'modules', 'libraries', 'fds', 'clean', 'cleanall', 'cleanlib', 'run']
56
57 ## build configuration file
58 gBuildConfiguration = "Conf/target.txt"
59 gBuildCacheDir = "Conf/.cache"
60 gToolsDefinition = "Conf/tools_def.txt"
61
62 ## Check environment PATH variable to make sure the specified tool is found
63 #
64 # If the tool is found in the PATH, then True is returned
65 # Otherwise, False is returned
66 #
67 def IsToolInPath(tool):
68 if os.environ.has_key('PATHEXT'):
69 extns = os.environ['PATHEXT'].split(os.path.pathsep)
70 else:
71 extns = ('',)
72 for pathDir in os.environ['PATH'].split(os.path.pathsep):
73 for ext in extns:
74 if os.path.exists(os.path.join(pathDir, tool + ext)):
75 return True
76 return False
77
78 ## Check environment variables
79 #
80 # Check environment variables that must be set for build. Currently they are
81 #
82 # WORKSPACE The directory all packages/platforms start from
83 # EDK_TOOLS_PATH The directory contains all tools needed by the build
84 # PATH $(EDK_TOOLS_PATH)/Bin/<sys> must be set in PATH
85 #
86 # If any of above environment variable is not set or has error, the build
87 # will be broken.
88 #
89 def CheckEnvVariable():
90 # check WORKSPACE
91 if "WORKSPACE" not in os.environ:
92 EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",
93 ExtraData="WORKSPACE")
94
95 WorkspaceDir = os.path.normcase(os.path.normpath(os.environ["WORKSPACE"]))
96 if not os.path.exists(WorkspaceDir):
97 EdkLogger.error("build", FILE_NOT_FOUND, "WORKSPACE doesn't exist", ExtraData="%s" % WorkspaceDir)
98 elif ' ' in WorkspaceDir:
99 EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in WORKSPACE path",
100 ExtraData=WorkspaceDir)
101 os.environ["WORKSPACE"] = WorkspaceDir
102
103 #
104 # Check EFI_SOURCE (Edk build convention). EDK_SOURCE will always point to ECP
105 #
106 if "ECP_SOURCE" not in os.environ:
107 os.environ["ECP_SOURCE"] = os.path.join(WorkspaceDir, GlobalData.gEdkCompatibilityPkg)
108 if "EFI_SOURCE" not in os.environ:
109 os.environ["EFI_SOURCE"] = os.environ["ECP_SOURCE"]
110 if "EDK_SOURCE" not in os.environ:
111 os.environ["EDK_SOURCE"] = os.environ["ECP_SOURCE"]
112
113 #
114 # Unify case of characters on case-insensitive systems
115 #
116 EfiSourceDir = os.path.normcase(os.path.normpath(os.environ["EFI_SOURCE"]))
117 EdkSourceDir = os.path.normcase(os.path.normpath(os.environ["EDK_SOURCE"]))
118 EcpSourceDir = os.path.normcase(os.path.normpath(os.environ["ECP_SOURCE"]))
119
120 os.environ["EFI_SOURCE"] = EfiSourceDir
121 os.environ["EDK_SOURCE"] = EdkSourceDir
122 os.environ["ECP_SOURCE"] = EcpSourceDir
123 os.environ["EDK_TOOLS_PATH"] = os.path.normcase(os.environ["EDK_TOOLS_PATH"])
124
125 if not os.path.exists(EcpSourceDir):
126 EdkLogger.verbose("ECP_SOURCE = %s doesn't exist. Edk modules could not be built." % EcpSourceDir)
127 elif ' ' in EcpSourceDir:
128 EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in ECP_SOURCE path",
129 ExtraData=EcpSourceDir)
130 if not os.path.exists(EdkSourceDir):
131 if EdkSourceDir == EcpSourceDir:
132 EdkLogger.verbose("EDK_SOURCE = %s doesn't exist. Edk modules could not be built." % EdkSourceDir)
133 else:
134 EdkLogger.error("build", PARAMETER_INVALID, "EDK_SOURCE does not exist",
135 ExtraData=EdkSourceDir)
136 elif ' ' in EdkSourceDir:
137 EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in EDK_SOURCE path",
138 ExtraData=EdkSourceDir)
139 if not os.path.exists(EfiSourceDir):
140 if EfiSourceDir == EcpSourceDir:
141 EdkLogger.verbose("EFI_SOURCE = %s doesn't exist. Edk modules could not be built." % EfiSourceDir)
142 else:
143 EdkLogger.error("build", PARAMETER_INVALID, "EFI_SOURCE does not exist",
144 ExtraData=EfiSourceDir)
145 elif ' ' in EfiSourceDir:
146 EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in EFI_SOURCE path",
147 ExtraData=EfiSourceDir)
148
149 # change absolute path to relative path to WORKSPACE
150 if EfiSourceDir.upper().find(WorkspaceDir.upper()) != 0:
151 EdkLogger.error("build", PARAMETER_INVALID, "EFI_SOURCE is not under WORKSPACE",
152 ExtraData="WORKSPACE = %s\n EFI_SOURCE = %s" % (WorkspaceDir, EfiSourceDir))
153 if EdkSourceDir.upper().find(WorkspaceDir.upper()) != 0:
154 EdkLogger.error("build", PARAMETER_INVALID, "EDK_SOURCE is not under WORKSPACE",
155 ExtraData="WORKSPACE = %s\n EDK_SOURCE = %s" % (WorkspaceDir, EdkSourceDir))
156 if EcpSourceDir.upper().find(WorkspaceDir.upper()) != 0:
157 EdkLogger.error("build", PARAMETER_INVALID, "ECP_SOURCE is not under WORKSPACE",
158 ExtraData="WORKSPACE = %s\n ECP_SOURCE = %s" % (WorkspaceDir, EcpSourceDir))
159
160 # check EDK_TOOLS_PATH
161 if "EDK_TOOLS_PATH" not in os.environ:
162 EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",
163 ExtraData="EDK_TOOLS_PATH")
164
165 # check PATH
166 if "PATH" not in os.environ:
167 EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",
168 ExtraData="PATH")
169
170 GlobalData.gWorkspace = WorkspaceDir
171 GlobalData.gEfiSource = EfiSourceDir
172 GlobalData.gEdkSource = EdkSourceDir
173 GlobalData.gEcpSource = EcpSourceDir
174
175 ## Get normalized file path
176 #
177 # Convert the path to be local format, and remove the WORKSPACE path at the
178 # beginning if the file path is given in full path.
179 #
180 # @param FilePath File path to be normalized
181 # @param Workspace Workspace path which the FilePath will be checked against
182 #
183 # @retval string The normalized file path
184 #
185 def NormFile(FilePath, Workspace):
186 # check if the path is absolute or relative
187 if os.path.isabs(FilePath):
188 FileFullPath = os.path.normpath(FilePath)
189 else:
190 FileFullPath = os.path.normpath(os.path.join(Workspace, FilePath))
191
192 # check if the file path exists or not
193 if not os.path.isfile(FileFullPath):
194 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData="\t%s (Please give file in absolute path or relative to WORKSPACE)" % FileFullPath)
195
196 # remove workspace directory from the beginning part of the file path
197 if Workspace[-1] in ["\\", "/"]:
198 return FileFullPath[len(Workspace):]
199 else:
200 return FileFullPath[(len(Workspace) + 1):]
201
202 ## Get the output of an external program
203 #
204 # This is the entrance method of thread reading output of an external program and
205 # putting them in STDOUT/STDERR of current program.
206 #
207 # @param From The stream message read from
208 # @param To The stream message put on
209 # @param ExitFlag The flag used to indicate stopping reading
210 #
211 def ReadMessage(From, To, ExitFlag):
212 while True:
213 # read one line a time
214 Line = From.readline()
215 # empty string means "end"
216 if Line != None and Line != "":
217 To(Line.rstrip())
218 else:
219 break
220 if ExitFlag.isSet():
221 break
222
223 ## Launch an external program
224 #
225 # This method will call subprocess.Popen to execute an external program with
226 # given options in specified directory. Because of the dead-lock issue during
227 # redirecting output of the external program, threads are used to to do the
228 # redirection work.
229 #
230 # @param Command A list or string containing the call of the program
231 # @param WorkingDir The directory in which the program will be running
232 #
233 def LaunchCommand(Command, WorkingDir):
234 # if working directory doesn't exist, Popen() will raise an exception
235 if not os.path.isdir(WorkingDir):
236 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=WorkingDir)
237
238 Proc = None
239 EndOfProcedure = None
240 try:
241 # launch the command
242 Proc = Popen(Command, stdout=PIPE, stderr=PIPE, env=os.environ, cwd=WorkingDir, bufsize=-1)
243
244 # launch two threads to read the STDOUT and STDERR
245 EndOfProcedure = Event()
246 EndOfProcedure.clear()
247 if Proc.stdout:
248 StdOutThread = Thread(target=ReadMessage, args=(Proc.stdout, EdkLogger.info, EndOfProcedure))
249 StdOutThread.setName("STDOUT-Redirector")
250 StdOutThread.setDaemon(False)
251 StdOutThread.start()
252
253 if Proc.stderr:
254 StdErrThread = Thread(target=ReadMessage, args=(Proc.stderr, EdkLogger.quiet, EndOfProcedure))
255 StdErrThread.setName("STDERR-Redirector")
256 StdErrThread.setDaemon(False)
257 StdErrThread.start()
258
259 # waiting for program exit
260 Proc.wait()
261 except: # in case of aborting
262 # terminate the threads redirecting the program output
263 if EndOfProcedure != None:
264 EndOfProcedure.set()
265 if Proc == None:
266 if type(Command) != type(""):
267 Command = " ".join(Command)
268 EdkLogger.error("build", COMMAND_FAILURE, "Failed to start command", ExtraData="%s [%s]" % (Command, WorkingDir))
269
270 if Proc.stdout:
271 StdOutThread.join()
272 if Proc.stderr:
273 StdErrThread.join()
274
275 # check the return code of the program
276 if Proc.returncode != 0:
277 if type(Command) != type(""):
278 Command = " ".join(Command)
279 EdkLogger.error("build", COMMAND_FAILURE, ExtraData="%s [%s]" % (Command, WorkingDir))
280
281 ## The smallest unit that can be built in multi-thread build mode
282 #
283 # This is the base class of build unit. The "Obj" parameter must provide
284 # __str__(), __eq__() and __hash__() methods. Otherwise there could be build units
285 # missing build.
286 #
287 # Currently the "Obj" should be only ModuleAutoGen or PlatformAutoGen objects.
288 #
289 class BuildUnit:
290 ## The constructor
291 #
292 # @param self The object pointer
293 # @param Obj The object the build is working on
294 # @param Target The build target name, one of gSupportedTarget
295 # @param Dependency The BuildUnit(s) which must be completed in advance
296 # @param WorkingDir The directory build command starts in
297 #
298 def __init__(self, Obj, BuildCommand, Target, Dependency, WorkingDir="."):
299 self.BuildObject = Obj
300 self.Dependency = Dependency
301 self.WorkingDir = WorkingDir
302 self.Target = Target
303 self.BuildCommand = BuildCommand
304 if BuildCommand == None or len(BuildCommand) == 0:
305 EdkLogger.error("build", OPTION_MISSING, "No build command found for",
306 ExtraData=str(Obj))
307
308 ## str() method
309 #
310 # It just returns the string representation of self.BuildObject
311 #
312 # @param self The object pointer
313 #
314 def __str__(self):
315 return str(self.BuildObject)
316
317 ## "==" operator method
318 #
319 # It just compares self.BuildObject with "Other". So self.BuildObject must
320 # provide its own __eq__() method.
321 #
322 # @param self The object pointer
323 # @param Other The other BuildUnit object compared to
324 #
325 def __eq__(self, Other):
326 return Other != None and self.BuildObject == Other.BuildObject \
327 and self.BuildObject.Arch == Other.BuildObject.Arch
328
329 ## hash() method
330 #
331 # It just returns the hash value of self.BuildObject which must be hashable.
332 #
333 # @param self The object pointer
334 #
335 def __hash__(self):
336 return hash(self.BuildObject) + hash(self.BuildObject.Arch)
337
338 def __repr__(self):
339 return repr(self.BuildObject)
340
341 ## The smallest module unit that can be built by nmake/make command in multi-thread build mode
342 #
343 # This class is for module build by nmake/make build system. The "Obj" parameter
344 # must provide __str__(), __eq__() and __hash__() methods. Otherwise there could
345 # be make units missing build.
346 #
347 # Currently the "Obj" should be only ModuleAutoGen object.
348 #
349 class ModuleMakeUnit(BuildUnit):
350 ## The constructor
351 #
352 # @param self The object pointer
353 # @param Obj The ModuleAutoGen object the build is working on
354 # @param Target The build target name, one of gSupportedTarget
355 #
356 def __init__(self, Obj, Target):
357 Dependency = [ModuleMakeUnit(La, Target) for La in Obj.LibraryAutoGenList]
358 BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir)
359 if Target in [None, "", "all"]:
360 self.Target = "tbuild"
361
362 ## The smallest platform unit that can be built by nmake/make command in multi-thread build mode
363 #
364 # This class is for platform build by nmake/make build system. The "Obj" parameter
365 # must provide __str__(), __eq__() and __hash__() methods. Otherwise there could
366 # be make units missing build.
367 #
368 # Currently the "Obj" should be only PlatformAutoGen object.
369 #
370 class PlatformMakeUnit(BuildUnit):
371 ## The constructor
372 #
373 # @param self The object pointer
374 # @param Obj The PlatformAutoGen object the build is working on
375 # @param Target The build target name, one of gSupportedTarget
376 #
377 def __init__(self, Obj, Target):
378 Dependency = [ModuleMakeUnit(Lib, Target) for Lib in self.BuildObject.LibraryAutoGenList]
379 Dependency.extend([ModuleMakeUnit(Mod, Target) for Mod in self.BuildObject.ModuleAutoGenList])
380 BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir)
381
382 ## The class representing the task of a module build or platform build
383 #
384 # This class manages the build tasks in multi-thread build mode. Its jobs include
385 # scheduling thread running, catching thread error, monitor the thread status, etc.
386 #
387 class BuildTask:
388 # queue for tasks waiting for schedule
389 _PendingQueue = sdict()
390 _PendingQueueLock = threading.Lock()
391
392 # queue for tasks ready for running
393 _ReadyQueue = sdict()
394 _ReadyQueueLock = threading.Lock()
395
396 # queue for run tasks
397 _RunningQueue = sdict()
398 _RunningQueueLock = threading.Lock()
399
400 # queue containing all build tasks, in case duplicate build
401 _TaskQueue = sdict()
402
403 # flag indicating error occurs in a running thread
404 _ErrorFlag = threading.Event()
405 _ErrorFlag.clear()
406 _ErrorMessage = ""
407
408 # BoundedSemaphore object used to control the number of running threads
409 _Thread = None
410
411 # flag indicating if the scheduler is started or not
412 _SchedulerStopped = threading.Event()
413 _SchedulerStopped.set()
414
415 ## Start the task scheduler thread
416 #
417 # @param MaxThreadNumber The maximum thread number
418 # @param ExitFlag Flag used to end the scheduler
419 #
420 @staticmethod
421 def StartScheduler(MaxThreadNumber, ExitFlag):
422 SchedulerThread = Thread(target=BuildTask.Scheduler, args=(MaxThreadNumber, ExitFlag))
423 SchedulerThread.setName("Build-Task-Scheduler")
424 SchedulerThread.setDaemon(False)
425 SchedulerThread.start()
426 # wait for the scheduler to be started, especially useful in Linux
427 while not BuildTask.IsOnGoing():
428 time.sleep(0.01)
429
430 ## Scheduler method
431 #
432 # @param MaxThreadNumber The maximum thread number
433 # @param ExitFlag Flag used to end the scheduler
434 #
435 @staticmethod
436 def Scheduler(MaxThreadNumber, ExitFlag):
437 BuildTask._SchedulerStopped.clear()
438 try:
439 # use BoundedSemaphore to control the maximum running threads
440 BuildTask._Thread = BoundedSemaphore(MaxThreadNumber)
441 #
442 # scheduling loop, which will exits when no pending/ready task and
443 # indicated to do so, or there's error in running thread
444 #
445 while (len(BuildTask._PendingQueue) > 0 or len(BuildTask._ReadyQueue) > 0 \
446 or not ExitFlag.isSet()) and not BuildTask._ErrorFlag.isSet():
447 EdkLogger.debug(EdkLogger.DEBUG_8, "Pending Queue (%d), Ready Queue (%d)"
448 % (len(BuildTask._PendingQueue), len(BuildTask._ReadyQueue)))
449
450 # get all pending tasks
451 BuildTask._PendingQueueLock.acquire()
452 BuildObjectList = BuildTask._PendingQueue.keys()
453 #
454 # check if their dependency is resolved, and if true, move them
455 # into ready queue
456 #
457 for BuildObject in BuildObjectList:
458 Bt = BuildTask._PendingQueue[BuildObject]
459 if Bt.IsReady():
460 BuildTask._ReadyQueue[BuildObject] = BuildTask._PendingQueue.pop(BuildObject)
461 BuildTask._PendingQueueLock.release()
462
463 # launch build thread until the maximum number of threads is reached
464 while not BuildTask._ErrorFlag.isSet():
465 # empty ready queue, do nothing further
466 if len(BuildTask._ReadyQueue) == 0:
467 break
468
469 # wait for active thread(s) exit
470 BuildTask._Thread.acquire(True)
471
472 # start a new build thread
473 Bo = BuildTask._ReadyQueue.keys()[0]
474 Bt = BuildTask._ReadyQueue.pop(Bo)
475
476 # move into running queue
477 BuildTask._RunningQueueLock.acquire()
478 BuildTask._RunningQueue[Bo] = Bt
479 BuildTask._RunningQueueLock.release()
480
481 Bt.Start()
482 # avoid tense loop
483 time.sleep(0.01)
484
485 # avoid tense loop
486 time.sleep(0.01)
487
488 # wait for all running threads exit
489 if BuildTask._ErrorFlag.isSet():
490 EdkLogger.quiet("\nWaiting for all build threads exit...")
491 # while not BuildTask._ErrorFlag.isSet() and \
492 while len(BuildTask._RunningQueue) > 0:
493 EdkLogger.verbose("Waiting for thread ending...(%d)" % len(BuildTask._RunningQueue))
494 EdkLogger.debug(EdkLogger.DEBUG_8, "Threads [%s]" % ", ".join([Th.getName() for Th in threading.enumerate()]))
495 # avoid tense loop
496 time.sleep(0.1)
497 except BaseException, X:
498 #
499 # TRICK: hide the output of threads left runing, so that the user can
500 # catch the error message easily
501 #
502 EdkLogger.SetLevel(EdkLogger.ERROR)
503 BuildTask._ErrorFlag.set()
504 BuildTask._ErrorMessage = "build thread scheduler error\n\t%s" % str(X)
505
506 BuildTask._PendingQueue.clear()
507 BuildTask._ReadyQueue.clear()
508 BuildTask._RunningQueue.clear()
509 BuildTask._TaskQueue.clear()
510 BuildTask._SchedulerStopped.set()
511
512 ## Wait for all running method exit
513 #
514 @staticmethod
515 def WaitForComplete():
516 BuildTask._SchedulerStopped.wait()
517
518 ## Check if the scheduler is running or not
519 #
520 @staticmethod
521 def IsOnGoing():
522 return not BuildTask._SchedulerStopped.isSet()
523
524 ## Abort the build
525 @staticmethod
526 def Abort():
527 if BuildTask.IsOnGoing():
528 BuildTask._ErrorFlag.set()
529 BuildTask.WaitForComplete()
530
531 ## Check if there's error in running thread
532 #
533 # Since the main thread cannot catch exceptions in other thread, we have to
534 # use threading.Event to communicate this formation to main thread.
535 #
536 @staticmethod
537 def HasError():
538 return BuildTask._ErrorFlag.isSet()
539
540 ## Get error message in running thread
541 #
542 # Since the main thread cannot catch exceptions in other thread, we have to
543 # use a static variable to communicate this message to main thread.
544 #
545 @staticmethod
546 def GetErrorMessage():
547 return BuildTask._ErrorMessage
548
549 ## Factory method to create a BuildTask object
550 #
551 # This method will check if a module is building or has been built. And if
552 # true, just return the associated BuildTask object in the _TaskQueue. If
553 # not, create and return a new BuildTask object. The new BuildTask object
554 # will be appended to the _PendingQueue for scheduling later.
555 #
556 # @param BuildItem A BuildUnit object representing a build object
557 # @param Dependency The dependent build object of BuildItem
558 #
559 @staticmethod
560 def New(BuildItem, Dependency=None):
561 if BuildItem in BuildTask._TaskQueue:
562 Bt = BuildTask._TaskQueue[BuildItem]
563 return Bt
564
565 Bt = BuildTask()
566 Bt._Init(BuildItem, Dependency)
567 BuildTask._TaskQueue[BuildItem] = Bt
568
569 BuildTask._PendingQueueLock.acquire()
570 BuildTask._PendingQueue[BuildItem] = Bt
571 BuildTask._PendingQueueLock.release()
572
573 return Bt
574
575 ## The real constructor of BuildTask
576 #
577 # @param BuildItem A BuildUnit object representing a build object
578 # @param Dependency The dependent build object of BuildItem
579 #
580 def _Init(self, BuildItem, Dependency=None):
581 self.BuildItem = BuildItem
582
583 self.DependencyList = []
584 if Dependency == None:
585 Dependency = BuildItem.Dependency
586 else:
587 Dependency.extend(BuildItem.Dependency)
588 self.AddDependency(Dependency)
589 # flag indicating build completes, used to avoid unnecessary re-build
590 self.CompleteFlag = False
591
592 ## Check if all dependent build tasks are completed or not
593 #
594 def IsReady(self):
595 ReadyFlag = True
596 for Dep in self.DependencyList:
597 if Dep.CompleteFlag == True:
598 continue
599 ReadyFlag = False
600 break
601
602 return ReadyFlag
603
604 ## Add dependent build task
605 #
606 # @param Dependency The list of dependent build objects
607 #
608 def AddDependency(self, Dependency):
609 for Dep in Dependency:
610 self.DependencyList.append(BuildTask.New(Dep)) # BuildTask list
611
612 ## The thread wrapper of LaunchCommand function
613 #
614 # @param Command A list or string contains the call of the command
615 # @param WorkingDir The directory in which the program will be running
616 #
617 def _CommandThread(self, Command, WorkingDir):
618 try:
619 LaunchCommand(Command, WorkingDir)
620 self.CompleteFlag = True
621 except:
622 #
623 # TRICK: hide the output of threads left runing, so that the user can
624 # catch the error message easily
625 #
626 if not BuildTask._ErrorFlag.isSet():
627 GlobalData.gBuildingModule = "%s [%s, %s, %s]" % (str(self.BuildItem.BuildObject),
628 self.BuildItem.BuildObject.Arch,
629 self.BuildItem.BuildObject.ToolChain,
630 self.BuildItem.BuildObject.BuildTarget
631 )
632 EdkLogger.SetLevel(EdkLogger.ERROR)
633 BuildTask._ErrorFlag.set()
634 BuildTask._ErrorMessage = "%s broken\n %s [%s]" % \
635 (threading.currentThread().getName(), Command, WorkingDir)
636 # indicate there's a thread is available for another build task
637 BuildTask._RunningQueueLock.acquire()
638 BuildTask._RunningQueue.pop(self.BuildItem)
639 BuildTask._RunningQueueLock.release()
640 BuildTask._Thread.release()
641
642 ## Start build task thread
643 #
644 def Start(self):
645 EdkLogger.quiet("Building ... %s" % repr(self.BuildItem))
646 Command = self.BuildItem.BuildCommand + [self.BuildItem.Target]
647 self.BuildTread = Thread(target=self._CommandThread, args=(Command, self.BuildItem.WorkingDir))
648 self.BuildTread.setName("build thread")
649 self.BuildTread.setDaemon(False)
650 self.BuildTread.start()
651
652 ## The class contains the information related to EFI image
653 #
654 class PeImageInfo():
655 ## Constructor
656 #
657 # Constructor will load all required image information.
658 #
659 # @param BaseName The full file path of image.
660 # @param Guid The GUID for image.
661 # @param Arch Arch of this image.
662 # @param OutputDir The output directory for image.
663 # @param DebugDir The debug directory for image.
664 # @param ImageClass PeImage Information
665 #
666 def __init__(self, BaseName, Guid, Arch, OutputDir, DebugDir, ImageClass):
667 self.BaseName = BaseName
668 self.Guid = Guid
669 self.Arch = Arch
670 self.OutputDir = OutputDir
671 self.DebugDir = DebugDir
672 self.Image = ImageClass
673 self.Image.Size = (self.Image.Size / 0x1000 + 1) * 0x1000
674
675 ## The class implementing the EDK2 build process
676 #
677 # The build process includes:
678 # 1. Load configuration from target.txt and tools_def.txt in $(WORKSPACE)/Conf
679 # 2. Parse DSC file of active platform
680 # 3. Parse FDF file if any
681 # 4. Establish build database, including parse all other files (module, package)
682 # 5. Create AutoGen files (C code file, depex file, makefile) if necessary
683 # 6. Call build command
684 #
685 class Build():
686 ## Constructor
687 #
688 # Constructor will load all necessary configurations, parse platform, modules
689 # and packages and the establish a database for AutoGen.
690 #
691 # @param Target The build command target, one of gSupportedTarget
692 # @param WorkspaceDir The directory of workspace
693 # @param Platform The DSC file of active platform
694 # @param Module The INF file of active module, if any
695 # @param Arch The Arch list of platform or module
696 # @param ToolChain The name list of toolchain
697 # @param BuildTarget The "DEBUG" or "RELEASE" build
698 # @param FlashDefinition The FDF file of active platform
699 # @param FdList=[] The FD names to be individually built
700 # @param FvList=[] The FV names to be individually built
701 # @param MakefileType The type of makefile (for MSFT make or GNU make)
702 # @param SilentMode Indicate multi-thread build mode
703 # @param ThreadNumber The maximum number of thread if in multi-thread build mode
704 # @param SkipAutoGen Skip AutoGen step
705 # @param Reparse Re-parse all meta files
706 # @param SkuId SKU id from command line
707 #
708 def __init__(self, Target, WorkspaceDir, Platform, Module, Arch, ToolChain,
709 BuildTarget, FlashDefinition, FdList=[], FvList=[], CapList=[],
710 MakefileType="nmake", SilentMode=False, ThreadNumber=2,
711 SkipAutoGen=False, Reparse=False, SkuId=None,
712 ReportFile=None, ReportType=None, UniFlag=None):
713
714 self.WorkspaceDir = WorkspaceDir
715 self.Target = Target
716 self.PlatformFile = Platform
717 self.ModuleFile = Module
718 self.ArchList = Arch
719 self.ToolChainList = ToolChain
720 self.BuildTargetList= BuildTarget
721 self.Fdf = FlashDefinition
722 self.FdList = FdList
723 self.FvList = FvList
724 self.CapList = CapList
725 self.MakefileType = MakefileType
726 self.SilentMode = SilentMode
727 self.ThreadNumber = ThreadNumber
728 self.SkipAutoGen = SkipAutoGen
729 self.Reparse = Reparse
730 self.SkuId = SkuId
731 self.SpawnMode = True
732 self.BuildReport = BuildReport(ReportFile, ReportType)
733 self.TargetTxt = TargetTxtClassObject()
734 self.ToolDef = ToolDefClassObject()
735 self.Db = WorkspaceDatabase(None, GlobalData.gGlobalDefines, self.Reparse)
736 #self.Db = WorkspaceDatabase(None, {}, self.Reparse)
737 self.BuildDatabase = self.Db.BuildObject
738 self.Platform = None
739 self.LoadFixAddress = 0
740 self.UniFlag = UniFlag
741
742 # print dot character during doing some time-consuming work
743 self.Progress = Utils.Progressor()
744
745 # parse target.txt, tools_def.txt, and platform file
746 #self.RestoreBuildData()
747 self.LoadConfiguration()
748
749 #
750 # @attention Treat $(TARGET)/$(TOOL_CHAIN_TAG) in meta data files as special macro when it has only one build target/toolchain.
751 # This is not a complete support for $(TARGET)/$(TOOL_CHAIN_TAG) macro as it can only support one build target/toolchain in ONE
752 # invocation of build command. However, it should cover the frequent usage model that $(TARGET)/$(TOOL_CHAIN_TAG) macro
753 # is used in DSC/FDF files to specify different libraries & PCD setting for debug/release build.
754 #
755 if len(self.BuildTargetList) == 1:
756 self.Db._GlobalMacros.setdefault("TARGET", self.BuildTargetList[0])
757 if len(self.ToolChainList) == 1:
758 self.Db._GlobalMacros.setdefault("TOOL_CHAIN_TAG", self.ToolChainList[0])
759
760 self.InitBuild()
761
762 # print current build environment and configuration
763 EdkLogger.quiet("%-24s = %s" % ("WORKSPACE", os.environ["WORKSPACE"]))
764 EdkLogger.quiet("%-24s = %s" % ("ECP_SOURCE", os.environ["ECP_SOURCE"]))
765 EdkLogger.quiet("%-24s = %s" % ("EDK_SOURCE", os.environ["EDK_SOURCE"]))
766 EdkLogger.quiet("%-24s = %s" % ("EFI_SOURCE", os.environ["EFI_SOURCE"]))
767 EdkLogger.quiet("%-24s = %s" % ("EDK_TOOLS_PATH", os.environ["EDK_TOOLS_PATH"]))
768
769 EdkLogger.info('\n%-24s = %s' % ("TARGET_ARCH", ' '.join(self.ArchList)))
770 EdkLogger.info('%-24s = %s' % ("TARGET", ' '.join(self.BuildTargetList)))
771 EdkLogger.info('%-24s = %s' % ("TOOL_CHAIN_TAG", ' '.join(self.ToolChainList)))
772
773 EdkLogger.info('\n%-24s = %s' % ("Active Platform", self.PlatformFile))
774
775 if self.Fdf != None and self.Fdf != "":
776 EdkLogger.info('%-24s = %s' % ("Flash Image Definition", self.Fdf))
777
778 if self.ModuleFile != None and self.ModuleFile != "":
779 EdkLogger.info('%-24s = %s' % ("Active Module", self.ModuleFile))
780
781 os.chdir(self.WorkspaceDir)
782 self.Progress.Start("\nProcessing meta-data")
783
784 ## Load configuration
785 #
786 # This method will parse target.txt and get the build configurations.
787 #
788 def LoadConfiguration(self):
789 #
790 # Check target.txt and tools_def.txt and Init them
791 #
792 BuildConfigurationFile = os.path.normpath(os.path.join(self.WorkspaceDir, gBuildConfiguration))
793 if os.path.isfile(BuildConfigurationFile) == True:
794 StatusCode = self.TargetTxt.LoadTargetTxtFile(BuildConfigurationFile)
795
796 ToolDefinitionFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_CONF]
797 if ToolDefinitionFile == '':
798 ToolDefinitionFile = gToolsDefinition
799 ToolDefinitionFile = os.path.normpath(os.path.join(self.WorkspaceDir, ToolDefinitionFile))
800 if os.path.isfile(ToolDefinitionFile) == True:
801 StatusCode = self.ToolDef.LoadToolDefFile(ToolDefinitionFile)
802 else:
803 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=ToolDefinitionFile)
804 else:
805 EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=BuildConfigurationFile)
806
807 # if no ARCH given in command line, get it from target.txt
808 if self.ArchList == None or len(self.ArchList) == 0:
809 self.ArchList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET_ARCH]
810
811 # if no build target given in command line, get it from target.txt
812 if self.BuildTargetList == None or len(self.BuildTargetList) == 0:
813 self.BuildTargetList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET]
814
815 # if no tool chain given in command line, get it from target.txt
816 if self.ToolChainList == None or len(self.ToolChainList) == 0:
817 self.ToolChainList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_TAG]
818 if self.ToolChainList == None or len(self.ToolChainList) == 0:
819 EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, ExtraData="No toolchain given. Don't know how to build.\n")
820
821 # check if the tool chains are defined or not
822 NewToolChainList = []
823 for ToolChain in self.ToolChainList:
824 if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG]:
825 EdkLogger.warn("build", "Tool chain [%s] is not defined" % ToolChain)
826 else:
827 NewToolChainList.append(ToolChain)
828 # if no tool chain available, break the build
829 if len(NewToolChainList) == 0:
830 EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,
831 ExtraData="[%s] not defined. No toolchain available for build!\n" % ", ".join(self.ToolChainList))
832 else:
833 self.ToolChainList = NewToolChainList
834
835 if self.ThreadNumber == None:
836 self.ThreadNumber = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER]
837 if self.ThreadNumber == '':
838 self.ThreadNumber = 0
839 else:
840 self.ThreadNumber = int(self.ThreadNumber, 0)
841
842 if self.ThreadNumber == 0:
843 self.ThreadNumber = 1
844
845 if not self.PlatformFile:
846 PlatformFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_ACTIVE_PLATFORM]
847 if not PlatformFile:
848 # Try to find one in current directory
849 WorkingDirectory = os.getcwd()
850 FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.dsc')))
851 FileNum = len(FileList)
852 if FileNum >= 2:
853 EdkLogger.error("build", OPTION_MISSING,
854 ExtraData="There are %d DSC files in %s. Use '-p' to specify one.\n" % (FileNum, WorkingDirectory))
855 elif FileNum == 1:
856 PlatformFile = FileList[0]
857 else:
858 EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,
859 ExtraData="No active platform specified in target.txt or command line! Nothing can be built.\n")
860
861 self.PlatformFile = PathClass(NormFile(PlatformFile, self.WorkspaceDir), self.WorkspaceDir)
862 ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False)
863 if ErrorCode != 0:
864 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)
865
866 ## Initialize build configuration
867 #
868 # This method will parse DSC file and merge the configurations from
869 # command line and target.txt, then get the final build configurations.
870 #
871 def InitBuild(self):
872 ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc")
873 if ErrorCode != 0:
874 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)
875
876 # create metafile database
877 self.Db.InitDatabase()
878
879 # we need information in platform description file to determine how to build
880 self.Platform = self.BuildDatabase[self.PlatformFile, 'COMMON']
881 if not self.Fdf:
882 self.Fdf = self.Platform.FlashDefinition
883
884 LoadFixAddressString = None
885 if TAB_FIX_LOAD_TOP_MEMORY_ADDRESS in GlobalData.gGlobalDefines:
886 LoadFixAddressString = GlobalData.gGlobalDefines[TAB_FIX_LOAD_TOP_MEMORY_ADDRESS]
887 else:
888 LoadFixAddressString = self.Platform.LoadFixAddress
889
890 if LoadFixAddressString != None and LoadFixAddressString != '':
891 try:
892 if LoadFixAddressString.upper().startswith('0X'):
893 self.LoadFixAddress = int (LoadFixAddressString, 16)
894 else:
895 self.LoadFixAddress = int (LoadFixAddressString)
896 except:
897 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS %s is not valid dec or hex string" % (LoadFixAddressString))
898 if self.LoadFixAddress < 0:
899 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS is set to the invalid negative value %s" % (LoadFixAddressString))
900 if self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress % 0x1000 != 0:
901 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS is set to the invalid unaligned 4K value %s" % (LoadFixAddressString))
902
903 if self.SkuId == None or self.SkuId == '':
904 self.SkuId = self.Platform.SkuName
905
906 # check FD/FV build target
907 if self.Fdf == None or self.Fdf == "":
908 if self.FdList != []:
909 EdkLogger.info("No flash definition file found. FD [%s] will be ignored." % " ".join(self.FdList))
910 self.FdList = []
911 if self.FvList != []:
912 EdkLogger.info("No flash definition file found. FV [%s] will be ignored." % " ".join(self.FvList))
913 self.FvList = []
914 else:
915 FdfParserObj = FdfParser(str(self.Fdf))
916 for key in self.Db._GlobalMacros:
917 InputMacroDict[key] = self.Db._GlobalMacros[key]
918 FdfParserObj.ParseFile()
919 for fvname in self.FvList:
920 if fvname.upper() not in FdfParserObj.Profile.FvDict.keys():
921 EdkLogger.error("build", OPTION_VALUE_INVALID,
922 "No such an FV in FDF file: %s" % fvname)
923
924 #
925 # Merge Arch
926 #
927 if self.ArchList == None or len(self.ArchList) == 0:
928 ArchList = set(self.Platform.SupArchList)
929 else:
930 ArchList = set(self.ArchList) & set(self.Platform.SupArchList)
931 if len(ArchList) == 0:
932 EdkLogger.error("build", PARAMETER_INVALID,
933 ExtraData = "Active platform supports [%s] only, but [%s] is given."
934 % (" ".join(self.Platform.SupArchList), " ".join(self.ArchList)))
935 elif len(ArchList) != len(self.ArchList):
936 SkippedArchList = set(self.ArchList).symmetric_difference(set(self.Platform.SupArchList))
937 EdkLogger.verbose("\nArch [%s] is ignored because active platform supports [%s] but [%s] is specified !"
938 % (" ".join(SkippedArchList), " ".join(self.Platform.SupArchList), " ".join(self.ArchList)))
939 self.ArchList = tuple(ArchList)
940
941 # Merge build target
942 if self.BuildTargetList == None or len(self.BuildTargetList) == 0:
943 BuildTargetList = self.Platform.BuildTargets
944 else:
945 BuildTargetList = list(set(self.BuildTargetList) & set(self.Platform.BuildTargets))
946 if BuildTargetList == []:
947 EdkLogger.error("build", PARAMETER_INVALID, "Active platform only supports [%s], but [%s] is given"
948 % (" ".join(self.Platform.BuildTargets), " ".join(self.BuildTargetList)))
949 self.BuildTargetList = BuildTargetList
950
951 ## Build a module or platform
952 #
953 # Create autogen code and makefile for a module or platform, and the launch
954 # "make" command to build it
955 #
956 # @param Target The target of build command
957 # @param Platform The platform file
958 # @param Module The module file
959 # @param BuildTarget The name of build target, one of "DEBUG", "RELEASE"
960 # @param ToolChain The name of toolchain to build
961 # @param Arch The arch of the module/platform
962 # @param CreateDepModuleCodeFile Flag used to indicate creating code
963 # for dependent modules/Libraries
964 # @param CreateDepModuleMakeFile Flag used to indicate creating makefile
965 # for dependent modules/Libraries
966 #
967 def _Build(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True):
968 if AutoGenObject == None:
969 return False
970
971 # skip file generation for cleanxxx targets, run and fds target
972 if Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:
973 # for target which must generate AutoGen code and makefile
974 if not self.SkipAutoGen or Target == 'genc':
975 self.Progress.Start("Generating code")
976 AutoGenObject.CreateCodeFile(CreateDepsCodeFile)
977 self.Progress.Stop("done!")
978 if Target == "genc":
979 return True
980
981 if not self.SkipAutoGen or Target == 'genmake':
982 self.Progress.Start("Generating makefile")
983 AutoGenObject.CreateMakeFile(CreateDepsMakeFile)
984 AutoGenObject.CreateAsBuiltInf()
985 self.Progress.Stop("done!")
986 if Target == "genmake":
987 return True
988 else:
989 # always recreate top/platform makefile when clean, just in case of inconsistency
990 AutoGenObject.CreateCodeFile(False)
991 AutoGenObject.CreateMakeFile(False)
992
993 if EdkLogger.GetLevel() == EdkLogger.QUIET:
994 EdkLogger.quiet("Building ... %s" % repr(AutoGenObject))
995
996 BuildCommand = AutoGenObject.BuildCommand
997 if BuildCommand == None or len(BuildCommand) == 0:
998 EdkLogger.error("build", OPTION_MISSING, ExtraData="No MAKE command found for [%s, %s, %s]" % Key)
999
1000 BuildCommand = BuildCommand + [Target]
1001 LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir)
1002 if Target == 'cleanall':
1003 try:
1004 #os.rmdir(AutoGenObject.BuildDir)
1005 RemoveDirectory(AutoGenObject.BuildDir, True)
1006 #
1007 # First should close DB.
1008 #
1009 self.Db.Close()
1010 RemoveDirectory(gBuildCacheDir, True)
1011 except WindowsError, X:
1012 EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X))
1013 return True
1014
1015 ## Rebase module image and Get function address for the input module list.
1016 #
1017 def _RebaseModule (self, MapBuffer, BaseAddress, ModuleList, AddrIsOffset = True, ModeIsSmm = False):
1018 if ModeIsSmm:
1019 AddrIsOffset = False
1020 InfFileNameList = ModuleList.keys()
1021 #InfFileNameList.sort()
1022 for InfFile in InfFileNameList:
1023 sys.stdout.write (".")
1024 sys.stdout.flush()
1025 ModuleInfo = ModuleList[InfFile]
1026 ModuleName = ModuleInfo.BaseName
1027 ModuleOutputImage = ModuleInfo.Image.FileName
1028 ModuleDebugImage = os.path.join(ModuleInfo.DebugDir, ModuleInfo.BaseName + '.efi')
1029 ## for SMM module in SMRAM, the SMRAM will be allocated from base to top.
1030 if not ModeIsSmm:
1031 BaseAddress = BaseAddress - ModuleInfo.Image.Size
1032 #
1033 # Update Image to new BaseAddress by GenFw tool
1034 #
1035 LaunchCommand(["GenFw", "--rebase", str(BaseAddress), "-r", ModuleOutputImage], ModuleInfo.OutputDir)
1036 LaunchCommand(["GenFw", "--rebase", str(BaseAddress), "-r", ModuleDebugImage], ModuleInfo.DebugDir)
1037 else:
1038 #
1039 # Set new address to the section header only for SMM driver.
1040 #
1041 LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleOutputImage], ModuleInfo.OutputDir)
1042 LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleDebugImage], ModuleInfo.DebugDir)
1043 #
1044 # Collect funtion address from Map file
1045 #
1046 ImageMapTable = ModuleOutputImage.replace('.efi', '.map')
1047 FunctionList = []
1048 if os.path.exists(ImageMapTable):
1049 OrigImageBaseAddress = 0
1050 ImageMap = open (ImageMapTable, 'r')
1051 for LinStr in ImageMap:
1052 if len (LinStr.strip()) == 0:
1053 continue
1054 #
1055 # Get the preferred address set on link time.
1056 #
1057 if LinStr.find ('Preferred load address is') != -1:
1058 StrList = LinStr.split()
1059 OrigImageBaseAddress = int (StrList[len(StrList) - 1], 16)
1060
1061 StrList = LinStr.split()
1062 if len (StrList) > 4:
1063 if StrList[3] == 'f' or StrList[3] =='F':
1064 Name = StrList[1]
1065 RelativeAddress = int (StrList[2], 16) - OrigImageBaseAddress
1066 FunctionList.append ((Name, RelativeAddress))
1067 if ModuleInfo.Arch == 'IPF' and Name.endswith('_ModuleEntryPoint'):
1068 #
1069 # Get the real entry point address for IPF image.
1070 #
1071 ModuleInfo.Image.EntryPoint = RelativeAddress
1072 ImageMap.close()
1073 #
1074 # Add general information.
1075 #
1076 if ModeIsSmm:
1077 MapBuffer.write('\n\n%s (Fixed SMRAM Offset, BaseAddress=0x%010X, EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint))
1078 elif AddrIsOffset:
1079 MapBuffer.write('\n\n%s (Fixed Memory Offset, BaseAddress=-0x%010X, EntryPoint=-0x%010X)\n' % (ModuleName, 0 - BaseAddress, 0 - (BaseAddress + ModuleInfo.Image.EntryPoint)))
1080 else:
1081 MapBuffer.write('\n\n%s (Fixed Memory Address, BaseAddress=0x%010X, EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint))
1082 #
1083 # Add guid and general seciton section.
1084 #
1085 TextSectionAddress = 0
1086 DataSectionAddress = 0
1087 for SectionHeader in ModuleInfo.Image.SectionHeaderList:
1088 if SectionHeader[0] == '.text':
1089 TextSectionAddress = SectionHeader[1]
1090 elif SectionHeader[0] in ['.data', '.sdata']:
1091 DataSectionAddress = SectionHeader[1]
1092 if AddrIsOffset:
1093 MapBuffer.write('(GUID=%s, .textbaseaddress=-0x%010X, .databaseaddress=-0x%010X)\n' % (ModuleInfo.Guid, 0 - (BaseAddress + TextSectionAddress), 0 - (BaseAddress + DataSectionAddress)))
1094 else:
1095 MapBuffer.write('(GUID=%s, .textbaseaddress=0x%010X, .databaseaddress=0x%010X)\n' % (ModuleInfo.Guid, BaseAddress + TextSectionAddress, BaseAddress + DataSectionAddress))
1096 #
1097 # Add debug image full path.
1098 #
1099 MapBuffer.write('(IMAGE=%s)\n\n' % (ModuleDebugImage))
1100 #
1101 # Add funtion address
1102 #
1103 for Function in FunctionList:
1104 if AddrIsOffset:
1105 MapBuffer.write(' -0x%010X %s\n' % (0 - (BaseAddress + Function[1]), Function[0]))
1106 else:
1107 MapBuffer.write(' 0x%010X %s\n' % (BaseAddress + Function[1], Function[0]))
1108 ImageMap.close()
1109
1110 #
1111 # for SMM module in SMRAM, the SMRAM will be allocated from base to top.
1112 #
1113 if ModeIsSmm:
1114 BaseAddress = BaseAddress + ModuleInfo.Image.Size
1115
1116 ## Collect MAP information of all FVs
1117 #
1118 def _CollectFvMapBuffer (self, MapBuffer, Wa, ModuleList):
1119 if self.Fdf != '':
1120 # First get the XIP base address for FV map file.
1121 GuidPattern = re.compile("[-a-fA-F0-9]+")
1122 GuidName = re.compile("\(GUID=[-a-fA-F0-9]+")
1123 for FvName in Wa.FdfProfile.FvDict.keys():
1124 FvMapBuffer = os.path.join(Wa.FvDir, FvName + '.Fv.map')
1125 if not os.path.exists(FvMapBuffer):
1126 continue
1127 FvMap = open (FvMapBuffer, 'r')
1128 #skip FV size information
1129 FvMap.readline()
1130 FvMap.readline()
1131 FvMap.readline()
1132 FvMap.readline()
1133 for Line in FvMap:
1134 MatchGuid = GuidPattern.match(Line)
1135 if MatchGuid != None:
1136 #
1137 # Replace GUID with module name
1138 #
1139 GuidString = MatchGuid.group()
1140 if GuidString.upper() in ModuleList:
1141 Line = Line.replace(GuidString, ModuleList[GuidString.upper()].Name)
1142 MapBuffer.write('%s' % (Line))
1143 #
1144 # Add the debug image full path.
1145 #
1146 MatchGuid = GuidName.match(Line)
1147 if MatchGuid != None:
1148 GuidString = MatchGuid.group().split("=")[1]
1149 if GuidString.upper() in ModuleList:
1150 MapBuffer.write('(IMAGE=%s)\n' % (os.path.join(ModuleList[GuidString.upper()].DebugDir, ModuleList[GuidString.upper()].Name + '.efi')))
1151
1152 FvMap.close()
1153
1154 ## Collect MAP information of all modules
1155 #
1156 def _CollectModuleMapBuffer (self, MapBuffer, ModuleList):
1157 sys.stdout.write ("Generate Load Module At Fix Address Map")
1158 sys.stdout.flush()
1159 PatchEfiImageList = []
1160 PeiModuleList = {}
1161 BtModuleList = {}
1162 RtModuleList = {}
1163 SmmModuleList = {}
1164 PeiSize = 0
1165 BtSize = 0
1166 RtSize = 0
1167 # reserve 4K size in SMRAM to make SMM module address not from 0.
1168 SmmSize = 0x1000
1169 IsIpfPlatform = False
1170 if 'IPF' in self.ArchList:
1171 IsIpfPlatform = True
1172 for ModuleGuid in ModuleList:
1173 Module = ModuleList[ModuleGuid]
1174 GlobalData.gProcessingFile = "%s [%s, %s, %s]" % (Module.MetaFile, Module.Arch, Module.ToolChain, Module.BuildTarget)
1175
1176 OutputImageFile = ''
1177 for ResultFile in Module.CodaTargetList:
1178 if str(ResultFile.Target).endswith('.efi'):
1179 #
1180 # module list for PEI, DXE, RUNTIME and SMM
1181 #
1182 OutputImageFile = os.path.join(Module.OutputDir, Module.Name + '.efi')
1183 ImageClass = PeImageClass (OutputImageFile)
1184 if not ImageClass.IsValid:
1185 EdkLogger.error("build", FILE_PARSE_FAILURE, ExtraData=ImageClass.ErrorInfo)
1186 ImageInfo = PeImageInfo(Module.Name, Module.Guid, Module.Arch, Module.OutputDir, Module.DebugDir, ImageClass)
1187 if Module.ModuleType in ['PEI_CORE', 'PEIM', 'COMBINED_PEIM_DRIVER','PIC_PEIM', 'RELOCATABLE_PEIM', 'DXE_CORE']:
1188 PeiModuleList[Module.MetaFile] = ImageInfo
1189 PeiSize += ImageInfo.Image.Size
1190 elif Module.ModuleType in ['BS_DRIVER', 'DXE_DRIVER', 'UEFI_DRIVER']:
1191 BtModuleList[Module.MetaFile] = ImageInfo
1192 BtSize += ImageInfo.Image.Size
1193 elif Module.ModuleType in ['DXE_RUNTIME_DRIVER', 'RT_DRIVER', 'DXE_SAL_DRIVER', 'SAL_RT_DRIVER']:
1194 RtModuleList[Module.MetaFile] = ImageInfo
1195 #IPF runtime driver needs to be at 2 page alignment.
1196 if IsIpfPlatform and ImageInfo.Image.Size % 0x2000 != 0:
1197 ImageInfo.Image.Size = (ImageInfo.Image.Size / 0x2000 + 1) * 0x2000
1198 RtSize += ImageInfo.Image.Size
1199 elif Module.ModuleType in ['SMM_CORE', 'DXE_SMM_DRIVER']:
1200 SmmModuleList[Module.MetaFile] = ImageInfo
1201 SmmSize += ImageInfo.Image.Size
1202 if Module.ModuleType == 'DXE_SMM_DRIVER':
1203 PiSpecVersion = '0x00000000'
1204 if 'PI_SPECIFICATION_VERSION' in Module.Module.Specification:
1205 PiSpecVersion = Module.Module.Specification['PI_SPECIFICATION_VERSION']
1206 # for PI specification < PI1.1, DXE_SMM_DRIVER also runs as BOOT time driver.
1207 if int(PiSpecVersion, 16) < 0x0001000A:
1208 BtModuleList[Module.MetaFile] = ImageInfo
1209 BtSize += ImageInfo.Image.Size
1210 break
1211 #
1212 # EFI image is final target.
1213 # Check EFI image contains patchable FixAddress related PCDs.
1214 #
1215 if OutputImageFile != '':
1216 ModuleIsPatch = False
1217 for Pcd in Module.ModulePcdList:
1218 if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_LIST:
1219 ModuleIsPatch = True
1220 break
1221 if not ModuleIsPatch:
1222 for Pcd in Module.LibraryPcdList:
1223 if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_LIST:
1224 ModuleIsPatch = True
1225 break
1226
1227 if not ModuleIsPatch:
1228 continue
1229 #
1230 # Module includes the patchable load fix address PCDs.
1231 # It will be fixed up later.
1232 #
1233 PatchEfiImageList.append (OutputImageFile)
1234
1235 #
1236 # Get Top Memory address
1237 #
1238 ReservedRuntimeMemorySize = 0
1239 TopMemoryAddress = 0
1240 if self.LoadFixAddress == 0xFFFFFFFFFFFFFFFF:
1241 TopMemoryAddress = 0
1242 else:
1243 TopMemoryAddress = self.LoadFixAddress
1244 if TopMemoryAddress < RtSize + BtSize + PeiSize:
1245 EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS is too low to load driver")
1246 # Make IPF runtime driver at 2 page alignment.
1247 if IsIpfPlatform:
1248 ReservedRuntimeMemorySize = TopMemoryAddress % 0x2000
1249 RtSize = RtSize + ReservedRuntimeMemorySize
1250
1251 #
1252 # Patch FixAddress related PCDs into EFI image
1253 #
1254 for EfiImage in PatchEfiImageList:
1255 EfiImageMap = EfiImage.replace('.efi', '.map')
1256 if not os.path.exists(EfiImageMap):
1257 continue
1258 #
1259 # Get PCD offset in EFI image by GenPatchPcdTable function
1260 #
1261 PcdTable = parsePcdInfoFromMapFile(EfiImageMap, EfiImage)
1262 #
1263 # Patch real PCD value by PatchPcdValue tool
1264 #
1265 for PcdInfo in PcdTable:
1266 ReturnValue = 0
1267 if PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE:
1268 ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE_DATA_TYPE, str (PeiSize/0x1000))
1269 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE:
1270 ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE_DATA_TYPE, str (BtSize/0x1000))
1271 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE:
1272 ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE_DATA_TYPE, str (RtSize/0x1000))
1273 elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE and len (SmmModuleList) > 0:
1274 ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE_DATA_TYPE, str (SmmSize/0x1000))
1275 if ReturnValue != 0:
1276 EdkLogger.error("build", PARAMETER_INVALID, "Patch PCD value failed", ExtraData=ErrorInfo)
1277
1278 MapBuffer.write('PEI_CODE_PAGE_NUMBER = 0x%x\n' % (PeiSize/0x1000))
1279 MapBuffer.write('BOOT_CODE_PAGE_NUMBER = 0x%x\n' % (BtSize/0x1000))
1280 MapBuffer.write('RUNTIME_CODE_PAGE_NUMBER = 0x%x\n' % (RtSize/0x1000))
1281 if len (SmmModuleList) > 0:
1282 MapBuffer.write('SMM_CODE_PAGE_NUMBER = 0x%x\n' % (SmmSize/0x1000))
1283
1284 PeiBaseAddr = TopMemoryAddress - RtSize - BtSize
1285 BtBaseAddr = TopMemoryAddress - RtSize
1286 RtBaseAddr = TopMemoryAddress - ReservedRuntimeMemorySize
1287
1288 self._RebaseModule (MapBuffer, PeiBaseAddr, PeiModuleList, TopMemoryAddress == 0)
1289 self._RebaseModule (MapBuffer, BtBaseAddr, BtModuleList, TopMemoryAddress == 0)
1290 self._RebaseModule (MapBuffer, RtBaseAddr, RtModuleList, TopMemoryAddress == 0)
1291 self._RebaseModule (MapBuffer, 0x1000, SmmModuleList, AddrIsOffset = False, ModeIsSmm = True)
1292 MapBuffer.write('\n\n')
1293 sys.stdout.write ("\n")
1294 sys.stdout.flush()
1295
1296 ## Save platform Map file
1297 #
1298 def _SaveMapFile (self, MapBuffer, Wa):
1299 #
1300 # Map file path is got.
1301 #
1302 MapFilePath = os.path.join(Wa.BuildDir, Wa.Name + '.map')
1303 #
1304 # Save address map into MAP file.
1305 #
1306 SaveFileOnChange(MapFilePath, MapBuffer.getvalue(), False)
1307 MapBuffer.close()
1308 if self.LoadFixAddress != 0:
1309 sys.stdout.write ("\nLoad Module At Fix Address Map file can be found at %s\n" %(MapFilePath))
1310 sys.stdout.flush()
1311
1312 ## Build active platform for different build targets and different tool chains
1313 #
1314 def _BuildPlatform(self):
1315 for BuildTarget in self.BuildTargetList:
1316 for ToolChain in self.ToolChainList:
1317 Wa = WorkspaceAutoGen(
1318 self.WorkspaceDir,
1319 self.Platform,
1320 BuildTarget,
1321 ToolChain,
1322 self.ArchList,
1323 self.BuildDatabase,
1324 self.TargetTxt,
1325 self.ToolDef,
1326 self.Fdf,
1327 self.FdList,
1328 self.FvList,
1329 self.CapList,
1330 self.SkuId,
1331 self.UniFlag
1332 )
1333 self.BuildReport.AddPlatformReport(Wa)
1334 self.Progress.Stop("done!")
1335 self._Build(self.Target, Wa)
1336
1337 # Create MAP file when Load Fix Address is enabled.
1338 if self.Target in ["", "all", "fds"]:
1339 for Arch in self.ArchList:
1340 #
1341 # Check whether the set fix address is above 4G for 32bit image.
1342 #
1343 if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:
1344 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")
1345 #
1346 # Get Module List
1347 #
1348 ModuleList = {}
1349 for Pa in Wa.AutoGenObjectList:
1350 for Ma in Pa.ModuleAutoGenList:
1351 if Ma == None:
1352 continue
1353 if not Ma.IsLibrary:
1354 ModuleList[Ma.Guid.upper()] = Ma
1355
1356 MapBuffer = StringIO('')
1357 if self.LoadFixAddress != 0:
1358 #
1359 # Rebase module to the preferred memory address before GenFds
1360 #
1361 self._CollectModuleMapBuffer(MapBuffer, ModuleList)
1362 if self.Fdf != '':
1363 #
1364 # create FDS again for the updated EFI image
1365 #
1366 self._Build("fds", Wa)
1367 if self.Fdf != '':
1368 #
1369 # Create MAP file for all platform FVs after GenFds.
1370 #
1371 self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)
1372 #
1373 # Save MAP buffer into MAP file.
1374 #
1375 self._SaveMapFile (MapBuffer, Wa)
1376
1377 ## Build active module for different build targets, different tool chains and different archs
1378 #
1379 def _BuildModule(self):
1380 for BuildTarget in self.BuildTargetList:
1381 for ToolChain in self.ToolChainList:
1382 #
1383 # module build needs platform build information, so get platform
1384 # AutoGen first
1385 #
1386 Wa = WorkspaceAutoGen(
1387 self.WorkspaceDir,
1388 self.Platform,
1389 BuildTarget,
1390 ToolChain,
1391 self.ArchList,
1392 self.BuildDatabase,
1393 self.TargetTxt,
1394 self.ToolDef,
1395 self.Fdf,
1396 self.FdList,
1397 self.FvList,
1398 self.CapList,
1399 self.SkuId,
1400 self.UniFlag
1401 )
1402 Wa.CreateMakeFile(False)
1403 self.Progress.Stop("done!")
1404 MaList = []
1405 for Arch in self.ArchList:
1406 Ma = ModuleAutoGen(Wa, self.ModuleFile, BuildTarget, ToolChain, Arch, self.PlatformFile)
1407 if Ma == None: continue
1408 MaList.append(Ma)
1409 self._Build(self.Target, Ma)
1410
1411 self.BuildReport.AddPlatformReport(Wa, MaList)
1412 if MaList == []:
1413 EdkLogger.error(
1414 'build',
1415 BUILD_ERROR,
1416 "Module for [%s] is not a component of active platform."\
1417 " Please make sure that the ARCH and inf file path are"\
1418 " given in the same as in [%s]" %\
1419 (', '.join(self.ArchList), self.Platform),
1420 ExtraData=self.ModuleFile
1421 )
1422 # Create MAP file when Load Fix Address is enabled.
1423 if self.Target == "fds" and self.Fdf != '':
1424 for Arch in self.ArchList:
1425 #
1426 # Check whether the set fix address is above 4G for 32bit image.
1427 #
1428 if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:
1429 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")
1430 #
1431 # Get Module List
1432 #
1433 ModuleList = {}
1434 for Pa in Wa.AutoGenObjectList:
1435 for Ma in Pa.ModuleAutoGenList:
1436 if Ma == None:
1437 continue
1438 if not Ma.IsLibrary:
1439 ModuleList[Ma.Guid.upper()] = Ma
1440
1441 MapBuffer = StringIO('')
1442 if self.LoadFixAddress != 0:
1443 #
1444 # Rebase module to the preferred memory address before GenFds
1445 #
1446 self._CollectModuleMapBuffer(MapBuffer, ModuleList)
1447 #
1448 # create FDS again for the updated EFI image
1449 #
1450 self._Build("fds", Wa)
1451 #
1452 # Create MAP file for all platform FVs after GenFds.
1453 #
1454 self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)
1455 #
1456 # Save MAP buffer into MAP file.
1457 #
1458 self._SaveMapFile (MapBuffer, Wa)
1459
1460 ## Build a platform in multi-thread mode
1461 #
1462 def _MultiThreadBuildPlatform(self):
1463 for BuildTarget in self.BuildTargetList:
1464 for ToolChain in self.ToolChainList:
1465 Wa = WorkspaceAutoGen(
1466 self.WorkspaceDir,
1467 self.Platform,
1468 BuildTarget,
1469 ToolChain,
1470 self.ArchList,
1471 self.BuildDatabase,
1472 self.TargetTxt,
1473 self.ToolDef,
1474 self.Fdf,
1475 self.FdList,
1476 self.FvList,
1477 self.CapList,
1478 self.SkuId,
1479 self.UniFlag
1480 )
1481 self.BuildReport.AddPlatformReport(Wa)
1482 Wa.CreateMakeFile(False)
1483
1484 # multi-thread exit flag
1485 ExitFlag = threading.Event()
1486 ExitFlag.clear()
1487 for Arch in self.ArchList:
1488 Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)
1489 if Pa == None:
1490 continue
1491 for Module in Pa.Platform.Modules:
1492 # Get ModuleAutoGen object to generate C code file and makefile
1493 Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)
1494 if Ma == None:
1495 continue
1496 # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'
1497 if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:
1498 # for target which must generate AutoGen code and makefile
1499 if not self.SkipAutoGen or self.Target == 'genc':
1500 Ma.CreateCodeFile(True)
1501 if self.Target == "genc":
1502 continue
1503
1504 if not self.SkipAutoGen or self.Target == 'genmake':
1505 Ma.CreateMakeFile(True)
1506 Ma.CreateAsBuiltInf()
1507 if self.Target == "genmake":
1508 continue
1509 self.Progress.Stop("done!")
1510 # Generate build task for the module
1511 Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))
1512 # Break build if any build thread has error
1513 if BuildTask.HasError():
1514 # we need a full version of makefile for platform
1515 ExitFlag.set()
1516 BuildTask.WaitForComplete()
1517 Pa.CreateMakeFile(False)
1518 EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)
1519 # Start task scheduler
1520 if not BuildTask.IsOnGoing():
1521 BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)
1522
1523 # in case there's an interruption. we need a full version of makefile for platform
1524 Pa.CreateMakeFile(False)
1525 if BuildTask.HasError():
1526 EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)
1527
1528 #
1529 # All modules have been put in build tasks queue. Tell task scheduler
1530 # to exit if all tasks are completed
1531 #
1532 ExitFlag.set()
1533 BuildTask.WaitForComplete()
1534
1535 #
1536 # Check for build error, and raise exception if one
1537 # has been signaled.
1538 #
1539 if BuildTask.HasError():
1540 EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)
1541
1542 # Create MAP file when Load Fix Address is enabled.
1543 if self.Target in ["", "all", "fds"]:
1544 for Arch in self.ArchList:
1545 #
1546 # Check whether the set fix address is above 4G for 32bit image.
1547 #
1548 if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:
1549 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")
1550 #
1551 # Get Module List
1552 #
1553 ModuleList = {}
1554 for Pa in Wa.AutoGenObjectList:
1555 for Ma in Pa.ModuleAutoGenList:
1556 if Ma == None:
1557 continue
1558 if not Ma.IsLibrary:
1559 ModuleList[Ma.Guid.upper()] = Ma
1560 #
1561 # Rebase module to the preferred memory address before GenFds
1562 #
1563 MapBuffer = StringIO('')
1564 if self.LoadFixAddress != 0:
1565 self._CollectModuleMapBuffer(MapBuffer, ModuleList)
1566
1567 if self.Fdf != '':
1568 #
1569 # Generate FD image if there's a FDF file found
1570 #
1571 LaunchCommand(Wa.BuildCommand + ["fds"], Wa.MakeFileDir)
1572 #
1573 # Create MAP file for all platform FVs after GenFds.
1574 #
1575 self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)
1576 #
1577 # Save MAP buffer into MAP file.
1578 #
1579 self._SaveMapFile(MapBuffer, Wa)
1580
1581 ## Generate GuidedSectionTools.txt in the FV directories.
1582 #
1583 def CreateGuidedSectionToolsFile(self):
1584 for Arch in self.ArchList:
1585 for BuildTarget in self.BuildTargetList:
1586 for ToolChain in self.ToolChainList:
1587 FvDir = os.path.join(
1588 self.WorkspaceDir,
1589 self.Platform.OutputDirectory,
1590 '_'.join((BuildTarget, ToolChain)),
1591 'FV'
1592 )
1593 if not os.path.exists(FvDir):
1594 continue
1595 # Build up the list of supported architectures for this build
1596 prefix = '%s_%s_%s_' % (BuildTarget, ToolChain, Arch)
1597
1598 # Look through the tool definitions for GUIDed tools
1599 guidAttribs = []
1600 for (attrib, value) in self.ToolDef.ToolsDefTxtDictionary.iteritems():
1601 if attrib.upper().endswith('_GUID'):
1602 split = attrib.split('_')
1603 thisPrefix = '_'.join(split[0:3]) + '_'
1604 if thisPrefix == prefix:
1605 guid = self.ToolDef.ToolsDefTxtDictionary[attrib]
1606 guid = guid.lower()
1607 toolName = split[3]
1608 path = '_'.join(split[0:4]) + '_PATH'
1609 path = self.ToolDef.ToolsDefTxtDictionary[path]
1610 path = self.GetFullPathOfTool(path)
1611 guidAttribs.append((guid, toolName, path))
1612
1613 # Write out GuidedSecTools.txt
1614 toolsFile = os.path.join(FvDir, 'GuidedSectionTools.txt')
1615 toolsFile = open(toolsFile, 'wt')
1616 for guidedSectionTool in guidAttribs:
1617 print >> toolsFile, ' '.join(guidedSectionTool)
1618 toolsFile.close()
1619
1620 ## Returns the full path of the tool.
1621 #
1622 def GetFullPathOfTool (self, tool):
1623 if os.path.exists(tool):
1624 return os.path.realpath(tool)
1625 else:
1626 # We need to search for the tool using the
1627 # PATH environment variable.
1628 for dirInPath in os.environ['PATH'].split(os.pathsep):
1629 foundPath = os.path.join(dirInPath, tool)
1630 if os.path.exists(foundPath):
1631 return os.path.realpath(foundPath)
1632
1633 # If the tool was not found in the path then we just return
1634 # the input tool.
1635 return tool
1636
1637 ## Launch the module or platform build
1638 #
1639 def Launch(self):
1640 if self.ModuleFile == None or self.ModuleFile == "":
1641 if not self.SpawnMode or self.Target not in ["", "all"]:
1642 self.SpawnMode = False
1643 self._BuildPlatform()
1644 else:
1645 self._MultiThreadBuildPlatform()
1646 self.CreateGuidedSectionToolsFile()
1647 else:
1648 self.SpawnMode = False
1649 self._BuildModule()
1650
1651 ## Do some clean-up works when error occurred
1652 def Relinquish(self):
1653 OldLogLevel = EdkLogger.GetLevel()
1654 EdkLogger.SetLevel(EdkLogger.ERROR)
1655 #self.DumpBuildData()
1656 Utils.Progressor.Abort()
1657 if self.SpawnMode == True:
1658 BuildTask.Abort()
1659 EdkLogger.SetLevel(OldLogLevel)
1660
1661 def DumpBuildData(self):
1662 CacheDirectory = os.path.join(self.WorkspaceDir, gBuildCacheDir)
1663 Utils.CreateDirectory(CacheDirectory)
1664 Utils.DataDump(Utils.gFileTimeStampCache, os.path.join(CacheDirectory, "gFileTimeStampCache"))
1665 Utils.DataDump(Utils.gDependencyDatabase, os.path.join(CacheDirectory, "gDependencyDatabase"))
1666
1667 def RestoreBuildData(self):
1668 FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gFileTimeStampCache")
1669 if Utils.gFileTimeStampCache == {} and os.path.isfile(FilePath):
1670 Utils.gFileTimeStampCache = Utils.DataRestore(FilePath)
1671 if Utils.gFileTimeStampCache == None:
1672 Utils.gFileTimeStampCache = {}
1673
1674 FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gDependencyDatabase")
1675 if Utils.gDependencyDatabase == {} and os.path.isfile(FilePath):
1676 Utils.gDependencyDatabase = Utils.DataRestore(FilePath)
1677 if Utils.gDependencyDatabase == None:
1678 Utils.gDependencyDatabase = {}
1679
1680 def ParseDefines(DefineList=[]):
1681 DefineDict = {}
1682 if DefineList != None:
1683 for Define in DefineList:
1684 DefineTokenList = Define.split("=", 1)
1685 if len(DefineTokenList) == 1:
1686 DefineDict[DefineTokenList[0]] = ""
1687 else:
1688 DefineDict[DefineTokenList[0]] = DefineTokenList[1].strip()
1689 return DefineDict
1690
1691 gParamCheck = []
1692 def SingleCheckCallback(option, opt_str, value, parser):
1693 if option not in gParamCheck:
1694 setattr(parser.values, option.dest, value)
1695 gParamCheck.append(option)
1696 else:
1697 parser.error("Option %s only allows one instance in command line!" % option)
1698
1699 ## Parse command line options
1700 #
1701 # Using standard Python module optparse to parse command line option of this tool.
1702 #
1703 # @retval Opt A optparse.Values object containing the parsed options
1704 # @retval Args Target of build command
1705 #
1706 def MyOptionParser():
1707 Parser = OptionParser(description=__copyright__,version=__version__,prog="build.exe",usage="%prog [options] [all|fds|genc|genmake|clean|cleanall|cleanlib|modules|libraries|run]")
1708 Parser.add_option("-a", "--arch", action="append", type="choice", choices=['IA32','X64','IPF','EBC','ARM'], dest="TargetArch",
1709 help="ARCHS is one of list: IA32, X64, IPF, ARM or EBC, which overrides target.txt's TARGET_ARCH definition. To specify more archs, please repeat this option.")
1710 Parser.add_option("-p", "--platform", action="callback", type="string", dest="PlatformFile", callback=SingleCheckCallback,
1711 help="Build the platform specified by the DSC file name argument, overriding target.txt's ACTIVE_PLATFORM definition.")
1712 Parser.add_option("-m", "--module", action="callback", type="string", dest="ModuleFile", callback=SingleCheckCallback,
1713 help="Build the module specified by the INF file name argument.")
1714 Parser.add_option("-b", "--buildtarget", action="append", type="choice", choices=['DEBUG','RELEASE','NOOPT'], dest="BuildTarget",
1715 help="BuildTarget is one of list: DEBUG, RELEASE, NOOPT, which overrides target.txt's TARGET definition. To specify more TARGET, please repeat this option.")
1716 Parser.add_option("-t", "--tagname", action="append", type="string", dest="ToolChain",
1717 help="Using the Tool Chain Tagname to build the platform, overriding target.txt's TOOL_CHAIN_TAG definition.")
1718 Parser.add_option("-x", "--sku-id", action="callback", type="string", dest="SkuId", callback=SingleCheckCallback,
1719 help="Using this name of SKU ID to build the platform, overriding SKUID_IDENTIFIER in DSC file.")
1720
1721 Parser.add_option("-n", action="callback", type="int", dest="ThreadNumber", callback=SingleCheckCallback,
1722 help="Build the platform using multi-threaded compiler. The value overrides target.txt's MAX_CONCURRENT_THREAD_NUMBER. Less than 2 will disable multi-thread builds.")
1723
1724 Parser.add_option("-f", "--fdf", action="callback", type="string", dest="FdfFile", callback=SingleCheckCallback,
1725 help="The name of the FDF file to use, which overrides the setting in the DSC file.")
1726 Parser.add_option("-r", "--rom-image", action="append", type="string", dest="RomImage", default=[],
1727 help="The name of FD to be generated. The name must be from [FD] section in FDF file.")
1728 Parser.add_option("-i", "--fv-image", action="append", type="string", dest="FvImage", default=[],
1729 help="The name of FV to be generated. The name must be from [FV] section in FDF file.")
1730 Parser.add_option("-C", "--capsule-image", action="append", type="string", dest="CapName", default=[],
1731 help="The name of Capsule to be generated. The name must be from [Capsule] section in FDF file.")
1732 Parser.add_option("-u", "--skip-autogen", action="store_true", dest="SkipAutoGen", help="Skip AutoGen step.")
1733 Parser.add_option("-e", "--re-parse", action="store_true", dest="Reparse", help="Re-parse all meta-data files.")
1734
1735 Parser.add_option("-c", "--case-insensitive", action="store_true", dest="CaseInsensitive", help="Don't check case of file name.")
1736
1737 # Parser.add_option("-D", "--define", action="append", dest="Defines", metavar="NAME[=[VALUE]]",
1738 # help="Define global macro which can be used in DSC/DEC/INF files.")
1739
1740 Parser.add_option("-w", "--warning-as-error", action="store_true", dest="WarningAsError", help="Treat warning in tools as error.")
1741 Parser.add_option("-j", "--log", action="store", dest="LogFile", help="Put log in specified file as well as on console.")
1742
1743 Parser.add_option("-s", "--silent", action="store_true", type=None, dest="SilentMode",
1744 help="Make use of silent mode of (n)make.")
1745 Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")
1746 Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\
1747 "including library instances selected, final dependency expression, "\
1748 "and warning messages, etc.")
1749 Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")
1750 Parser.add_option("-D", "--define", action="append", type="string", dest="Macros", help="Macro: \"Name [= Value]\".")
1751
1752 Parser.add_option("-y", "--report-file", action="store", dest="ReportFile", help="Create/overwrite the report to the specified filename.")
1753 Parser.add_option("-Y", "--report-type", action="append", type="choice", choices=['PCD','LIBRARY','FLASH','DEPEX','BUILD_FLAGS','FIXED_ADDRESS', 'EXECUTION_ORDER'], dest="ReportType", default=[],
1754 help="Flags that control the type of build report to generate. Must be one of: [PCD, LIBRARY, FLASH, DEPEX, BUILD_FLAGS, FIXED_ADDRESS, EXECUTION_ORDER]. "\
1755 "To specify more than one flag, repeat this option on the command line and the default flag set is [PCD, LIBRARY, FLASH, DEPEX, BUILD_FLAGS, FIXED_ADDRESS]")
1756 Parser.add_option("-F", "--flag", action="store", type="string", dest="Flag",
1757 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. "\
1758 "This option can also be specified by setting *_*_*_BUILD_FLAGS in [BuildOptions] section of platform DSC. If they are both specified, this value "\
1759 "will override the setting in [BuildOptions] section of platform DSC.")
1760
1761 (Opt, Args)=Parser.parse_args()
1762 return (Opt, Args)
1763
1764 ## Tool entrance method
1765 #
1766 # This method mainly dispatch specific methods per the command line options.
1767 # If no error found, return zero value so the caller of this tool can know
1768 # if it's executed successfully or not.
1769 #
1770 # @retval 0 Tool was successful
1771 # @retval 1 Tool failed
1772 #
1773 def Main():
1774 StartTime = time.time()
1775
1776 # Initialize log system
1777 EdkLogger.Initialize()
1778
1779 #
1780 # Parse the options and args
1781 #
1782 (Option, Target) = MyOptionParser()
1783 GlobalData.gOptions = Option
1784 GlobalData.gCaseInsensitive = Option.CaseInsensitive
1785
1786 # Set log level
1787 if Option.verbose != None:
1788 EdkLogger.SetLevel(EdkLogger.VERBOSE)
1789 elif Option.quiet != None:
1790 EdkLogger.SetLevel(EdkLogger.QUIET)
1791 elif Option.debug != None:
1792 EdkLogger.SetLevel(Option.debug + 1)
1793 else:
1794 EdkLogger.SetLevel(EdkLogger.INFO)
1795
1796 if Option.LogFile != None:
1797 EdkLogger.SetLogFile(Option.LogFile)
1798
1799 if Option.WarningAsError == True:
1800 EdkLogger.SetWarningAsError()
1801
1802 if platform.platform().find("Windows") >= 0:
1803 GlobalData.gIsWindows = True
1804 else:
1805 GlobalData.gIsWindows = False
1806
1807 EdkLogger.quiet("Build environment: %s" % platform.platform())
1808 EdkLogger.quiet(time.strftime("Build start time: %H:%M:%S, %b.%d %Y\n", time.localtime()));
1809 ReturnCode = 0
1810 MyBuild = None
1811 try:
1812 if len(Target) == 0:
1813 Target = "all"
1814 elif len(Target) >= 2:
1815 EdkLogger.error("build", OPTION_NOT_SUPPORTED, "More than one targets are not supported.",
1816 ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))
1817 else:
1818 Target = Target[0].lower()
1819
1820 if Target not in gSupportedTarget:
1821 EdkLogger.error("build", OPTION_NOT_SUPPORTED, "Not supported target [%s]." % Target,
1822 ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))
1823
1824 GlobalData.gGlobalDefines = ParseDefines(Option.Macros)
1825 #
1826 # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH
1827 #
1828 CheckEnvVariable()
1829 Workspace = os.getenv("WORKSPACE")
1830 #
1831 # Get files real name in workspace dir
1832 #
1833 GlobalData.gAllFiles = Utils.DirCache(Workspace)
1834
1835 WorkingDirectory = os.getcwd()
1836 if not Option.ModuleFile:
1837 FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.inf')))
1838 FileNum = len(FileList)
1839 if FileNum >= 2:
1840 EdkLogger.error("build", OPTION_NOT_SUPPORTED, "There are %d INF files in %s." % (FileNum, WorkingDirectory),
1841 ExtraData="Please use '-m <INF_FILE_PATH>' switch to choose one.")
1842 elif FileNum == 1:
1843 Option.ModuleFile = NormFile(FileList[0], Workspace)
1844
1845 if Option.ModuleFile:
1846 if os.path.isabs (Option.ModuleFile):
1847 if os.path.normcase (os.path.normpath(Option.ModuleFile)).find (Workspace) == 0:
1848 Option.ModuleFile = NormFile(os.path.normpath(Option.ModuleFile), Workspace)
1849 Option.ModuleFile = PathClass(Option.ModuleFile, Workspace)
1850 ErrorCode, ErrorInfo = Option.ModuleFile.Validate(".inf", False)
1851 if ErrorCode != 0:
1852 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)
1853
1854 if Option.PlatformFile != None:
1855 if os.path.isabs (Option.PlatformFile):
1856 if os.path.normcase (os.path.normpath(Option.PlatformFile)).find (Workspace) == 0:
1857 Option.PlatformFile = NormFile(os.path.normpath(Option.PlatformFile), Workspace)
1858 Option.PlatformFile = PathClass(Option.PlatformFile, Workspace)
1859 ErrorCode, ErrorInfo = Option.PlatformFile.Validate(".dsc", False)
1860 if ErrorCode != 0:
1861 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)
1862
1863 if Option.FdfFile != None:
1864 if os.path.isabs (Option.FdfFile):
1865 if os.path.normcase (os.path.normpath(Option.FdfFile)).find (Workspace) == 0:
1866 Option.FdfFile = NormFile(os.path.normpath(Option.FdfFile), Workspace)
1867 Option.FdfFile = PathClass(Option.FdfFile, Workspace)
1868 ErrorCode, ErrorInfo = Option.FdfFile.Validate(".fdf", False)
1869 if ErrorCode != 0:
1870 EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)
1871
1872 if Option.Flag != None and Option.Flag not in ['-c', '-s']:
1873 EdkLogger.error("build", OPTION_VALUE_INVALID, "UNI flag must be one of -c or -s")
1874
1875 MyBuild = Build(Target, Workspace, Option.PlatformFile, Option.ModuleFile,
1876 Option.TargetArch, Option.ToolChain, Option.BuildTarget,
1877 Option.FdfFile, Option.RomImage, Option.FvImage, Option.CapName,
1878 None, Option.SilentMode, Option.ThreadNumber,
1879 Option.SkipAutoGen, Option.Reparse, Option.SkuId,
1880 Option.ReportFile, Option.ReportType, Option.Flag)
1881 MyBuild.Launch()
1882 #MyBuild.DumpBuildData()
1883 except FatalError, X:
1884 if MyBuild != None:
1885 # for multi-thread build exits safely
1886 MyBuild.Relinquish()
1887 if Option != None and Option.debug != None:
1888 EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())
1889 ReturnCode = X.args[0]
1890 except Warning, X:
1891 # error from Fdf parser
1892 if MyBuild != None:
1893 # for multi-thread build exits safely
1894 MyBuild.Relinquish()
1895 if Option != None and Option.debug != None:
1896 EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())
1897 else:
1898 EdkLogger.error(X.ToolName, FORMAT_INVALID, File=X.FileName, Line=X.LineNumber, ExtraData=X.Message, RaiseError = False)
1899 ReturnCode = FORMAT_INVALID
1900 except KeyboardInterrupt:
1901 ReturnCode = ABORT_ERROR
1902 if Option != None and Option.debug != None:
1903 EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())
1904 except:
1905 if MyBuild != None:
1906 # for multi-thread build exits safely
1907 MyBuild.Relinquish()
1908
1909 # try to get the meta-file from the object causing exception
1910 Tb = sys.exc_info()[-1]
1911 MetaFile = GlobalData.gProcessingFile
1912 while Tb != None:
1913 if 'self' in Tb.tb_frame.f_locals and hasattr(Tb.tb_frame.f_locals['self'], 'MetaFile'):
1914 MetaFile = Tb.tb_frame.f_locals['self'].MetaFile
1915 Tb = Tb.tb_next
1916 EdkLogger.error(
1917 "\nbuild",
1918 CODE_ERROR,
1919 "Unknown fatal error when processing [%s]" % MetaFile,
1920 ExtraData="\n(Please send email to edk2-buildtools-devel@lists.sourceforge.net for help, attaching following call stack trace!)\n",
1921 RaiseError=False
1922 )
1923 EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())
1924 ReturnCode = CODE_ERROR
1925 finally:
1926 Utils.Progressor.Abort()
1927
1928 if ReturnCode == 0:
1929 Conclusion = "Done"
1930 elif ReturnCode == ABORT_ERROR:
1931 Conclusion = "Aborted"
1932 else:
1933 Conclusion = "Failed"
1934 FinishTime = time.time()
1935 BuildDuration = time.gmtime(int(round(FinishTime - StartTime)))
1936 BuildDurationStr = ""
1937 if BuildDuration.tm_yday > 1:
1938 BuildDurationStr = time.strftime("%H:%M:%S", BuildDuration) + ", %d day(s)"%(BuildDuration.tm_yday - 1)
1939 else:
1940 BuildDurationStr = time.strftime("%H:%M:%S", BuildDuration)
1941 if MyBuild != None:
1942 MyBuild.BuildReport.GenerateReport(BuildDurationStr)
1943 MyBuild.Db.Close()
1944 EdkLogger.SetLevel(EdkLogger.QUIET)
1945 EdkLogger.quiet("\n- %s -" % Conclusion)
1946 EdkLogger.quiet(time.strftime("Build end time: %H:%M:%S, %b.%d %Y", time.localtime()))
1947 EdkLogger.quiet("Build total time: %s\n" % BuildDurationStr)
1948 return ReturnCode
1949
1950 if __name__ == '__main__':
1951 r = Main()
1952 ## 0-127 is a safe return range, and 1 is a standard default error
1953 if r < 0 or r > 127: r = 1
1954 sys.exit(r)
1955