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