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