mirror of
https://github.com/acidanthera/audk.git
synced 2025-04-08 17:05:09 +02:00
NMAKE is limited to command-line length of 4096 characters. Due to the large number of /I directives specified on command line (one per include directory), the path length of WORKSPACE is multiplied by the number of /I directives and can exceed the limit. This patch: 1. Add new build option -l, --cmd-len to set the maximum command line length, default value is 4096. 2. Generate the response file only if the command line length exceed its maximum characters (default is 4096) when build the module. Cover PP_FLAGS, CC_FLAGS, VFRPP_FLAGS, APP_FLAGS, ASLPP_FLAGS, ASLCC_FLAGS and ASM_FLAGS. 3. The content of the response file is combine from the FLAGS option and INC option. 4. When build failure, it would print out the response file's file location and its content. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Yonghong Zhu <yonghong.zhu@intel.com> Reviewed-by: Liming Gao <liming.gao@intel.com>
2147 lines
100 KiB
Python
2147 lines
100 KiB
Python
## @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
|
|
|
|
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)
|
|
|
|
#
|
|
# 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:
|
|
# 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.LoadFixAddress = 0
|
|
self.UniFlag = BuildOptions.Flag
|
|
self.BuildModules = []
|
|
|
|
if BuildOptions.CommandLength:
|
|
GlobalData.gCommandMaxLength = BuildOptions.CommandLength
|
|
|
|
# print dot character during doing some time-consuming work
|
|
self.Progress = Utils.Progressor()
|
|
|
|
self.InitBuild()
|
|
|
|
# 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.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
|
|
|
|
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
|
|
self.Db.InitDatabase()
|
|
|
|
## 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):
|
|
for BuildTarget in self.BuildTargetList:
|
|
GlobalData.gGlobalDefines['TARGET'] = BuildTarget
|
|
for ToolChain in self.ToolChainList:
|
|
GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain
|
|
GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain
|
|
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
|
|
for ToolChain in self.ToolChainList:
|
|
GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain
|
|
GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain
|
|
#
|
|
# 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):
|
|
for BuildTarget in self.BuildTargetList:
|
|
GlobalData.gGlobalDefines['TARGET'] = BuildTarget
|
|
for ToolChain in self.ToolChainList:
|
|
GlobalData.gGlobalDefines['TOOLCHAIN'] = ToolChain
|
|
GlobalData.gGlobalDefines['TOOL_CHAIN_TAG'] = ToolChain
|
|
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', '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, 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()
|
|
|
|
#
|
|
# 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)
|
|
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:
|
|
Conclusion = "Done"
|
|
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)
|
|
|