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