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