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