| ## @file | |
| # build a platform or a module | |
| # | |
| # Copyright (c) 2014, Hewlett-Packard Development Company, L.P.<BR> | |
| # Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR> | |
| # | |
| # 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 Common.LongFilePathOs as os | |
| import re | |
| import StringIO | |
| import sys | |
| import glob | |
| import time | |
| import platform | |
| import traceback | |
| import encodings.ascii | |
| import itertools | |
| from struct import * | |
| from threading import * | |
| from optparse import OptionParser | |
| from subprocess import * | |
| from Common import Misc as Utils | |
| from Common.LongFilePathSupport import OpenLongFilePath as open | |
| from Common.LongFilePathSupport import LongFilePath | |
| from Common.TargetTxtClassObject import * | |
| from Common.ToolDefClassObject import * | |
| from Common.DataType import * | |
| from Common.BuildVersion import gBUILD_VERSION | |
| from AutoGen.AutoGen import * | |
| from Common.BuildToolError import * | |
| from Workspace.WorkspaceDatabase import * | |
| from Common.MultipleWorkspace import MultipleWorkspace as mws | |
| from BuildReport import BuildReport | |
| from GenPatchPcdTable.GenPatchPcdTable import * | |
| from PatchPcdValue.PatchPcdValue import * | |
| import Common.EdkLogger | |
| import Common.GlobalData as GlobalData | |
| # Version and Copyright | |
| VersionNumber = "0.60" + ' ' + gBUILD_VERSION | |
| __version__ = "%prog Version " + VersionNumber | |
| __copyright__ = "Copyright (c) 2007 - 2016, 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 = "target.txt" | |
| gToolsDefinition = "tools_def.txt" | |
| TemporaryTablePattern = re.compile(r'^_\d+_\d+_[a-fA-F0-9]+$') | |
| TmpTableDict = {} | |
| ## 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 | |
| # set multiple workspace | |
| PackagesPath = os.getenv("PACKAGES_PATH") | |
| mws.setWs(WorkspaceDir, PackagesPath) | |
| if mws.PACKAGES_PATH: | |
| for Path in mws.PACKAGES_PATH: | |
| if not os.path.exists(Path): | |
| EdkLogger.error("build", FILE_NOT_FOUND, "One Path in PACKAGES_PATH doesn't exist", ExtraData="%s" % Path) | |
| elif ' ' in Path: | |
| EdkLogger.error("build", FORMAT_NOT_SUPPORTED, "No space is allowed in PACKAGES_PATH", ExtraData=Path) | |
| # | |
| # Check EFI_SOURCE (Edk build convention). EDK_SOURCE will always point to ECP | |
| # | |
| if "ECP_SOURCE" not in os.environ: | |
| os.environ["ECP_SOURCE"] = mws.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. Edk 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. Edk 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. Edk 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) | |
| # check those variables on single workspace case | |
| if not PackagesPath: | |
| # 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 | |
| GlobalData.gGlobalDefines["WORKSPACE"] = WorkspaceDir | |
| GlobalData.gGlobalDefines["EFI_SOURCE"] = EfiSourceDir | |
| GlobalData.gGlobalDefines["EDK_SOURCE"] = EdkSourceDir | |
| GlobalData.gGlobalDefines["ECP_SOURCE"] = EcpSourceDir | |
| GlobalData.gGlobalDefines["EDK_TOOLS_PATH"] = os.environ["EDK_TOOLS_PATH"] | |
| ## 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(mws.join(Workspace, FilePath)) | |
| Workspace = mws.getWs(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) | |
| # Command is used as the first Argument in following Popen(). | |
| # It could be a string or sequence. We find that if command is a string in following Popen(), | |
| # ubuntu may fail with an error message that the command is not found. | |
| # So here we may need convert command from string to list instance. | |
| if not isinstance(Command, list): | |
| if platform.system() != 'Windows': | |
| Command = Command.split() | |
| 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 | |
| EdkLogger.quiet("(Python %s on %s) " % (platform.python_version(), sys.platform) + traceback.format_exc()) | |
| 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) | |
| # print out the Response file and its content when make failure | |
| RespFile = os.path.join(WorkingDir, 'OUTPUT', 'respfilelist.txt') | |
| if os.path.isfile(RespFile): | |
| f = open(RespFile) | |
| RespContent = f.read() | |
| f.close() | |
| EdkLogger.info(RespContent) | |
| 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 not BuildCommand: | |
| EdkLogger.error("build", OPTION_MISSING, | |
| "No build command found for this module. " | |
| "Please check your setting of %s_%s_%s_MAKE_PATH in Conf/tools_def.txt file." % | |
| (Obj.BuildTarget, Obj.ToolChain, Obj.Arch), | |
| ExtraData=str(Obj)) | |
| ## str() method | |
| # | |
| # It just returns the string representation 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: | |
| if not Dep.BuildObject.IsBinaryModule: | |
| 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 contains the information related to EFI image | |
| # | |
| class PeImageInfo(): | |
| ## Constructor | |
| # | |
| # Constructor will load all required image information. | |
| # | |
| # @param BaseName The full file path of image. | |
| # @param Guid The GUID for image. | |
| # @param Arch Arch of this image. | |
| # @param OutputDir The output directory for image. | |
| # @param DebugDir The debug directory for image. | |
| # @param ImageClass PeImage Information | |
| # | |
| def __init__(self, BaseName, Guid, Arch, OutputDir, DebugDir, ImageClass): | |
| self.BaseName = BaseName | |
| self.Guid = Guid | |
| self.Arch = Arch | |
| self.OutputDir = OutputDir | |
| self.DebugDir = DebugDir | |
| self.Image = ImageClass | |
| self.Image.Size = (self.Image.Size / 0x1000 + 1) * 0x1000 | |
| ## 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 BuildOptions Build options passed from command line | |
| # | |
| def __init__(self, Target, WorkspaceDir, BuildOptions): | |
| self.WorkspaceDir = WorkspaceDir | |
| self.Target = Target | |
| self.PlatformFile = BuildOptions.PlatformFile | |
| self.ModuleFile = BuildOptions.ModuleFile | |
| self.ArchList = BuildOptions.TargetArch | |
| self.ToolChainList = BuildOptions.ToolChain | |
| self.BuildTargetList= BuildOptions.BuildTarget | |
| self.Fdf = BuildOptions.FdfFile | |
| self.FdList = BuildOptions.RomImage | |
| self.FvList = BuildOptions.FvImage | |
| self.CapList = BuildOptions.CapName | |
| self.SilentMode = BuildOptions.SilentMode | |
| self.ThreadNumber = BuildOptions.ThreadNumber | |
| self.SkipAutoGen = BuildOptions.SkipAutoGen | |
| self.Reparse = BuildOptions.Reparse | |
| self.SkuId = BuildOptions.SkuId | |
| self.ConfDirectory = BuildOptions.ConfDirectory | |
| self.SpawnMode = True | |
| self.BuildReport = BuildReport(BuildOptions.ReportFile, BuildOptions.ReportType) | |
| self.TargetTxt = TargetTxtClassObject() | |
| self.ToolDef = ToolDefClassObject() | |
| GlobalData.BuildOptionPcd = BuildOptions.OptionPcd | |
| #Set global flag for build mode | |
| GlobalData.gIgnoreSource = BuildOptions.IgnoreSources | |
| if self.ConfDirectory: | |
| # Get alternate Conf location, if it is absolute, then just use the absolute directory name | |
| ConfDirectoryPath = os.path.normpath(self.ConfDirectory) | |
| if not os.path.isabs(ConfDirectoryPath): | |
| # Since alternate directory name is not absolute, the alternate directory is located within the WORKSPACE | |
| # This also handles someone specifying the Conf directory in the workspace. Using --conf=Conf | |
| ConfDirectoryPath = mws.join(self.WorkspaceDir, ConfDirectoryPath) | |
| else: | |
| if "CONF_PATH" in os.environ: | |
| ConfDirectoryPath = os.path.normcase(os.path.normpath(os.environ["CONF_PATH"])) | |
| else: | |
| # Get standard WORKSPACE/Conf use the absolute path to the WORKSPACE/Conf | |
| ConfDirectoryPath = mws.join(self.WorkspaceDir, 'Conf') | |
| GlobalData.gConfDirectory = ConfDirectoryPath | |
| GlobalData.gDatabasePath = os.path.normpath(os.path.join(ConfDirectoryPath, GlobalData.gDatabasePath)) | |
| if BuildOptions.DisableCache: | |
| self.Db = WorkspaceDatabase(":memory:") | |
| else: | |
| self.Db = WorkspaceDatabase(GlobalData.gDatabasePath, self.Reparse) | |
| self.BuildDatabase = self.Db.BuildObject | |
| self.Platform = None | |
| self.ToolChainFamily = None | |
| self.LoadFixAddress = 0 | |
| self.UniFlag = BuildOptions.Flag | |
| self.BuildModules = [] | |
| self.Db_Flag = False | |
| self.LaunchPrebuildFlag = False | |
| self.PrebuildScript = '' | |
| self.PostbuildScript = '' | |
| self.PlatformBuildPath = os.path.join(GlobalData.gConfDirectory,'.cache', '.PlatformBuild') | |
| if BuildOptions.CommandLength: | |
| GlobalData.gCommandMaxLength = BuildOptions.CommandLength | |
| # print dot character during doing some time-consuming work | |
| self.Progress = Utils.Progressor() | |
| # print current build environment and configuration | |
| EdkLogger.quiet("%-16s = %s" % ("WORKSPACE", os.environ["WORKSPACE"])) | |
| if "PACKAGES_PATH" in os.environ: | |
| # WORKSPACE env has been converted before. Print the same path style with WORKSPACE env. | |
| EdkLogger.quiet("%-16s = %s" % ("PACKAGES_PATH", os.path.normcase(os.path.normpath(os.environ["PACKAGES_PATH"])))) | |
| EdkLogger.quiet("%-16s = %s" % ("ECP_SOURCE", os.environ["ECP_SOURCE"])) | |
| EdkLogger.quiet("%-16s = %s" % ("EDK_SOURCE", os.environ["EDK_SOURCE"])) | |
| EdkLogger.quiet("%-16s = %s" % ("EFI_SOURCE", os.environ["EFI_SOURCE"])) | |
| EdkLogger.quiet("%-16s = %s" % ("EDK_TOOLS_PATH", os.environ["EDK_TOOLS_PATH"])) | |
| if "EDK_TOOLS_BIN" in os.environ: | |
| # Print the same path style with WORKSPACE env. | |
| EdkLogger.quiet("%-16s = %s" % ("EDK_TOOLS_BIN", os.path.normcase(os.path.normpath(os.environ["EDK_TOOLS_BIN"])))) | |
| EdkLogger.quiet("%-16s = %s" % ("CONF_PATH", GlobalData.gConfDirectory)) | |
| self.InitPreBuild() | |
| self.InitPostBuild() | |
| if self.PrebuildScript: | |
| EdkLogger.quiet("%-16s = %s" % ("PREBUILD", self.PrebuildScript)) | |
| if self.PostbuildScript: | |
| EdkLogger.quiet("%-16s = %s" % ("POSTBUILD", self.PostbuildScript)) | |
| if self.PrebuildScript: | |
| self.LaunchPrebuild() | |
| if not (self.LaunchPrebuildFlag and os.path.exists(self.PlatformBuildPath)): | |
| self.InitBuild() | |
| EdkLogger.info("") | |
| os.chdir(self.WorkspaceDir) | |
| ## 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(GlobalData.gConfDirectory, 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(mws.join(self.WorkspaceDir, 'Conf', 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 not self.ArchList: | |
| self.ArchList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET_ARCH] | |
| self.ArchList = tuple(self.ArchList) | |
| # if no build target given in command line, get it from target.txt | |
| if not self.BuildTargetList: | |
| self.BuildTargetList = self.TargetTxt.TargetTxtDictionary[DataType.TAB_TAT_DEFINES_TARGET] | |
| # if no tool chain given in command line, get it from target.txt | |
| if not self.ToolChainList: | |
| 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 | |
| ToolChainFamily = [] | |
| ToolDefinition = self.ToolDef.ToolsDefTxtDatabase | |
| for Tool in self.ToolChainList: | |
| if TAB_TOD_DEFINES_FAMILY not in ToolDefinition or Tool not in ToolDefinition[TAB_TOD_DEFINES_FAMILY] \ | |
| or not ToolDefinition[TAB_TOD_DEFINES_FAMILY][Tool]: | |
| EdkLogger.warn("No tool chain family found in configuration for %s. Default to MSFT." % Tool) | |
| ToolChainFamily.append("MSFT") | |
| else: | |
| ToolChainFamily.append(ToolDefinition[TAB_TOD_DEFINES_FAMILY][Tool]) | |
| self.ToolChainFamily = ToolChainFamily | |
| 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) | |
| ## 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): | |
| # parse target.txt, tools_def.txt, and platform file | |
| self.LoadConfiguration() | |
| # Allow case-insensitive for those from command line or configuration file | |
| ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False) | |
| if ErrorCode != 0: | |
| EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo) | |
| # create metafile database | |
| if not self.Db_Flag: | |
| self.Db.InitDatabase() | |
| def InitPreBuild(self): | |
| self.LoadConfiguration() | |
| ErrorCode, ErrorInfo = self.PlatformFile.Validate(".dsc", False) | |
| if ErrorCode != 0: | |
| EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo) | |
| if self.BuildTargetList: | |
| GlobalData.gGlobalDefines['TARGET'] = self.BuildTargetList[0] | |
| if self.ArchList: | |
| GlobalData.gGlobalDefines['ARCH'] = self.ArchList[0] | |
| if self.ToolChainList: | |
| GlobalData.gGlobalDefines['TOOLCHAIN'] = self.ToolChainList[0] | |
| GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = self.ToolChainList[0] | |
| if self.ToolChainFamily: | |
| GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[0] | |
| if 'PREBUILD' in GlobalData.gCommandLineDefines.keys(): | |
| self.Prebuild = GlobalData.gCommandLineDefines.get('PREBUILD') | |
| else: | |
| self.Db.InitDatabase() | |
| self.Db_Flag = True | |
| Platform = self.Db._MapPlatform(str(self.PlatformFile)) | |
| self.Prebuild = str(Platform.Prebuild) | |
| if self.Prebuild: | |
| PrebuildList = self.Prebuild.split() | |
| if not os.path.isabs(PrebuildList[0]): | |
| PrebuildList[0] = mws.join(self.WorkspaceDir, PrebuildList[0]) | |
| if os.path.isfile(PrebuildList[0]): | |
| self.PrebuildScript = PrebuildList[0] | |
| self.Prebuild = ' '.join(PrebuildList) | |
| self.Prebuild += self.PassCommandOption(self.BuildTargetList, self.ArchList, self.ToolChainList) | |
| #self.LaunchPrebuild() | |
| else: | |
| EdkLogger.error("Prebuild", PREBUILD_ERROR, "the prebuild script %s is not exist.\n If you'd like to disable the Prebuild process, please use the format: -D PREBUILD=\"\" " %(PrebuildList[0])) | |
| def InitPostBuild(self): | |
| if 'POSTBUILD' in GlobalData.gCommandLineDefines.keys(): | |
| self.Postbuild = GlobalData.gCommandLineDefines.get('POSTBUILD') | |
| else: | |
| Platform = self.Db._MapPlatform(str(self.PlatformFile)) | |
| self.Postbuild = str(Platform.Postbuild) | |
| if self.Postbuild: | |
| PostbuildList = self.Postbuild.split() | |
| if not os.path.isabs(PostbuildList[0]): | |
| PostbuildList[0] = mws.join(self.WorkspaceDir, PostbuildList[0]) | |
| if os.path.isfile(PostbuildList[0]): | |
| self.PostbuildScript = PostbuildList[0] | |
| self.Postbuild = ' '.join(PostbuildList) | |
| self.Postbuild += self.PassCommandOption(self.BuildTargetList, self.ArchList, self.ToolChainList) | |
| #self.LanuchPostbuild() | |
| else: | |
| EdkLogger.error("Postbuild", POSTBUILD_ERROR, "the postbuild script %s is not exist.\n If you'd like to disable the Postbuild process, please use the format: -D POSTBUILD=\"\" " %(PostbuildList[0])) | |
| def PassCommandOption(self, BuildTarget, TargetArch, ToolChain): | |
| BuildStr = '' | |
| if GlobalData.gCommand and isinstance(GlobalData.gCommand, list): | |
| BuildStr += ' ' + ' '.join(GlobalData.gCommand) | |
| TargetFlag = False | |
| ArchFlag = False | |
| ToolChainFlag = False | |
| if GlobalData.gOptions and not GlobalData.gOptions.BuildTarget: | |
| TargetFlag = True | |
| if GlobalData.gOptions and not GlobalData.gOptions.TargetArch: | |
| ArchFlag = True | |
| if GlobalData.gOptions and not GlobalData.gOptions.ToolChain: | |
| ToolChainFlag = True | |
| if TargetFlag and BuildTarget: | |
| if isinstance(BuildTarget, list) or isinstance(BuildTarget, tuple): | |
| BuildStr += ' -b ' + ' -b '.join(BuildTarget) | |
| elif isinstance(BuildTarget, str): | |
| BuildStr += ' -b ' + BuildTarget | |
| if ArchFlag and TargetArch: | |
| if isinstance(TargetArch, list) or isinstance(TargetArch, tuple): | |
| BuildStr += ' -a ' + ' -a '.join(TargetArch) | |
| elif isinstance(TargetArch, str): | |
| BuildStr += ' -a ' + TargetArch | |
| if ToolChainFlag and ToolChain: | |
| if isinstance(ToolChain, list) or isinstance(ToolChain, tuple): | |
| BuildStr += ' -t ' + ' -t '.join(ToolChain) | |
| elif isinstance(ToolChain, str): | |
| BuildStr += ' -t ' + ToolChain | |
| return BuildStr | |
| def LaunchPrebuild(self): | |
| if self.Prebuild: | |
| EdkLogger.info("\n- Prebuild Start -\n") | |
| self.LaunchPrebuildFlag = True | |
| PrebuildEnvFile = os.path.join(GlobalData.gConfDirectory,'.cache','.PrebuildEnv') | |
| if os.path.isfile(PrebuildEnvFile): | |
| os.remove(PrebuildEnvFile) | |
| if os.path.isfile(self.PlatformBuildPath): | |
| os.remove(self.PlatformBuildPath) | |
| if sys.platform == "win32": | |
| args = ' && '.join((self.Prebuild, 'set > ' + PrebuildEnvFile)) | |
| Process = Popen(args, stdout=PIPE, stderr=PIPE) | |
| else: | |
| args = ' && '.join((self.Prebuild, 'env > ' + PrebuildEnvFile)) | |
| Process = Popen(args, stdout=PIPE, stderr=PIPE, shell=True, executable="/bin/bash") | |
| # launch two threads to read the STDOUT and STDERR | |
| EndOfProcedure = Event() | |
| EndOfProcedure.clear() | |
| if Process.stdout: | |
| StdOutThread = Thread(target=ReadMessage, args=(Process.stdout, EdkLogger.info, EndOfProcedure)) | |
| StdOutThread.setName("STDOUT-Redirector") | |
| StdOutThread.setDaemon(False) | |
| StdOutThread.start() | |
| if Process.stderr: | |
| StdErrThread = Thread(target=ReadMessage, args=(Process.stderr, EdkLogger.quiet, EndOfProcedure)) | |
| StdErrThread.setName("STDERR-Redirector") | |
| StdErrThread.setDaemon(False) | |
| StdErrThread.start() | |
| # waiting for program exit | |
| Process.wait() | |
| if Process.stdout: | |
| StdOutThread.join() | |
| if Process.stderr: | |
| StdErrThread.join() | |
| if Process.returncode != 0 : | |
| EdkLogger.error("Prebuild", PREBUILD_ERROR, 'Prebuild process is not success!') | |
| if os.path.exists(PrebuildEnvFile): | |
| f = open(PrebuildEnvFile) | |
| envs = f.readlines() | |
| f.close() | |
| envs = itertools.imap(lambda l: l.split('=',1), envs) | |
| envs = itertools.ifilter(lambda l: len(l) == 2, envs) | |
| envs = itertools.imap(lambda l: [i.strip() for i in l], envs) | |
| os.environ.update(dict(envs)) | |
| EdkLogger.info("\n- Prebuild Done -\n") | |
| def LanuchPostbuild(self): | |
| if self.Postbuild: | |
| EdkLogger.info("\n- Postbuild Start -\n") | |
| if sys.platform == "win32": | |
| Process = Popen(self.Postbuild, stdout=PIPE, stderr=PIPE) | |
| else: | |
| Process = Popen(self.Postbuild, stdout=PIPE, stderr=PIPE, shell=True, executable="/bin/bash") | |
| # launch two threads to read the STDOUT and STDERR | |
| EndOfProcedure = Event() | |
| EndOfProcedure.clear() | |
| if Process.stdout: | |
| StdOutThread = Thread(target=ReadMessage, args=(Process.stdout, EdkLogger.info, EndOfProcedure)) | |
| StdOutThread.setName("STDOUT-Redirector") | |
| StdOutThread.setDaemon(False) | |
| StdOutThread.start() | |
| if Process.stderr: | |
| StdErrThread = Thread(target=ReadMessage, args=(Process.stderr, EdkLogger.quiet, EndOfProcedure)) | |
| StdErrThread.setName("STDERR-Redirector") | |
| StdErrThread.setDaemon(False) | |
| StdErrThread.start() | |
| # waiting for program exit | |
| Process.wait() | |
| if Process.stdout: | |
| StdOutThread.join() | |
| if Process.stderr: | |
| StdErrThread.join() | |
| if Process.returncode != 0 : | |
| EdkLogger.error("Postbuild", POSTBUILD_ERROR, 'Postbuild process is not success!') | |
| EdkLogger.info("\n- Postbuild Done -\n") | |
| ## Build a module or platform | |
| # | |
| # Create autogen code and makefile 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 _BuildPa(self, Target, AutoGenObject, CreateDepsCodeFile=True, CreateDepsMakeFile=True, BuildModule=False): | |
| 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, | |
| "No build command found for this module. " | |
| "Please check your setting of %s_%s_%s_MAKE_PATH in Conf/tools_def.txt file." % | |
| (AutoGenObject.BuildTarget, AutoGenObject.ToolChain, AutoGenObject.Arch), | |
| ExtraData=str(AutoGenObject)) | |
| makefile = GenMake.BuildFile(AutoGenObject)._FILE_NAME_[GenMake.gMakeType] | |
| # run | |
| if Target == 'run': | |
| RunDir = os.path.normpath(os.path.join(AutoGenObject.BuildDir, GlobalData.gGlobalDefines['ARCH'])) | |
| Command = '.\SecMain' | |
| os.chdir(RunDir) | |
| LaunchCommand(Command, RunDir) | |
| return True | |
| # build modules | |
| if BuildModule: | |
| BuildCommand = BuildCommand + [Target] | |
| LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir) | |
| self.CreateAsBuiltInf() | |
| return True | |
| # build library | |
| if Target == 'libraries': | |
| for Lib in AutoGenObject.LibraryBuildDirectoryList: | |
| NewBuildCommand = BuildCommand + ['-f', os.path.normpath(os.path.join(Lib, makefile)), 'pbuild'] | |
| LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir) | |
| return True | |
| # build module | |
| if Target == 'modules': | |
| for Lib in AutoGenObject.LibraryBuildDirectoryList: | |
| NewBuildCommand = BuildCommand + ['-f', os.path.normpath(os.path.join(Lib, makefile)), 'pbuild'] | |
| LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir) | |
| for Mod in AutoGenObject.ModuleBuildDirectoryList: | |
| NewBuildCommand = BuildCommand + ['-f', os.path.normpath(os.path.join(Mod, makefile)), 'pbuild'] | |
| LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir) | |
| self.CreateAsBuiltInf() | |
| return True | |
| # cleanlib | |
| if Target == 'cleanlib': | |
| for Lib in AutoGenObject.LibraryBuildDirectoryList: | |
| LibMakefile = os.path.normpath(os.path.join(Lib, makefile)) | |
| if os.path.exists(LibMakefile): | |
| NewBuildCommand = BuildCommand + ['-f', LibMakefile, 'cleanall'] | |
| LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir) | |
| return True | |
| # clean | |
| if Target == 'clean': | |
| for Mod in AutoGenObject.ModuleBuildDirectoryList: | |
| ModMakefile = os.path.normpath(os.path.join(Mod, makefile)) | |
| if os.path.exists(ModMakefile): | |
| NewBuildCommand = BuildCommand + ['-f', ModMakefile, 'cleanall'] | |
| LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir) | |
| for Lib in AutoGenObject.LibraryBuildDirectoryList: | |
| LibMakefile = os.path.normpath(os.path.join(Lib, makefile)) | |
| if os.path.exists(LibMakefile): | |
| NewBuildCommand = BuildCommand + ['-f', LibMakefile, 'cleanall'] | |
| LaunchCommand(NewBuildCommand, AutoGenObject.MakeFileDir) | |
| return True | |
| # cleanall | |
| 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 a module or platform | |
| # | |
| # Create autogen code and makefile 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, BuildModule=False): | |
| 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) | |
| #AutoGenObject.CreateAsBuiltInf() | |
| 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, | |
| "No build command found for this module. " | |
| "Please check your setting of %s_%s_%s_MAKE_PATH in Conf/tools_def.txt file." % | |
| (AutoGenObject.BuildTarget, AutoGenObject.ToolChain, AutoGenObject.Arch), | |
| ExtraData=str(AutoGenObject)) | |
| # build modules | |
| if BuildModule: | |
| if Target != 'fds': | |
| BuildCommand = BuildCommand + [Target] | |
| LaunchCommand(BuildCommand, AutoGenObject.MakeFileDir) | |
| self.CreateAsBuiltInf() | |
| return True | |
| # genfds | |
| if Target == 'fds': | |
| LaunchCommand(AutoGenObject.GenFdsCommand, AutoGenObject.MakeFileDir) | |
| return True | |
| # run | |
| if Target == 'run': | |
| RunDir = os.path.normpath(os.path.join(AutoGenObject.BuildDir, GlobalData.gGlobalDefines['ARCH'])) | |
| Command = '.\SecMain' | |
| os.chdir(RunDir) | |
| LaunchCommand(Command, RunDir) | |
| return True | |
| # build library | |
| if Target == 'libraries': | |
| pass | |
| # not build modules | |
| # cleanall | |
| 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 | |
| ## Rebase module image and Get function address for the input module list. | |
| # | |
| def _RebaseModule (self, MapBuffer, BaseAddress, ModuleList, AddrIsOffset = True, ModeIsSmm = False): | |
| if ModeIsSmm: | |
| AddrIsOffset = False | |
| InfFileNameList = ModuleList.keys() | |
| #InfFileNameList.sort() | |
| for InfFile in InfFileNameList: | |
| sys.stdout.write (".") | |
| sys.stdout.flush() | |
| ModuleInfo = ModuleList[InfFile] | |
| ModuleName = ModuleInfo.BaseName | |
| ModuleOutputImage = ModuleInfo.Image.FileName | |
| ModuleDebugImage = os.path.join(ModuleInfo.DebugDir, ModuleInfo.BaseName + '.efi') | |
| ## for SMM module in SMRAM, the SMRAM will be allocated from base to top. | |
| if not ModeIsSmm: | |
| BaseAddress = BaseAddress - ModuleInfo.Image.Size | |
| # | |
| # Update Image to new BaseAddress by GenFw tool | |
| # | |
| LaunchCommand(["GenFw", "--rebase", str(BaseAddress), "-r", ModuleOutputImage], ModuleInfo.OutputDir) | |
| LaunchCommand(["GenFw", "--rebase", str(BaseAddress), "-r", ModuleDebugImage], ModuleInfo.DebugDir) | |
| else: | |
| # | |
| # Set new address to the section header only for SMM driver. | |
| # | |
| LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleOutputImage], ModuleInfo.OutputDir) | |
| LaunchCommand(["GenFw", "--address", str(BaseAddress), "-r", ModuleDebugImage], ModuleInfo.DebugDir) | |
| # | |
| # Collect funtion address from Map file | |
| # | |
| ImageMapTable = ModuleOutputImage.replace('.efi', '.map') | |
| FunctionList = [] | |
| if os.path.exists(ImageMapTable): | |
| OrigImageBaseAddress = 0 | |
| ImageMap = open(ImageMapTable, 'r') | |
| for LinStr in ImageMap: | |
| if len (LinStr.strip()) == 0: | |
| continue | |
| # | |
| # Get the preferred address set on link time. | |
| # | |
| if LinStr.find ('Preferred load address is') != -1: | |
| StrList = LinStr.split() | |
| OrigImageBaseAddress = int (StrList[len(StrList) - 1], 16) | |
| StrList = LinStr.split() | |
| if len (StrList) > 4: | |
| if StrList[3] == 'f' or StrList[3] == 'F': | |
| Name = StrList[1] | |
| RelativeAddress = int (StrList[2], 16) - OrigImageBaseAddress | |
| FunctionList.append ((Name, RelativeAddress)) | |
| if ModuleInfo.Arch == 'IPF' and Name.endswith('_ModuleEntryPoint'): | |
| # | |
| # Get the real entry point address for IPF image. | |
| # | |
| ModuleInfo.Image.EntryPoint = RelativeAddress | |
| ImageMap.close() | |
| # | |
| # Add general information. | |
| # | |
| if ModeIsSmm: | |
| MapBuffer.write('\n\n%s (Fixed SMRAM Offset, BaseAddress=0x%010X, EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint)) | |
| elif AddrIsOffset: | |
| MapBuffer.write('\n\n%s (Fixed Memory Offset, BaseAddress=-0x%010X, EntryPoint=-0x%010X)\n' % (ModuleName, 0 - BaseAddress, 0 - (BaseAddress + ModuleInfo.Image.EntryPoint))) | |
| else: | |
| MapBuffer.write('\n\n%s (Fixed Memory Address, BaseAddress=0x%010X, EntryPoint=0x%010X)\n' % (ModuleName, BaseAddress, BaseAddress + ModuleInfo.Image.EntryPoint)) | |
| # | |
| # Add guid and general seciton section. | |
| # | |
| TextSectionAddress = 0 | |
| DataSectionAddress = 0 | |
| for SectionHeader in ModuleInfo.Image.SectionHeaderList: | |
| if SectionHeader[0] == '.text': | |
| TextSectionAddress = SectionHeader[1] | |
| elif SectionHeader[0] in ['.data', '.sdata']: | |
| DataSectionAddress = SectionHeader[1] | |
| if AddrIsOffset: | |
| MapBuffer.write('(GUID=%s, .textbaseaddress=-0x%010X, .databaseaddress=-0x%010X)\n' % (ModuleInfo.Guid, 0 - (BaseAddress + TextSectionAddress), 0 - (BaseAddress + DataSectionAddress))) | |
| else: | |
| MapBuffer.write('(GUID=%s, .textbaseaddress=0x%010X, .databaseaddress=0x%010X)\n' % (ModuleInfo.Guid, BaseAddress + TextSectionAddress, BaseAddress + DataSectionAddress)) | |
| # | |
| # Add debug image full path. | |
| # | |
| MapBuffer.write('(IMAGE=%s)\n\n' % (ModuleDebugImage)) | |
| # | |
| # Add funtion address | |
| # | |
| for Function in FunctionList: | |
| if AddrIsOffset: | |
| MapBuffer.write(' -0x%010X %s\n' % (0 - (BaseAddress + Function[1]), Function[0])) | |
| else: | |
| MapBuffer.write(' 0x%010X %s\n' % (BaseAddress + Function[1], Function[0])) | |
| ImageMap.close() | |
| # | |
| # for SMM module in SMRAM, the SMRAM will be allocated from base to top. | |
| # | |
| if ModeIsSmm: | |
| BaseAddress = BaseAddress + ModuleInfo.Image.Size | |
| ## Collect MAP information of all FVs | |
| # | |
| def _CollectFvMapBuffer (self, MapBuffer, Wa, ModuleList): | |
| if self.Fdf: | |
| # First get the XIP base address for FV map file. | |
| GuidPattern = re.compile("[-a-fA-F0-9]+") | |
| GuidName = re.compile("\(GUID=[-a-fA-F0-9]+") | |
| for FvName in Wa.FdfProfile.FvDict.keys(): | |
| FvMapBuffer = os.path.join(Wa.FvDir, FvName + '.Fv.map') | |
| if not os.path.exists(FvMapBuffer): | |
| continue | |
| FvMap = open(FvMapBuffer, 'r') | |
| #skip FV size information | |
| FvMap.readline() | |
| FvMap.readline() | |
| FvMap.readline() | |
| FvMap.readline() | |
| for Line in FvMap: | |
| MatchGuid = GuidPattern.match(Line) | |
| if MatchGuid != None: | |
| # | |
| # Replace GUID with module name | |
| # | |
| GuidString = MatchGuid.group() | |
| if GuidString.upper() in ModuleList: | |
| Line = Line.replace(GuidString, ModuleList[GuidString.upper()].Name) | |
| MapBuffer.write('%s' % (Line)) | |
| # | |
| # Add the debug image full path. | |
| # | |
| MatchGuid = GuidName.match(Line) | |
| if MatchGuid != None: | |
| GuidString = MatchGuid.group().split("=")[1] | |
| if GuidString.upper() in ModuleList: | |
| MapBuffer.write('(IMAGE=%s)\n' % (os.path.join(ModuleList[GuidString.upper()].DebugDir, ModuleList[GuidString.upper()].Name + '.efi'))) | |
| FvMap.close() | |
| ## Collect MAP information of all modules | |
| # | |
| def _CollectModuleMapBuffer (self, MapBuffer, ModuleList): | |
| sys.stdout.write ("Generate Load Module At Fix Address Map") | |
| sys.stdout.flush() | |
| PatchEfiImageList = [] | |
| PeiModuleList = {} | |
| BtModuleList = {} | |
| RtModuleList = {} | |
| SmmModuleList = {} | |
| PeiSize = 0 | |
| BtSize = 0 | |
| RtSize = 0 | |
| # reserve 4K size in SMRAM to make SMM module address not from 0. | |
| SmmSize = 0x1000 | |
| IsIpfPlatform = False | |
| if 'IPF' in self.ArchList: | |
| IsIpfPlatform = True | |
| for ModuleGuid in ModuleList: | |
| Module = ModuleList[ModuleGuid] | |
| GlobalData.gProcessingFile = "%s [%s, %s, %s]" % (Module.MetaFile, Module.Arch, Module.ToolChain, Module.BuildTarget) | |
| OutputImageFile = '' | |
| for ResultFile in Module.CodaTargetList: | |
| if str(ResultFile.Target).endswith('.efi'): | |
| # | |
| # module list for PEI, DXE, RUNTIME and SMM | |
| # | |
| OutputImageFile = os.path.join(Module.OutputDir, Module.Name + '.efi') | |
| ImageClass = PeImageClass (OutputImageFile) | |
| if not ImageClass.IsValid: | |
| EdkLogger.error("build", FILE_PARSE_FAILURE, ExtraData=ImageClass.ErrorInfo) | |
| ImageInfo = PeImageInfo(Module.Name, Module.Guid, Module.Arch, Module.OutputDir, Module.DebugDir, ImageClass) | |
| if Module.ModuleType in ['PEI_CORE', 'PEIM', 'COMBINED_PEIM_DRIVER', 'PIC_PEIM', 'RELOCATABLE_PEIM', 'DXE_CORE']: | |
| PeiModuleList[Module.MetaFile] = ImageInfo | |
| PeiSize += ImageInfo.Image.Size | |
| elif Module.ModuleType in ['BS_DRIVER', 'DXE_DRIVER', 'UEFI_DRIVER']: | |
| BtModuleList[Module.MetaFile] = ImageInfo | |
| BtSize += ImageInfo.Image.Size | |
| elif Module.ModuleType in ['DXE_RUNTIME_DRIVER', 'RT_DRIVER', 'DXE_SAL_DRIVER', 'SAL_RT_DRIVER']: | |
| RtModuleList[Module.MetaFile] = ImageInfo | |
| #IPF runtime driver needs to be at 2 page alignment. | |
| if IsIpfPlatform and ImageInfo.Image.Size % 0x2000 != 0: | |
| ImageInfo.Image.Size = (ImageInfo.Image.Size / 0x2000 + 1) * 0x2000 | |
| RtSize += ImageInfo.Image.Size | |
| elif Module.ModuleType in ['SMM_CORE', 'DXE_SMM_DRIVER']: | |
| SmmModuleList[Module.MetaFile] = ImageInfo | |
| SmmSize += ImageInfo.Image.Size | |
| if Module.ModuleType == 'DXE_SMM_DRIVER': | |
| PiSpecVersion = '0x00000000' | |
| if 'PI_SPECIFICATION_VERSION' in Module.Module.Specification: | |
| PiSpecVersion = Module.Module.Specification['PI_SPECIFICATION_VERSION'] | |
| # for PI specification < PI1.1, DXE_SMM_DRIVER also runs as BOOT time driver. | |
| if int(PiSpecVersion, 16) < 0x0001000A: | |
| BtModuleList[Module.MetaFile] = ImageInfo | |
| BtSize += ImageInfo.Image.Size | |
| break | |
| # | |
| # EFI image is final target. | |
| # Check EFI image contains patchable FixAddress related PCDs. | |
| # | |
| if OutputImageFile != '': | |
| ModuleIsPatch = False | |
| for Pcd in Module.ModulePcdList: | |
| if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_LIST: | |
| ModuleIsPatch = True | |
| break | |
| if not ModuleIsPatch: | |
| for Pcd in Module.LibraryPcdList: | |
| if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE and Pcd.TokenCName in TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_LIST: | |
| ModuleIsPatch = True | |
| break | |
| if not ModuleIsPatch: | |
| continue | |
| # | |
| # Module includes the patchable load fix address PCDs. | |
| # It will be fixed up later. | |
| # | |
| PatchEfiImageList.append (OutputImageFile) | |
| # | |
| # Get Top Memory address | |
| # | |
| ReservedRuntimeMemorySize = 0 | |
| TopMemoryAddress = 0 | |
| if self.LoadFixAddress == 0xFFFFFFFFFFFFFFFF: | |
| TopMemoryAddress = 0 | |
| else: | |
| TopMemoryAddress = self.LoadFixAddress | |
| if TopMemoryAddress < RtSize + BtSize + PeiSize: | |
| EdkLogger.error("build", PARAMETER_INVALID, "FIX_LOAD_TOP_MEMORY_ADDRESS is too low to load driver") | |
| # Make IPF runtime driver at 2 page alignment. | |
| if IsIpfPlatform: | |
| ReservedRuntimeMemorySize = TopMemoryAddress % 0x2000 | |
| RtSize = RtSize + ReservedRuntimeMemorySize | |
| # | |
| # Patch FixAddress related PCDs into EFI image | |
| # | |
| for EfiImage in PatchEfiImageList: | |
| EfiImageMap = EfiImage.replace('.efi', '.map') | |
| if not os.path.exists(EfiImageMap): | |
| continue | |
| # | |
| # Get PCD offset in EFI image by GenPatchPcdTable function | |
| # | |
| PcdTable = parsePcdInfoFromMapFile(EfiImageMap, EfiImage) | |
| # | |
| # Patch real PCD value by PatchPcdValue tool | |
| # | |
| for PcdInfo in PcdTable: | |
| ReturnValue = 0 | |
| if PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE: | |
| ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_PEI_PAGE_SIZE_DATA_TYPE, str (PeiSize / 0x1000)) | |
| elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE: | |
| ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_DXE_PAGE_SIZE_DATA_TYPE, str (BtSize / 0x1000)) | |
| elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE: | |
| ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_RUNTIME_PAGE_SIZE_DATA_TYPE, str (RtSize / 0x1000)) | |
| elif PcdInfo[0] == TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE and len (SmmModuleList) > 0: | |
| ReturnValue, ErrorInfo = PatchBinaryFile (EfiImage, PcdInfo[1], TAB_PCDS_PATCHABLE_LOAD_FIX_ADDRESS_SMM_PAGE_SIZE_DATA_TYPE, str (SmmSize / 0x1000)) | |
| if ReturnValue != 0: | |
| EdkLogger.error("build", PARAMETER_INVALID, "Patch PCD value failed", ExtraData=ErrorInfo) | |
| MapBuffer.write('PEI_CODE_PAGE_NUMBER = 0x%x\n' % (PeiSize / 0x1000)) | |
| MapBuffer.write('BOOT_CODE_PAGE_NUMBER = 0x%x\n' % (BtSize / 0x1000)) | |
| MapBuffer.write('RUNTIME_CODE_PAGE_NUMBER = 0x%x\n' % (RtSize / 0x1000)) | |
| if len (SmmModuleList) > 0: | |
| MapBuffer.write('SMM_CODE_PAGE_NUMBER = 0x%x\n' % (SmmSize / 0x1000)) | |
| PeiBaseAddr = TopMemoryAddress - RtSize - BtSize | |
| BtBaseAddr = TopMemoryAddress - RtSize | |
| RtBaseAddr = TopMemoryAddress - ReservedRuntimeMemorySize | |
| self._RebaseModule (MapBuffer, PeiBaseAddr, PeiModuleList, TopMemoryAddress == 0) | |
| self._RebaseModule (MapBuffer, BtBaseAddr, BtModuleList, TopMemoryAddress == 0) | |
| self._RebaseModule (MapBuffer, RtBaseAddr, RtModuleList, TopMemoryAddress == 0) | |
| self._RebaseModule (MapBuffer, 0x1000, SmmModuleList, AddrIsOffset=False, ModeIsSmm=True) | |
| MapBuffer.write('\n\n') | |
| sys.stdout.write ("\n") | |
| sys.stdout.flush() | |
| ## Save platform Map file | |
| # | |
| def _SaveMapFile (self, MapBuffer, Wa): | |
| # | |
| # Map file path is got. | |
| # | |
| MapFilePath = os.path.join(Wa.BuildDir, Wa.Name + '.map') | |
| # | |
| # Save address map into MAP file. | |
| # | |
| SaveFileOnChange(MapFilePath, MapBuffer.getvalue(), False) | |
| MapBuffer.close() | |
| if self.LoadFixAddress != 0: | |
| sys.stdout.write ("\nLoad Module At Fix Address Map file can be found at %s\n" % (MapFilePath)) | |
| sys.stdout.flush() | |
| ## Build active platform for different build targets and different tool chains | |
| # | |
| def _BuildPlatform(self): | |
| SaveFileOnChange(self.PlatformBuildPath, '# DO NOT EDIT \n# FILE auto-generated\n', False) | |
| for BuildTarget in self.BuildTargetList: | |
| GlobalData.gGlobalDefines['TARGET'] = BuildTarget | |
| index = 0 | |
| for ToolChain in self.ToolChainList: | |
| GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain | |
| GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain | |
| GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index] | |
| index += 1 | |
| Wa = WorkspaceAutoGen( | |
| self.WorkspaceDir, | |
| self.PlatformFile, | |
| BuildTarget, | |
| ToolChain, | |
| self.ArchList, | |
| self.BuildDatabase, | |
| self.TargetTxt, | |
| self.ToolDef, | |
| self.Fdf, | |
| self.FdList, | |
| self.FvList, | |
| self.CapList, | |
| self.SkuId, | |
| self.UniFlag, | |
| self.Progress | |
| ) | |
| self.Fdf = Wa.FdfFile | |
| self.LoadFixAddress = Wa.Platform.LoadFixAddress | |
| self.BuildReport.AddPlatformReport(Wa) | |
| self.Progress.Stop("done!") | |
| for Arch in Wa.ArchList: | |
| GlobalData.gGlobalDefines['ARCH'] = Arch | |
| Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch) | |
| 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 | |
| self.BuildModules.append(Ma) | |
| self._BuildPa(self.Target, Pa) | |
| # Create MAP file when Load Fix Address is enabled. | |
| if self.Target in ["", "all", "fds"]: | |
| for Arch in Wa.ArchList: | |
| GlobalData.gGlobalDefines['ARCH'] = Arch | |
| # | |
| # Check whether the set fix address is above 4G for 32bit image. | |
| # | |
| if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000: | |
| 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") | |
| # | |
| # Get Module List | |
| # | |
| ModuleList = {} | |
| for Pa in Wa.AutoGenObjectList: | |
| for Ma in Pa.ModuleAutoGenList: | |
| if Ma == None: | |
| continue | |
| if not Ma.IsLibrary: | |
| ModuleList[Ma.Guid.upper()] = Ma | |
| MapBuffer = StringIO('') | |
| if self.LoadFixAddress != 0: | |
| # | |
| # Rebase module to the preferred memory address before GenFds | |
| # | |
| self._CollectModuleMapBuffer(MapBuffer, ModuleList) | |
| if self.Fdf: | |
| # | |
| # create FDS again for the updated EFI image | |
| # | |
| self._Build("fds", Wa) | |
| # | |
| # Create MAP file for all platform FVs after GenFds. | |
| # | |
| self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList) | |
| # | |
| # Save MAP buffer into MAP file. | |
| # | |
| self._SaveMapFile (MapBuffer, Wa) | |
| ## Build active module for different build targets, different tool chains and different archs | |
| # | |
| def _BuildModule(self): | |
| for BuildTarget in self.BuildTargetList: | |
| GlobalData.gGlobalDefines['TARGET'] = BuildTarget | |
| index = 0 | |
| for ToolChain in self.ToolChainList: | |
| GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain | |
| GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain | |
| GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index] | |
| index += 1 | |
| # | |
| # module build needs platform build information, so get platform | |
| # AutoGen first | |
| # | |
| Wa = WorkspaceAutoGen( | |
| self.WorkspaceDir, | |
| self.PlatformFile, | |
| BuildTarget, | |
| ToolChain, | |
| self.ArchList, | |
| self.BuildDatabase, | |
| self.TargetTxt, | |
| self.ToolDef, | |
| self.Fdf, | |
| self.FdList, | |
| self.FvList, | |
| self.CapList, | |
| self.SkuId, | |
| self.UniFlag, | |
| self.Progress, | |
| self.ModuleFile | |
| ) | |
| self.Fdf = Wa.FdfFile | |
| self.LoadFixAddress = Wa.Platform.LoadFixAddress | |
| Wa.CreateMakeFile(False) | |
| self.Progress.Stop("done!") | |
| MaList = [] | |
| for Arch in Wa.ArchList: | |
| GlobalData.gGlobalDefines['ARCH'] = Arch | |
| Ma = ModuleAutoGen(Wa, self.ModuleFile, BuildTarget, ToolChain, Arch, self.PlatformFile) | |
| if Ma == None: continue | |
| MaList.append(Ma) | |
| self.BuildModules.append(Ma) | |
| if not Ma.IsBinaryModule: | |
| self._Build(self.Target, Ma, BuildModule=True) | |
| self.BuildReport.AddPlatformReport(Wa, MaList) | |
| 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(Wa.ArchList), self.PlatformFile), | |
| ExtraData=self.ModuleFile | |
| ) | |
| # Create MAP file when Load Fix Address is enabled. | |
| if self.Target == "fds" and self.Fdf: | |
| for Arch in Wa.ArchList: | |
| # | |
| # Check whether the set fix address is above 4G for 32bit image. | |
| # | |
| if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000: | |
| 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") | |
| # | |
| # Get Module List | |
| # | |
| ModuleList = {} | |
| for Pa in Wa.AutoGenObjectList: | |
| for Ma in Pa.ModuleAutoGenList: | |
| if Ma == None: | |
| continue | |
| if not Ma.IsLibrary: | |
| ModuleList[Ma.Guid.upper()] = Ma | |
| MapBuffer = StringIO('') | |
| if self.LoadFixAddress != 0: | |
| # | |
| # Rebase module to the preferred memory address before GenFds | |
| # | |
| self._CollectModuleMapBuffer(MapBuffer, ModuleList) | |
| # | |
| # create FDS again for the updated EFI image | |
| # | |
| self._Build("fds", Wa) | |
| # | |
| # Create MAP file for all platform FVs after GenFds. | |
| # | |
| self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList) | |
| # | |
| # Save MAP buffer into MAP file. | |
| # | |
| self._SaveMapFile (MapBuffer, Wa) | |
| ## Build a platform in multi-thread mode | |
| # | |
| def _MultiThreadBuildPlatform(self): | |
| SaveFileOnChange(self.PlatformBuildPath, '# DO NOT EDIT \n# FILE auto-generated\n', False) | |
| for BuildTarget in self.BuildTargetList: | |
| GlobalData.gGlobalDefines['TARGET'] = BuildTarget | |
| index = 0 | |
| for ToolChain in self.ToolChainList: | |
| GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain | |
| GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain | |
| GlobalData.gGlobalDefines['FAMILY'] = self.ToolChainFamily[index] | |
| index += 1 | |
| Wa = WorkspaceAutoGen( | |
| self.WorkspaceDir, | |
| self.PlatformFile, | |
| BuildTarget, | |
| ToolChain, | |
| self.ArchList, | |
| self.BuildDatabase, | |
| self.TargetTxt, | |
| self.ToolDef, | |
| self.Fdf, | |
| self.FdList, | |
| self.FvList, | |
| self.CapList, | |
| self.SkuId, | |
| self.UniFlag, | |
| self.Progress | |
| ) | |
| self.Fdf = Wa.FdfFile | |
| self.LoadFixAddress = Wa.Platform.LoadFixAddress | |
| self.BuildReport.AddPlatformReport(Wa) | |
| Wa.CreateMakeFile(False) | |
| # multi-thread exit flag | |
| ExitFlag = threading.Event() | |
| ExitFlag.clear() | |
| for Arch in Wa.ArchList: | |
| GlobalData.gGlobalDefines['ARCH'] = Arch | |
| Pa = PlatformAutoGen(Wa, self.PlatformFile, BuildTarget, ToolChain, Arch) | |
| if Pa == None: | |
| continue | |
| ModuleList = [] | |
| for Inf in Pa.Platform.Modules: | |
| ModuleList.append(Inf) | |
| # Add the INF only list in FDF | |
| if GlobalData.gFdfParser != None: | |
| for InfName in GlobalData.gFdfParser.Profile.InfList: | |
| Inf = PathClass(NormPath(InfName), self.WorkspaceDir, Arch) | |
| if Inf in Pa.Platform.Modules: | |
| continue | |
| ModuleList.append(Inf) | |
| for Module in ModuleList: | |
| # 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.BuildModules.append(Ma) | |
| self.Progress.Stop("done!") | |
| for Ma in self.BuildModules: | |
| # Generate build task for the module | |
| if not Ma.IsBinaryModule: | |
| 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) | |
| # | |
| # Save temp tables to a TmpTableDict. | |
| # | |
| for Key in Wa.BuildDatabase._CACHE_: | |
| if Wa.BuildDatabase._CACHE_[Key]._RawData and Wa.BuildDatabase._CACHE_[Key]._RawData._Table and Wa.BuildDatabase._CACHE_[Key]._RawData._Table.Table: | |
| if TemporaryTablePattern.match(Wa.BuildDatabase._CACHE_[Key]._RawData._Table.Table): | |
| TmpTableDict[Wa.BuildDatabase._CACHE_[Key]._RawData._Table.Table] = Wa.BuildDatabase._CACHE_[Key]._RawData._Table.Cur | |
| # | |
| # | |
| # All modules have been put in build tasks queue. Tell task scheduler | |
| # to exit if all tasks are completed | |
| # | |
| ExitFlag.set() | |
| BuildTask.WaitForComplete() | |
| self.CreateAsBuiltInf() | |
| # | |
| # 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) | |
| # Create MAP file when Load Fix Address is enabled. | |
| if self.Target in ["", "all", "fds"]: | |
| for Arch in Wa.ArchList: | |
| # | |
| # Check whether the set fix address is above 4G for 32bit image. | |
| # | |
| if (Arch == 'IA32' or Arch == 'ARM') and self.LoadFixAddress != 0xFFFFFFFFFFFFFFFF and self.LoadFixAddress >= 0x100000000: | |
| 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") | |
| # | |
| # Get Module List | |
| # | |
| ModuleList = {} | |
| for Pa in Wa.AutoGenObjectList: | |
| for Ma in Pa.ModuleAutoGenList: | |
| if Ma == None: | |
| continue | |
| if not Ma.IsLibrary: | |
| ModuleList[Ma.Guid.upper()] = Ma | |
| # | |
| # Rebase module to the preferred memory address before GenFds | |
| # | |
| MapBuffer = StringIO('') | |
| if self.LoadFixAddress != 0: | |
| self._CollectModuleMapBuffer(MapBuffer, ModuleList) | |
| if self.Fdf: | |
| # | |
| # Generate FD image if there's a FDF file found | |
| # | |
| LaunchCommand(Wa.GenFdsCommand, os.getcwd()) | |
| # | |
| # Create MAP file for all platform FVs after GenFds. | |
| # | |
| self._CollectFvMapBuffer(MapBuffer, Wa, ModuleList) | |
| # | |
| # Save MAP buffer into MAP file. | |
| # | |
| self._SaveMapFile(MapBuffer, Wa) | |
| ## Generate GuidedSectionTools.txt in the FV directories. | |
| # | |
| def CreateGuidedSectionToolsFile(self): | |
| for BuildTarget in self.BuildTargetList: | |
| for ToolChain in self.ToolChainList: | |
| Wa = WorkspaceAutoGen( | |
| self.WorkspaceDir, | |
| self.PlatformFile, | |
| BuildTarget, | |
| ToolChain, | |
| self.ArchList, | |
| self.BuildDatabase, | |
| self.TargetTxt, | |
| self.ToolDef, | |
| self.Fdf, | |
| self.FdList, | |
| self.FvList, | |
| self.CapList, | |
| self.SkuId, | |
| self.UniFlag | |
| ) | |
| FvDir = Wa.FvDir | |
| if not os.path.exists(FvDir): | |
| continue | |
| for Arch in self.ArchList: | |
| # 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 not 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() | |
| if self.Target == 'cleanall': | |
| self.Db.Close() | |
| RemoveDirectory(os.path.dirname(GlobalData.gDatabasePath), True) | |
| def CreateAsBuiltInf(self): | |
| for Module in self.BuildModules: | |
| Module.CreateAsBuiltInf() | |
| self.BuildModules = [] | |
| ## 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.dirname(GlobalData.gDatabasePath) | |
| 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(os.path.dirname(GlobalData.gDatabasePath), "gFileTimeStampCache") | |
| if Utils.gFileTimeStampCache == {} and os.path.isfile(FilePath): | |
| Utils.gFileTimeStampCache = Utils.DataRestore(FilePath) | |
| if Utils.gFileTimeStampCache == None: | |
| Utils.gFileTimeStampCache = {} | |
| FilePath = os.path.join(os.path.dirname(GlobalData.gDatabasePath), "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 not GlobalData.gMacroNamePattern.match(DefineTokenList[0]): | |
| EdkLogger.error('build', FORMAT_INVALID, | |
| "The macro name must be in the pattern [A-Z][A-Z0-9_]*", | |
| ExtraData=DefineTokenList[0]) | |
| if len(DefineTokenList) == 1: | |
| DefineDict[DefineTokenList[0]] = "TRUE" | |
| 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', 'AARCH64'], dest="TargetArch", | |
| 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.") | |
| 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", type="string", dest="BuildTarget", help="Using the TARGET to build the platform, overriding target.txt's TARGET definition.", | |
| action="append") | |
| 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("-C", "--capsule-image", action="append", type="string", dest="CapName", default=[], | |
| help="The name of Capsule to be generated. The name must be from [Capsule] 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", default=False, help="Don't check case of file name.") | |
| 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=['PCD','LIBRARY','FLASH','DEPEX','BUILD_FLAGS','FIXED_ADDRESS','HASH','EXECUTION_ORDER'], dest="ReportType", default=[], | |
| 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]. "\ | |
| "To specify more than one flag, repeat this option on the command line and the default flag set is [PCD, LIBRARY, FLASH, DEPEX, BUILD_FLAGS, FIXED_ADDRESS]") | |
| Parser.add_option("-F", "--flag", action="store", type="string", dest="Flag", | |
| 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. "\ | |
| "This option can also be specified by setting *_*_*_BUILD_FLAGS in [BuildOptions] section of platform DSC. If they are both specified, this value "\ | |
| "will override the setting in [BuildOptions] section of platform DSC.") | |
| Parser.add_option("-N", "--no-cache", action="store_true", dest="DisableCache", default=False, help="Disable build cache mechanism") | |
| Parser.add_option("--conf", action="store", type="string", dest="ConfDirectory", help="Specify the customized Conf directory.") | |
| Parser.add_option("--check-usage", action="store_true", dest="CheckUsage", default=False, help="Check usage content of entries listed in INF file.") | |
| Parser.add_option("--ignore-sources", action="store_true", dest="IgnoreSources", default=False, help="Focus to a binary build and ignore all source files") | |
| Parser.add_option("--pcd", action="append", dest="OptionPcd", help="Set PCD value by command line. Format: \"PcdName=Value\" ") | |
| Parser.add_option("-l", "--cmd-len", action="store", type="int", dest="CommandLength", help="Specify the maximum line length of build command. Default is 4096.") | |
| (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() | |
| GlobalData.gCommand = sys.argv[1:] | |
| # | |
| # 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("Build environment: %s" % platform.platform()) | |
| EdkLogger.quiet(time.strftime("Build start time: %H:%M:%S, %b.%d %Y\n", time.localtime())); | |
| ReturnCode = 0 | |
| MyBuild = None | |
| BuildError = True | |
| 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))) | |
| # | |
| # Check environment variable: EDK_TOOLS_PATH, WORKSPACE, PATH | |
| # | |
| CheckEnvVariable() | |
| GlobalData.gCommandLineDefines.update(ParseDefines(Option.Macros)) | |
| 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: | |
| if os.path.isabs (Option.ModuleFile): | |
| if os.path.normcase (os.path.normpath(Option.ModuleFile)).find (Workspace) == 0: | |
| Option.ModuleFile = NormFile(os.path.normpath(Option.ModuleFile), Workspace) | |
| 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: | |
| if os.path.isabs (Option.PlatformFile): | |
| if os.path.normcase (os.path.normpath(Option.PlatformFile)).find (Workspace) == 0: | |
| Option.PlatformFile = NormFile(os.path.normpath(Option.PlatformFile), Workspace) | |
| Option.PlatformFile = PathClass(Option.PlatformFile, Workspace) | |
| if Option.FdfFile != None: | |
| if os.path.isabs (Option.FdfFile): | |
| if os.path.normcase (os.path.normpath(Option.FdfFile)).find (Workspace) == 0: | |
| Option.FdfFile = NormFile(os.path.normpath(Option.FdfFile), Workspace) | |
| Option.FdfFile = PathClass(Option.FdfFile, Workspace) | |
| ErrorCode, ErrorInfo = Option.FdfFile.Validate(".fdf", False) | |
| if ErrorCode != 0: | |
| EdkLogger.error("build", ErrorCode, ExtraData=ErrorInfo) | |
| if Option.Flag != None and Option.Flag not in ['-c', '-s']: | |
| EdkLogger.error("build", OPTION_VALUE_INVALID, "UNI flag must be one of -c or -s") | |
| MyBuild = Build(Target, Workspace, Option) | |
| GlobalData.gCommandLineDefines['ARCH'] = ' '.join(MyBuild.ArchList) | |
| if not (MyBuild.LaunchPrebuildFlag and os.path.exists(MyBuild.PlatformBuildPath)): | |
| MyBuild.Launch() | |
| # Drop temp tables to avoid database locked. | |
| for TmpTableName in TmpTableDict: | |
| SqlCommand = """drop table IF EXISTS %s""" % TmpTableName | |
| TmpTableDict[TmpTableName].execute(SqlCommand) | |
| #MyBuild.DumpBuildData() | |
| # | |
| # All job done, no error found and no exception raised | |
| # | |
| BuildError = False | |
| 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 edk2-devel@lists.01.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() | |
| Utils.ClearDuplicatedInf() | |
| if ReturnCode == 0: | |
| try: | |
| MyBuild.LanuchPostbuild() | |
| Conclusion = "Done" | |
| except: | |
| Conclusion = "Failed" | |
| elif ReturnCode == ABORT_ERROR: | |
| Conclusion = "Aborted" | |
| else: | |
| Conclusion = "Failed" | |
| FinishTime = time.time() | |
| BuildDuration = time.gmtime(int(round(FinishTime - StartTime))) | |
| BuildDurationStr = "" | |
| if BuildDuration.tm_yday > 1: | |
| BuildDurationStr = time.strftime("%H:%M:%S", BuildDuration) + ", %d day(s)" % (BuildDuration.tm_yday - 1) | |
| else: | |
| BuildDurationStr = time.strftime("%H:%M:%S", BuildDuration) | |
| if MyBuild != None: | |
| if not BuildError: | |
| MyBuild.BuildReport.GenerateReport(BuildDurationStr) | |
| MyBuild.Db.Close() | |
| EdkLogger.SetLevel(EdkLogger.QUIET) | |
| EdkLogger.quiet("\n- %s -" % Conclusion) | |
| EdkLogger.quiet(time.strftime("Build end time: %H:%M:%S, %b.%d %Y", time.localtime())) | |
| EdkLogger.quiet("Build total time: %s\n" % BuildDurationStr) | |
| 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) | |