-## @file
-# build a platform or a module
-#
-# Copyright (c) 2007, Intel Corporation
-#
-# All rights reserved. This program and the accompanying materials
-# are licensed and made available under the terms and conditions of the BSD License
-# which accompanies this distribution. The full text of the license may be found at
-# http://opensource.org/licenses/bsd-license.php
-#
-# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
-# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
-#
-
-##
-# Import Modules
-#
-import os
-import re
-import sys
-import glob
-import time
-import platform
-import traceback
-
-from threading import *
-from optparse import OptionParser
-from subprocess import *
-from Common import Misc as Utils
-
-from Common.TargetTxtClassObject import *
-from Common.ToolDefClassObject import *
-from Common.DataType import *
-from AutoGen.AutoGen import *
-from Common.BuildToolError import *
-from Workspace.WorkspaceDatabase import *
-
-import Common.EdkLogger
-import Common.GlobalData as GlobalData
-
-# Version and Copyright
-VersionNumber = "0.5"
-__version__ = "%prog Version " + VersionNumber
-__copyright__ = "Copyright (c) 2007, Intel Corporation All rights reserved."
-
-## standard targets of build command
-gSupportedTarget = ['all', 'genc', 'genmake', 'modules', 'libraries', 'fds', 'clean', 'cleanall', 'cleanlib', 'run']
-
-## build configuration file
-gBuildConfiguration = "Conf/target.txt"
-gBuildCacheDir = "Conf/.cache"
-gToolsDefinition = "Conf/tools_def.txt"
-
-## Check environment PATH variable to make sure the specified tool is found
-#
-# If the tool is found in the PATH, then True is returned
-# Otherwise, False is returned
-#
-def IsToolInPath(tool):
- if os.environ.has_key('PATHEXT'):
- extns = os.environ['PATHEXT'].split(os.path.pathsep)
- else:
- extns = ('',)
- for pathDir in os.environ['PATH'].split(os.path.pathsep):
- for ext in extns:
- if os.path.exists(os.path.join(pathDir, tool + ext)):
- return True
- return False
-
-## Check environment variables
-#
-# Check environment variables that must be set for build. Currently they are
-#
-# WORKSPACE The directory all packages/platforms start from
-# EDK_TOOLS_PATH The directory contains all tools needed by the build
-# PATH $(EDK_TOOLS_PATH)/Bin/<sys> must be set in PATH
-#
-# If any of above environment variable is not set or has error, the build
-# will be broken.
-#
-def CheckEnvVariable():
- # check WORKSPACE
- if "WORKSPACE" not in os.environ:
- EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",
- ExtraData="WORKSPACE")
-
- WorkspaceDir = os.path.normcase(os.path.normpath(os.environ["WORKSPACE"]))
- if not os.path.exists(WorkspaceDir):
- EdkLogger.error("build", FILE_NOT_FOUND, "WORKSPACE doesn't exist", ExtraData="%s" % WorkspaceDir)
- elif ' ' in WorkspaceDir:
- EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in WORKSPACE path",
- ExtraData=WorkspaceDir)
- os.environ["WORKSPACE"] = WorkspaceDir
-
- #
- # Check EFI_SOURCE (R8 build convention). EDK_SOURCE will always point to ECP
- #
- os.environ["ECP_SOURCE"] = os.path.join(WorkspaceDir, GlobalData.gEdkCompatibilityPkg)
- if "EFI_SOURCE" not in os.environ:
- os.environ["EFI_SOURCE"] = os.environ["ECP_SOURCE"]
- if "EDK_SOURCE" not in os.environ:
- os.environ["EDK_SOURCE"] = os.environ["ECP_SOURCE"]
-
- #
- # Unify case of characters on case-insensitive systems
- #
- EfiSourceDir = os.path.normcase(os.path.normpath(os.environ["EFI_SOURCE"]))
- EdkSourceDir = os.path.normcase(os.path.normpath(os.environ["EDK_SOURCE"]))
- EcpSourceDir = os.path.normcase(os.path.normpath(os.environ["ECP_SOURCE"]))
-
- os.environ["EFI_SOURCE"] = EfiSourceDir
- os.environ["EDK_SOURCE"] = EdkSourceDir
- os.environ["ECP_SOURCE"] = EcpSourceDir
- os.environ["EDK_TOOLS_PATH"] = os.path.normcase(os.environ["EDK_TOOLS_PATH"])
-
- if not os.path.exists(EcpSourceDir):
- EdkLogger.verbose("ECP_SOURCE = %s doesn't exist. R8 modules could not be built." % EcpSourceDir)
- elif ' ' in EcpSourceDir:
- EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in ECP_SOURCE path",
- ExtraData=EcpSourceDir)
- if not os.path.exists(EdkSourceDir):
- if EdkSourceDir == EcpSourceDir:
- EdkLogger.verbose("EDK_SOURCE = %s doesn't exist. R8 modules could not be built." % EdkSourceDir)
- else:
- EdkLogger.error("build", PARAMETER_INVALID, "EDK_SOURCE does not exist",
- ExtraData=EdkSourceDir)
- elif ' ' in EdkSourceDir:
- EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in EDK_SOURCE path",
- ExtraData=EdkSourceDir)
- if not os.path.exists(EfiSourceDir):
- if EfiSourceDir == EcpSourceDir:
- EdkLogger.verbose("EFI_SOURCE = %s doesn't exist. R8 modules could not be built." % EfiSourceDir)
- else:
- EdkLogger.error("build", PARAMETER_INVALID, "EFI_SOURCE does not exist",
- ExtraData=EfiSourceDir)
- elif ' ' in EfiSourceDir:
- EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in EFI_SOURCE path",
- ExtraData=EfiSourceDir)
-
- # change absolute path to relative path to WORKSPACE
- if EfiSourceDir.upper().find(WorkspaceDir.upper()) != 0:
- EdkLogger.error("build", PARAMETER_INVALID, "EFI_SOURCE is not under WORKSPACE",
- ExtraData="WORKSPACE = %s\n EFI_SOURCE = %s" % (WorkspaceDir, EfiSourceDir))
- if EdkSourceDir.upper().find(WorkspaceDir.upper()) != 0:
- EdkLogger.error("build", PARAMETER_INVALID, "EDK_SOURCE is not under WORKSPACE",
- ExtraData="WORKSPACE = %s\n EDK_SOURCE = %s" % (WorkspaceDir, EdkSourceDir))
- if EcpSourceDir.upper().find(WorkspaceDir.upper()) != 0:
- EdkLogger.error("build", PARAMETER_INVALID, "ECP_SOURCE is not under WORKSPACE",
- ExtraData="WORKSPACE = %s\n ECP_SOURCE = %s" % (WorkspaceDir, EcpSourceDir))
-
- # check EDK_TOOLS_PATH
- if "EDK_TOOLS_PATH" not in os.environ:
- EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",
- ExtraData="EDK_TOOLS_PATH")
-
- # check PATH
- if "PATH" not in os.environ:
- EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",
- ExtraData="PATH")
-
- GlobalData.gWorkspace = WorkspaceDir
- GlobalData.gEfiSource = EfiSourceDir
- GlobalData.gEdkSource = EdkSourceDir
- GlobalData.gEcpSource = EcpSourceDir
-
-## Get normalized file path
-#
-# Convert the path to be local format, and remove the WORKSPACE path at the
-# beginning if the file path is given in full path.
-#
-# @param FilePath File path to be normalized
-# @param Workspace Workspace path which the FilePath will be checked against
-#
-# @retval string The normalized file path
-#
-def NormFile(FilePath, Workspace):
- # check if the path is absolute or relative
- if os.path.isabs(FilePath):
- FileFullPath = os.path.normpath(FilePath)
- else:
- FileFullPath = os.path.normpath(os.path.join(Workspace, FilePath))
-
- # check if the file path exists or not
- if not os.path.isfile(FileFullPath):
- EdkLogger.error("build", FILE_NOT_FOUND, ExtraData="\t%s (Please give file in absolute path or relative to WORKSPACE)" % FileFullPath)
-
- # remove workspace directory from the beginning part of the file path
- if Workspace[-1] in ["\\", "/"]:
- return FileFullPath[len(Workspace):]
- else:
- return FileFullPath[(len(Workspace) + 1):]
-
-## Get the output of an external program
-#
-# This is the entrance method of thread reading output of an external program and
-# putting them in STDOUT/STDERR of current program.
-#
-# @param From The stream message read from
-# @param To The stream message put on
-# @param ExitFlag The flag used to indicate stopping reading
-#
-def ReadMessage(From, To, ExitFlag):
- while True:
- # read one line a time
- Line = From.readline()
- # empty string means "end"
- if Line != None and Line != "":
- To(Line.rstrip())
- else:
- break
- if ExitFlag.isSet():
- break
-
-## Launch an external program
-#
-# This method will call subprocess.Popen to execute an external program with
-# given options in specified directory. Because of the dead-lock issue during
-# redirecting output of the external program, threads are used to to do the
-# redirection work.
-#
-# @param Command A list or string containing the call of the program
-# @param WorkingDir The directory in which the program will be running
-#
-def LaunchCommand(Command, WorkingDir):
- # if working directory doesn't exist, Popen() will raise an exception
- if not os.path.isdir(WorkingDir):
- EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=WorkingDir)
-
- Proc = None
- EndOfProcedure = None
- try:
- # launch the command
- Proc = Popen(Command, stdout=PIPE, stderr=PIPE, env=os.environ, cwd=WorkingDir, bufsize=-1)
-
- # launch two threads to read the STDOUT and STDERR
- EndOfProcedure = Event()
- EndOfProcedure.clear()
- if Proc.stdout:
- StdOutThread = Thread(target=ReadMessage, args=(Proc.stdout, EdkLogger.info, EndOfProcedure))
- StdOutThread.setName("STDOUT-Redirector")
- StdOutThread.setDaemon(False)
- StdOutThread.start()
-
- if Proc.stderr:
- StdErrThread = Thread(target=ReadMessage, args=(Proc.stderr, EdkLogger.quiet, EndOfProcedure))
- StdErrThread.setName("STDERR-Redirector")
- StdErrThread.setDaemon(False)
- StdErrThread.start()
-
- # waiting for program exit
- Proc.wait()
- except: # in case of aborting
- # terminate the threads redirecting the program output
- if EndOfProcedure != None:
- EndOfProcedure.set()
- if Proc == None:
- if type(Command) != type(""):
- Command = " ".join(Command)
- EdkLogger.error("build", COMMAND_FAILURE, "Failed to start command", ExtraData="%s [%s]" % (Command, WorkingDir))
-
- if Proc.stdout:
- StdOutThread.join()
- if Proc.stderr:
- StdErrThread.join()
-
- # check the return code of the program
- if Proc.returncode != 0:
- if type(Command) != type(""):
- Command = " ".join(Command)
- EdkLogger.error("build", COMMAND_FAILURE, ExtraData="%s [%s]" % (Command, WorkingDir))
-
-## The smallest unit that can be built in multi-thread build mode
-#
-# This is the base class of build unit. The "Obj" parameter must provide
-# __str__(), __eq__() and __hash__() methods. Otherwise there could be build units
-# missing build.
-#
-# Currently the "Obj" should be only ModuleAutoGen or PlatformAutoGen objects.
-#
-class BuildUnit:
- ## The constructor
- #
- # @param self The object pointer
- # @param Obj The object the build is working on
- # @param Target The build target name, one of gSupportedTarget
- # @param Dependency The BuildUnit(s) which must be completed in advance
- # @param WorkingDir The directory build command starts in
- #
- def __init__(self, Obj, BuildCommand, Target, Dependency, WorkingDir="."):
- self.BuildObject = Obj
- self.Dependency = Dependency
- self.WorkingDir = WorkingDir
- self.Target = Target
- self.BuildCommand = BuildCommand
- if BuildCommand == None or len(BuildCommand) == 0:
- EdkLogger.error("build", OPTION_MISSING, "No build command found for",
- ExtraData=str(Obj))
-
- ## str() method
- #
- # It just returns the string representaion of self.BuildObject
- #
- # @param self The object pointer
- #
- def __str__(self):
- return str(self.BuildObject)
-
- ## "==" operator method
- #
- # It just compares self.BuildObject with "Other". So self.BuildObject must
- # provide its own __eq__() method.
- #
- # @param self The object pointer
- # @param Other The other BuildUnit object compared to
- #
- def __eq__(self, Other):
- return Other != None and self.BuildObject == Other.BuildObject \
- and self.BuildObject.Arch == Other.BuildObject.Arch
-
- ## hash() method
- #
- # It just returns the hash value of self.BuildObject which must be hashable.
- #
- # @param self The object pointer
- #
- def __hash__(self):
- return hash(self.BuildObject) + hash(self.BuildObject.Arch)
-
- def __repr__(self):
- return repr(self.BuildObject)
-
-## The smallest module unit that can be built by nmake/make command in multi-thread build mode
-#
-# This class is for module build by nmake/make build system. The "Obj" parameter
-# must provide __str__(), __eq__() and __hash__() methods. Otherwise there could
-# be make units missing build.
-#
-# Currently the "Obj" should be only ModuleAutoGen object.
-#
-class ModuleMakeUnit(BuildUnit):
- ## The constructor
- #
- # @param self The object pointer
- # @param Obj The ModuleAutoGen object the build is working on
- # @param Target The build target name, one of gSupportedTarget
- #
- def __init__(self, Obj, Target):
- Dependency = [ModuleMakeUnit(La, Target) for La in Obj.LibraryAutoGenList]
- BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir)
- if Target in [None, "", "all"]:
- self.Target = "tbuild"
-
-## The smallest platform unit that can be built by nmake/make command in multi-thread build mode
-#
-# This class is for platform build by nmake/make build system. The "Obj" parameter
-# must provide __str__(), __eq__() and __hash__() methods. Otherwise there could
-# be make units missing build.
-#
-# Currently the "Obj" should be only PlatformAutoGen object.
-#
-class PlatformMakeUnit(BuildUnit):
- ## The constructor
- #
- # @param self The object pointer
- # @param Obj The PlatformAutoGen object the build is working on
- # @param Target The build target name, one of gSupportedTarget
- #
- def __init__(self, Obj, Target):
- Dependency = [ModuleMakeUnit(Lib, Target) for Lib in self.BuildObject.LibraryAutoGenList]
- Dependency.extend([ModuleMakeUnit(Mod, Target) for Mod in self.BuildObject.ModuleAutoGenList])
- BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir)
-
-## The class representing the task of a module build or platform build
-#
-# This class manages the build tasks in multi-thread build mode. Its jobs include
-# scheduling thread running, catching thread error, monitor the thread status, etc.
-#
-class BuildTask:
- # queue for tasks waiting for schedule
- _PendingQueue = sdict()
- _PendingQueueLock = threading.Lock()
-
- # queue for tasks ready for running
- _ReadyQueue = sdict()
- _ReadyQueueLock = threading.Lock()
-
- # queue for run tasks
- _RunningQueue = sdict()
- _RunningQueueLock = threading.Lock()
-
- # queue containing all build tasks, in case duplicate build
- _TaskQueue = sdict()
-
- # flag indicating error occurs in a running thread
- _ErrorFlag = threading.Event()
- _ErrorFlag.clear()
- _ErrorMessage = ""
-
- # BoundedSemaphore object used to control the number of running threads
- _Thread = None
-
- # flag indicating if the scheduler is started or not
- _SchedulerStopped = threading.Event()
- _SchedulerStopped.set()
-
- ## Start the task scheduler thread
- #
- # @param MaxThreadNumber The maximum thread number
- # @param ExitFlag Flag used to end the scheduler
- #
- @staticmethod
- def StartScheduler(MaxThreadNumber, ExitFlag):
- SchedulerThread = Thread(target=BuildTask.Scheduler, args=(MaxThreadNumber, ExitFlag))
- SchedulerThread.setName("Build-Task-Scheduler")
- SchedulerThread.setDaemon(False)
- SchedulerThread.start()
- # wait for the scheduler to be started, especially useful in Linux
- while not BuildTask.IsOnGoing():
- time.sleep(0.01)
-
- ## Scheduler method
- #
- # @param MaxThreadNumber The maximum thread number
- # @param ExitFlag Flag used to end the scheduler
- #
- @staticmethod
- def Scheduler(MaxThreadNumber, ExitFlag):
- BuildTask._SchedulerStopped.clear()
- try:
- # use BoundedSemaphore to control the maximum running threads
- BuildTask._Thread = BoundedSemaphore(MaxThreadNumber)
- #
- # scheduling loop, which will exits when no pending/ready task and
- # indicated to do so, or there's error in running thread
- #
- while (len(BuildTask._PendingQueue) > 0 or len(BuildTask._ReadyQueue) > 0 \
- or not ExitFlag.isSet()) and not BuildTask._ErrorFlag.isSet():
- EdkLogger.debug(EdkLogger.DEBUG_8, "Pending Queue (%d), Ready Queue (%d)"
- % (len(BuildTask._PendingQueue), len(BuildTask._ReadyQueue)))
-
- # get all pending tasks
- BuildTask._PendingQueueLock.acquire()
- BuildObjectList = BuildTask._PendingQueue.keys()
- #
- # check if their dependency is resolved, and if true, move them
- # into ready queue
- #
- for BuildObject in BuildObjectList:
- Bt = BuildTask._PendingQueue[BuildObject]
- if Bt.IsReady():
- BuildTask._ReadyQueue[BuildObject] = BuildTask._PendingQueue.pop(BuildObject)
- BuildTask._PendingQueueLock.release()
-
- # launch build thread until the maximum number of threads is reached
- while not BuildTask._ErrorFlag.isSet():
- # empty ready queue, do nothing further
- if len(BuildTask._ReadyQueue) == 0:
- break
-
- # wait for active thread(s) exit
- BuildTask._Thread.acquire(True)
-
- # start a new build thread
- Bo = BuildTask._ReadyQueue.keys()[0]
- Bt = BuildTask._ReadyQueue.pop(Bo)
-
- # move into running queue
- BuildTask._RunningQueueLock.acquire()
- BuildTask._RunningQueue[Bo] = Bt
- BuildTask._RunningQueueLock.release()
-
- Bt.Start()
- # avoid tense loop
- time.sleep(0.01)
-
- # avoid tense loop
- time.sleep(0.01)
-
- # wait for all running threads exit
- if BuildTask._ErrorFlag.isSet():
- EdkLogger.quiet("\nWaiting for all build threads exit...")
- # while not BuildTask._ErrorFlag.isSet() and \
- while len(BuildTask._RunningQueue) > 0:
- EdkLogger.verbose("Waiting for thread ending...(%d)" % len(BuildTask._RunningQueue))
- EdkLogger.debug(EdkLogger.DEBUG_8, "Threads [%s]" % ", ".join([Th.getName() for Th in threading.enumerate()]))
- # avoid tense loop
- time.sleep(0.1)
- except BaseException, X:
- #
- # TRICK: hide the output of threads left runing, so that the user can
- # catch the error message easily
- #
- EdkLogger.SetLevel(EdkLogger.ERROR)
- BuildTask._ErrorFlag.set()
- BuildTask._ErrorMessage = "build thread scheduler error\n\t%s" % str(X)
-
- BuildTask._PendingQueue.clear()
- BuildTask._ReadyQueue.clear()
- BuildTask._RunningQueue.clear()
- BuildTask._TaskQueue.clear()
- BuildTask._SchedulerStopped.set()
-
- ## Wait for all running method exit
- #
- @staticmethod
- def WaitForComplete():
- BuildTask._SchedulerStopped.wait()
-
- ## Check if the scheduler is running or not
- #
- @staticmethod
- def IsOnGoing():
- return not BuildTask._SchedulerStopped.isSet()
-
- ## Abort the build
- @staticmethod
- def Abort():
- if BuildTask.IsOnGoing():
- BuildTask._ErrorFlag.set()
- BuildTask.WaitForComplete()
-
- ## Check if there's error in running thread
- #
- # Since the main thread cannot catch exceptions in other thread, we have to
- # use threading.Event to communicate this formation to main thread.
- #
- @staticmethod
- def HasError():
- return BuildTask._ErrorFlag.isSet()
-
- ## Get error message in running thread
- #
- # Since the main thread cannot catch exceptions in other thread, we have to
- # use a static variable to communicate this message to main thread.
- #
- @staticmethod
- def GetErrorMessage():
- return BuildTask._ErrorMessage
-
- ## Factory method to create a BuildTask object
- #
- # This method will check if a module is building or has been built. And if
- # true, just return the associated BuildTask object in the _TaskQueue. If
- # not, create and return a new BuildTask object. The new BuildTask object
- # will be appended to the _PendingQueue for scheduling later.
- #
- # @param BuildItem A BuildUnit object representing a build object
- # @param Dependency The dependent build object of BuildItem
- #
- @staticmethod
- def New(BuildItem, Dependency=None):
- if BuildItem in BuildTask._TaskQueue:
- Bt = BuildTask._TaskQueue[BuildItem]
- return Bt
-
- Bt = BuildTask()
- Bt._Init(BuildItem, Dependency)
- BuildTask._TaskQueue[BuildItem] = Bt
-
- BuildTask._PendingQueueLock.acquire()
- BuildTask._PendingQueue[BuildItem] = Bt
- BuildTask._PendingQueueLock.release()
-
- return Bt
-
- ## The real constructor of BuildTask
- #
- # @param BuildItem A BuildUnit object representing a build object
- # @param Dependency The dependent build object of BuildItem
- #
- def _Init(self, BuildItem, Dependency=None):
- self.BuildItem = BuildItem
-
- self.DependencyList = []
- if Dependency == None:
- Dependency = BuildItem.Dependency
- else:
- Dependency.extend(BuildItem.Dependency)
- self.AddDependency(Dependency)
- # flag indicating build completes, used to avoid unnecessary re-build
- self.CompleteFlag = False
-
- ## Check if all dependent build tasks are completed or not
- #
- def IsReady(self):
- ReadyFlag = True
- for Dep in self.DependencyList:
- if Dep.CompleteFlag == True:
- continue
- ReadyFlag = False
- break
-
- return ReadyFlag
-
- ## Add dependent build task
- #
- # @param Dependency The list of dependent build objects
- #
- def AddDependency(self, Dependency):
- for Dep in Dependency:
- self.DependencyList.append(BuildTask.New(Dep)) # BuildTask list
-
- ## The thread wrapper of LaunchCommand function
- #
- # @param Command A list or string contains the call of the command
- # @param WorkingDir The directory in which the program will be running
- #
- def _CommandThread(self, Command, WorkingDir):
- try:
- LaunchCommand(Command, WorkingDir)
- self.CompleteFlag = True
- except:
- #
- # TRICK: hide the output of threads left runing, so that the user can
- # catch the error message easily
- #
- if not BuildTask._ErrorFlag.isSet():
- GlobalData.gBuildingModule = "%s [%s, %s, %s]" % (str(self.BuildItem.BuildObject),
- self.BuildItem.BuildObject.Arch,
- self.BuildItem.BuildObject.ToolChain,
- self.BuildItem.BuildObject.BuildTarget
- )
- EdkLogger.SetLevel(EdkLogger.ERROR)
- BuildTask._ErrorFlag.set()
- BuildTask._ErrorMessage = "%s broken\n %s [%s]" % \
- (threading.currentThread().getName(), Command, WorkingDir)
- # indicate there's a thread is available for another build task
- BuildTask._RunningQueueLock.acquire()
- BuildTask._RunningQueue.pop(self.BuildItem)
- BuildTask._RunningQueueLock.release()
- BuildTask._Thread.release()
-
- ## Start build task thread
- #
- def Start(self):
- EdkLogger.quiet("Building ... %s" % repr(self.BuildItem))
- Command = self.BuildItem.BuildCommand + [self.BuildItem.Target]
- self.BuildTread = Thread(target=self._CommandThread, args=(Command, self.BuildItem.WorkingDir))
- self.BuildTread.setName("build thread")
- self.BuildTread.setDaemon(False)
- self.BuildTread.start()
-
-## The class implementing the EDK2 build process
-#
-# The build process includes:
-# 1. Load configuration from target.txt and tools_def.txt in $(WORKSPACE)/Conf
-# 2. Parse DSC file of active platform
-# 3. Parse FDF file if any
-# 4. Establish build database, including parse all other files (module, package)
-# 5. Create AutoGen files (C code file, depex file, makefile) if necessary
-# 6. Call build command
-#
-class Build():
- ## Constructor
- #
- # Constructor will load all necessary configurations, parse platform, modules
- # and packages and the establish a database for AutoGen.
- #
- # @param Target The build command target, one of gSupportedTarget
- # @param WorkspaceDir The directory of workspace
- # @param Platform The DSC file of active platform
- # @param Module The INF file of active module, if any
- # @param Arch The Arch list of platform or module
- # @param ToolChain The name list of toolchain
- # @param BuildTarget The "DEBUG" or "RELEASE" build
- # @param FlashDefinition The FDF file of active platform
- # @param FdList=[] The FD names to be individually built
- # @param FvList=[] The FV names to be individually built
- # @param MakefileType The type of makefile (for MSFT make or GNU make)
- # @param SilentMode Indicate multi-thread build mode
- # @param ThreadNumber The maximum number of thread if in multi-thread build mode
- # @param SkipAutoGen Skip AutoGen step
- # @param Reparse Re-parse all meta files
- # @param SkuId SKU id from command line
- #
- def __init__(self, Target, WorkspaceDir, Platform, Module, Arch, ToolChain,
- BuildTarget, FlashDefinition, FdList=[], FvList=[],
- MakefileType="nmake", SilentMode=False, ThreadNumber=2,
- SkipAutoGen=False, Reparse=False, SkuId=None,
- ReportFile=None, ReportType=None):
-
- self.WorkspaceDir = WorkspaceDir
- self.Target = Target
- self.PlatformFile = Platform
- self.ModuleFile = Module
- self.ArchList = Arch
- self.ToolChainList = ToolChain
- self.BuildTargetList= BuildTarget
- self.Fdf = FlashDefinition
- self.FdList = FdList
- self.FvList = FvList
- self.MakefileType = MakefileType
- self.SilentMode = SilentMode
- self.ThreadNumber = ThreadNumber
- self.SkipAutoGen = SkipAutoGen
- self.Reparse = Reparse
- self.SkuId = SkuId
- self.SpawnMode = True
- self.ReportFile = ReportFile
- if ReportType == None:
- self.ReportType = ['ALL']
- else:
- self.ReportType = ReportType
-
- self.TargetTxt = TargetTxtClassObject()
- self.ToolDef = ToolDefClassObject()
- self.Db = WorkspaceDatabase(None, GlobalData.gGlobalDefines, self.Reparse)
- #self.Db = WorkspaceDatabase(None, {}, self.Reparse)
- self.BuildDatabase = self.Db.BuildObject
- self.Platform = None
-
- # print dot charater during doing some time-consuming work
- self.Progress = Utils.Progressor()
-
- # parse target.txt, tools_def.txt, and platform file
- #self.RestoreBuildData()
- self.LoadConfiguration()
- self.InitBuild()
-
- # print current build environment and configuration
- EdkLogger.quiet("%-24s = %s" % ("WORKSPACE", os.environ["WORKSPACE"]))
- EdkLogger.quiet("%-24s = %s" % ("ECP_SOURCE", os.environ["ECP_SOURCE"]))
- EdkLogger.quiet("%-24s = %s" % ("EDK_SOURCE", os.environ["EDK_SOURCE"]))
- EdkLogger.quiet("%-24s = %s" % ("EFI_SOURCE", os.environ["EFI_SOURCE"]))
- EdkLogger.quiet("%-24s = %s" % ("EDK_TOOLS_PATH", os.environ["EDK_TOOLS_PATH"]))
-
- EdkLogger.info('\n%-24s = %s' % ("TARGET_ARCH", ' '.join(self.ArchList)))
- EdkLogger.info('%-24s = %s' % ("TARGET", ' '.join(self.BuildTargetList)))
- EdkLogger.info('%-24s = %s' % ("TOOL_CHAIN_TAG", ' '.join(self.ToolChainList)))
-
- EdkLogger.info('\n%-24s = %s' % ("Active Platform", self.PlatformFile))
-
- if self.Fdf != None and self.Fdf != "":
- EdkLogger.info('%-24s = %s' % ("Flash Image Definition", self.Fdf))
-
- if self.ModuleFile != None and self.ModuleFile != "":
- EdkLogger.info('%-24s = %s' % ("Active Module", self.ModuleFile))
-
- os.chdir(self.WorkspaceDir)
- self.Progress.Start("\nProcessing meta-data")
-
- ## Load configuration
- #
- # This method will parse target.txt and get the build configurations.
- #
- def LoadConfiguration(self):
- #
- # Check target.txt and tools_def.txt and Init them
- #
- BuildConfigurationFile = os.path.normpath(os.path.join(self.WorkspaceDir, gBuildConfiguration))
- if os.path.isfile(BuildConfigurationFile) == True:
- StatusCode = self.TargetTxt.LoadTargetTxtFile(BuildConfigurationFile)
-
- ToolDefinitionFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_CONF]
- if ToolDefinitionFile == '':
- ToolDefinitionFile = gToolsDefinition
- ToolDefinitionFile = os.path.normpath(os.path.join(self.WorkspaceDir, ToolDefinitionFile))
- if os.path.isfile(ToolDefinitionFile) == True:
- StatusCode = self.ToolDef.LoadToolDefFile(ToolDefinitionFile)
- else:
- EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=ToolDefinitionFile)
- else:
- EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=BuildConfigurationFile)
-
- # if no ARCH given in command line, get it from target.txt
- if self.ArchList == None or len(self.ArchList) == 0:
- self.ArchList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET_ARCH]
-
- # if no build target given in command line, get it from target.txt
- if self.BuildTargetList == None or len(self.BuildTargetList) == 0:
- self.BuildTargetList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET]
-
- # if no tool chain given in command line, get it from target.txt
- if self.ToolChainList == None or len(self.ToolChainList) == 0:
- self.ToolChainList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_TAG]
- if self.ToolChainList == None or len(self.ToolChainList) == 0:
- EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, ExtraData="No toolchain given. Don't know how to build.\n")
-
- # check if the tool chains are defined or not
- NewToolChainList = []
- for ToolChain in self.ToolChainList:
- if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG]:
- EdkLogger.warn("build", "Tool chain [%s] is not defined" % ToolChain)
- else:
- NewToolChainList.append(ToolChain)
- # if no tool chain available, break the build
- if len(NewToolChainList) == 0:
- EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,
- ExtraData="[%s] not defined. No toolchain available for build!\n" % ", ".join(self.ToolChainList))
- else:
- self.ToolChainList = NewToolChainList
-
- if self.ThreadNumber == None:
- self.ThreadNumber = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER]
- if self.ThreadNumber == '':
- self.ThreadNumber = 0
- else:
- self.ThreadNumber = int(self.ThreadNumber, 0)
-
- if self.ThreadNumber == 0:
- self.ThreadNumber = 1
-
- if not self.PlatformFile:
- PlatformFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_ACTIVE_PLATFORM]
- if not PlatformFile:
- # Try to find one in current directory
- WorkingDirectory = os.getcwd()
- FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.dsc')))
- FileNum = len(FileList)
- if FileNum >= 2:
- EdkLogger.error("build", OPTION_MISSING,
- ExtraData="There are %d DSC files in %s. Use '-p' to specify one.\n" % (FileNum, WorkingDirectory))
- elif FileNum == 1:
- PlatformFile = FileList[0]
- else:
- EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,
- ExtraData="No active platform specified in target.txt or command line! Nothing can be built.\n")
-
- self.PlatformFile = PathClass(NormFile(PlatformFile, self.WorkspaceDir), self.WorkspaceDir)
- ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False)
- if ErrorCode != 0:
- EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)
-
- ## Initialize build configuration
- #
- # This method will parse DSC file and merge the configurations from
- # command line and target.txt, then get the final build configurations.
- #
- def InitBuild(self):
- ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc")
- if ErrorCode != 0:
- EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)
-
- # create metafile database
- self.Db.InitDatabase()
-
- # we need information in platform description file to determine how to build
- self.Platform = self.BuildDatabase[self.PlatformFile, 'COMMON']
- if not self.Fdf:
- self.Fdf = self.Platform.FlashDefinition
-
- if self.SkuId == None or self.SkuId == '':
- self.SkuId = self.Platform.SkuName
-
- # check FD/FV build target
- if self.Fdf == None or self.Fdf == "":
- if self.FdList != []:
- EdkLogger.info("No flash definition file found. FD [%s] will be ignored." % " ".join(self.FdList))
- self.FdList = []
- if self.FvList != []:
- EdkLogger.info("No flash definition file found. FV [%s] will be ignored." % " ".join(self.FvList))
- self.FvList = []
- else:
- FdfParserObj = FdfParser(str(self.Fdf))
- FdfParserObj.ParseFile()
- for fvname in self.FvList:
- if fvname.upper() not in FdfParserObj.Profile.FvDict.keys():
- EdkLogger.error("build", OPTION_VALUE_INVALID,
- "No such an FV in FDF file: %s" % fvname)
-
- #
- # Merge Arch
- #
- if self.ArchList == None or len(self.ArchList) == 0:
- ArchList = set(self.Platform.SupArchList)
- else:
- ArchList = set(self.ArchList) & set(self.Platform.SupArchList)
- if len(ArchList) == 0:
- EdkLogger.error("build", PARAMETER_INVALID,
- ExtraData = "Active platform supports [%s] only, but [%s] is given."
- % (" ".join(self.Platform.SupArchList), " ".join(self.ArchList)))
- elif len(ArchList) != len(self.ArchList):
- SkippedArchList = set(self.ArchList).symmetric_difference(set(self.Platform.SupArchList))
- EdkLogger.verbose("\nArch [%s] is ignored because active platform supports [%s] but [%s] is specified !"
- % (" ".join(SkippedArchList), " ".join(self.Platform.SupArchList), " ".join(self.ArchList)))
- self.ArchList = tuple(ArchList)
-
- # Merge build target
- if self.BuildTargetList == None or len(self.BuildTargetList) == 0:
- BuildTargetList = self.Platform.BuildTargets
- else:
- BuildTargetList = list(set(self.BuildTargetList) & set(self.Platform.BuildTargets))
- if BuildTargetList == []:
- EdkLogger.error("build", PARAMETER_INVALID, "Active platform only supports [%s], but [%s] is given"
- % (" ".join(self.Platform.BuildTargets), " ".join(self.BuildTargetList)))
- self.BuildTargetList = BuildTargetList
-
- ## Build a module or platform
- #
- # Create autogen code and makfile for a module or platform, and the launch
- # "make" command to build it
- #
- # @param Target The target of build command
- # @param Platform The platform file
- # @param Module The module file
- # @param BuildTarget The name of build target, one of "DEBUG", "RELEASE"
- # @param ToolChain The name of toolchain to build
- # @param Arch The arch of the module/platform
- # @param CreateDepModuleCodeFile Flag used to indicate creating code
- # for dependent modules/Libraries
- # @param CreateDepModuleMakeFile Flag used to indicate creating makefile
- # for dependent modules/Libraries
- #
- def _Build(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True):
- if AutoGenObject == None:
- return False
-
- # skip file generation for cleanxxx targets, run and fds target
- if Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:
- # for target which must generate AutoGen code and makefile
- if not self.SkipAutoGen or Target == 'genc':
- self.Progress.Start("Generating code")
- AutoGenObject.CreateCodeFile(CreateDepsCodeFile)
- self.Progress.Stop("done!")
- if Target == "genc":
- return True
-
- if not self.SkipAutoGen or Target == 'genmake':
- self.Progress.Start("Generating makefile")
- AutoGenObject.CreateMakeFile(CreateDepsMakeFile)
- self.Progress.Stop("done!")
- if Target == "genmake":
- return True
- else:
- # always recreate top/platform makefile when clean, just in case of inconsistency
- AutoGenObject.CreateCodeFile(False)
- AutoGenObject.CreateMakeFile(False)
-
- if EdkLogger.GetLevel() == EdkLogger.QUIET:
- EdkLogger.quiet("Building ... %s" % repr(AutoGenObject))
-
- BuildCommand = AutoGenObject.BuildCommand
- if BuildCommand == None or len(BuildCommand) == 0:
- EdkLogger.error("build", OPTION_MISSING, ExtraData="No MAKE command found for [%s, %s, %s]" % Key)
-
- BuildCommand = BuildCommand + [Target]
- LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir)
- if Target == 'cleanall':
- try:
- #os.rmdir(AutoGenObject.BuildDir)
- RemoveDirectory(AutoGenObject.BuildDir, True)
- except WindowsError, X:
- EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X))
- return True
-
- ## Build active platform for different build targets and different tool chains
- #
- def _BuildPlatform(self):
- for BuildTarget in self.BuildTargetList:
- for ToolChain in self.ToolChainList:
- Wa = WorkspaceAutoGen(
- self.WorkspaceDir,
- self.Platform,
- BuildTarget,
- ToolChain,
- self.ArchList,
- self.BuildDatabase,
- self.TargetTxt,
- self.ToolDef,
- self.Fdf,
- self.FdList,
- self.FvList,
- self.SkuId,
- self.ReportFile,
- self.ReportType
- )
- self.Progress.Stop("done!")
- self._Build(self.Target, Wa)
-
- ## Build active module for different build targets, different tool chains and different archs
- #
- def _BuildModule(self):
- for BuildTarget in self.BuildTargetList:
- for ToolChain in self.ToolChainList:
- #
- # module build needs platform build information, so get platform
- # AutoGen first
- #
- Wa = WorkspaceAutoGen(
- self.WorkspaceDir,
- self.Platform,
- BuildTarget,
- ToolChain,
- self.ArchList,
- self.BuildDatabase,
- self.TargetTxt,
- self.ToolDef,
- self.Fdf,
- self.FdList,
- self.FvList,
- self.SkuId,
- self.ReportFile,
- self.ReportType
- )
- Wa.CreateMakeFile(False)
- self.Progress.Stop("done!")
- MaList = []
- for Arch in self.ArchList:
- Ma = ModuleAutoGen(Wa, self.ModuleFile, BuildTarget, ToolChain, Arch, self.PlatformFile)
- if Ma == None: continue
- MaList.append(Ma)
- self._Build(self.Target, Ma)
- if MaList == []:
- EdkLogger.error(
- 'build',
- BUILD_ERROR,
- "Module for [%s] is not a component of active platform."\
- " Please make sure that the ARCH and inf file path are"\
- " given in the same as in [%s]" %\
- (', '.join(self.ArchList), self.Platform),
- ExtraData=self.ModuleFile
- )
-
- ## Build a platform in multi-thread mode
- #
- def _MultiThreadBuildPlatform(self):
- for BuildTarget in self.BuildTargetList:
- for ToolChain in self.ToolChainList:
- Wa = WorkspaceAutoGen(
- self.WorkspaceDir,
- self.Platform,
- BuildTarget,
- ToolChain,
- self.ArchList,
- self.BuildDatabase,
- self.TargetTxt,
- self.ToolDef,
- self.Fdf,
- self.FdList,
- self.FvList,
- self.SkuId,
- self.ReportFile,
- self.ReportType
- )
- Wa.CreateMakeFile(False)
-
- # multi-thread exit flag
- ExitFlag = threading.Event()
- ExitFlag.clear()
- for Arch in self.ArchList:
- Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)
- if Pa == None:
- continue
- for Module in Pa.Platform.Modules:
- # Get ModuleAutoGen object to generate C code file and makefile
- Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)
- if Ma == None:
- continue
- # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'
- if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:
- # for target which must generate AutoGen code and makefile
- if not self.SkipAutoGen or self.Target == 'genc':
- Ma.CreateCodeFile(True)
- if self.Target == "genc":
- continue
-
- if not self.SkipAutoGen or self.Target == 'genmake':
- Ma.CreateMakeFile(True)
- if self.Target == "genmake":
- continue
- self.Progress.Stop("done!")
- # Generate build task for the module
- Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))
- # Break build if any build thread has error
- if BuildTask.HasError():
- # we need a full version of makefile for platform
- ExitFlag.set()
- BuildTask.WaitForComplete()
- Pa.CreateMakeFile(False)
- EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)
- # Start task scheduler
- if not BuildTask.IsOnGoing():
- BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)
-
- # in case there's an interruption. we need a full version of makefile for platform
- Pa.CreateMakeFile(False)
- if BuildTask.HasError():
- EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)
-
- #
- # All modules have been put in build tasks queue. Tell task scheduler
- # to exit if all tasks are completed
- #
- ExitFlag.set()
- BuildTask.WaitForComplete()
-
- #
- # Check for build error, and raise exception if one
- # has been signaled.
- #
- if BuildTask.HasError():
- EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)
-
- # Generate FD image if there's a FDF file found
- if self.Fdf != '' and self.Target in ["", "all", "fds"]:
- LaunchCommand(Wa.BuildCommand + ["fds"], Wa.MakeFileDir)
-
- ## Generate GuidedSectionTools.txt in the FV directories.
- #
- def CreateGuidedSectionToolsFile(self):
- for Arch in self.ArchList:
- for BuildTarget in self.BuildTargetList:
- for ToolChain in self.ToolChainList:
- FvDir = os.path.join(
- self.WorkspaceDir,
- self.Platform.OutputDirectory,
- '_'.join((BuildTarget, ToolChain)),
- 'FV'
- )
- if not os.path.exists(FvDir):
- continue
- # Build up the list of supported architectures for this build
- prefix = '%s_%s_%s_' % (BuildTarget, ToolChain, Arch)
-
- # Look through the tool definitions for GUIDed tools
- guidAttribs = []
- for (attrib, value) in self.ToolDef.ToolsDefTxtDictionary.iteritems():
- if attrib.upper().endswith('_GUID'):
- split = attrib.split('_')
- thisPrefix = '_'.join(split[0:3]) + '_'
- if thisPrefix == prefix:
- guid = self.ToolDef.ToolsDefTxtDictionary[attrib]
- guid = guid.lower()
- toolName = split[3]
- path = '_'.join(split[0:4]) + '_PATH'
- path = self.ToolDef.ToolsDefTxtDictionary[path]
- path = self.GetFullPathOfTool(path)
- guidAttribs.append((guid, toolName, path))
-
- # Write out GuidedSecTools.txt
- toolsFile = os.path.join(FvDir, 'GuidedSectionTools.txt')
- toolsFile = open(toolsFile, 'wt')
- for guidedSectionTool in guidAttribs:
- print >> toolsFile, ' '.join(guidedSectionTool)
- toolsFile.close()
-
- ## Returns the full path of the tool.
- #
- def GetFullPathOfTool (self, tool):
- if os.path.exists(tool):
- return os.path.realpath(tool)
- else:
- # We need to search for the tool using the
- # PATH environment variable.
- for dirInPath in os.environ['PATH'].split(os.pathsep):
- foundPath = os.path.join(dirInPath, tool)
- if os.path.exists(foundPath):
- return os.path.realpath(foundPath)
-
- # If the tool was not found in the path then we just return
- # the input tool.
- return tool
-
- ## Launch the module or platform build
- #
- def Launch(self):
- if self.ModuleFile == None or self.ModuleFile == "":
- if not self.SpawnMode or self.Target not in ["", "all"]:
- self.SpawnMode = False
- self._BuildPlatform()
- else:
- self._MultiThreadBuildPlatform()
- self.CreateGuidedSectionToolsFile()
- else:
- self.SpawnMode = False
- self._BuildModule()
-
- ## Do some clean-up works when error occurred
- def Relinquish(self):
- OldLogLevel = EdkLogger.GetLevel()
- EdkLogger.SetLevel(EdkLogger.ERROR)
- #self.DumpBuildData()
- Utils.Progressor.Abort()
- if self.SpawnMode == True:
- BuildTask.Abort()
- EdkLogger.SetLevel(OldLogLevel)
-
- def DumpBuildData(self):
- CacheDirectory = os.path.join(self.WorkspaceDir, gBuildCacheDir)
- Utils.CreateDirectory(CacheDirectory)
- Utils.DataDump(Utils.gFileTimeStampCache, os.path.join(CacheDirectory, "gFileTimeStampCache"))
- Utils.DataDump(Utils.gDependencyDatabase, os.path.join(CacheDirectory, "gDependencyDatabase"))
-
- def RestoreBuildData(self):
- FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gFileTimeStampCache")
- if Utils.gFileTimeStampCache == {} and os.path.isfile(FilePath):
- Utils.gFileTimeStampCache = Utils.DataRestore(FilePath)
- if Utils.gFileTimeStampCache == None:
- Utils.gFileTimeStampCache = {}
-
- FilePath = os.path.join(self.WorkspaceDir, gBuildCacheDir, "gDependencyDatabase")
- if Utils.gDependencyDatabase == {} and os.path.isfile(FilePath):
- Utils.gDependencyDatabase = Utils.DataRestore(FilePath)
- if Utils.gDependencyDatabase == None:
- Utils.gDependencyDatabase = {}
-
-def ParseDefines(DefineList=[]):
- DefineDict = {}
- if DefineList != None:
- for Define in DefineList:
- DefineTokenList = Define.split("=", 1)
- if len(DefineTokenList) == 1:
- DefineDict[DefineTokenList[0]] = ""
- else:
- DefineDict[DefineTokenList[0]] = DefineTokenList[1].strip()
- return DefineDict
-
-gParamCheck = []
-def SingleCheckCallback(option, opt_str, value, parser):
- if option not in gParamCheck:
- setattr(parser.values, option.dest, value)
- gParamCheck.append(option)
- else:
- parser.error("Option %s only allows one instance in command line!" % option)
-
-## Parse command line options
-#
-# Using standard Python module optparse to parse command line option of this tool.
-#
-# @retval Opt A optparse.Values object containing the parsed options
-# @retval Args Target of build command
-#
-def MyOptionParser():
- Parser = OptionParser(description=__copyright__,version=__version__,prog="build.exe",usage="%prog [options] [all|fds|genc|genmake|clean|cleanall|cleanlib|modules|libraries|run]")
- Parser.add_option("-a", "--arch", action="append", type="choice", choices=['IA32','X64','IPF','EBC','ARM'], dest="TargetArch",
- help="ARCHS is one of list: IA32, X64, IPF, ARM or EBC, which overrides target.txt's TARGET_ARCH definition. To specify more archs, please repeat this option.")
- Parser.add_option("-p", "--platform", action="callback", type="string", dest="PlatformFile", callback=SingleCheckCallback,
- help="Build the platform specified by the DSC file name argument, overriding target.txt's ACTIVE_PLATFORM definition.")
- Parser.add_option("-m", "--module", action="callback", type="string", dest="ModuleFile", callback=SingleCheckCallback,
- help="Build the module specified by the INF file name argument.")
- Parser.add_option("-b", "--buildtarget", action="append", type="choice", choices=['DEBUG','RELEASE'], dest="BuildTarget",
- help="BuildTarget is one of list: DEBUG, RELEASE, which overrides target.txt's TARGET definition. To specify more TARGET, please repeat this option.")
- Parser.add_option("-t", "--tagname", action="append", type="string", dest="ToolChain",
- help="Using the Tool Chain Tagname to build the platform, overriding target.txt's TOOL_CHAIN_TAG definition.")
- Parser.add_option("-x", "--sku-id", action="callback", type="string", dest="SkuId", callback=SingleCheckCallback,
- help="Using this name of SKU ID to build the platform, overriding SKUID_IDENTIFIER in DSC file.")
-
- Parser.add_option("-n", action="callback", type="int", dest="ThreadNumber", callback=SingleCheckCallback,
- 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.")
-
- Parser.add_option("-f", "--fdf", action="callback", type="string", dest="FdfFile", callback=SingleCheckCallback,
- help="The name of the FDF file to use, which overrides the setting in the DSC file.")
- Parser.add_option("-r", "--rom-image", action="append", type="string", dest="RomImage", default=[],
- help="The name of FD to be generated. The name must be from [FD] section in FDF file.")
- Parser.add_option("-i", "--fv-image", action="append", type="string", dest="FvImage", default=[],
- help="The name of FV to be generated. The name must be from [FV] section in FDF file.")
-
- Parser.add_option("-u", "--skip-autogen", action="store_true", dest="SkipAutoGen", help="Skip AutoGen step.")
- Parser.add_option("-e", "--re-parse", action="store_true", dest="Reparse", help="Re-parse all meta-data files.")
-
- Parser.add_option("-c", "--case-insensitive", action="store_true", dest="CaseInsensitive", help="Don't check case of file name.")
-
- # Parser.add_option("-D", "--define", action="append", dest="Defines", metavar="NAME[=[VALUE]]",
- # help="Define global macro which can be used in DSC/DEC/INF files.")
-
- Parser.add_option("-w", "--warning-as-error", action="store_true", dest="WarningAsError", help="Treat warning in tools as error.")
- Parser.add_option("-j", "--log", action="store", dest="LogFile", help="Put log in specified file as well as on console.")
-
- Parser.add_option("-s", "--silent", action="store_true", type=None, dest="SilentMode",
- help="Make use of silent mode of (n)make.")
- Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")
- Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\
- "including library instances selected, final dependency expression, "\
- "and warning messages, etc.")
- Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")
- Parser.add_option("-D", "--define", action="append", type="string", dest="Macros", help="Macro: \"Name [= Value]\".")
-
- Parser.add_option("-y", "--report-file", action="store", dest="ReportFile", help="Create/overwrite the report to the specified filename.")
- Parser.add_option("-Y", "--report-type", action="append", type="choice", choices=['ALL','PCD',], dest="ReportType",
- help="Flags that control the type of build report to generate. Must be one of: [ALL, PCD]. To specify more than one flag, repeat this option on the command line.")
-
- (Opt, Args)=Parser.parse_args()
- return (Opt, Args)
-
-## Tool entrance method
-#
-# This method mainly dispatch specific methods per the command line options.
-# If no error found, return zero value so the caller of this tool can know
-# if it's executed successfully or not.
-#
-# @retval 0 Tool was successful
-# @retval 1 Tool failed
-#
-def Main():
- StartTime = time.time()
-
- # Initialize log system
- EdkLogger.Initialize()
-
- #
- # Parse the options and args
- #
- (Option, Target) = MyOptionParser()
- GlobalData.gOptions = Option
- GlobalData.gCaseInsensitive = Option.CaseInsensitive
-
- # Set log level
- if Option.verbose != None:
- EdkLogger.SetLevel(EdkLogger.VERBOSE)
- elif Option.quiet != None:
- EdkLogger.SetLevel(EdkLogger.QUIET)
- elif Option.debug != None:
- EdkLogger.SetLevel(Option.debug + 1)
- else:
- EdkLogger.SetLevel(EdkLogger.INFO)
-
- if Option.LogFile != None:
- EdkLogger.SetLogFile(Option.LogFile)
-
- if Option.WarningAsError == True:
- EdkLogger.SetWarningAsError()
-
- if platform.platform().find("Windows") >= 0:
- GlobalData.gIsWindows = True
- else:
- GlobalData.gIsWindows = False
-
- EdkLogger.quiet(time.strftime("%H:%M:%S, %b.%d %Y ", time.localtime()) + "[%s]\n" % platform.platform())
- ReturnCode = 0
- MyBuild = None
- try:
- if len(Target) == 0:
- Target = "all"
- elif len(Target) >= 2:
- EdkLogger.error("build", OPTION_NOT_SUPPORTED, "More than one targets are not supported.",
- ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))
- else:
- Target = Target[0].lower()
-
- if Target not in gSupportedTarget:
- EdkLogger.error("build", OPTION_NOT_SUPPORTED, "Not supported target [%s]." % Target,
- ExtraData="Please select one of: %s" %(' '.join(gSupportedTarget)))
-
- GlobalData.gGlobalDefines = ParseDefines(Option.Macros)
- #
- # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH
- #
- CheckEnvVariable()
- Workspace = os.getenv("WORKSPACE")
- #
- # Get files real name in workspace dir
- #
- GlobalData.gAllFiles = Utils.DirCache(Workspace)
-
- WorkingDirectory = os.getcwd()
- if not Option.ModuleFile:
- FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.inf')))
- FileNum = len(FileList)
- if FileNum >= 2:
- EdkLogger.error("build", OPTION_NOT_SUPPORTED, "There are %d INF files in %s." % (FileNum, WorkingDirectory),
- ExtraData="Please use '-m <INF_FILE_PATH>' switch to choose one.")
- elif FileNum == 1:
- Option.ModuleFile = NormFile(FileList[0], Workspace)
-
- if Option.ModuleFile:
- Option.ModuleFile = PathClass(Option.ModuleFile, Workspace)
- ErrorCode, ErrorInfo = Option.ModuleFile.Validate(".inf", False)
- if ErrorCode != 0:
- EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)
-
- if Option.PlatformFile != None:
- Option.PlatformFile = PathClass(Option.PlatformFile, Workspace)
- ErrorCode, ErrorInfo = Option.PlatformFile.Validate(".dsc", False)
- if ErrorCode != 0:
- EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)
-
- if Option.FdfFile != None:
- Option.FdfFile = PathClass(Option.FdfFile, Workspace)
- ErrorCode, ErrorInfo = Option.FdfFile.Validate(".fdf", False)
- if ErrorCode != 0:
- EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)
-
- MyBuild = Build(Target, Workspace, Option.PlatformFile, Option.ModuleFile,
- Option.TargetArch, Option.ToolChain, Option.BuildTarget,
- Option.FdfFile, Option.RomImage, Option.FvImage,
- None, Option.SilentMode, Option.ThreadNumber,
- Option.SkipAutoGen, Option.Reparse, Option.SkuId,
- Option.ReportFile, Option.ReportType)
- MyBuild.Launch()
- #MyBuild.DumpBuildData()
- except FatalError, X:
- if MyBuild != None:
- # for multi-thread build exits safely
- MyBuild.Relinquish()
- if Option != None and Option.debug != None:
- EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())
- ReturnCode = X.args[0]
- except Warning, X:
- # error from Fdf parser
- if MyBuild != None:
- # for multi-thread build exits safely
- MyBuild.Relinquish()
- if Option != None and Option.debug != None:
- EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())
- else:
- EdkLogger.error(X.ToolName, FORMAT_INVALID, File=X.FileName, Line=X.LineNumber, ExtraData=X.Message, RaiseError = False)
- ReturnCode = FORMAT_INVALID
- except KeyboardInterrupt:
- ReturnCode = ABORT_ERROR
- if Option != None and Option.debug != None:
- EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())
- except:
- if MyBuild != None:
- # for multi-thread build exits safely
- MyBuild.Relinquish()
-
- # try to get the meta-file from the object causing exception
- Tb = sys.exc_info()[-1]
- MetaFile = GlobalData.gProcessingFile
- while Tb != None:
- if 'self' in Tb.tb_frame.f_locals and hasattr(Tb.tb_frame.f_locals['self'], 'MetaFile'):
- MetaFile = Tb.tb_frame.f_locals['self'].MetaFile
- Tb = Tb.tb_next
- EdkLogger.error(
- "\nbuild",
- CODE_ERROR,
- "Unknown fatal error when processing [%s]" % MetaFile,
- ExtraData="\n(Please send email to dev@buildtools.tianocore.org for help, attaching following call stack trace!)\n",
- RaiseError=False
- )
- EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())
- ReturnCode = CODE_ERROR
- finally:
- Utils.Progressor.Abort()
-
- if MyBuild != None:
- MyBuild.Db.Close()
-
- if ReturnCode == 0:
- Conclusion = "Done"
- elif ReturnCode == ABORT_ERROR:
- Conclusion = "Aborted"
- else:
- Conclusion = "Failed"
- FinishTime = time.time()
- BuildDuration = time.strftime("%M:%S", time.gmtime(int(round(FinishTime - StartTime))))
- EdkLogger.SetLevel(EdkLogger.QUIET)
- EdkLogger.quiet("\n- %s -\n%s [%s]" % (Conclusion, time.strftime("%H:%M:%S, %b.%d %Y", time.localtime()), BuildDuration))
-
- return ReturnCode
-
-if __name__ == '__main__':
- r = Main()
- ## 0-127 is a safe return range, and 1 is a standard default error
- if r < 0 or r > 127: r = 1
- sys.exit(r)
-
+## @file\r
+# build a platform or a module\r
+#\r
+# Copyright (c) 2014, Hewlett-Packard Development Company, L.P.<BR>\r
+# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>\r
+#\r
+# This program and the accompanying materials\r
+# are licensed and made available under the terms and conditions of the BSD License\r
+# which accompanies this distribution. The full text of the license may be found at\r
+# http://opensource.org/licenses/bsd-license.php\r
+#\r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+\r
+##\r
+# Import Modules\r
+#\r
+from __future__ import print_function\r
+import Common.LongFilePathOs as os\r
+import re\r
+from io import BytesIO\r
+import sys\r
+import glob\r
+import time\r
+import platform\r
+import traceback\r
+import encodings.ascii\r
+import itertools\r
+import multiprocessing\r
+\r
+from struct import *\r
+from threading import *\r
+from optparse import OptionParser\r
+from subprocess import *\r
+from Common import Misc as Utils\r
+\r
+from Common.LongFilePathSupport import OpenLongFilePath as open\r
+from Common.LongFilePathSupport import LongFilePath\r
+from Common.TargetTxtClassObject import *\r
+from Common.ToolDefClassObject import *\r
+from Common.DataType import *\r
+from Common.BuildVersion import gBUILD_VERSION\r
+from AutoGen.AutoGen import *\r
+from Common.BuildToolError import *\r
+from Workspace.WorkspaceDatabase import *\r
+from Common.MultipleWorkspace import MultipleWorkspace as mws\r
+\r
+from BuildReport import BuildReport\r
+from GenPatchPcdTable.GenPatchPcdTable import *\r
+from PatchPcdValue.PatchPcdValue import *\r
+\r
+import Common.EdkLogger\r
+import Common.GlobalData as GlobalData\r
+from GenFds.GenFds import GenFds\r
+\r
+from collections import OrderedDict, defaultdict\r
+\r
+# Version and Copyright\r
+VersionNumber = "0.60" + ' ' + gBUILD_VERSION\r
+__version__ = "%prog Version " + VersionNumber\r
+__copyright__ = "Copyright (c) 2007 - 2018, Intel Corporation All rights reserved."\r
+\r
+## standard targets of build command\r
+gSupportedTarget = ['all', 'genc', 'genmake', 'modules', 'libraries', 'fds', 'clean', 'cleanall', 'cleanlib', 'run']\r
+\r
+## build configuration file\r
+gBuildConfiguration = "target.txt"\r
+gToolsDefinition = "tools_def.txt"\r
+\r
+TemporaryTablePattern = re.compile(r'^_\d+_\d+_[a-fA-F0-9]+$')\r
+TmpTableDict = {}\r
+\r
+## Check environment PATH variable to make sure the specified tool is found\r
+#\r
+# If the tool is found in the PATH, then True is returned\r
+# Otherwise, False is returned\r
+#\r
+def IsToolInPath(tool):\r
+ if 'PATHEXT' in os.environ:\r
+ extns = os.environ['PATHEXT'].split(os.path.pathsep)\r
+ else:\r
+ extns = ('',)\r
+ for pathDir in os.environ['PATH'].split(os.path.pathsep):\r
+ for ext in extns:\r
+ if os.path.exists(os.path.join(pathDir, tool + ext)):\r
+ return True\r
+ return False\r
+\r
+## Check environment variables\r
+#\r
+# Check environment variables that must be set for build. Currently they are\r
+#\r
+# WORKSPACE The directory all packages/platforms start from\r
+# EDK_TOOLS_PATH The directory contains all tools needed by the build\r
+# PATH $(EDK_TOOLS_PATH)/Bin/<sys> must be set in PATH\r
+#\r
+# If any of above environment variable is not set or has error, the build\r
+# will be broken.\r
+#\r
+def CheckEnvVariable():\r
+ # check WORKSPACE\r
+ if "WORKSPACE" not in os.environ:\r
+ EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",\r
+ ExtraData="WORKSPACE")\r
+\r
+ WorkspaceDir = os.path.normcase(os.path.normpath(os.environ["WORKSPACE"]))\r
+ if not os.path.exists(WorkspaceDir):\r
+ EdkLogger.error("build", FILE_NOT_FOUND, "WORKSPACE doesn't exist", ExtraData=WorkspaceDir)\r
+ elif ' ' in WorkspaceDir:\r
+ EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in WORKSPACE path",\r
+ ExtraData=WorkspaceDir)\r
+ os.environ["WORKSPACE"] = WorkspaceDir\r
+\r
+ # set multiple workspace\r
+ PackagesPath = os.getenv("PACKAGES_PATH")\r
+ mws.setWs(WorkspaceDir, PackagesPath)\r
+ if mws.PACKAGES_PATH:\r
+ for Path in mws.PACKAGES_PATH:\r
+ if not os.path.exists(Path):\r
+ EdkLogger.error("build", FILE_NOT_FOUND, "One Path in PACKAGES_PATH doesn't exist", ExtraData=Path)\r
+ elif ' ' in Path:\r
+ EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in PACKAGES_PATH", ExtraData=Path)\r
+\r
+ #\r
+ # Check EFI_SOURCE (Edk build convention). EDK_SOURCE will always point to ECP\r
+ #\r
+ if "ECP_SOURCE" not in os.environ:\r
+ os.environ["ECP_SOURCE"] = mws.join(WorkspaceDir, GlobalData.gEdkCompatibilityPkg)\r
+ if "EFI_SOURCE" not in os.environ:\r
+ os.environ["EFI_SOURCE"] = os.environ["ECP_SOURCE"]\r
+ if "EDK_SOURCE" not in os.environ:\r
+ os.environ["EDK_SOURCE"] = os.environ["ECP_SOURCE"]\r
+\r
+ #\r
+ # Unify case of characters on case-insensitive systems\r
+ #\r
+ EfiSourceDir = os.path.normcase(os.path.normpath(os.environ["EFI_SOURCE"]))\r
+ EdkSourceDir = os.path.normcase(os.path.normpath(os.environ["EDK_SOURCE"]))\r
+ EcpSourceDir = os.path.normcase(os.path.normpath(os.environ["ECP_SOURCE"]))\r
+\r
+ os.environ["EFI_SOURCE"] = EfiSourceDir\r
+ os.environ["EDK_SOURCE"] = EdkSourceDir\r
+ os.environ["ECP_SOURCE"] = EcpSourceDir\r
+ os.environ["EDK_TOOLS_PATH"] = os.path.normcase(os.environ["EDK_TOOLS_PATH"])\r
+\r
+ if not os.path.exists(EcpSourceDir):\r
+ EdkLogger.verbose("ECP_SOURCE = %s doesn't exist. Edk modules could not be built." % EcpSourceDir)\r
+ elif ' ' in EcpSourceDir:\r
+ EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in ECP_SOURCE path",\r
+ ExtraData=EcpSourceDir)\r
+ if not os.path.exists(EdkSourceDir):\r
+ if EdkSourceDir == EcpSourceDir:\r
+ EdkLogger.verbose("EDK_SOURCE = %s doesn't exist. Edk modules could not be built." % EdkSourceDir)\r
+ else:\r
+ EdkLogger.error("build", PARAMETER_INVALID, "EDK_SOURCE does not exist",\r
+ ExtraData=EdkSourceDir)\r
+ elif ' ' in EdkSourceDir:\r
+ EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in EDK_SOURCE path",\r
+ ExtraData=EdkSourceDir)\r
+ if not os.path.exists(EfiSourceDir):\r
+ if EfiSourceDir == EcpSourceDir:\r
+ EdkLogger.verbose("EFI_SOURCE = %s doesn't exist. Edk modules could not be built." % EfiSourceDir)\r
+ else:\r
+ EdkLogger.error("build", PARAMETER_INVALID, "EFI_SOURCE does not exist",\r
+ ExtraData=EfiSourceDir)\r
+ elif ' ' in EfiSourceDir:\r
+ EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in EFI_SOURCE path",\r
+ ExtraData=EfiSourceDir)\r
+\r
+ # check those variables on single workspace case\r
+ if not PackagesPath:\r
+ # change absolute path to relative path to WORKSPACE\r
+ if EfiSourceDir.upper().find(WorkspaceDir.upper()) != 0:\r
+ EdkLogger.error("build", PARAMETER_INVALID, "EFI_SOURCE is not under WORKSPACE",\r
+ ExtraData="WORKSPACE = %s\n EFI_SOURCE = %s" % (WorkspaceDir, EfiSourceDir))\r
+ if EdkSourceDir.upper().find(WorkspaceDir.upper()) != 0:\r
+ EdkLogger.error("build", PARAMETER_INVALID, "EDK_SOURCE is not under WORKSPACE",\r
+ ExtraData="WORKSPACE = %s\n EDK_SOURCE = %s" % (WorkspaceDir, EdkSourceDir))\r
+ if EcpSourceDir.upper().find(WorkspaceDir.upper()) != 0:\r
+ EdkLogger.error("build", PARAMETER_INVALID, "ECP_SOURCE is not under WORKSPACE",\r
+ ExtraData="WORKSPACE = %s\n ECP_SOURCE = %s" % (WorkspaceDir, EcpSourceDir))\r
+\r
+ # check EDK_TOOLS_PATH\r
+ if "EDK_TOOLS_PATH" not in os.environ:\r
+ EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",\r
+ ExtraData="EDK_TOOLS_PATH")\r
+\r
+ # check PATH\r
+ if "PATH" not in os.environ:\r
+ EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",\r
+ ExtraData="PATH")\r
+\r
+ GlobalData.gWorkspace = WorkspaceDir\r
+ GlobalData.gEfiSource = EfiSourceDir\r
+ GlobalData.gEdkSource = EdkSourceDir\r
+ GlobalData.gEcpSource = EcpSourceDir\r
+\r
+ GlobalData.gGlobalDefines["WORKSPACE"] = WorkspaceDir\r
+ GlobalData.gGlobalDefines["EFI_SOURCE"] = EfiSourceDir\r
+ GlobalData.gGlobalDefines["EDK_SOURCE"] = EdkSourceDir\r
+ GlobalData.gGlobalDefines["ECP_SOURCE"] = EcpSourceDir\r
+ GlobalData.gGlobalDefines["EDK_TOOLS_PATH"] = os.environ["EDK_TOOLS_PATH"]\r
+\r
+## Get normalized file path\r
+#\r
+# Convert the path to be local format, and remove the WORKSPACE path at the\r
+# beginning if the file path is given in full path.\r
+#\r
+# @param FilePath File path to be normalized\r
+# @param Workspace Workspace path which the FilePath will be checked against\r
+#\r
+# @retval string The normalized file path\r
+#\r
+def NormFile(FilePath, Workspace):\r
+ # check if the path is absolute or relative\r
+ if os.path.isabs(FilePath):\r
+ FileFullPath = os.path.normpath(FilePath)\r
+ else:\r
+ FileFullPath = os.path.normpath(mws.join(Workspace, FilePath))\r
+ Workspace = mws.getWs(Workspace, FilePath)\r
+\r
+ # check if the file path exists or not\r
+ if not os.path.isfile(FileFullPath):\r
+ EdkLogger.error("build", FILE_NOT_FOUND, ExtraData="\t%s (Please give file in absolute path or relative to WORKSPACE)" % FileFullPath)\r
+\r
+ # remove workspace directory from the beginning part of the file path\r
+ if Workspace[-1] in ["\\", "/"]:\r
+ return FileFullPath[len(Workspace):]\r
+ else:\r
+ return FileFullPath[(len(Workspace) + 1):]\r
+\r
+## Get the output of an external program\r
+#\r
+# This is the entrance method of thread reading output of an external program and\r
+# putting them in STDOUT/STDERR of current program.\r
+#\r
+# @param From The stream message read from\r
+# @param To The stream message put on\r
+# @param ExitFlag The flag used to indicate stopping reading\r
+#\r
+def ReadMessage(From, To, ExitFlag):\r
+ while True:\r
+ # read one line a time\r
+ Line = From.readline()\r
+ # empty string means "end"\r
+ if Line is not None and Line != "":\r
+ To(Line.rstrip())\r
+ else:\r
+ break\r
+ if ExitFlag.isSet():\r
+ break\r
+\r
+## Launch an external program\r
+#\r
+# This method will call subprocess.Popen to execute an external program with\r
+# given options in specified directory. Because of the dead-lock issue during\r
+# redirecting output of the external program, threads are used to to do the\r
+# redirection work.\r
+#\r
+# @param Command A list or string containing the call of the program\r
+# @param WorkingDir The directory in which the program will be running\r
+#\r
+def LaunchCommand(Command, WorkingDir):\r
+ BeginTime = time.time()\r
+ # if working directory doesn't exist, Popen() will raise an exception\r
+ if not os.path.isdir(WorkingDir):\r
+ EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=WorkingDir)\r
+\r
+ # Command is used as the first Argument in following Popen().\r
+ # It could be a string or sequence. We find that if command is a string in following Popen(),\r
+ # ubuntu may fail with an error message that the command is not found.\r
+ # So here we may need convert command from string to list instance.\r
+ if platform.system() != 'Windows':\r
+ if not isinstance(Command, list):\r
+ Command = Command.split()\r
+ Command = ' '.join(Command)\r
+\r
+ Proc = None\r
+ EndOfProcedure = None\r
+ try:\r
+ # launch the command\r
+ Proc = Popen(Command, stdout=PIPE, stderr=PIPE, env=os.environ, cwd=WorkingDir, bufsize=-1, shell=True)\r
+\r
+ # launch two threads to read the STDOUT and STDERR\r
+ EndOfProcedure = Event()\r
+ EndOfProcedure.clear()\r
+ if Proc.stdout:\r
+ StdOutThread = Thread(target=ReadMessage, args=(Proc.stdout, EdkLogger.info, EndOfProcedure))\r
+ StdOutThread.setName("STDOUT-Redirector")\r
+ StdOutThread.setDaemon(False)\r
+ StdOutThread.start()\r
+\r
+ if Proc.stderr:\r
+ StdErrThread = Thread(target=ReadMessage, args=(Proc.stderr, EdkLogger.quiet, EndOfProcedure))\r
+ StdErrThread.setName("STDERR-Redirector")\r
+ StdErrThread.setDaemon(False)\r
+ StdErrThread.start()\r
+\r
+ # waiting for program exit\r
+ Proc.wait()\r
+ except: # in case of aborting\r
+ # terminate the threads redirecting the program output\r
+ EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
+ if EndOfProcedure is not None:\r
+ EndOfProcedure.set()\r
+ if Proc is None:\r
+ if not isinstance(Command, type("")):\r
+ Command = " ".join(Command)\r
+ EdkLogger.error("build", COMMAND_FAILURE, "Failed to start command", ExtraData="%s [%s]" % (Command, WorkingDir))\r
+\r
+ if Proc.stdout:\r
+ StdOutThread.join()\r
+ if Proc.stderr:\r
+ StdErrThread.join()\r
+\r
+ # check the return code of the program\r
+ if Proc.returncode != 0:\r
+ if not isinstance(Command, type("")):\r
+ Command = " ".join(Command)\r
+ # print out the Response file and its content when make failure\r
+ RespFile = os.path.join(WorkingDir, 'OUTPUT', 'respfilelist.txt')\r
+ if os.path.isfile(RespFile):\r
+ f = open(RespFile)\r
+ RespContent = f.read()\r
+ f.close()\r
+ EdkLogger.info(RespContent)\r
+\r
+ EdkLogger.error("build", COMMAND_FAILURE, ExtraData="%s [%s]" % (Command, WorkingDir))\r
+ return "%dms" % (int(round((time.time() - BeginTime) * 1000)))\r
+\r
+## The smallest unit that can be built in multi-thread build mode\r
+#\r
+# This is the base class of build unit. The "Obj" parameter must provide\r
+# __str__(), __eq__() and __hash__() methods. Otherwise there could be build units\r
+# missing build.\r
+#\r
+# Currently the "Obj" should be only ModuleAutoGen or PlatformAutoGen objects.\r
+#\r
+class BuildUnit:\r
+ ## The constructor\r
+ #\r
+ # @param self The object pointer\r
+ # @param Obj The object the build is working on\r
+ # @param Target The build target name, one of gSupportedTarget\r
+ # @param Dependency The BuildUnit(s) which must be completed in advance\r
+ # @param WorkingDir The directory build command starts in\r
+ #\r
+ def __init__(self, Obj, BuildCommand, Target, Dependency, WorkingDir="."):\r
+ self.BuildObject = Obj\r
+ self.Dependency = Dependency\r
+ self.WorkingDir = WorkingDir\r
+ self.Target = Target\r
+ self.BuildCommand = BuildCommand\r
+ if not BuildCommand:\r
+ EdkLogger.error("build", OPTION_MISSING,\r
+ "No build command found for this module. "\r
+ "Please check your setting of %s_%s_%s_MAKE_PATH in Conf/tools_def.txt file." %\r
+ (Obj.BuildTarget, Obj.ToolChain, Obj.Arch),\r
+ ExtraData=str(Obj))\r
+\r
+\r
+ ## str() method\r
+ #\r
+ # It just returns the string representation of self.BuildObject\r
+ #\r
+ # @param self The object pointer\r
+ #\r
+ def __str__(self):\r
+ return str(self.BuildObject)\r
+\r
+ ## "==" operator method\r
+ #\r
+ # It just compares self.BuildObject with "Other". So self.BuildObject must\r
+ # provide its own __eq__() method.\r
+ #\r
+ # @param self The object pointer\r
+ # @param Other The other BuildUnit object compared to\r
+ #\r
+ def __eq__(self, Other):\r
+ return Other and self.BuildObject == Other.BuildObject \\r
+ and Other.BuildObject \\r
+ and self.BuildObject.Arch == Other.BuildObject.Arch\r
+\r
+ ## hash() method\r
+ #\r
+ # It just returns the hash value of self.BuildObject which must be hashable.\r
+ #\r
+ # @param self The object pointer\r
+ #\r
+ def __hash__(self):\r
+ return hash(self.BuildObject) + hash(self.BuildObject.Arch)\r
+\r
+ def __repr__(self):\r
+ return repr(self.BuildObject)\r
+\r
+## The smallest module unit that can be built by nmake/make command in multi-thread build mode\r
+#\r
+# This class is for module build by nmake/make build system. The "Obj" parameter\r
+# must provide __str__(), __eq__() and __hash__() methods. Otherwise there could\r
+# be make units missing build.\r
+#\r
+# Currently the "Obj" should be only ModuleAutoGen object.\r
+#\r
+class ModuleMakeUnit(BuildUnit):\r
+ ## The constructor\r
+ #\r
+ # @param self The object pointer\r
+ # @param Obj The ModuleAutoGen object the build is working on\r
+ # @param Target The build target name, one of gSupportedTarget\r
+ #\r
+ def __init__(self, Obj, Target):\r
+ Dependency = [ModuleMakeUnit(La, Target) for La in Obj.LibraryAutoGenList]\r
+ BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir)\r
+ if Target in [None, "", "all"]:\r
+ self.Target = "tbuild"\r
+\r
+## The smallest platform unit that can be built by nmake/make command in multi-thread build mode\r
+#\r
+# This class is for platform build by nmake/make build system. The "Obj" parameter\r
+# must provide __str__(), __eq__() and __hash__() methods. Otherwise there could\r
+# be make units missing build.\r
+#\r
+# Currently the "Obj" should be only PlatformAutoGen object.\r
+#\r
+class PlatformMakeUnit(BuildUnit):\r
+ ## The constructor\r
+ #\r
+ # @param self The object pointer\r
+ # @param Obj The PlatformAutoGen object the build is working on\r
+ # @param Target The build target name, one of gSupportedTarget\r
+ #\r
+ def __init__(self, Obj, Target):\r
+ Dependency = [ModuleMakeUnit(Lib, Target) for Lib in self.BuildObject.LibraryAutoGenList]\r
+ Dependency.extend([ModuleMakeUnit(Mod, Target) for Mod in self.BuildObject.ModuleAutoGenList])\r
+ BuildUnit.__init__(self, Obj, Obj.BuildCommand, Target, Dependency, Obj.MakeFileDir)\r
+\r
+## The class representing the task of a module build or platform build\r
+#\r
+# This class manages the build tasks in multi-thread build mode. Its jobs include\r
+# scheduling thread running, catching thread error, monitor the thread status, etc.\r
+#\r
+class BuildTask:\r
+ # queue for tasks waiting for schedule\r
+ _PendingQueue = OrderedDict()\r
+ _PendingQueueLock = threading.Lock()\r
+\r
+ # queue for tasks ready for running\r
+ _ReadyQueue = OrderedDict()\r
+ _ReadyQueueLock = threading.Lock()\r
+\r
+ # queue for run tasks\r
+ _RunningQueue = OrderedDict()\r
+ _RunningQueueLock = threading.Lock()\r
+\r
+ # queue containing all build tasks, in case duplicate build\r
+ _TaskQueue = OrderedDict()\r
+\r
+ # flag indicating error occurs in a running thread\r
+ _ErrorFlag = threading.Event()\r
+ _ErrorFlag.clear()\r
+ _ErrorMessage = ""\r
+\r
+ # BoundedSemaphore object used to control the number of running threads\r
+ _Thread = None\r
+\r
+ # flag indicating if the scheduler is started or not\r
+ _SchedulerStopped = threading.Event()\r
+ _SchedulerStopped.set()\r
+\r
+ ## Start the task scheduler thread\r
+ #\r
+ # @param MaxThreadNumber The maximum thread number\r
+ # @param ExitFlag Flag used to end the scheduler\r
+ #\r
+ @staticmethod\r
+ def StartScheduler(MaxThreadNumber, ExitFlag):\r
+ SchedulerThread = Thread(target=BuildTask.Scheduler, args=(MaxThreadNumber, ExitFlag))\r
+ SchedulerThread.setName("Build-Task-Scheduler")\r
+ SchedulerThread.setDaemon(False)\r
+ SchedulerThread.start()\r
+ # wait for the scheduler to be started, especially useful in Linux\r
+ while not BuildTask.IsOnGoing():\r
+ time.sleep(0.01)\r
+\r
+ ## Scheduler method\r
+ #\r
+ # @param MaxThreadNumber The maximum thread number\r
+ # @param ExitFlag Flag used to end the scheduler\r
+ #\r
+ @staticmethod\r
+ def Scheduler(MaxThreadNumber, ExitFlag):\r
+ BuildTask._SchedulerStopped.clear()\r
+ try:\r
+ # use BoundedSemaphore to control the maximum running threads\r
+ BuildTask._Thread = BoundedSemaphore(MaxThreadNumber)\r
+ #\r
+ # scheduling loop, which will exits when no pending/ready task and\r
+ # indicated to do so, or there's error in running thread\r
+ #\r
+ while (len(BuildTask._PendingQueue) > 0 or len(BuildTask._ReadyQueue) > 0 \\r
+ or not ExitFlag.isSet()) and not BuildTask._ErrorFlag.isSet():\r
+ EdkLogger.debug(EdkLogger.DEBUG_8, "Pending Queue (%d), Ready Queue (%d)"\r
+ % (len(BuildTask._PendingQueue), len(BuildTask._ReadyQueue)))\r
+\r
+ # get all pending tasks\r
+ BuildTask._PendingQueueLock.acquire()\r
+ BuildObjectList = BuildTask._PendingQueue.keys()\r
+ #\r
+ # check if their dependency is resolved, and if true, move them\r
+ # into ready queue\r
+ #\r
+ for BuildObject in BuildObjectList:\r
+ Bt = BuildTask._PendingQueue[BuildObject]\r
+ if Bt.IsReady():\r
+ BuildTask._ReadyQueue[BuildObject] = BuildTask._PendingQueue.pop(BuildObject)\r
+ BuildTask._PendingQueueLock.release()\r
+\r
+ # launch build thread until the maximum number of threads is reached\r
+ while not BuildTask._ErrorFlag.isSet():\r
+ # empty ready queue, do nothing further\r
+ if len(BuildTask._ReadyQueue) == 0:\r
+ break\r
+\r
+ # wait for active thread(s) exit\r
+ BuildTask._Thread.acquire(True)\r
+\r
+ # start a new build thread\r
+ Bo, Bt = BuildTask._ReadyQueue.popitem()\r
+\r
+ # move into running queue\r
+ BuildTask._RunningQueueLock.acquire()\r
+ BuildTask._RunningQueue[Bo] = Bt\r
+ BuildTask._RunningQueueLock.release()\r
+\r
+ Bt.Start()\r
+ # avoid tense loop\r
+ time.sleep(0.01)\r
+\r
+ # avoid tense loop\r
+ time.sleep(0.01)\r
+\r
+ # wait for all running threads exit\r
+ if BuildTask._ErrorFlag.isSet():\r
+ EdkLogger.quiet("\nWaiting for all build threads exit...")\r
+ # while not BuildTask._ErrorFlag.isSet() and \\r
+ while len(BuildTask._RunningQueue) > 0:\r
+ EdkLogger.verbose("Waiting for thread ending...(%d)" % len(BuildTask._RunningQueue))\r
+ EdkLogger.debug(EdkLogger.DEBUG_8, "Threads [%s]" % ", ".join(Th.getName() for Th in threading.enumerate()))\r
+ # avoid tense loop\r
+ time.sleep(0.1)\r
+ except BaseException as X:\r
+ #\r
+ # TRICK: hide the output of threads left runing, so that the user can\r
+ # catch the error message easily\r
+ #\r
+ EdkLogger.SetLevel(EdkLogger.ERROR)\r
+ BuildTask._ErrorFlag.set()\r
+ BuildTask._ErrorMessage = "build thread scheduler error\n\t%s" % str(X)\r
+\r
+ BuildTask._PendingQueue.clear()\r
+ BuildTask._ReadyQueue.clear()\r
+ BuildTask._RunningQueue.clear()\r
+ BuildTask._TaskQueue.clear()\r
+ BuildTask._SchedulerStopped.set()\r
+\r
+ ## Wait for all running method exit\r
+ #\r
+ @staticmethod\r
+ def WaitForComplete():\r
+ BuildTask._SchedulerStopped.wait()\r
+\r
+ ## Check if the scheduler is running or not\r
+ #\r
+ @staticmethod\r
+ def IsOnGoing():\r
+ return not BuildTask._SchedulerStopped.isSet()\r
+\r
+ ## Abort the build\r
+ @staticmethod\r
+ def Abort():\r
+ if BuildTask.IsOnGoing():\r
+ BuildTask._ErrorFlag.set()\r
+ BuildTask.WaitForComplete()\r
+\r
+ ## Check if there's error in running thread\r
+ #\r
+ # Since the main thread cannot catch exceptions in other thread, we have to\r
+ # use threading.Event to communicate this formation to main thread.\r
+ #\r
+ @staticmethod\r
+ def HasError():\r
+ return BuildTask._ErrorFlag.isSet()\r
+\r
+ ## Get error message in running thread\r
+ #\r
+ # Since the main thread cannot catch exceptions in other thread, we have to\r
+ # use a static variable to communicate this message to main thread.\r
+ #\r
+ @staticmethod\r
+ def GetErrorMessage():\r
+ return BuildTask._ErrorMessage\r
+\r
+ ## Factory method to create a BuildTask object\r
+ #\r
+ # This method will check if a module is building or has been built. And if\r
+ # true, just return the associated BuildTask object in the _TaskQueue. If\r
+ # not, create and return a new BuildTask object. The new BuildTask object\r
+ # will be appended to the _PendingQueue for scheduling later.\r
+ #\r
+ # @param BuildItem A BuildUnit object representing a build object\r
+ # @param Dependency The dependent build object of BuildItem\r
+ #\r
+ @staticmethod\r
+ def New(BuildItem, Dependency=None):\r
+ if BuildItem in BuildTask._TaskQueue:\r
+ Bt = BuildTask._TaskQueue[BuildItem]\r
+ return Bt\r
+\r
+ Bt = BuildTask()\r
+ Bt._Init(BuildItem, Dependency)\r
+ BuildTask._TaskQueue[BuildItem] = Bt\r
+\r
+ BuildTask._PendingQueueLock.acquire()\r
+ BuildTask._PendingQueue[BuildItem] = Bt\r
+ BuildTask._PendingQueueLock.release()\r
+\r
+ return Bt\r
+\r
+ ## The real constructor of BuildTask\r
+ #\r
+ # @param BuildItem A BuildUnit object representing a build object\r
+ # @param Dependency The dependent build object of BuildItem\r
+ #\r
+ def _Init(self, BuildItem, Dependency=None):\r
+ self.BuildItem = BuildItem\r
+\r
+ self.DependencyList = []\r
+ if Dependency is None:\r
+ Dependency = BuildItem.Dependency\r
+ else:\r
+ Dependency.extend(BuildItem.Dependency)\r
+ self.AddDependency(Dependency)\r
+ # flag indicating build completes, used to avoid unnecessary re-build\r
+ self.CompleteFlag = False\r
+\r
+ ## Check if all dependent build tasks are completed or not\r
+ #\r
+ def IsReady(self):\r
+ ReadyFlag = True\r
+ for Dep in self.DependencyList:\r
+ if Dep.CompleteFlag == True:\r
+ continue\r
+ ReadyFlag = False\r
+ break\r
+\r
+ return ReadyFlag\r
+\r
+ ## Add dependent build task\r
+ #\r
+ # @param Dependency The list of dependent build objects\r
+ #\r
+ def AddDependency(self, Dependency):\r
+ for Dep in Dependency:\r
+ if not Dep.BuildObject.IsBinaryModule:\r
+ self.DependencyList.append(BuildTask.New(Dep)) # BuildTask list\r
+\r
+ ## The thread wrapper of LaunchCommand function\r
+ #\r
+ # @param Command A list or string contains the call of the command\r
+ # @param WorkingDir The directory in which the program will be running\r
+ #\r
+ def _CommandThread(self, Command, WorkingDir):\r
+ try:\r
+ self.BuildItem.BuildObject.BuildTime = LaunchCommand(Command, WorkingDir)\r
+ self.CompleteFlag = True\r
+ except:\r
+ #\r
+ # TRICK: hide the output of threads left runing, so that the user can\r
+ # catch the error message easily\r
+ #\r
+ if not BuildTask._ErrorFlag.isSet():\r
+ GlobalData.gBuildingModule = "%s [%s, %s, %s]" % (str(self.BuildItem.BuildObject),\r
+ self.BuildItem.BuildObject.Arch,\r
+ self.BuildItem.BuildObject.ToolChain,\r
+ self.BuildItem.BuildObject.BuildTarget\r
+ )\r
+ EdkLogger.SetLevel(EdkLogger.ERROR)\r
+ BuildTask._ErrorFlag.set()\r
+ BuildTask._ErrorMessage = "%s broken\n %s [%s]" % \\r
+ (threading.currentThread().getName(), Command, WorkingDir)\r
+ # indicate there's a thread is available for another build task\r
+ BuildTask._RunningQueueLock.acquire()\r
+ BuildTask._RunningQueue.pop(self.BuildItem)\r
+ BuildTask._RunningQueueLock.release()\r
+ BuildTask._Thread.release()\r
+\r
+ ## Start build task thread\r
+ #\r
+ def Start(self):\r
+ EdkLogger.quiet("Building ... %s" % repr(self.BuildItem))\r
+ Command = self.BuildItem.BuildCommand + [self.BuildItem.Target]\r
+ self.BuildTread = Thread(target=self._CommandThread, args=(Command, self.BuildItem.WorkingDir))\r
+ self.BuildTread.setName("build thread")\r
+ self.BuildTread.setDaemon(False)\r
+ self.BuildTread.start()\r
+\r
+## The class contains the information related to EFI image\r
+#\r
+class PeImageInfo():\r
+ ## Constructor\r
+ #\r
+ # Constructor will load all required image information.\r
+ #\r
+ # @param BaseName The full file path of image.\r
+ # @param Guid The GUID for image.\r
+ # @param Arch Arch of this image.\r
+ # @param OutputDir The output directory for image.\r
+ # @param DebugDir The debug directory for image.\r
+ # @param ImageClass PeImage Information\r
+ #\r
+ def __init__(self, BaseName, Guid, Arch, OutputDir, DebugDir, ImageClass):\r
+ self.BaseName = BaseName\r
+ self.Guid = Guid\r
+ self.Arch = Arch\r
+ self.OutputDir = OutputDir\r
+ self.DebugDir = DebugDir\r
+ self.Image = ImageClass\r
+ self.Image.Size = (self.Image.Size / 0x1000 + 1) * 0x1000\r
+\r
+## The class implementing the EDK2 build process\r
+#\r
+# The build process includes:\r
+# 1. Load configuration from target.txt and tools_def.txt in $(WORKSPACE)/Conf\r
+# 2. Parse DSC file of active platform\r
+# 3. Parse FDF file if any\r
+# 4. Establish build database, including parse all other files (module, package)\r
+# 5. Create AutoGen files (C code file, depex file, makefile) if necessary\r
+# 6. Call build command\r
+#\r
+class Build():\r
+ ## Constructor\r
+ #\r
+ # Constructor will load all necessary configurations, parse platform, modules\r
+ # and packages and the establish a database for AutoGen.\r
+ #\r
+ # @param Target The build command target, one of gSupportedTarget\r
+ # @param WorkspaceDir The directory of workspace\r
+ # @param BuildOptions Build options passed from command line\r
+ #\r
+ def __init__(self, Target, WorkspaceDir, BuildOptions):\r
+ self.WorkspaceDir = WorkspaceDir\r
+ self.Target = Target\r
+ self.PlatformFile = BuildOptions.PlatformFile\r
+ self.ModuleFile = BuildOptions.ModuleFile\r
+ self.ArchList = BuildOptions.TargetArch\r
+ self.ToolChainList = BuildOptions.ToolChain\r
+ self.BuildTargetList= BuildOptions.BuildTarget\r
+ self.Fdf = BuildOptions.FdfFile\r
+ self.FdList = BuildOptions.RomImage\r
+ self.FvList = BuildOptions.FvImage\r
+ self.CapList = BuildOptions.CapName\r
+ self.SilentMode = BuildOptions.SilentMode\r
+ self.ThreadNumber = BuildOptions.ThreadNumber\r
+ self.SkipAutoGen = BuildOptions.SkipAutoGen\r
+ self.Reparse = BuildOptions.Reparse\r
+ self.SkuId = BuildOptions.SkuId\r
+ if self.SkuId:\r
+ GlobalData.gSKUID_CMD = self.SkuId\r
+ self.ConfDirectory = BuildOptions.ConfDirectory\r
+ self.SpawnMode = True\r
+ self.BuildReport = BuildReport(BuildOptions.ReportFile, BuildOptions.ReportType)\r
+ self.TargetTxt = TargetTxtClassObject()\r
+ self.ToolDef = ToolDefClassObject()\r
+ self.AutoGenTime = 0\r
+ self.MakeTime = 0\r
+ self.GenFdsTime = 0\r
+ GlobalData.BuildOptionPcd = BuildOptions.OptionPcd if BuildOptions.OptionPcd else []\r
+ #Set global flag for build mode\r
+ GlobalData.gIgnoreSource = BuildOptions.IgnoreSources\r
+ GlobalData.gUseHashCache = BuildOptions.UseHashCache\r
+ GlobalData.gBinCacheDest = BuildOptions.BinCacheDest\r
+ GlobalData.gBinCacheSource = BuildOptions.BinCacheSource\r
+ GlobalData.gEnableGenfdsMultiThread = BuildOptions.GenfdsMultiThread\r
+\r
+ if GlobalData.gBinCacheDest and not GlobalData.gUseHashCache:\r
+ EdkLogger.error("build", OPTION_NOT_SUPPORTED, ExtraData="--binary-destination must be used together with --hash.")\r
+\r
+ if GlobalData.gBinCacheSource and not GlobalData.gUseHashCache:\r
+ EdkLogger.error("build", OPTION_NOT_SUPPORTED, ExtraData="--binary-source must be used together with --hash.")\r
+\r
+ if GlobalData.gBinCacheDest and GlobalData.gBinCacheSource:\r
+ EdkLogger.error("build", OPTION_NOT_SUPPORTED, ExtraData="--binary-destination can not be used together with --binary-source.")\r
+\r
+ if GlobalData.gBinCacheSource:\r
+ BinCacheSource = os.path.normpath(GlobalData.gBinCacheSource)\r
+ if not os.path.isabs(BinCacheSource):\r
+ BinCacheSource = mws.join(self.WorkspaceDir, BinCacheSource)\r
+ GlobalData.gBinCacheSource = BinCacheSource\r
+ else:\r
+ if GlobalData.gBinCacheSource is not None:\r
+ EdkLogger.error("build", OPTION_VALUE_INVALID, ExtraData="Invalid value of option --binary-source.")\r
+\r
+ if GlobalData.gBinCacheDest:\r
+ BinCacheDest = os.path.normpath(GlobalData.gBinCacheDest)\r
+ if not os.path.isabs(BinCacheDest):\r
+ BinCacheDest = mws.join(self.WorkspaceDir, BinCacheDest)\r
+ GlobalData.gBinCacheDest = BinCacheDest\r
+ else:\r
+ if GlobalData.gBinCacheDest is not None:\r
+ EdkLogger.error("build", OPTION_VALUE_INVALID, ExtraData="Invalid value of option --binary-destination.")\r
+\r
+ if self.ConfDirectory:\r
+ # Get alternate Conf location, if it is absolute, then just use the absolute directory name\r
+ ConfDirectoryPath = os.path.normpath(self.ConfDirectory)\r
+\r
+ if not os.path.isabs(ConfDirectoryPath):\r
+ # Since alternate directory name is not absolute, the alternate directory is located within the WORKSPACE\r
+ # This also handles someone specifying the Conf directory in the workspace. Using --conf=Conf\r
+ ConfDirectoryPath = mws.join(self.WorkspaceDir, ConfDirectoryPath)\r
+ else:\r
+ if "CONF_PATH" in os.environ:\r
+ ConfDirectoryPath = os.path.normcase(os.path.normpath(os.environ["CONF_PATH"]))\r
+ else:\r
+ # Get standard WORKSPACE/Conf use the absolute path to the WORKSPACE/Conf\r
+ ConfDirectoryPath = mws.join(self.WorkspaceDir, 'Conf')\r
+ GlobalData.gConfDirectory = ConfDirectoryPath\r
+ GlobalData.gDatabasePath = os.path.normpath(os.path.join(ConfDirectoryPath, GlobalData.gDatabasePath))\r
+\r
+ if BuildOptions.DisableCache:\r
+ self.Db = WorkspaceDatabase(":memory:")\r
+ else:\r
+ self.Db = WorkspaceDatabase(GlobalData.gDatabasePath, self.Reparse)\r
+ self.BuildDatabase = self.Db.BuildObject\r
+ self.Platform = None\r
+ self.ToolChainFamily = None\r
+ self.LoadFixAddress = 0\r
+ self.UniFlag = BuildOptions.Flag\r
+ self.BuildModules = []\r
+ self.HashSkipModules = []\r
+ self.Db_Flag = False\r
+ self.LaunchPrebuildFlag = False\r
+ self.PlatformBuildPath = os.path.join(GlobalData.gConfDirectory, '.cache', '.PlatformBuild')\r
+ if BuildOptions.CommandLength:\r
+ GlobalData.gCommandMaxLength = BuildOptions.CommandLength\r
+\r
+ # print dot character during doing some time-consuming work\r
+ self.Progress = Utils.Progressor()\r
+ # print current build environment and configuration\r
+ EdkLogger.quiet("%-16s = %s" % ("WORKSPACE", os.environ["WORKSPACE"]))\r
+ if "PACKAGES_PATH" in os.environ:\r
+ # WORKSPACE env has been converted before. Print the same path style with WORKSPACE env.\r
+ EdkLogger.quiet("%-16s = %s" % ("PACKAGES_PATH", os.path.normcase(os.path.normpath(os.environ["PACKAGES_PATH"]))))\r
+ EdkLogger.quiet("%-16s = %s" % ("ECP_SOURCE", os.environ["ECP_SOURCE"]))\r
+ EdkLogger.quiet("%-16s = %s" % ("EDK_SOURCE", os.environ["EDK_SOURCE"]))\r
+ EdkLogger.quiet("%-16s = %s" % ("EFI_SOURCE", os.environ["EFI_SOURCE"]))\r
+ EdkLogger.quiet("%-16s = %s" % ("EDK_TOOLS_PATH", os.environ["EDK_TOOLS_PATH"]))\r
+ if "EDK_TOOLS_BIN" in os.environ:\r
+ # Print the same path style with WORKSPACE env.\r
+ EdkLogger.quiet("%-16s = %s" % ("EDK_TOOLS_BIN", os.path.normcase(os.path.normpath(os.environ["EDK_TOOLS_BIN"]))))\r
+ EdkLogger.quiet("%-16s = %s" % ("CONF_PATH", GlobalData.gConfDirectory))\r
+ self.InitPreBuild()\r
+ self.InitPostBuild()\r
+ if self.Prebuild:\r
+ EdkLogger.quiet("%-16s = %s" % ("PREBUILD", self.Prebuild))\r
+ if self.Postbuild:\r
+ EdkLogger.quiet("%-16s = %s" % ("POSTBUILD", self.Postbuild))\r
+ if self.Prebuild:\r
+ self.LaunchPrebuild()\r
+ self.TargetTxt = TargetTxtClassObject()\r
+ self.ToolDef = ToolDefClassObject()\r
+ if not (self.LaunchPrebuildFlag and os.path.exists(self.PlatformBuildPath)):\r
+ self.InitBuild()\r
+\r
+ EdkLogger.info("")\r
+ os.chdir(self.WorkspaceDir)\r
+\r
+ ## Load configuration\r
+ #\r
+ # This method will parse target.txt and get the build configurations.\r
+ #\r
+ def LoadConfiguration(self):\r
+ #\r
+ # Check target.txt and tools_def.txt and Init them\r
+ #\r
+ BuildConfigurationFile = os.path.normpath(os.path.join(GlobalData.gConfDirectory, gBuildConfiguration))\r
+ if os.path.isfile(BuildConfigurationFile) == True:\r
+ StatusCode = self.TargetTxt.LoadTargetTxtFile(BuildConfigurationFile)\r
+\r
+ ToolDefinitionFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_CONF]\r
+ if ToolDefinitionFile == '':\r
+ ToolDefinitionFile = gToolsDefinition\r
+ ToolDefinitionFile = os.path.normpath(mws.join(self.WorkspaceDir, 'Conf', ToolDefinitionFile))\r
+ if os.path.isfile(ToolDefinitionFile) == True:\r
+ StatusCode = self.ToolDef.LoadToolDefFile(ToolDefinitionFile)\r
+ else:\r
+ EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=ToolDefinitionFile)\r
+ else:\r
+ EdkLogger.error("build", FILE_NOT_FOUND, ExtraData=BuildConfigurationFile)\r
+\r
+ # if no ARCH given in command line, get it from target.txt\r
+ if not self.ArchList:\r
+ self.ArchList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET_ARCH]\r
+ self.ArchList = tuple(self.ArchList)\r
+\r
+ # if no build target given in command line, get it from target.txt\r
+ if not self.BuildTargetList:\r
+ self.BuildTargetList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET]\r
+\r
+ # if no tool chain given in command line, get it from target.txt\r
+ if not self.ToolChainList:\r
+ self.ToolChainList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TOOL_CHAIN_TAG]\r
+ if self.ToolChainList is None or len(self.ToolChainList) == 0:\r
+ EdkLogger.error("build", RESOURCE_NOT_AVAILABLE, ExtraData="No toolchain given. Don't know how to build.\n")\r
+\r
+ # check if the tool chains are defined or not\r
+ NewToolChainList = []\r
+ for ToolChain in self.ToolChainList:\r
+ if ToolChain not in self.ToolDef.ToolsDefTxtDatabase[TAB_TOD_DEFINES_TOOL_CHAIN_TAG]:\r
+ EdkLogger.warn("build", "Tool chain [%s] is not defined" % ToolChain)\r
+ else:\r
+ NewToolChainList.append(ToolChain)\r
+ # if no tool chain available, break the build\r
+ if len(NewToolChainList) == 0:\r
+ EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,\r
+ ExtraData="[%s] not defined. No toolchain available for build!\n" % ", ".join(self.ToolChainList))\r
+ else:\r
+ self.ToolChainList = NewToolChainList\r
+\r
+ ToolChainFamily = []\r
+ ToolDefinition = self.ToolDef.ToolsDefTxtDatabase\r
+ for Tool in self.ToolChainList:\r
+ if TAB_TOD_DEFINES_FAMILY not in ToolDefinition or Tool not in ToolDefinition[TAB_TOD_DEFINES_FAMILY] \\r
+ or not ToolDefinition[TAB_TOD_DEFINES_FAMILY][Tool]:\r
+ EdkLogger.warn("build", "No tool chain family found in configuration for %s. Default to MSFT." % Tool)\r
+ ToolChainFamily.append(TAB_COMPILER_MSFT)\r
+ else:\r
+ ToolChainFamily.append(ToolDefinition[TAB_TOD_DEFINES_FAMILY][Tool])\r
+ self.ToolChainFamily = ToolChainFamily\r
+\r
+ if self.ThreadNumber is None:\r
+ self.ThreadNumber = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_MAX_CONCURRENT_THREAD_NUMBER]\r
+ if self.ThreadNumber == '':\r
+ self.ThreadNumber = 0\r
+ else:\r
+ self.ThreadNumber = int(self.ThreadNumber, 0)\r
+\r
+ if self.ThreadNumber == 0:\r
+ try:\r
+ self.ThreadNumber = multiprocessing.cpu_count()\r
+ except (ImportError, NotImplementedError):\r
+ self.ThreadNumber = 1\r
+\r
+ if not self.PlatformFile:\r
+ PlatformFile = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_ACTIVE_PLATFORM]\r
+ if not PlatformFile:\r
+ # Try to find one in current directory\r
+ WorkingDirectory = os.getcwd()\r
+ FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.dsc')))\r
+ FileNum = len(FileList)\r
+ if FileNum >= 2:\r
+ EdkLogger.error("build", OPTION_MISSING,\r
+ ExtraData="There are %d DSC files in %s. Use '-p' to specify one.\n" % (FileNum, WorkingDirectory))\r
+ elif FileNum == 1:\r
+ PlatformFile = FileList[0]\r
+ else:\r
+ EdkLogger.error("build", RESOURCE_NOT_AVAILABLE,\r
+ ExtraData="No active platform specified in target.txt or command line! Nothing can be built.\n")\r
+\r
+ self.PlatformFile = PathClass(NormFile(PlatformFile, self.WorkspaceDir), self.WorkspaceDir)\r
+\r
+ ## Initialize build configuration\r
+ #\r
+ # This method will parse DSC file and merge the configurations from\r
+ # command line and target.txt, then get the final build configurations.\r
+ #\r
+ def InitBuild(self):\r
+ # parse target.txt, tools_def.txt, and platform file\r
+ self.LoadConfiguration()\r
+\r
+ # Allow case-insensitive for those from command line or configuration file\r
+ ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False)\r
+ if ErrorCode != 0:\r
+ EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
+\r
+ # create metafile database\r
+ if not self.Db_Flag:\r
+ self.Db.InitDatabase()\r
+\r
+ def InitPreBuild(self):\r
+ self.LoadConfiguration()\r
+ ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False)\r
+ if ErrorCode != 0:\r
+ EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
+ if self.BuildTargetList:\r
+ GlobalData.gGlobalDefines['TARGET'] = self.BuildTargetList[0]\r
+ if self.ArchList:\r
+ GlobalData.gGlobalDefines['ARCH'] = self.ArchList[0]\r
+ if self.ToolChainList:\r
+ GlobalData.gGlobalDefines['TOOLCHAIN'] = self.ToolChainList[0]\r
+ GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = self.ToolChainList[0]\r
+ if self.ToolChainFamily:\r
+ GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[0]\r
+ if 'PREBUILD' in GlobalData.gCommandLineDefines:\r
+ self.Prebuild = GlobalData.gCommandLineDefines.get('PREBUILD')\r
+ else:\r
+ self.Db.InitDatabase()\r
+ self.Db_Flag = True\r
+ Platform = self.Db._MapPlatform(str(self.PlatformFile))\r
+ self.Prebuild = str(Platform.Prebuild)\r
+ if self.Prebuild:\r
+ PrebuildList = []\r
+ #\r
+ # Evaluate all arguments and convert arguments that are WORKSPACE\r
+ # relative paths to absolute paths. Filter arguments that look like\r
+ # flags or do not follow the file/dir naming rules to avoid false\r
+ # positives on this conversion.\r
+ #\r
+ for Arg in self.Prebuild.split():\r
+ #\r
+ # Do not modify Arg if it looks like a flag or an absolute file path\r
+ #\r
+ if Arg.startswith('-') or os.path.isabs(Arg):\r
+ PrebuildList.append(Arg)\r
+ continue\r
+ #\r
+ # Do not modify Arg if it does not look like a Workspace relative\r
+ # path that starts with a valid package directory name\r
+ #\r
+ if not Arg[0].isalpha() or os.path.dirname(Arg) == '':\r
+ PrebuildList.append(Arg)\r
+ continue\r
+ #\r
+ # If Arg looks like a WORKSPACE relative path, then convert to an\r
+ # absolute path and check to see if the file exists.\r
+ #\r
+ Temp = mws.join(self.WorkspaceDir, Arg)\r
+ if os.path.isfile(Temp):\r
+ Arg = Temp\r
+ PrebuildList.append(Arg)\r
+ self.Prebuild = ' '.join(PrebuildList)\r
+ self.Prebuild += self.PassCommandOption(self.BuildTargetList, self.ArchList, self.ToolChainList, self.PlatformFile, self.Target)\r
+\r
+ def InitPostBuild(self):\r
+ if 'POSTBUILD' in GlobalData.gCommandLineDefines:\r
+ self.Postbuild = GlobalData.gCommandLineDefines.get('POSTBUILD')\r
+ else:\r
+ Platform = self.Db._MapPlatform(str(self.PlatformFile))\r
+ self.Postbuild = str(Platform.Postbuild)\r
+ if self.Postbuild:\r
+ PostbuildList = []\r
+ #\r
+ # Evaluate all arguments and convert arguments that are WORKSPACE\r
+ # relative paths to absolute paths. Filter arguments that look like\r
+ # flags or do not follow the file/dir naming rules to avoid false\r
+ # positives on this conversion.\r
+ #\r
+ for Arg in self.Postbuild.split():\r
+ #\r
+ # Do not modify Arg if it looks like a flag or an absolute file path\r
+ #\r
+ if Arg.startswith('-') or os.path.isabs(Arg):\r
+ PostbuildList.append(Arg)\r
+ continue\r
+ #\r
+ # Do not modify Arg if it does not look like a Workspace relative\r
+ # path that starts with a valid package directory name\r
+ #\r
+ if not Arg[0].isalpha() or os.path.dirname(Arg) == '':\r
+ PostbuildList.append(Arg)\r
+ continue\r
+ #\r
+ # If Arg looks like a WORKSPACE relative path, then convert to an\r
+ # absolute path and check to see if the file exists.\r
+ #\r
+ Temp = mws.join(self.WorkspaceDir, Arg)\r
+ if os.path.isfile(Temp):\r
+ Arg = Temp\r
+ PostbuildList.append(Arg)\r
+ self.Postbuild = ' '.join(PostbuildList)\r
+ self.Postbuild += self.PassCommandOption(self.BuildTargetList, self.ArchList, self.ToolChainList, self.PlatformFile, self.Target)\r
+\r
+ def PassCommandOption(self, BuildTarget, TargetArch, ToolChain, PlatformFile, Target):\r
+ BuildStr = ''\r
+ if GlobalData.gCommand and isinstance(GlobalData.gCommand, list):\r
+ BuildStr += ' ' + ' '.join(GlobalData.gCommand)\r
+ TargetFlag = False\r
+ ArchFlag = False\r
+ ToolChainFlag = False\r
+ PlatformFileFlag = False\r
+\r
+ if GlobalData.gOptions and not GlobalData.gOptions.BuildTarget:\r
+ TargetFlag = True\r
+ if GlobalData.gOptions and not GlobalData.gOptions.TargetArch:\r
+ ArchFlag = True\r
+ if GlobalData.gOptions and not GlobalData.gOptions.ToolChain:\r
+ ToolChainFlag = True\r
+ if GlobalData.gOptions and not GlobalData.gOptions.PlatformFile:\r
+ PlatformFileFlag = True\r
+\r
+ if TargetFlag and BuildTarget:\r
+ if isinstance(BuildTarget, list) or isinstance(BuildTarget, tuple):\r
+ BuildStr += ' -b ' + ' -b '.join(BuildTarget)\r
+ elif isinstance(BuildTarget, str):\r
+ BuildStr += ' -b ' + BuildTarget\r
+ if ArchFlag and TargetArch:\r
+ if isinstance(TargetArch, list) or isinstance(TargetArch, tuple):\r
+ BuildStr += ' -a ' + ' -a '.join(TargetArch)\r
+ elif isinstance(TargetArch, str):\r
+ BuildStr += ' -a ' + TargetArch\r
+ if ToolChainFlag and ToolChain:\r
+ if isinstance(ToolChain, list) or isinstance(ToolChain, tuple):\r
+ BuildStr += ' -t ' + ' -t '.join(ToolChain)\r
+ elif isinstance(ToolChain, str):\r
+ BuildStr += ' -t ' + ToolChain\r
+ if PlatformFileFlag and PlatformFile:\r
+ if isinstance(PlatformFile, list) or isinstance(PlatformFile, tuple):\r
+ BuildStr += ' -p ' + ' -p '.join(PlatformFile)\r
+ elif isinstance(PlatformFile, str):\r
+ BuildStr += ' -p' + PlatformFile\r
+ BuildStr += ' --conf=' + GlobalData.gConfDirectory\r
+ if Target:\r
+ BuildStr += ' ' + Target\r
+\r
+ return BuildStr\r
+\r
+ def LaunchPrebuild(self):\r
+ if self.Prebuild:\r
+ EdkLogger.info("\n- Prebuild Start -\n")\r
+ self.LaunchPrebuildFlag = True\r
+ #\r
+ # The purpose of .PrebuildEnv file is capture environment variable settings set by the prebuild script\r
+ # and preserve them for the rest of the main build step, because the child process environment will\r
+ # evaporate as soon as it exits, we cannot get it in build step.\r
+ #\r
+ PrebuildEnvFile = os.path.join(GlobalData.gConfDirectory, '.cache', '.PrebuildEnv')\r
+ if os.path.isfile(PrebuildEnvFile):\r
+ os.remove(PrebuildEnvFile)\r
+ if os.path.isfile(self.PlatformBuildPath):\r
+ os.remove(self.PlatformBuildPath)\r
+ if sys.platform == "win32":\r
+ args = ' && '.join((self.Prebuild, 'set > ' + PrebuildEnvFile))\r
+ Process = Popen(args, stdout=PIPE, stderr=PIPE, shell=True)\r
+ else:\r
+ args = ' && '.join((self.Prebuild, 'env > ' + PrebuildEnvFile))\r
+ Process = Popen(args, stdout=PIPE, stderr=PIPE, shell=True)\r
+\r
+ # launch two threads to read the STDOUT and STDERR\r
+ EndOfProcedure = Event()\r
+ EndOfProcedure.clear()\r
+ if Process.stdout:\r
+ StdOutThread = Thread(target=ReadMessage, args=(Process.stdout, EdkLogger.info, EndOfProcedure))\r
+ StdOutThread.setName("STDOUT-Redirector")\r
+ StdOutThread.setDaemon(False)\r
+ StdOutThread.start()\r
+\r
+ if Process.stderr:\r
+ StdErrThread = Thread(target=ReadMessage, args=(Process.stderr, EdkLogger.quiet, EndOfProcedure))\r
+ StdErrThread.setName("STDERR-Redirector")\r
+ StdErrThread.setDaemon(False)\r
+ StdErrThread.start()\r
+ # waiting for program exit\r
+ Process.wait()\r
+\r
+ if Process.stdout:\r
+ StdOutThread.join()\r
+ if Process.stderr:\r
+ StdErrThread.join()\r
+ if Process.returncode != 0 :\r
+ EdkLogger.error("Prebuild", PREBUILD_ERROR, 'Prebuild process is not success!')\r
+\r
+ if os.path.exists(PrebuildEnvFile):\r
+ f = open(PrebuildEnvFile)\r
+ envs = f.readlines()\r
+ f.close()\r
+ envs = itertools.imap(lambda l: l.split('=', 1), envs)\r
+ envs = itertools.ifilter(lambda l: len(l) == 2, envs)\r
+ envs = itertools.imap(lambda l: [i.strip() for i in l], envs)\r
+ os.environ.update(dict(envs))\r
+ EdkLogger.info("\n- Prebuild Done -\n")\r
+\r
+ def LaunchPostbuild(self):\r
+ if self.Postbuild:\r
+ EdkLogger.info("\n- Postbuild Start -\n")\r
+ if sys.platform == "win32":\r
+ Process = Popen(self.Postbuild, stdout=PIPE, stderr=PIPE, shell=True)\r
+ else:\r
+ Process = Popen(self.Postbuild, stdout=PIPE, stderr=PIPE, shell=True)\r
+ # launch two threads to read the STDOUT and STDERR\r
+ EndOfProcedure = Event()\r
+ EndOfProcedure.clear()\r
+ if Process.stdout:\r
+ StdOutThread = Thread(target=ReadMessage, args=(Process.stdout, EdkLogger.info, EndOfProcedure))\r
+ StdOutThread.setName("STDOUT-Redirector")\r
+ StdOutThread.setDaemon(False)\r
+ StdOutThread.start()\r
+\r
+ if Process.stderr:\r
+ StdErrThread = Thread(target=ReadMessage, args=(Process.stderr, EdkLogger.quiet, EndOfProcedure))\r
+ StdErrThread.setName("STDERR-Redirector")\r
+ StdErrThread.setDaemon(False)\r
+ StdErrThread.start()\r
+ # waiting for program exit\r
+ Process.wait()\r
+\r
+ if Process.stdout:\r
+ StdOutThread.join()\r
+ if Process.stderr:\r
+ StdErrThread.join()\r
+ if Process.returncode != 0 :\r
+ EdkLogger.error("Postbuild", POSTBUILD_ERROR, 'Postbuild process is not success!')\r
+ EdkLogger.info("\n- Postbuild Done -\n")\r
+ ## Build a module or platform\r
+ #\r
+ # Create autogen code and makefile for a module or platform, and the launch\r
+ # "make" command to build it\r
+ #\r
+ # @param Target The target of build command\r
+ # @param Platform The platform file\r
+ # @param Module The module file\r
+ # @param BuildTarget The name of build target, one of "DEBUG", "RELEASE"\r
+ # @param ToolChain The name of toolchain to build\r
+ # @param Arch The arch of the module/platform\r
+ # @param CreateDepModuleCodeFile Flag used to indicate creating code\r
+ # for dependent modules/Libraries\r
+ # @param CreateDepModuleMakeFile Flag used to indicate creating makefile\r
+ # for dependent modules/Libraries\r
+ #\r
+ def _BuildPa(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True, BuildModule=False, FfsCommand={}):\r
+ if AutoGenObject is None:\r
+ return False\r
+\r
+ # skip file generation for cleanxxx targets, run and fds target\r
+ if Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
+ # for target which must generate AutoGen code and makefile\r
+ if not self.SkipAutoGen or Target == 'genc':\r
+ self.Progress.Start("Generating code")\r
+ AutoGenObject.CreateCodeFile(CreateDepsCodeFile)\r
+ self.Progress.Stop("done!")\r
+ if Target == "genc":\r
+ return True\r
+\r
+ if not self.SkipAutoGen or Target == 'genmake':\r
+ self.Progress.Start("Generating makefile")\r
+ AutoGenObject.CreateMakeFile(CreateDepsMakeFile, FfsCommand)\r
+ self.Progress.Stop("done!")\r
+ if Target == "genmake":\r
+ return True\r
+ else:\r
+ # always recreate top/platform makefile when clean, just in case of inconsistency\r
+ AutoGenObject.CreateCodeFile(False)\r
+ AutoGenObject.CreateMakeFile(False)\r
+\r
+ if EdkLogger.GetLevel() == EdkLogger.QUIET:\r
+ EdkLogger.quiet("Building ... %s" % repr(AutoGenObject))\r
+\r
+ BuildCommand = AutoGenObject.BuildCommand\r
+ if BuildCommand is None or len(BuildCommand) == 0:\r
+ EdkLogger.error("build", OPTION_MISSING,\r
+ "No build command found for this module. "\r
+ "Please check your setting of %s_%s_%s_MAKE_PATH in Conf/tools_def.txt file." %\r
+ (AutoGenObject.BuildTarget, AutoGenObject.ToolChain, AutoGenObject.Arch),\r
+ ExtraData=str(AutoGenObject))\r
+\r
+ makefile = GenMake.BuildFile(AutoGenObject)._FILE_NAME_[GenMake.gMakeType]\r
+\r
+ # run\r
+ if Target == 'run':\r
+ RunDir = os.path.normpath(os.path.join(AutoGenObject.BuildDir, GlobalData.gGlobalDefines['ARCH']))\r
+ Command = '.\SecMain'\r
+ os.chdir(RunDir)\r
+ LaunchCommand(Command, RunDir)\r
+ return True\r
+\r
+ # build modules\r
+ if BuildModule:\r
+ BuildCommand = BuildCommand + [Target]\r
+ LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir)\r
+ self.CreateAsBuiltInf()\r
+ return True\r
+\r
+ # build library\r
+ if Target == 'libraries':\r
+ for Lib in AutoGenObject.LibraryBuildDirectoryList:\r
+ NewBuildCommand = BuildCommand + ['-f', os.path.normpath(os.path.join(Lib, makefile)), 'pbuild']\r
+ LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)\r
+ return True\r
+\r
+ # build module\r
+ if Target == 'modules':\r
+ for Lib in AutoGenObject.LibraryBuildDirectoryList:\r
+ NewBuildCommand = BuildCommand + ['-f', os.path.normpath(os.path.join(Lib, makefile)), 'pbuild']\r
+ LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)\r
+ for Mod in AutoGenObject.ModuleBuildDirectoryList:\r
+ NewBuildCommand = BuildCommand + ['-f', os.path.normpath(os.path.join(Mod, makefile)), 'pbuild']\r
+ LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)\r
+ self.CreateAsBuiltInf()\r
+ return True\r
+\r
+ # cleanlib\r
+ if Target == 'cleanlib':\r
+ for Lib in AutoGenObject.LibraryBuildDirectoryList:\r
+ LibMakefile = os.path.normpath(os.path.join(Lib, makefile))\r
+ if os.path.exists(LibMakefile):\r
+ NewBuildCommand = BuildCommand + ['-f', LibMakefile, 'cleanall']\r
+ LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)\r
+ return True\r
+\r
+ # clean\r
+ if Target == 'clean':\r
+ for Mod in AutoGenObject.ModuleBuildDirectoryList:\r
+ ModMakefile = os.path.normpath(os.path.join(Mod, makefile))\r
+ if os.path.exists(ModMakefile):\r
+ NewBuildCommand = BuildCommand + ['-f', ModMakefile, 'cleanall']\r
+ LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)\r
+ for Lib in AutoGenObject.LibraryBuildDirectoryList:\r
+ LibMakefile = os.path.normpath(os.path.join(Lib, makefile))\r
+ if os.path.exists(LibMakefile):\r
+ NewBuildCommand = BuildCommand + ['-f', LibMakefile, 'cleanall']\r
+ LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir)\r
+ return True\r
+\r
+ # cleanall\r
+ if Target == 'cleanall':\r
+ try:\r
+ #os.rmdir(AutoGenObject.BuildDir)\r
+ RemoveDirectory(AutoGenObject.BuildDir, True)\r
+ except WindowsError as X:\r
+ EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X))\r
+ return True\r
+\r
+ ## Build a module or platform\r
+ #\r
+ # Create autogen code and makefile for a module or platform, and the launch\r
+ # "make" command to build it\r
+ #\r
+ # @param Target The target of build command\r
+ # @param Platform The platform file\r
+ # @param Module The module file\r
+ # @param BuildTarget The name of build target, one of "DEBUG", "RELEASE"\r
+ # @param ToolChain The name of toolchain to build\r
+ # @param Arch The arch of the module/platform\r
+ # @param CreateDepModuleCodeFile Flag used to indicate creating code\r
+ # for dependent modules/Libraries\r
+ # @param CreateDepModuleMakeFile Flag used to indicate creating makefile\r
+ # for dependent modules/Libraries\r
+ #\r
+ def _Build(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True, BuildModule=False):\r
+ if AutoGenObject is None:\r
+ return False\r
+\r
+ # skip file generation for cleanxxx targets, run and fds target\r
+ if Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
+ # for target which must generate AutoGen code and makefile\r
+ if not self.SkipAutoGen or Target == 'genc':\r
+ self.Progress.Start("Generating code")\r
+ AutoGenObject.CreateCodeFile(CreateDepsCodeFile)\r
+ self.Progress.Stop("done!")\r
+ if Target == "genc":\r
+ return True\r
+\r
+ if not self.SkipAutoGen or Target == 'genmake':\r
+ self.Progress.Start("Generating makefile")\r
+ AutoGenObject.CreateMakeFile(CreateDepsMakeFile)\r
+ #AutoGenObject.CreateAsBuiltInf()\r
+ self.Progress.Stop("done!")\r
+ if Target == "genmake":\r
+ return True\r
+ else:\r
+ # always recreate top/platform makefile when clean, just in case of inconsistency\r
+ AutoGenObject.CreateCodeFile(False)\r
+ AutoGenObject.CreateMakeFile(False)\r
+\r
+ if EdkLogger.GetLevel() == EdkLogger.QUIET:\r
+ EdkLogger.quiet("Building ... %s" % repr(AutoGenObject))\r
+\r
+ BuildCommand = AutoGenObject.BuildCommand\r
+ if BuildCommand is None or len(BuildCommand) == 0:\r
+ EdkLogger.error("build", OPTION_MISSING,\r
+ "No build command found for this module. "\r
+ "Please check your setting of %s_%s_%s_MAKE_PATH in Conf/tools_def.txt file." %\r
+ (AutoGenObject.BuildTarget, AutoGenObject.ToolChain, AutoGenObject.Arch),\r
+ ExtraData=str(AutoGenObject))\r
+\r
+ # build modules\r
+ if BuildModule:\r
+ if Target != 'fds':\r
+ BuildCommand = BuildCommand + [Target]\r
+ AutoGenObject.BuildTime = LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir)\r
+ self.CreateAsBuiltInf()\r
+ return True\r
+\r
+ # genfds\r
+ if Target == 'fds':\r
+ LaunchCommand(AutoGenObject.GenFdsCommand, AutoGenObject.MakeFileDir)\r
+ return True\r
+\r
+ # run\r
+ if Target == 'run':\r
+ RunDir = os.path.normpath(os.path.join(AutoGenObject.BuildDir, GlobalData.gGlobalDefines['ARCH']))\r
+ Command = '.\SecMain'\r
+ os.chdir(RunDir)\r
+ LaunchCommand(Command, RunDir)\r
+ return True\r
+\r
+ # build library\r
+ if Target == 'libraries':\r
+ pass\r
+\r
+ # not build modules\r
+\r
+\r
+ # cleanall\r
+ if Target == 'cleanall':\r
+ try:\r
+ #os.rmdir(AutoGenObject.BuildDir)\r
+ RemoveDirectory(AutoGenObject.BuildDir, True)\r
+ except WindowsError as X:\r
+ EdkLogger.error("build", FILE_DELETE_FAILURE, ExtraData=str(X))\r
+ return True\r
+\r
+ ## Rebase module image and Get function address for the input module list.\r
+ #\r
+ def _RebaseModule (self, MapBuffer, BaseAddress, ModuleList, AddrIsOffset = True, ModeIsSmm = False):\r
+ if ModeIsSmm:\r
+ AddrIsOffset = False\r
+ for InfFile in ModuleList:\r
+ sys.stdout.write (".")\r
+ sys.stdout.flush()\r
+ ModuleInfo = ModuleList[InfFile]\r
+ ModuleName = ModuleInfo.BaseName\r
+ ModuleOutputImage = ModuleInfo.Image.FileName\r
+ ModuleDebugImage = os.path.join(ModuleInfo.DebugDir, ModuleInfo.BaseName + '.efi')\r
+ ## for SMM module in SMRAM, the SMRAM will be allocated from base to top.\r
+ if not ModeIsSmm:\r
+ BaseAddress = BaseAddress - ModuleInfo.Image.Size\r
+ #\r
+ # Update Image to new BaseAddress by GenFw tool\r
+ #\r
+ LaunchCommand(["GenFw", "--rebase", str(BaseAddress), "-r", ModuleOutputImage], ModuleInfo.OutputDir)\r
+ LaunchCommand(["GenFw", "--rebase", str(BaseAddress), "-r", ModuleDebugImage], ModuleInfo.DebugDir)\r
+ else:\r
+ #\r
+ # Set new address to the section header only for SMM driver.\r
+ #\r
+ LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleOutputImage], ModuleInfo.OutputDir)\r
+ LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleDebugImage], ModuleInfo.DebugDir)\r
+ #\r
+ # Collect funtion address from Map file\r
+ #\r
+ ImageMapTable = ModuleOutputImage.replace('.efi', '.map')\r
+ FunctionList = []\r
+ if os.path.exists(ImageMapTable):\r
+ OrigImageBaseAddress = 0\r
+ ImageMap = open(ImageMapTable, 'r')\r
+ for LinStr in ImageMap:\r
+ if len (LinStr.strip()) == 0:\r
+ continue\r
+ #\r
+ # Get the preferred address set on link time.\r
+ #\r
+ if LinStr.find ('Preferred load address is') != -1:\r
+ StrList = LinStr.split()\r
+ OrigImageBaseAddress = int (StrList[len(StrList) - 1], 16)\r
+\r
+ StrList = LinStr.split()\r
+ if len (StrList) > 4:\r
+ if StrList[3] == 'f' or StrList[3] == 'F':\r
+ Name = StrList[1]\r
+ RelativeAddress = int (StrList[2], 16) - OrigImageBaseAddress\r
+ FunctionList.append ((Name, RelativeAddress))\r
+ if ModuleInfo.Arch == 'IPF' and Name.endswith('_ModuleEntryPoint'):\r
+ #\r
+ # Get the real entry point address for IPF image.\r
+ #\r
+ ModuleInfo.Image.EntryPoint = RelativeAddress\r
+ ImageMap.close()\r
+ #\r
+ # Add general information.\r
+ #\r
+ if ModeIsSmm:\r
+ MapBuffer.write('\n\n%s (Fixed SMRAM Offset, BaseAddress=0x%010X, EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint))\r
+ elif AddrIsOffset:\r
+ MapBuffer.write('\n\n%s (Fixed Memory Offset, BaseAddress=-0x%010X, EntryPoint=-0x%010X)\n' % (ModuleName, 0 - BaseAddress, 0 - (BaseAddress + ModuleInfo.Image.EntryPoint)))\r
+ else:\r
+ MapBuffer.write('\n\n%s (Fixed Memory Address, BaseAddress=0x%010X, EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint))\r
+ #\r
+ # Add guid and general seciton section.\r
+ #\r
+ TextSectionAddress = 0\r
+ DataSectionAddress = 0\r
+ for SectionHeader in ModuleInfo.Image.SectionHeaderList:\r
+ if SectionHeader[0] == '.text':\r
+ TextSectionAddress = SectionHeader[1]\r
+ elif SectionHeader[0] in ['.data', '.sdata']:\r
+ DataSectionAddress = SectionHeader[1]\r
+ if AddrIsOffset:\r
+ MapBuffer.write('(GUID=%s, .textbaseaddress=-0x%010X, .databaseaddress=-0x%010X)\n' % (ModuleInfo.Guid, 0 - (BaseAddress + TextSectionAddress), 0 - (BaseAddress + DataSectionAddress)))\r
+ else:\r
+ MapBuffer.write('(GUID=%s, .textbaseaddress=0x%010X, .databaseaddress=0x%010X)\n' % (ModuleInfo.Guid, BaseAddress + TextSectionAddress, BaseAddress + DataSectionAddress))\r
+ #\r
+ # Add debug image full path.\r
+ #\r
+ MapBuffer.write('(IMAGE=%s)\n\n' % (ModuleDebugImage))\r
+ #\r
+ # Add funtion address\r
+ #\r
+ for Function in FunctionList:\r
+ if AddrIsOffset:\r
+ MapBuffer.write(' -0x%010X %s\n' % (0 - (BaseAddress + Function[1]), Function[0]))\r
+ else:\r
+ MapBuffer.write(' 0x%010X %s\n' % (BaseAddress + Function[1], Function[0]))\r
+ ImageMap.close()\r
+\r
+ #\r
+ # for SMM module in SMRAM, the SMRAM will be allocated from base to top.\r
+ #\r
+ if ModeIsSmm:\r
+ BaseAddress = BaseAddress + ModuleInfo.Image.Size\r
+\r
+ ## Collect MAP information of all FVs\r
+ #\r
+ def _CollectFvMapBuffer (self, MapBuffer, Wa, ModuleList):\r
+ if self.Fdf:\r
+ # First get the XIP base address for FV map file.\r
+ GuidPattern = re.compile("[-a-fA-F0-9]+")\r
+ GuidName = re.compile("\(GUID=[-a-fA-F0-9]+")\r
+ for FvName in Wa.FdfProfile.FvDict:\r
+ FvMapBuffer = os.path.join(Wa.FvDir, FvName + '.Fv.map')\r
+ if not os.path.exists(FvMapBuffer):\r
+ continue\r
+ FvMap = open(FvMapBuffer, 'r')\r
+ #skip FV size information\r
+ FvMap.readline()\r
+ FvMap.readline()\r
+ FvMap.readline()\r
+ FvMap.readline()\r
+ for Line in FvMap:\r
+ MatchGuid = GuidPattern.match(Line)\r
+ if MatchGuid is not None:\r
+ #\r
+ # Replace GUID with module name\r
+ #\r
+ GuidString = MatchGuid.group()\r
+ if GuidString.upper() in ModuleList:\r
+ Line = Line.replace(GuidString, ModuleList[GuidString.upper()].Name)\r
+ MapBuffer.write(Line)\r
+ #\r
+ # Add the debug image full path.\r
+ #\r
+ MatchGuid = GuidName.match(Line)\r
+ if MatchGuid is not None:\r
+ GuidString = MatchGuid.group().split("=")[1]\r
+ if GuidString.upper() in ModuleList:\r
+ MapBuffer.write('(IMAGE=%s)\n' % (os.path.join(ModuleList[GuidString.upper()].DebugDir, ModuleList[GuidString.upper()].Name + '.efi')))\r
+\r
+ FvMap.close()\r
+\r
+ ## Collect MAP information of all modules\r
+ #\r
+ def _CollectModuleMapBuffer (self, MapBuffer, ModuleList):\r
+ sys.stdout.write ("Generate Load Module At Fix Address Map")\r
+ sys.stdout.flush()\r
+ PatchEfiImageList = []\r
+ PeiModuleList = {}\r
+ BtModuleList = {}\r
+ RtModuleList = {}\r
+ SmmModuleList = {}\r
+ PeiSize = 0\r
+ BtSize = 0\r
+ RtSize = 0\r
+ # reserve 4K size in SMRAM to make SMM module address not from 0.\r
+ SmmSize = 0x1000\r
+ IsIpfPlatform = False\r
+ if 'IPF' in self.ArchList:\r
+ IsIpfPlatform = True\r
+ for ModuleGuid in ModuleList:\r
+ Module = ModuleList[ModuleGuid]\r
+ GlobalData.gProcessingFile = "%s [%s, %s, %s]" % (Module.MetaFile, Module.Arch, Module.ToolChain, Module.BuildTarget)\r
+\r
+ OutputImageFile = ''\r
+ for ResultFile in Module.CodaTargetList:\r
+ if str(ResultFile.Target).endswith('.efi'):\r
+ #\r
+ # module list for PEI, DXE, RUNTIME and SMM\r
+ #\r
+ OutputImageFile = os.path.join(Module.OutputDir, Module.Name + '.efi')\r
+ ImageClass = PeImageClass (OutputImageFile)\r
+ if not ImageClass.IsValid:\r
+ EdkLogger.error("build", FILE_PARSE_FAILURE, ExtraData=ImageClass.ErrorInfo)\r
+ ImageInfo = PeImageInfo(Module.Name, Module.Guid, Module.Arch, Module.OutputDir, Module.DebugDir, ImageClass)\r
+ if Module.ModuleType in [SUP_MODULE_PEI_CORE, SUP_MODULE_PEIM, EDK_COMPONENT_TYPE_COMBINED_PEIM_DRIVER, EDK_COMPONENT_TYPE_PIC_PEIM, EDK_COMPONENT_TYPE_RELOCATABLE_PEIM, SUP_MODULE_DXE_CORE]:\r
+ PeiModuleList[Module.MetaFile] = ImageInfo\r
+ PeiSize += ImageInfo.Image.Size\r
+ elif Module.ModuleType in [EDK_COMPONENT_TYPE_BS_DRIVER, SUP_MODULE_DXE_DRIVER, SUP_MODULE_UEFI_DRIVER]:\r
+ BtModuleList[Module.MetaFile] = ImageInfo\r
+ BtSize += ImageInfo.Image.Size\r
+ elif Module.ModuleType in [SUP_MODULE_DXE_RUNTIME_DRIVER, EDK_COMPONENT_TYPE_RT_DRIVER, SUP_MODULE_DXE_SAL_DRIVER, EDK_COMPONENT_TYPE_SAL_RT_DRIVER]:\r
+ RtModuleList[Module.MetaFile] = ImageInfo\r
+ #IPF runtime driver needs to be at 2 page alignment.\r
+ if IsIpfPlatform and ImageInfo.Image.Size % 0x2000 != 0:\r
+ ImageInfo.Image.Size = (ImageInfo.Image.Size / 0x2000 + 1) * 0x2000\r
+ RtSize += ImageInfo.Image.Size\r
+ elif Module.ModuleType in [SUP_MODULE_SMM_CORE, SUP_MODULE_DXE_SMM_DRIVER, SUP_MODULE_MM_STANDALONE, SUP_MODULE_MM_CORE_STANDALONE]:\r
+ SmmModuleList[Module.MetaFile] = ImageInfo\r
+ SmmSize += ImageInfo.Image.Size\r
+ if Module.ModuleType == SUP_MODULE_DXE_SMM_DRIVER:\r
+ PiSpecVersion = Module.Module.Specification.get('PI_SPECIFICATION_VERSION', '0x00000000')\r
+ # for PI specification < PI1.1, DXE_SMM_DRIVER also runs as BOOT time driver.\r
+ if int(PiSpecVersion, 16) < 0x0001000A:\r
+ BtModuleList[Module.MetaFile] = ImageInfo\r
+ BtSize += ImageInfo.Image.Size\r
+ break\r
+ #\r
+ # EFI image is final target.\r
+ # Check EFI image contains patchable FixAddress related PCDs.\r
+ #\r
+ if OutputImageFile != '':\r
+ ModuleIsPatch = False\r
+ for Pcd in Module.ModulePcdList:\r
+ if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SET:\r
+ ModuleIsPatch = True\r
+ break\r
+ if not ModuleIsPatch:\r
+ for Pcd in Module.LibraryPcdList:\r
+ if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SET:\r
+ ModuleIsPatch = True\r
+ break\r
+\r
+ if not ModuleIsPatch:\r
+ continue\r
+ #\r
+ # Module includes the patchable load fix address PCDs.\r
+ # It will be fixed up later.\r
+ #\r
+ PatchEfiImageList.append (OutputImageFile)\r
+\r
+ #\r
+ # Get Top Memory address\r
+ #\r
+ ReservedRuntimeMemorySize = 0\r
+ TopMemoryAddress = 0\r
+ if self.LoadFixAddress == 0xFFFFFFFFFFFFFFFF:\r
+ TopMemoryAddress = 0\r
+ else:\r
+ TopMemoryAddress = self.LoadFixAddress\r
+ if TopMemoryAddress < RtSize + BtSize + PeiSize:\r
+ EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS is too low to load driver")\r
+ # Make IPF runtime driver at 2 page alignment.\r
+ if IsIpfPlatform:\r
+ ReservedRuntimeMemorySize = TopMemoryAddress % 0x2000\r
+ RtSize = RtSize + ReservedRuntimeMemorySize\r
+\r
+ #\r
+ # Patch FixAddress related PCDs into EFI image\r
+ #\r
+ for EfiImage in PatchEfiImageList:\r
+ EfiImageMap = EfiImage.replace('.efi', '.map')\r
+ if not os.path.exists(EfiImageMap):\r
+ continue\r
+ #\r
+ # Get PCD offset in EFI image by GenPatchPcdTable function\r
+ #\r
+ PcdTable = parsePcdInfoFromMapFile(EfiImageMap, EfiImage)\r
+ #\r
+ # Patch real PCD value by PatchPcdValue tool\r
+ #\r
+ for PcdInfo in PcdTable:\r
+ ReturnValue = 0\r
+ if PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE:\r
+ ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE_DATA_TYPE, str (PeiSize / 0x1000))\r
+ elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE:\r
+ ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE_DATA_TYPE, str (BtSize / 0x1000))\r
+ elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE:\r
+ ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE_DATA_TYPE, str (RtSize / 0x1000))\r
+ elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE and len (SmmModuleList) > 0:\r
+ ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE_DATA_TYPE, str (SmmSize / 0x1000))\r
+ if ReturnValue != 0:\r
+ EdkLogger.error("build", PARAMETER_INVALID, "Patch PCD value failed", ExtraData=ErrorInfo)\r
+\r
+ MapBuffer.write('PEI_CODE_PAGE_NUMBER = 0x%x\n' % (PeiSize / 0x1000))\r
+ MapBuffer.write('BOOT_CODE_PAGE_NUMBER = 0x%x\n' % (BtSize / 0x1000))\r
+ MapBuffer.write('RUNTIME_CODE_PAGE_NUMBER = 0x%x\n' % (RtSize / 0x1000))\r
+ if len (SmmModuleList) > 0:\r
+ MapBuffer.write('SMM_CODE_PAGE_NUMBER = 0x%x\n' % (SmmSize / 0x1000))\r
+\r
+ PeiBaseAddr = TopMemoryAddress - RtSize - BtSize\r
+ BtBaseAddr = TopMemoryAddress - RtSize\r
+ RtBaseAddr = TopMemoryAddress - ReservedRuntimeMemorySize\r
+\r
+ self._RebaseModule (MapBuffer, PeiBaseAddr, PeiModuleList, TopMemoryAddress == 0)\r
+ self._RebaseModule (MapBuffer, BtBaseAddr, BtModuleList, TopMemoryAddress == 0)\r
+ self._RebaseModule (MapBuffer, RtBaseAddr, RtModuleList, TopMemoryAddress == 0)\r
+ self._RebaseModule (MapBuffer, 0x1000, SmmModuleList, AddrIsOffset=False, ModeIsSmm=True)\r
+ MapBuffer.write('\n\n')\r
+ sys.stdout.write ("\n")\r
+ sys.stdout.flush()\r
+\r
+ ## Save platform Map file\r
+ #\r
+ def _SaveMapFile (self, MapBuffer, Wa):\r
+ #\r
+ # Map file path is got.\r
+ #\r
+ MapFilePath = os.path.join(Wa.BuildDir, Wa.Name + '.map')\r
+ #\r
+ # Save address map into MAP file.\r
+ #\r
+ SaveFileOnChange(MapFilePath, MapBuffer.getvalue(), False)\r
+ MapBuffer.close()\r
+ if self.LoadFixAddress != 0:\r
+ sys.stdout.write ("\nLoad Module At Fix Address Map file can be found at %s\n" % (MapFilePath))\r
+ sys.stdout.flush()\r
+\r
+ ## Build active platform for different build targets and different tool chains\r
+ #\r
+ def _BuildPlatform(self):\r
+ SaveFileOnChange(self.PlatformBuildPath, '# DO NOT EDIT \n# FILE auto-generated\n', False)\r
+ for BuildTarget in self.BuildTargetList:\r
+ GlobalData.gGlobalDefines['TARGET'] = BuildTarget\r
+ index = 0\r
+ for ToolChain in self.ToolChainList:\r
+ GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain\r
+ GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain\r
+ GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index]\r
+ index += 1\r
+ Wa = WorkspaceAutoGen(\r
+ self.WorkspaceDir,\r
+ self.PlatformFile,\r
+ BuildTarget,\r
+ ToolChain,\r
+ self.ArchList,\r
+ self.BuildDatabase,\r
+ self.TargetTxt,\r
+ self.ToolDef,\r
+ self.Fdf,\r
+ self.FdList,\r
+ self.FvList,\r
+ self.CapList,\r
+ self.SkuId,\r
+ self.UniFlag,\r
+ self.Progress\r
+ )\r
+ self.Fdf = Wa.FdfFile\r
+ self.LoadFixAddress = Wa.Platform.LoadFixAddress\r
+ self.BuildReport.AddPlatformReport(Wa)\r
+ self.Progress.Stop("done!")\r
+\r
+ # Add ffs build to makefile\r
+ CmdListDict = {}\r
+ if GlobalData.gEnableGenfdsMultiThread and self.Fdf:\r
+ CmdListDict = self._GenFfsCmd()\r
+\r
+ for Arch in Wa.ArchList:\r
+ GlobalData.gGlobalDefines['ARCH'] = Arch\r
+ Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)\r
+ for Module in Pa.Platform.Modules:\r
+ # Get ModuleAutoGen object to generate C code file and makefile\r
+ Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
+ if Ma is None:\r
+ continue\r
+ self.BuildModules.append(Ma)\r
+ self._BuildPa(self.Target, Pa, FfsCommand=CmdListDict)\r
+\r
+ # Create MAP file when Load Fix Address is enabled.\r
+ if self.Target in ["", "all", "fds"]:\r
+ for Arch in Wa.ArchList:\r
+ GlobalData.gGlobalDefines['ARCH'] = Arch\r
+ #\r
+ # Check whether the set fix address is above 4G for 32bit image.\r
+ #\r
+ if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
+ 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")\r
+ #\r
+ # Get Module List\r
+ #\r
+ ModuleList = {}\r
+ for Pa in Wa.AutoGenObjectList:\r
+ for Ma in Pa.ModuleAutoGenList:\r
+ if Ma is None:\r
+ continue\r
+ if not Ma.IsLibrary:\r
+ ModuleList[Ma.Guid.upper()] = Ma\r
+\r
+ MapBuffer = BytesIO('')\r
+ if self.LoadFixAddress != 0:\r
+ #\r
+ # Rebase module to the preferred memory address before GenFds\r
+ #\r
+ self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
+ if self.Fdf:\r
+ #\r
+ # create FDS again for the updated EFI image\r
+ #\r
+ self._Build("fds", Wa)\r
+ #\r
+ # Create MAP file for all platform FVs after GenFds.\r
+ #\r
+ self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)\r
+ #\r
+ # Save MAP buffer into MAP file.\r
+ #\r
+ self._SaveMapFile (MapBuffer, Wa)\r
+\r
+ ## Build active module for different build targets, different tool chains and different archs\r
+ #\r
+ def _BuildModule(self):\r
+ for BuildTarget in self.BuildTargetList:\r
+ GlobalData.gGlobalDefines['TARGET'] = BuildTarget\r
+ index = 0\r
+ for ToolChain in self.ToolChainList:\r
+ WorkspaceAutoGenTime = time.time()\r
+ GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain\r
+ GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain\r
+ GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index]\r
+ index += 1\r
+ #\r
+ # module build needs platform build information, so get platform\r
+ # AutoGen first\r
+ #\r
+ Wa = WorkspaceAutoGen(\r
+ self.WorkspaceDir,\r
+ self.PlatformFile,\r
+ BuildTarget,\r
+ ToolChain,\r
+ self.ArchList,\r
+ self.BuildDatabase,\r
+ self.TargetTxt,\r
+ self.ToolDef,\r
+ self.Fdf,\r
+ self.FdList,\r
+ self.FvList,\r
+ self.CapList,\r
+ self.SkuId,\r
+ self.UniFlag,\r
+ self.Progress,\r
+ self.ModuleFile\r
+ )\r
+ self.Fdf = Wa.FdfFile\r
+ self.LoadFixAddress = Wa.Platform.LoadFixAddress\r
+ Wa.CreateMakeFile(False)\r
+ # Add ffs build to makefile\r
+ CmdListDict = None\r
+ if GlobalData.gEnableGenfdsMultiThread and self.Fdf:\r
+ CmdListDict = self._GenFfsCmd()\r
+ self.Progress.Stop("done!")\r
+ MaList = []\r
+ ExitFlag = threading.Event()\r
+ ExitFlag.clear()\r
+ self.AutoGenTime += int(round((time.time() - WorkspaceAutoGenTime)))\r
+ for Arch in Wa.ArchList:\r
+ AutoGenStart = time.time()\r
+ GlobalData.gGlobalDefines['ARCH'] = Arch\r
+ Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)\r
+ for Module in Pa.Platform.Modules:\r
+ if self.ModuleFile.Dir == Module.Dir and self.ModuleFile.Name == Module.Name:\r
+ Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
+ if Ma is None: continue\r
+ MaList.append(Ma)\r
+ if Ma.CanSkipbyHash():\r
+ self.HashSkipModules.append(Ma)\r
+ continue\r
+ # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'\r
+ if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
+ # for target which must generate AutoGen code and makefile\r
+ if not self.SkipAutoGen or self.Target == 'genc':\r
+ self.Progress.Start("Generating code")\r
+ Ma.CreateCodeFile(True)\r
+ self.Progress.Stop("done!")\r
+ if self.Target == "genc":\r
+ return True\r
+ if not self.SkipAutoGen or self.Target == 'genmake':\r
+ self.Progress.Start("Generating makefile")\r
+ if CmdListDict and self.Fdf and (Module.File, Arch) in CmdListDict:\r
+ Ma.CreateMakeFile(True, CmdListDict[Module.File, Arch])\r
+ del CmdListDict[Module.File, Arch]\r
+ else:\r
+ Ma.CreateMakeFile(True)\r
+ self.Progress.Stop("done!")\r
+ if self.Target == "genmake":\r
+ return True\r
+ self.BuildModules.append(Ma)\r
+ self.AutoGenTime += int(round((time.time() - AutoGenStart)))\r
+ MakeStart = time.time()\r
+ for Ma in self.BuildModules:\r
+ if not Ma.IsBinaryModule:\r
+ Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))\r
+ # Break build if any build thread has error\r
+ if BuildTask.HasError():\r
+ # we need a full version of makefile for platform\r
+ ExitFlag.set()\r
+ BuildTask.WaitForComplete()\r
+ Pa.CreateMakeFile(False)\r
+ EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
+ # Start task scheduler\r
+ if not BuildTask.IsOnGoing():\r
+ BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)\r
+\r
+ # in case there's an interruption. we need a full version of makefile for platform\r
+ Pa.CreateMakeFile(False)\r
+ if BuildTask.HasError():\r
+ EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
+ self.MakeTime += int(round((time.time() - MakeStart)))\r
+\r
+ MakeContiue = time.time()\r
+ ExitFlag.set()\r
+ BuildTask.WaitForComplete()\r
+ self.CreateAsBuiltInf()\r
+ self.MakeTime += int(round((time.time() - MakeContiue)))\r
+ if BuildTask.HasError():\r
+ EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
+\r
+ self.BuildReport.AddPlatformReport(Wa, MaList)\r
+ if MaList == []:\r
+ EdkLogger.error(\r
+ 'build',\r
+ BUILD_ERROR,\r
+ "Module for [%s] is not a component of active platform."\\r
+ " Please make sure that the ARCH and inf file path are"\\r
+ " given in the same as in [%s]" % \\r
+ (', '.join(Wa.ArchList), self.PlatformFile),\r
+ ExtraData=self.ModuleFile\r
+ )\r
+ # Create MAP file when Load Fix Address is enabled.\r
+ if self.Target == "fds" and self.Fdf:\r
+ for Arch in Wa.ArchList:\r
+ #\r
+ # Check whether the set fix address is above 4G for 32bit image.\r
+ #\r
+ if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
+ 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")\r
+ #\r
+ # Get Module List\r
+ #\r
+ ModuleList = {}\r
+ for Pa in Wa.AutoGenObjectList:\r
+ for Ma in Pa.ModuleAutoGenList:\r
+ if Ma is None:\r
+ continue\r
+ if not Ma.IsLibrary:\r
+ ModuleList[Ma.Guid.upper()] = Ma\r
+\r
+ MapBuffer = BytesIO('')\r
+ if self.LoadFixAddress != 0:\r
+ #\r
+ # Rebase module to the preferred memory address before GenFds\r
+ #\r
+ self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
+ #\r
+ # create FDS again for the updated EFI image\r
+ #\r
+ GenFdsStart = time.time()\r
+ self._Build("fds", Wa)\r
+ self.GenFdsTime += int(round((time.time() - GenFdsStart)))\r
+ #\r
+ # Create MAP file for all platform FVs after GenFds.\r
+ #\r
+ self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)\r
+ #\r
+ # Save MAP buffer into MAP file.\r
+ #\r
+ self._SaveMapFile (MapBuffer, Wa)\r
+\r
+ def _GenFfsCmd(self):\r
+ # convert dictionary of Cmd:(Inf,Arch)\r
+ # to a new dictionary of (Inf,Arch):Cmd,Cmd,Cmd...\r
+ CmdSetDict = defaultdict(set)\r
+ GenFfsDict = GenFds.GenFfsMakefile('', GlobalData.gFdfParser, self, self.ArchList, GlobalData)\r
+ for Cmd in GenFfsDict:\r
+ tmpInf, tmpArch = GenFfsDict[Cmd]\r
+ CmdSetDict[tmpInf, tmpArch].add(Cmd)\r
+ return CmdSetDict\r
+\r
+ ## Build a platform in multi-thread mode\r
+ #\r
+ def _MultiThreadBuildPlatform(self):\r
+ SaveFileOnChange(self.PlatformBuildPath, '# DO NOT EDIT \n# FILE auto-generated\n', False)\r
+ for BuildTarget in self.BuildTargetList:\r
+ GlobalData.gGlobalDefines['TARGET'] = BuildTarget\r
+ index = 0\r
+ for ToolChain in self.ToolChainList:\r
+ WorkspaceAutoGenTime = time.time()\r
+ GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain\r
+ GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain\r
+ GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index]\r
+ index += 1\r
+ Wa = WorkspaceAutoGen(\r
+ self.WorkspaceDir,\r
+ self.PlatformFile,\r
+ BuildTarget,\r
+ ToolChain,\r
+ self.ArchList,\r
+ self.BuildDatabase,\r
+ self.TargetTxt,\r
+ self.ToolDef,\r
+ self.Fdf,\r
+ self.FdList,\r
+ self.FvList,\r
+ self.CapList,\r
+ self.SkuId,\r
+ self.UniFlag,\r
+ self.Progress\r
+ )\r
+ self.Fdf = Wa.FdfFile\r
+ self.LoadFixAddress = Wa.Platform.LoadFixAddress\r
+ self.BuildReport.AddPlatformReport(Wa)\r
+ Wa.CreateMakeFile(False)\r
+\r
+ # Add ffs build to makefile\r
+ CmdListDict = None\r
+ if GlobalData.gEnableGenfdsMultiThread and self.Fdf:\r
+ CmdListDict = self._GenFfsCmd()\r
+\r
+ # multi-thread exit flag\r
+ ExitFlag = threading.Event()\r
+ ExitFlag.clear()\r
+ self.AutoGenTime += int(round((time.time() - WorkspaceAutoGenTime)))\r
+ for Arch in Wa.ArchList:\r
+ AutoGenStart = time.time()\r
+ GlobalData.gGlobalDefines['ARCH'] = Arch\r
+ Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch)\r
+ if Pa is None:\r
+ continue\r
+ ModuleList = []\r
+ for Inf in Pa.Platform.Modules:\r
+ ModuleList.append(Inf)\r
+ # Add the INF only list in FDF\r
+ if GlobalData.gFdfParser is not None:\r
+ for InfName in GlobalData.gFdfParser.Profile.InfList:\r
+ Inf = PathClass(NormPath(InfName), self.WorkspaceDir, Arch)\r
+ if Inf in Pa.Platform.Modules:\r
+ continue\r
+ ModuleList.append(Inf)\r
+ for Module in ModuleList:\r
+ # Get ModuleAutoGen object to generate C code file and makefile\r
+ Ma = ModuleAutoGen(Wa, Module, BuildTarget, ToolChain, Arch, self.PlatformFile)\r
+\r
+ if Ma is None:\r
+ continue\r
+ if Ma.CanSkipbyHash():\r
+ self.HashSkipModules.append(Ma)\r
+ continue\r
+\r
+ # Not to auto-gen for targets 'clean', 'cleanlib', 'cleanall', 'run', 'fds'\r
+ if self.Target not in ['clean', 'cleanlib', 'cleanall', 'run', 'fds']:\r
+ # for target which must generate AutoGen code and makefile\r
+ if not self.SkipAutoGen or self.Target == 'genc':\r
+ Ma.CreateCodeFile(True)\r
+ if self.Target == "genc":\r
+ continue\r
+\r
+ if not self.SkipAutoGen or self.Target == 'genmake':\r
+ if CmdListDict and self.Fdf and (Module.File, Arch) in CmdListDict:\r
+ Ma.CreateMakeFile(True, CmdListDict[Module.File, Arch])\r
+ del CmdListDict[Module.File, Arch]\r
+ else:\r
+ Ma.CreateMakeFile(True)\r
+ if self.Target == "genmake":\r
+ continue\r
+ self.BuildModules.append(Ma)\r
+ self.Progress.Stop("done!")\r
+ self.AutoGenTime += int(round((time.time() - AutoGenStart)))\r
+ MakeStart = time.time()\r
+ for Ma in self.BuildModules:\r
+ # Generate build task for the module\r
+ if not Ma.IsBinaryModule:\r
+ Bt = BuildTask.New(ModuleMakeUnit(Ma, self.Target))\r
+ # Break build if any build thread has error\r
+ if BuildTask.HasError():\r
+ # we need a full version of makefile for platform\r
+ ExitFlag.set()\r
+ BuildTask.WaitForComplete()\r
+ Pa.CreateMakeFile(False)\r
+ EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
+ # Start task scheduler\r
+ if not BuildTask.IsOnGoing():\r
+ BuildTask.StartScheduler(self.ThreadNumber, ExitFlag)\r
+\r
+ # in case there's an interruption. we need a full version of makefile for platform\r
+ Pa.CreateMakeFile(False)\r
+ if BuildTask.HasError():\r
+ EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
+ self.MakeTime += int(round((time.time() - MakeStart)))\r
+\r
+ MakeContiue = time.time()\r
+ #\r
+ # Save temp tables to a TmpTableDict.\r
+ #\r
+ for Key in Wa.BuildDatabase._CACHE_:\r
+ if Wa.BuildDatabase._CACHE_[Key]._RawData and Wa.BuildDatabase._CACHE_[Key]._RawData._Table and Wa.BuildDatabase._CACHE_[Key]._RawData._Table.Table:\r
+ if TemporaryTablePattern.match(Wa.BuildDatabase._CACHE_[Key]._RawData._Table.Table):\r
+ TmpTableDict[Wa.BuildDatabase._CACHE_[Key]._RawData._Table.Table] = Wa.BuildDatabase._CACHE_[Key]._RawData._Table.Cur\r
+ #\r
+ #\r
+ # All modules have been put in build tasks queue. Tell task scheduler\r
+ # to exit if all tasks are completed\r
+ #\r
+ ExitFlag.set()\r
+ BuildTask.WaitForComplete()\r
+ self.CreateAsBuiltInf()\r
+ self.MakeTime += int(round((time.time() - MakeContiue)))\r
+ #\r
+ # Check for build error, and raise exception if one\r
+ # has been signaled.\r
+ #\r
+ if BuildTask.HasError():\r
+ EdkLogger.error("build", BUILD_ERROR, "Failed to build module", ExtraData=GlobalData.gBuildingModule)\r
+\r
+ # Create MAP file when Load Fix Address is enabled.\r
+ if self.Target in ["", "all", "fds"]:\r
+ for Arch in Wa.ArchList:\r
+ #\r
+ # Check whether the set fix address is above 4G for 32bit image.\r
+ #\r
+ if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000:\r
+ 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")\r
+ #\r
+ # Get Module List\r
+ #\r
+ ModuleList = {}\r
+ for Pa in Wa.AutoGenObjectList:\r
+ for Ma in Pa.ModuleAutoGenList:\r
+ if Ma is None:\r
+ continue\r
+ if not Ma.IsLibrary:\r
+ ModuleList[Ma.Guid.upper()] = Ma\r
+ #\r
+ # Rebase module to the preferred memory address before GenFds\r
+ #\r
+ MapBuffer = BytesIO('')\r
+ if self.LoadFixAddress != 0:\r
+ self._CollectModuleMapBuffer(MapBuffer, ModuleList)\r
+\r
+ if self.Fdf:\r
+ #\r
+ # Generate FD image if there's a FDF file found\r
+ #\r
+ GenFdsStart = time.time()\r
+ LaunchCommand(Wa.GenFdsCommand, os.getcwd())\r
+\r
+ #\r
+ # Create MAP file for all platform FVs after GenFds.\r
+ #\r
+ self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList)\r
+ self.GenFdsTime += int(round((time.time() - GenFdsStart)))\r
+ #\r
+ # Save MAP buffer into MAP file.\r
+ #\r
+ self._SaveMapFile(MapBuffer, Wa)\r
+\r
+ ## Generate GuidedSectionTools.txt in the FV directories.\r
+ #\r
+ def CreateGuidedSectionToolsFile(self):\r
+ for BuildTarget in self.BuildTargetList:\r
+ for ToolChain in self.ToolChainList:\r
+ Wa = WorkspaceAutoGen(\r
+ self.WorkspaceDir,\r
+ self.PlatformFile,\r
+ BuildTarget,\r
+ ToolChain,\r
+ self.ArchList,\r
+ self.BuildDatabase,\r
+ self.TargetTxt,\r
+ self.ToolDef,\r
+ self.Fdf,\r
+ self.FdList,\r
+ self.FvList,\r
+ self.CapList,\r
+ self.SkuId,\r
+ self.UniFlag\r
+ )\r
+ FvDir = Wa.FvDir\r
+ if not os.path.exists(FvDir):\r
+ continue\r
+\r
+ for Arch in self.ArchList:\r
+ # Build up the list of supported architectures for this build\r
+ prefix = '%s_%s_%s_' % (BuildTarget, ToolChain, Arch)\r
+\r
+ # Look through the tool definitions for GUIDed tools\r
+ guidAttribs = []\r
+ for (attrib, value) in self.ToolDef.ToolsDefTxtDictionary.iteritems():\r
+ if attrib.upper().endswith('_GUID'):\r
+ split = attrib.split('_')\r
+ thisPrefix = '_'.join(split[0:3]) + '_'\r
+ if thisPrefix == prefix:\r
+ guid = self.ToolDef.ToolsDefTxtDictionary[attrib]\r
+ guid = guid.lower()\r
+ toolName = split[3]\r
+ path = '_'.join(split[0:4]) + '_PATH'\r
+ path = self.ToolDef.ToolsDefTxtDictionary[path]\r
+ path = self.GetFullPathOfTool(path)\r
+ guidAttribs.append((guid, toolName, path))\r
+\r
+ # Write out GuidedSecTools.txt\r
+ toolsFile = os.path.join(FvDir, 'GuidedSectionTools.txt')\r
+ toolsFile = open(toolsFile, 'wt')\r
+ for guidedSectionTool in guidAttribs:\r
+ print(' '.join(guidedSectionTool), file=toolsFile)\r
+ toolsFile.close()\r
+\r
+ ## Returns the full path of the tool.\r
+ #\r
+ def GetFullPathOfTool (self, tool):\r
+ if os.path.exists(tool):\r
+ return os.path.realpath(tool)\r
+ else:\r
+ # We need to search for the tool using the\r
+ # PATH environment variable.\r
+ for dirInPath in os.environ['PATH'].split(os.pathsep):\r
+ foundPath = os.path.join(dirInPath, tool)\r
+ if os.path.exists(foundPath):\r
+ return os.path.realpath(foundPath)\r
+\r
+ # If the tool was not found in the path then we just return\r
+ # the input tool.\r
+ return tool\r
+\r
+ ## Launch the module or platform build\r
+ #\r
+ def Launch(self):\r
+ if not self.ModuleFile:\r
+ if not self.SpawnMode or self.Target not in ["", "all"]:\r
+ self.SpawnMode = False\r
+ self._BuildPlatform()\r
+ else:\r
+ self._MultiThreadBuildPlatform()\r
+ self.CreateGuidedSectionToolsFile()\r
+ else:\r
+ self.SpawnMode = False\r
+ self._BuildModule()\r
+\r
+ if self.Target == 'cleanall':\r
+ self.Db.Close()\r
+ RemoveDirectory(os.path.dirname(GlobalData.gDatabasePath), True)\r
+\r
+ def CreateAsBuiltInf(self):\r
+ for Module in self.BuildModules:\r
+ Module.CreateAsBuiltInf()\r
+ for Module in self.HashSkipModules:\r
+ Module.CreateAsBuiltInf(True)\r
+ self.BuildModules = []\r
+ self.HashSkipModules = []\r
+ ## Do some clean-up works when error occurred\r
+ def Relinquish(self):\r
+ OldLogLevel = EdkLogger.GetLevel()\r
+ EdkLogger.SetLevel(EdkLogger.ERROR)\r
+ #self.DumpBuildData()\r
+ Utils.Progressor.Abort()\r
+ if self.SpawnMode == True:\r
+ BuildTask.Abort()\r
+ EdkLogger.SetLevel(OldLogLevel)\r
+\r
+ def DumpBuildData(self):\r
+ CacheDirectory = os.path.dirname(GlobalData.gDatabasePath)\r
+ Utils.CreateDirectory(CacheDirectory)\r
+ Utils.DataDump(Utils.gFileTimeStampCache, os.path.join(CacheDirectory, "gFileTimeStampCache"))\r
+ Utils.DataDump(Utils.gDependencyDatabase, os.path.join(CacheDirectory, "gDependencyDatabase"))\r
+\r
+ def RestoreBuildData(self):\r
+ FilePath = os.path.join(os.path.dirname(GlobalData.gDatabasePath), "gFileTimeStampCache")\r
+ if Utils.gFileTimeStampCache == {} and os.path.isfile(FilePath):\r
+ Utils.gFileTimeStampCache = Utils.DataRestore(FilePath)\r
+ if Utils.gFileTimeStampCache is None:\r
+ Utils.gFileTimeStampCache = {}\r
+\r
+ FilePath = os.path.join(os.path.dirname(GlobalData.gDatabasePath), "gDependencyDatabase")\r
+ if Utils.gDependencyDatabase == {} and os.path.isfile(FilePath):\r
+ Utils.gDependencyDatabase = Utils.DataRestore(FilePath)\r
+ if Utils.gDependencyDatabase is None:\r
+ Utils.gDependencyDatabase = {}\r
+\r
+def ParseDefines(DefineList=[]):\r
+ DefineDict = {}\r
+ if DefineList is not None:\r
+ for Define in DefineList:\r
+ DefineTokenList = Define.split("=", 1)\r
+ if not GlobalData.gMacroNamePattern.match(DefineTokenList[0]):\r
+ EdkLogger.error('build', FORMAT_INVALID,\r
+ "The macro name must be in the pattern [A-Z][A-Z0-9_]*",\r
+ ExtraData=DefineTokenList[0])\r
+\r
+ if len(DefineTokenList) == 1:\r
+ DefineDict[DefineTokenList[0]] = "TRUE"\r
+ else:\r
+ DefineDict[DefineTokenList[0]] = DefineTokenList[1].strip()\r
+ return DefineDict\r
+\r
+gParamCheck = []\r
+def SingleCheckCallback(option, opt_str, value, parser):\r
+ if option not in gParamCheck:\r
+ setattr(parser.values, option.dest, value)\r
+ gParamCheck.append(option)\r
+ else:\r
+ parser.error("Option %s only allows one instance in command line!" % option)\r
+\r
+def LogBuildTime(Time):\r
+ if Time:\r
+ TimeDurStr = ''\r
+ TimeDur = time.gmtime(Time)\r
+ if TimeDur.tm_yday > 1:\r
+ TimeDurStr = time.strftime("%H:%M:%S", TimeDur) + ", %d day(s)" % (TimeDur.tm_yday - 1)\r
+ else:\r
+ TimeDurStr = time.strftime("%H:%M:%S", TimeDur)\r
+ return TimeDurStr\r
+ else:\r
+ return None\r
+\r
+## Parse command line options\r
+#\r
+# Using standard Python module optparse to parse command line option of this tool.\r
+#\r
+# @retval Opt A optparse.Values object containing the parsed options\r
+# @retval Args Target of build command\r
+#\r
+def MyOptionParser():\r
+ Parser = OptionParser(description=__copyright__, version=__version__, prog="build.exe", usage="%prog [options] [all|fds|genc|genmake|clean|cleanall|cleanlib|modules|libraries|run]")\r
+ Parser.add_option("-a", "--arch", action="append", type="choice", choices=['IA32', 'X64', 'IPF', 'EBC', 'ARM', 'AARCH64'], dest="TargetArch",\r
+ 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.")\r
+ Parser.add_option("-p", "--platform", action="callback", type="string", dest="PlatformFile", callback=SingleCheckCallback,\r
+ help="Build the platform specified by the DSC file name argument, overriding target.txt's ACTIVE_PLATFORM definition.")\r
+ Parser.add_option("-m", "--module", action="callback", type="string", dest="ModuleFile", callback=SingleCheckCallback,\r
+ help="Build the module specified by the INF file name argument.")\r
+ Parser.add_option("-b", "--buildtarget", type="string", dest="BuildTarget", help="Using the TARGET to build the platform, overriding target.txt's TARGET definition.",\r
+ action="append")\r
+ Parser.add_option("-t", "--tagname", action="append", type="string", dest="ToolChain",\r
+ help="Using the Tool Chain Tagname to build the platform, overriding target.txt's TOOL_CHAIN_TAG definition.")\r
+ Parser.add_option("-x", "--sku-id", action="callback", type="string", dest="SkuId", callback=SingleCheckCallback,\r
+ help="Using this name of SKU ID to build the platform, overriding SKUID_IDENTIFIER in DSC file.")\r
+\r
+ Parser.add_option("-n", action="callback", type="int", dest="ThreadNumber", callback=SingleCheckCallback,\r
+ help="Build the platform using multi-threaded compiler. The value overrides target.txt's MAX_CONCURRENT_THREAD_NUMBER. When value is set to 0, tool automatically detect number of "\\r
+ "processor threads, set value to 1 means disable multi-thread build, and set value to more than 1 means user specify the threads number to build.")\r
+\r
+ Parser.add_option("-f", "--fdf", action="callback", type="string", dest="FdfFile", callback=SingleCheckCallback,\r
+ help="The name of the FDF file to use, which overrides the setting in the DSC file.")\r
+ Parser.add_option("-r", "--rom-image", action="append", type="string", dest="RomImage", default=[],\r
+ help="The name of FD to be generated. The name must be from [FD] section in FDF file.")\r
+ Parser.add_option("-i", "--fv-image", action="append", type="string", dest="FvImage", default=[],\r
+ help="The name of FV to be generated. The name must be from [FV] section in FDF file.")\r
+ Parser.add_option("-C", "--capsule-image", action="append", type="string", dest="CapName", default=[],\r
+ help="The name of Capsule to be generated. The name must be from [Capsule] section in FDF file.")\r
+ Parser.add_option("-u", "--skip-autogen", action="store_true", dest="SkipAutoGen", help="Skip AutoGen step.")\r
+ Parser.add_option("-e", "--re-parse", action="store_true", dest="Reparse", help="Re-parse all meta-data files.")\r
+\r
+ Parser.add_option("-c", "--case-insensitive", action="store_true", dest="CaseInsensitive", default=False, help="Don't check case of file name.")\r
+\r
+ Parser.add_option("-w", "--warning-as-error", action="store_true", dest="WarningAsError", help="Treat warning in tools as error.")\r
+ Parser.add_option("-j", "--log", action="store", dest="LogFile", help="Put log in specified file as well as on console.")\r
+\r
+ Parser.add_option("-s", "--silent", action="store_true", type=None, dest="SilentMode",\r
+ help="Make use of silent mode of (n)make.")\r
+ Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")\r
+ Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\\r
+ "including library instances selected, final dependency expression, "\\r
+ "and warning messages, etc.")\r
+ Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")\r
+ Parser.add_option("-D", "--define", action="append", type="string", dest="Macros", help="Macro: \"Name [= Value]\".")\r
+\r
+ Parser.add_option("-y", "--report-file", action="store", dest="ReportFile", help="Create/overwrite the report to the specified filename.")\r
+ Parser.add_option("-Y", "--report-type", action="append", type="choice", choices=['PCD', 'LIBRARY', 'FLASH', 'DEPEX', 'BUILD_FLAGS', 'FIXED_ADDRESS', 'HASH', 'EXECUTION_ORDER'], dest="ReportType", default=[],\r
+ help="Flags that control the type of build report to generate. Must be one of: [PCD, LIBRARY, FLASH, DEPEX, BUILD_FLAGS, FIXED_ADDRESS, HASH, EXECUTION_ORDER]. "\\r
+ "To specify more than one flag, repeat this option on the command line and the default flag set is [PCD, LIBRARY, FLASH, DEPEX, HASH, BUILD_FLAGS, FIXED_ADDRESS]")\r
+ Parser.add_option("-F", "--flag", action="store", type="string", dest="Flag",\r
+ 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. "\\r
+ "This option can also be specified by setting *_*_*_BUILD_FLAGS in [BuildOptions] section of platform DSC. If they are both specified, this value "\\r
+ "will override the setting in [BuildOptions] section of platform DSC.")\r
+ Parser.add_option("-N", "--no-cache", action="store_true", dest="DisableCache", default=False, help="Disable build cache mechanism")\r
+ Parser.add_option("--conf", action="store", type="string", dest="ConfDirectory", help="Specify the customized Conf directory.")\r
+ Parser.add_option("--check-usage", action="store_true", dest="CheckUsage", default=False, help="Check usage content of entries listed in INF file.")\r
+ Parser.add_option("--ignore-sources", action="store_true", dest="IgnoreSources", default=False, help="Focus to a binary build and ignore all source files")\r
+ Parser.add_option("--pcd", action="append", dest="OptionPcd", help="Set PCD value by command line. Format: \"PcdName=Value\" ")\r
+ Parser.add_option("-l", "--cmd-len", action="store", type="int", dest="CommandLength", help="Specify the maximum line length of build command. Default is 4096.")\r
+ Parser.add_option("--hash", action="store_true", dest="UseHashCache", default=False, help="Enable hash-based caching during build process.")\r
+ Parser.add_option("--binary-destination", action="store", type="string", dest="BinCacheDest", help="Generate a cache of binary files in the specified directory.")\r
+ Parser.add_option("--binary-source", action="store", type="string", dest="BinCacheSource", help="Consume a cache of binary files from the specified directory.")\r
+ Parser.add_option("--genfds-multi-thread", action="store_true", dest="GenfdsMultiThread", default=False, help="Enable GenFds multi thread to generate ffs file.")\r
+ (Opt, Args) = Parser.parse_args()\r
+ return (Opt, Args)\r
+\r
+## Tool entrance method\r
+#\r
+# This method mainly dispatch specific methods per the command line options.\r
+# If no error found, return zero value so the caller of this tool can know\r
+# if it's executed successfully or not.\r
+#\r
+# @retval 0 Tool was successful\r
+# @retval 1 Tool failed\r
+#\r
+def Main():\r
+ StartTime = time.time()\r
+\r
+ # Initialize log system\r
+ EdkLogger.Initialize()\r
+ GlobalData.gCommand = sys.argv[1:]\r
+ #\r
+ # Parse the options and args\r
+ #\r
+ (Option, Target) = MyOptionParser()\r
+ GlobalData.gOptions = Option\r
+ GlobalData.gCaseInsensitive = Option.CaseInsensitive\r
+\r
+ # Set log level\r
+ if Option.verbose is not None:\r
+ EdkLogger.SetLevel(EdkLogger.VERBOSE)\r
+ elif Option.quiet is not None:\r
+ EdkLogger.SetLevel(EdkLogger.QUIET)\r
+ elif Option.debug is not None:\r
+ EdkLogger.SetLevel(Option.debug + 1)\r
+ else:\r
+ EdkLogger.SetLevel(EdkLogger.INFO)\r
+\r
+ if Option.LogFile is not None:\r
+ EdkLogger.SetLogFile(Option.LogFile)\r
+\r
+ if Option.WarningAsError == True:\r
+ EdkLogger.SetWarningAsError()\r
+\r
+ if platform.platform().find("Windows") >= 0:\r
+ GlobalData.gIsWindows = True\r
+ else:\r
+ GlobalData.gIsWindows = False\r
+\r
+ EdkLogger.quiet("Build environment: %s" % platform.platform())\r
+ EdkLogger.quiet(time.strftime("Build start time: %H:%M:%S, %b.%d %Y\n", time.localtime()));\r
+ ReturnCode = 0\r
+ MyBuild = None\r
+ BuildError = True\r
+ try:\r
+ if len(Target) == 0:\r
+ Target = "all"\r
+ elif len(Target) >= 2:\r
+ EdkLogger.error("build", OPTION_NOT_SUPPORTED, "More than one targets are not supported.",\r
+ ExtraData="Please select one of: %s" % (' '.join(gSupportedTarget)))\r
+ else:\r
+ Target = Target[0].lower()\r
+\r
+ if Target not in gSupportedTarget:\r
+ EdkLogger.error("build", OPTION_NOT_SUPPORTED, "Not supported target [%s]." % Target,\r
+ ExtraData="Please select one of: %s" % (' '.join(gSupportedTarget)))\r
+\r
+ #\r
+ # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH\r
+ #\r
+ CheckEnvVariable()\r
+ GlobalData.gCommandLineDefines.update(ParseDefines(Option.Macros))\r
+\r
+ Workspace = os.getenv("WORKSPACE")\r
+ #\r
+ # Get files real name in workspace dir\r
+ #\r
+ GlobalData.gAllFiles = Utils.DirCache(Workspace)\r
+\r
+ WorkingDirectory = os.getcwd()\r
+ if not Option.ModuleFile:\r
+ FileList = glob.glob(os.path.normpath(os.path.join(WorkingDirectory, '*.inf')))\r
+ FileNum = len(FileList)\r
+ if FileNum >= 2:\r
+ EdkLogger.error("build", OPTION_NOT_SUPPORTED, "There are %d INF files in %s." % (FileNum, WorkingDirectory),\r
+ ExtraData="Please use '-m <INF_FILE_PATH>' switch to choose one.")\r
+ elif FileNum == 1:\r
+ Option.ModuleFile = NormFile(FileList[0], Workspace)\r
+\r
+ if Option.ModuleFile:\r
+ if os.path.isabs (Option.ModuleFile):\r
+ if os.path.normcase (os.path.normpath(Option.ModuleFile)).find (Workspace) == 0:\r
+ Option.ModuleFile = NormFile(os.path.normpath(Option.ModuleFile), Workspace)\r
+ Option.ModuleFile = PathClass(Option.ModuleFile, Workspace)\r
+ ErrorCode, ErrorInfo = Option.ModuleFile.Validate(".inf", False)\r
+ if ErrorCode != 0:\r
+ EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
+\r
+ if Option.PlatformFile is not None:\r
+ if os.path.isabs (Option.PlatformFile):\r
+ if os.path.normcase (os.path.normpath(Option.PlatformFile)).find (Workspace) == 0:\r
+ Option.PlatformFile = NormFile(os.path.normpath(Option.PlatformFile), Workspace)\r
+ Option.PlatformFile = PathClass(Option.PlatformFile, Workspace)\r
+\r
+ if Option.FdfFile is not None:\r
+ if os.path.isabs (Option.FdfFile):\r
+ if os.path.normcase (os.path.normpath(Option.FdfFile)).find (Workspace) == 0:\r
+ Option.FdfFile = NormFile(os.path.normpath(Option.FdfFile), Workspace)\r
+ Option.FdfFile = PathClass(Option.FdfFile, Workspace)\r
+ ErrorCode, ErrorInfo = Option.FdfFile.Validate(".fdf", False)\r
+ if ErrorCode != 0:\r
+ EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo)\r
+\r
+ if Option.Flag is not None and Option.Flag not in ['-c', '-s']:\r
+ EdkLogger.error("build", OPTION_VALUE_INVALID, "UNI flag must be one of -c or -s")\r
+\r
+ MyBuild = Build(Target, Workspace, Option)\r
+ GlobalData.gCommandLineDefines['ARCH'] = ' '.join(MyBuild.ArchList)\r
+ if not (MyBuild.LaunchPrebuildFlag and os.path.exists(MyBuild.PlatformBuildPath)):\r
+ MyBuild.Launch()\r
+ # Drop temp tables to avoid database locked.\r
+ for TmpTableName in TmpTableDict:\r
+ SqlCommand = """drop table IF EXISTS %s""" % TmpTableName\r
+ TmpTableDict[TmpTableName].execute(SqlCommand)\r
+ #MyBuild.DumpBuildData()\r
+ #\r
+ # All job done, no error found and no exception raised\r
+ #\r
+ BuildError = False\r
+ except FatalError as X:\r
+ if MyBuild is not None:\r
+ # for multi-thread build exits safely\r
+ MyBuild.Relinquish()\r
+ if Option is not None and Option.debug is not None:\r
+ EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
+ ReturnCode = X.args[0]\r
+ except Warning as X:\r
+ # error from Fdf parser\r
+ if MyBuild is not None:\r
+ # for multi-thread build exits safely\r
+ MyBuild.Relinquish()\r
+ if Option is not None and Option.debug is not None:\r
+ EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
+ else:\r
+ EdkLogger.error(X.ToolName, FORMAT_INVALID, File=X.FileName, Line=X.LineNumber, ExtraData=X.Message, RaiseError=False)\r
+ ReturnCode = FORMAT_INVALID\r
+ except KeyboardInterrupt:\r
+ ReturnCode = ABORT_ERROR\r
+ if Option is not None and Option.debug is not None:\r
+ EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
+ except:\r
+ if MyBuild is not None:\r
+ # for multi-thread build exits safely\r
+ MyBuild.Relinquish()\r
+\r
+ # try to get the meta-file from the object causing exception\r
+ Tb = sys.exc_info()[-1]\r
+ MetaFile = GlobalData.gProcessingFile\r
+ while Tb is not None:\r
+ if 'self' in Tb.tb_frame.f_locals and hasattr(Tb.tb_frame.f_locals['self'], 'MetaFile'):\r
+ MetaFile = Tb.tb_frame.f_locals['self'].MetaFile\r
+ Tb = Tb.tb_next\r
+ EdkLogger.error(\r
+ "\nbuild",\r
+ CODE_ERROR,\r
+ "Unknown fatal error when processing [%s]" % MetaFile,\r
+ ExtraData="\n(Please send email to edk2-devel@lists.01.org for help, attaching following call stack trace!)\n",\r
+ RaiseError=False\r
+ )\r
+ EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc())\r
+ ReturnCode = CODE_ERROR\r
+ finally:\r
+ Utils.Progressor.Abort()\r
+ Utils.ClearDuplicatedInf()\r
+\r
+ if ReturnCode == 0:\r
+ try:\r
+ MyBuild.LaunchPostbuild()\r
+ Conclusion = "Done"\r
+ except:\r
+ Conclusion = "Failed"\r
+ elif ReturnCode == ABORT_ERROR:\r
+ Conclusion = "Aborted"\r
+ else:\r
+ Conclusion = "Failed"\r
+ FinishTime = time.time()\r
+ BuildDuration = time.gmtime(int(round(FinishTime - StartTime)))\r
+ BuildDurationStr = ""\r
+ if BuildDuration.tm_yday > 1:\r
+ BuildDurationStr = time.strftime("%H:%M:%S", BuildDuration) + ", %d day(s)" % (BuildDuration.tm_yday - 1)\r
+ else:\r
+ BuildDurationStr = time.strftime("%H:%M:%S", BuildDuration)\r
+ if MyBuild is not None:\r
+ if not BuildError:\r
+ MyBuild.BuildReport.GenerateReport(BuildDurationStr, LogBuildTime(MyBuild.AutoGenTime), LogBuildTime(MyBuild.MakeTime), LogBuildTime(MyBuild.GenFdsTime))\r
+ MyBuild.Db.Close()\r
+ EdkLogger.SetLevel(EdkLogger.QUIET)\r
+ EdkLogger.quiet("\n- %s -" % Conclusion)\r
+ EdkLogger.quiet(time.strftime("Build end time: %H:%M:%S, %b.%d %Y", time.localtime()))\r
+ EdkLogger.quiet("Build total time: %s\n" % BuildDurationStr)\r
+ return ReturnCode\r
+\r
+if __name__ == '__main__':\r
+ r = Main()\r
+ ## 0-127 is a safe return range, and 1 is a standard default error\r
+ if r < 0 or r > 127: r = 1\r
+ sys.exit(r)\r
+\r