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