## @file # Create makefile for MS nmake and GNU make # # Copyright (c) 2007 - 2021, Intel Corporation. All rights reserved.
# Copyright (c) 2020, ARM Limited. All rights reserved.
# SPDX-License-Identifier: BSD-2-Clause-Patent # ## Import Modules # from __future__ import absolute_import import Common.LongFilePathOs as os import sys import string import re import os.path as path from Common.LongFilePathSupport import OpenLongFilePath as open from Common.MultipleWorkspace import MultipleWorkspace as mws from Common.BuildToolError import * from Common.Misc import * from Common.StringUtils import * from .BuildEngine import * import Common.GlobalData as GlobalData from collections import OrderedDict from Common.DataType import TAB_COMPILER_MSFT ## Regular expression for finding header file inclusions gIncludePattern = re.compile(r"^[ \t]*[#%]?[ \t]*include(?:[ \t]*(?:\\(?:\r\n|\r|\n))*[ \t]*)*(?:\(?[\"<]?[ \t]*)([-\w.\\/() \t]+)(?:[ \t]*[\">]?\)?)", re.MULTILINE | re.UNICODE | re.IGNORECASE) ## Regular expression for matching macro used in header file inclusion gMacroPattern = re.compile("([_A-Z][_A-Z0-9]*)[ \t]*\((.+)\)", re.UNICODE) gIsFileMap = {} ## pattern for include style in Edk.x code gProtocolDefinition = "Protocol/%(HeaderKey)s/%(HeaderKey)s.h" gGuidDefinition = "Guid/%(HeaderKey)s/%(HeaderKey)s.h" gArchProtocolDefinition = "ArchProtocol/%(HeaderKey)s/%(HeaderKey)s.h" gPpiDefinition = "Ppi/%(HeaderKey)s/%(HeaderKey)s.h" gIncludeMacroConversion = { "EFI_PROTOCOL_DEFINITION" : gProtocolDefinition, "EFI_GUID_DEFINITION" : gGuidDefinition, "EFI_ARCH_PROTOCOL_DEFINITION" : gArchProtocolDefinition, "EFI_PROTOCOL_PRODUCER" : gProtocolDefinition, "EFI_PROTOCOL_CONSUMER" : gProtocolDefinition, "EFI_PROTOCOL_DEPENDENCY" : gProtocolDefinition, "EFI_ARCH_PROTOCOL_PRODUCER" : gArchProtocolDefinition, "EFI_ARCH_PROTOCOL_CONSUMER" : gArchProtocolDefinition, "EFI_ARCH_PROTOCOL_DEPENDENCY" : gArchProtocolDefinition, "EFI_PPI_DEFINITION" : gPpiDefinition, "EFI_PPI_PRODUCER" : gPpiDefinition, "EFI_PPI_CONSUMER" : gPpiDefinition, "EFI_PPI_DEPENDENCY" : gPpiDefinition, } NMAKE_FILETYPE = "nmake" GMAKE_FILETYPE = "gmake" WIN32_PLATFORM = "win32" POSIX_PLATFORM = "posix" ## BuildFile class # # This base class encapsules build file and its generation. It uses template to generate # the content of build file. The content of build file will be got from AutoGen objects. # class BuildFile(object): ## template used to generate the build file (i.e. makefile if using make) _TEMPLATE_ = TemplateString('') _DEFAULT_FILE_NAME_ = "Makefile" ## default file name for each type of build file _FILE_NAME_ = { NMAKE_FILETYPE : "Makefile", GMAKE_FILETYPE : "GNUmakefile" } # Get Makefile name. def getMakefileName(self): if not self._FileType: return self._DEFAULT_FILE_NAME_ else: return self._FILE_NAME_[self._FileType] ## Fixed header string for makefile _MAKEFILE_HEADER = '''# # DO NOT EDIT # This file is auto-generated by build utility # # Module Name: # # %s # # Abstract: # # Auto-generated makefile for building modules, libraries or platform # ''' ## Header string for each type of build file _FILE_HEADER_ = { NMAKE_FILETYPE : _MAKEFILE_HEADER % _FILE_NAME_[NMAKE_FILETYPE], GMAKE_FILETYPE : _MAKEFILE_HEADER % _FILE_NAME_[GMAKE_FILETYPE] } ## shell commands which can be used in build file in the form of macro # $(CP) copy file command # $(MV) move file command # $(RM) remove file command # $(MD) create dir command # $(RD) remove dir command # _SHELL_CMD_ = { WIN32_PLATFORM : { "CP" : "copy /y", "MV" : "move /y", "RM" : "del /f /q", "MD" : "mkdir", "RD" : "rmdir /s /q", }, POSIX_PLATFORM : { "CP" : "cp -f", "MV" : "mv -f", "RM" : "rm -f", "MD" : "mkdir -p", "RD" : "rm -r -f", } } ## directory separator _SEP_ = { WIN32_PLATFORM : "\\", POSIX_PLATFORM : "/" } ## directory creation template _MD_TEMPLATE_ = { WIN32_PLATFORM : 'if not exist %(dir)s $(MD) %(dir)s', POSIX_PLATFORM : "$(MD) %(dir)s" } ## directory removal template _RD_TEMPLATE_ = { WIN32_PLATFORM : 'if exist %(dir)s $(RD) %(dir)s', POSIX_PLATFORM : "$(RD) %(dir)s" } ## cp if exist _CP_TEMPLATE_ = { WIN32_PLATFORM : 'if exist %(Src)s $(CP) %(Src)s %(Dst)s', POSIX_PLATFORM : "test -f %(Src)s && $(CP) %(Src)s %(Dst)s" } _CD_TEMPLATE_ = { WIN32_PLATFORM : 'if exist %(dir)s cd %(dir)s', POSIX_PLATFORM : "test -e %(dir)s && cd %(dir)s" } _MAKE_TEMPLATE_ = { WIN32_PLATFORM : 'if exist %(file)s "$(MAKE)" $(MAKE_FLAGS) -f %(file)s', POSIX_PLATFORM : 'test -e %(file)s && "$(MAKE)" $(MAKE_FLAGS) -f %(file)s' } _INCLUDE_CMD_ = { NMAKE_FILETYPE : '!INCLUDE', GMAKE_FILETYPE : "include" } _INC_FLAG_ = {TAB_COMPILER_MSFT : "/I", "GCC" : "-I", "INTEL" : "-I", "RVCT" : "-I", "NASM" : "-I"} ## Constructor of BuildFile # # @param AutoGenObject Object of AutoGen class # def __init__(self, AutoGenObject): self._AutoGenObject = AutoGenObject MakePath = AutoGenObject.BuildOption.get('MAKE', {}).get('PATH') if not MakePath: self._FileType = "" elif "nmake" in MakePath: self._FileType = NMAKE_FILETYPE else: self._FileType = "gmake" if sys.platform == "win32": self._Platform = WIN32_PLATFORM else: self._Platform = POSIX_PLATFORM ## Create build file. # # Only nmake and gmake are supported. # # @retval TRUE The build file is created or re-created successfully. # @retval FALSE The build file exists and is the same as the one to be generated. # def Generate(self): FileContent = self._TEMPLATE_.Replace(self._TemplateDict) FileName = self.getMakefileName() if not os.path.exists(os.path.join(self._AutoGenObject.MakeFileDir, "deps.txt")): with open(os.path.join(self._AutoGenObject.MakeFileDir, "deps.txt"),"w+") as fd: fd.write("") if not os.path.exists(os.path.join(self._AutoGenObject.MakeFileDir, "dependency")): with open(os.path.join(self._AutoGenObject.MakeFileDir, "dependency"),"w+") as fd: fd.write("") if not os.path.exists(os.path.join(self._AutoGenObject.MakeFileDir, "deps_target")): with open(os.path.join(self._AutoGenObject.MakeFileDir, "deps_target"),"w+") as fd: fd.write("") return SaveFileOnChange(os.path.join(self._AutoGenObject.MakeFileDir, FileName), FileContent, False) ## Return a list of directory creation command string # # @param DirList The list of directory to be created # # @retval list The directory creation command list # def GetCreateDirectoryCommand(self, DirList): return [self._MD_TEMPLATE_[self._Platform] % {'dir':Dir} for Dir in DirList] ## Return a list of directory removal command string # # @param DirList The list of directory to be removed # # @retval list The directory removal command list # def GetRemoveDirectoryCommand(self, DirList): return [self._RD_TEMPLATE_[self._Platform] % {'dir':Dir} for Dir in DirList] def PlaceMacro(self, Path, MacroDefinitions=None): if Path.startswith("$("): return Path else: if MacroDefinitions is None: MacroDefinitions = {} PathLength = len(Path) for MacroName in MacroDefinitions: MacroValue = MacroDefinitions[MacroName] MacroValueLength = len(MacroValue) if MacroValueLength == 0: continue if MacroValueLength <= PathLength and Path.startswith(MacroValue): Path = "$(%s)%s" % (MacroName, Path[MacroValueLength:]) break return Path ## ModuleMakefile class # # This class encapsules makefie and its generation for module. It uses template to generate # the content of makefile. The content of makefile will be got from ModuleAutoGen object. # class ModuleMakefile(BuildFile): ## template used to generate the makefile for module _TEMPLATE_ = TemplateString('''\ ${makefile_header} # # Platform Macro Definition # PLATFORM_NAME = ${platform_name} PLATFORM_GUID = ${platform_guid} PLATFORM_VERSION = ${platform_version} PLATFORM_RELATIVE_DIR = ${platform_relative_directory} PLATFORM_DIR = ${platform_dir} PLATFORM_OUTPUT_DIR = ${platform_output_directory} # # Module Macro Definition # MODULE_NAME = ${module_name} MODULE_GUID = ${module_guid} MODULE_NAME_GUID = ${module_name_guid} MODULE_VERSION = ${module_version} MODULE_TYPE = ${module_type} MODULE_FILE = ${module_file} MODULE_FILE_BASE_NAME = ${module_file_base_name} BASE_NAME = $(MODULE_NAME) MODULE_RELATIVE_DIR = ${module_relative_directory} PACKAGE_RELATIVE_DIR = ${package_relative_directory} MODULE_DIR = ${module_dir} FFS_OUTPUT_DIR = ${ffs_output_directory} MODULE_ENTRY_POINT = ${module_entry_point} ARCH_ENTRY_POINT = ${arch_entry_point} IMAGE_ENTRY_POINT = ${image_entry_point} ${BEGIN}${module_extra_defines} ${END} # # Build Configuration Macro Definition # ARCH = ${architecture} TOOLCHAIN = ${toolchain_tag} TOOLCHAIN_TAG = ${toolchain_tag} TARGET = ${build_target} # # Build Directory Macro Definition # # PLATFORM_BUILD_DIR = ${platform_build_directory} BUILD_DIR = ${platform_build_directory} BIN_DIR = $(BUILD_DIR)${separator}${architecture} LIB_DIR = $(BIN_DIR) MODULE_BUILD_DIR = ${module_build_directory} OUTPUT_DIR = ${module_output_directory} DEBUG_DIR = ${module_debug_directory} DEST_DIR_OUTPUT = $(OUTPUT_DIR) DEST_DIR_DEBUG = $(DEBUG_DIR) # # Shell Command Macro # ${BEGIN}${shell_command_code} = ${shell_command} ${END} # # Tools definitions specific to this module # ${BEGIN}${module_tool_definitions} ${END} MAKE_FILE = ${makefile_path} # # Build Macro # ${BEGIN}${file_macro} ${END} # # Overridable Target Macro Definitions # FORCE_REBUILD = force_build INIT_TARGET = init PCH_TARGET = BC_TARGET = ${BEGIN}${backward_compatible_target} ${END} CODA_TARGET = ${BEGIN}${remaining_build_target} \\ ${END} # # Default target, which will build dependent libraries in addition to source files # all: mbuild # # Target used when called from platform makefile, which will bypass the build of dependent libraries # pbuild: $(INIT_TARGET) $(BC_TARGET) $(PCH_TARGET) $(CODA_TARGET) # # ModuleTarget # mbuild: $(INIT_TARGET) $(BC_TARGET) gen_libs $(PCH_TARGET) $(CODA_TARGET) # # Build Target used in multi-thread build mode, which will bypass the init and gen_libs targets # tbuild: $(BC_TARGET) $(PCH_TARGET) $(CODA_TARGET) # # Phony target which is used to force executing commands for a target # force_build: \t-@ # # Target to update the FD # fds: mbuild gen_fds # # Initialization target: print build information and create necessary directories # init: info dirs info: \t-@echo Building ... $(MODULE_DIR)${separator}$(MODULE_FILE) [$(ARCH)] dirs: ${BEGIN}\t-@${create_directory_command}\n${END} strdefs: \t-@$(CP) $(DEBUG_DIR)${separator}AutoGen.h $(DEBUG_DIR)${separator}$(MODULE_NAME)StrDefs.h # # GenLibsTarget # gen_libs: \t${BEGIN}@"$(MAKE)" $(MAKE_FLAGS) -f ${dependent_library_build_directory}${separator}${makefile_name} \t${END}@cd $(MODULE_BUILD_DIR) # # Build Flash Device Image # gen_fds: \t@"$(MAKE)" $(MAKE_FLAGS) -f $(BUILD_DIR)${separator}${makefile_name} fds \t@cd $(MODULE_BUILD_DIR) ${INCLUDETAG} # # Individual Object Build Targets # ${BEGIN}${file_build_target} ${END} # # clean all intermediate files # clean: \t${BEGIN}${clean_command} \t${END}\t$(RM) AutoGenTimeStamp # # clean all generated files # cleanall: ${BEGIN}\t${cleanall_command} ${END}\t$(RM) *.pdb *.idb > NUL 2>&1 \t$(RM) $(BIN_DIR)${separator}$(MODULE_NAME).efi \t$(RM) AutoGenTimeStamp # # clean all dependent libraries built # cleanlib: \t${BEGIN}-@${library_build_command} cleanall \t${END}@cd $(MODULE_BUILD_DIR)\n\n''') _FILE_MACRO_TEMPLATE = TemplateString("${macro_name} = ${BEGIN} \\\n ${source_file}${END}\n") _BUILD_TARGET_TEMPLATE = TemplateString("${BEGIN}${target} : ${deps}\n${END}\t${cmd}\n") ## Constructor of ModuleMakefile # # @param ModuleAutoGen Object of ModuleAutoGen class # def __init__(self, ModuleAutoGen): BuildFile.__init__(self, ModuleAutoGen) self.PlatformInfo = self._AutoGenObject.PlatformInfo self.ResultFileList = [] self.IntermediateDirectoryList = ["$(DEBUG_DIR)", "$(OUTPUT_DIR)"] self.FileBuildTargetList = [] # [(src, target string)] self.BuildTargetList = [] # [target string] self.PendingBuildTargetList = [] # [FileBuildRule objects] self.CommonFileDependency = [] self.FileListMacros = {} self.ListFileMacros = {} self.ObjTargetDict = OrderedDict() self.FileCache = {} self.LibraryBuildCommandList = [] self.LibraryFileList = [] self.LibraryMakefileList = [] self.LibraryBuildDirectoryList = [] self.SystemLibraryList = [] self.Macros = OrderedDict() self.Macros["OUTPUT_DIR" ] = self._AutoGenObject.Macros["OUTPUT_DIR"] self.Macros["DEBUG_DIR" ] = self._AutoGenObject.Macros["DEBUG_DIR"] self.Macros["MODULE_BUILD_DIR"] = self._AutoGenObject.Macros["MODULE_BUILD_DIR"] self.Macros["BIN_DIR" ] = self._AutoGenObject.Macros["BIN_DIR"] self.Macros["BUILD_DIR" ] = self._AutoGenObject.Macros["BUILD_DIR"] self.Macros["WORKSPACE" ] = self._AutoGenObject.Macros["WORKSPACE"] self.Macros["FFS_OUTPUT_DIR" ] = self._AutoGenObject.Macros["FFS_OUTPUT_DIR"] self.GenFfsList = ModuleAutoGen.GenFfsList self.MacroList = ['FFS_OUTPUT_DIR', 'MODULE_GUID', 'OUTPUT_DIR'] self.FfsOutputFileList = [] self.DependencyHeaderFileSet = set() # Compose a dict object containing information used to do replacement in template @property def _TemplateDict(self): MyAgo = self._AutoGenObject Separator = self._SEP_[self._Platform] # break build if no source files and binary files are found if len(MyAgo.SourceFileList) == 0 and len(MyAgo.BinaryFileList) == 0: EdkLogger.error("build", AUTOGEN_ERROR, "No files to be built in module [%s, %s, %s]" % (MyAgo.BuildTarget, MyAgo.ToolChain, MyAgo.Arch), ExtraData="[%s]" % str(MyAgo)) # convert dependent libraries to build command self.ProcessDependentLibrary() if len(MyAgo.Module.ModuleEntryPointList) > 0: ModuleEntryPoint = MyAgo.Module.ModuleEntryPointList[0] else: ModuleEntryPoint = "_ModuleEntryPoint" ArchEntryPoint = ModuleEntryPoint if MyAgo.Arch == "EBC": # EBC compiler always use "EfiStart" as entry point. Only applies to EdkII modules ImageEntryPoint = "EfiStart" else: # EdkII modules always use "_ModuleEntryPoint" as entry point ImageEntryPoint = "_ModuleEntryPoint" for k, v in MyAgo.Module.Defines.items(): if k not in MyAgo.Macros: MyAgo.Macros[k] = v if 'MODULE_ENTRY_POINT' not in MyAgo.Macros: MyAgo.Macros['MODULE_ENTRY_POINT'] = ModuleEntryPoint if 'ARCH_ENTRY_POINT' not in MyAgo.Macros: MyAgo.Macros['ARCH_ENTRY_POINT'] = ArchEntryPoint if 'IMAGE_ENTRY_POINT' not in MyAgo.Macros: MyAgo.Macros['IMAGE_ENTRY_POINT'] = ImageEntryPoint PCI_COMPRESS_Flag = False for k, v in MyAgo.Module.Defines.items(): if 'PCI_COMPRESS' == k and 'TRUE' == v: PCI_COMPRESS_Flag = True # tools definitions ToolsDef = [] IncPrefix = self._INC_FLAG_[MyAgo.ToolChainFamily] for Tool in sorted(list(MyAgo.BuildOption)): Appended = False for Attr in sorted(list(MyAgo.BuildOption[Tool])): Value = MyAgo.BuildOption[Tool][Attr] if Attr == "FAMILY": continue elif Attr == "PATH": ToolsDef.append("%s = %s" % (Tool, Value)) Appended = True else: # Don't generate MAKE_FLAGS in makefile. It's put in environment variable. if Tool == "MAKE": continue # Remove duplicated include path, if any if Attr == "FLAGS": Value = RemoveDupOption(Value, IncPrefix, MyAgo.IncludePathList) if Tool == "OPTROM" and PCI_COMPRESS_Flag: ValueList = Value.split() if ValueList: for i, v in enumerate(ValueList): if '-e' == v: ValueList[i] = '-ec' Value = ' '.join(ValueList) ToolsDef.append("%s_%s = %s" % (Tool, Attr, Value)) Appended = True if Appended: ToolsDef.append("") # generate the Response file and Response flag RespDict = self.CommandExceedLimit() RespFileList = os.path.join(MyAgo.OutputDir, 'respfilelist.txt') if RespDict: RespFileListContent = '' for Resp in RespDict: RespFile = os.path.join(MyAgo.OutputDir, str(Resp).lower() + '.txt') StrList = RespDict[Resp].split(' ') UnexpandMacro = [] NewStr = [] for Str in StrList: if '$' in Str or '-MMD' in Str or '-MF' in Str: UnexpandMacro.append(Str) else: NewStr.append(Str) UnexpandMacroStr = ' '.join(UnexpandMacro) NewRespStr = ' '.join(NewStr) SaveFileOnChange(RespFile, NewRespStr, False) ToolsDef.append("%s = %s" % (Resp, UnexpandMacroStr + ' @' + RespFile)) RespFileListContent += '@' + RespFile + TAB_LINE_BREAK RespFileListContent += NewRespStr + TAB_LINE_BREAK SaveFileOnChange(RespFileList, RespFileListContent, False) else: if os.path.exists(RespFileList): os.remove(RespFileList) # convert source files and binary files to build targets self.ResultFileList = [str(T.Target) for T in MyAgo.CodaTargetList] if len(self.ResultFileList) == 0 and len(MyAgo.SourceFileList) != 0: EdkLogger.error("build", AUTOGEN_ERROR, "Nothing to build", ExtraData="[%s]" % str(MyAgo)) self.ProcessBuildTargetList(MyAgo.OutputDir, ToolsDef) self.ParserGenerateFfsCmd() # Generate macros used to represent input files FileMacroList = [] # macro name = file list for FileListMacro in self.FileListMacros: FileMacro = self._FILE_MACRO_TEMPLATE.Replace( { "macro_name" : FileListMacro, "source_file" : self.FileListMacros[FileListMacro] } ) FileMacroList.append(FileMacro) # INC_LIST is special FileMacro = "" IncludePathList = [] for P in MyAgo.IncludePathList: IncludePathList.append(IncPrefix + self.PlaceMacro(P, self.Macros)) if FileBuildRule.INC_LIST_MACRO in self.ListFileMacros: self.ListFileMacros[FileBuildRule.INC_LIST_MACRO].append(IncPrefix + P) FileMacro += self._FILE_MACRO_TEMPLATE.Replace( { "macro_name" : "INC", "source_file" : IncludePathList } ) FileMacroList.append(FileMacro) # Add support when compiling .nasm source files IncludePathList = [] asmsource = [item for item in MyAgo.SourceFileList if item.File.upper().endswith((".NASM",".ASM",".NASMB","S"))] if asmsource: for P in MyAgo.IncludePathList: IncludePath = self._INC_FLAG_['NASM'] + self.PlaceMacro(P, self.Macros) if IncludePath.endswith(os.sep): IncludePath = IncludePath.rstrip(os.sep) # When compiling .nasm files, need to add a literal backslash at each path. # In nmake makfiles, a trailing literal backslash must be escaped with a caret ('^'). # It is otherwise replaced with a space (' '). This is not necessary for GNU makfefiles. if P == MyAgo.IncludePathList[-1] and self._Platform == WIN32_PLATFORM and self._FileType == NMAKE_FILETYPE: IncludePath = ''.join([IncludePath, '^', os.sep]) else: IncludePath = os.path.join(IncludePath, '') IncludePathList.append(IncludePath) FileMacroList.append(self._FILE_MACRO_TEMPLATE.Replace({"macro_name": "NASM_INC", "source_file": IncludePathList})) # Generate macros used to represent files containing list of input files for ListFileMacro in self.ListFileMacros: ListFileName = os.path.join(MyAgo.OutputDir, "%s.lst" % ListFileMacro.lower()[:len(ListFileMacro) - 5]) FileMacroList.append("%s = %s" % (ListFileMacro, ListFileName)) SaveFileOnChange( ListFileName, "\n".join(self.ListFileMacros[ListFileMacro]), False ) # Generate objlist used to create .obj file for Type in self.ObjTargetDict: NewLine = ' '.join(list(self.ObjTargetDict[Type])) FileMacroList.append("OBJLIST_%s = %s" % (list(self.ObjTargetDict.keys()).index(Type), NewLine)) BcTargetList = [] MakefileName = self.getMakefileName() LibraryMakeCommandList = [] for D in self.LibraryBuildDirectoryList: Command = self._MAKE_TEMPLATE_[self._Platform] % {"file":os.path.join(D, MakefileName)} LibraryMakeCommandList.append(Command) package_rel_dir = MyAgo.SourceDir current_dir = self.Macros["WORKSPACE"] found = False while not found and os.sep in package_rel_dir: index = package_rel_dir.index(os.sep) current_dir = mws.join(current_dir, package_rel_dir[:index]) if os.path.exists(current_dir): for fl in os.listdir(current_dir): if fl.endswith('.dec'): found = True break package_rel_dir = package_rel_dir[index + 1:] MakefileTemplateDict = { "makefile_header" : self._FILE_HEADER_[self._FileType], "makefile_path" : os.path.join("$(MODULE_BUILD_DIR)", MakefileName), "makefile_name" : MakefileName, "platform_name" : self.PlatformInfo.Name, "platform_guid" : self.PlatformInfo.Guid, "platform_version" : self.PlatformInfo.Version, "platform_relative_directory": self.PlatformInfo.SourceDir, "platform_output_directory" : self.PlatformInfo.OutputDir, "ffs_output_directory" : MyAgo.Macros["FFS_OUTPUT_DIR"], "platform_dir" : MyAgo.Macros["PLATFORM_DIR"], "module_name" : MyAgo.Name, "module_guid" : MyAgo.Guid, "module_name_guid" : MyAgo.UniqueBaseName, "module_version" : MyAgo.Version, "module_type" : MyAgo.ModuleType, "module_file" : MyAgo.MetaFile.Name, "module_file_base_name" : MyAgo.MetaFile.BaseName, "module_relative_directory" : MyAgo.SourceDir, "module_dir" : mws.join (self.Macros["WORKSPACE"], MyAgo.SourceDir), "package_relative_directory": package_rel_dir, "module_extra_defines" : ["%s = %s" % (k, v) for k, v in MyAgo.Module.Defines.items()], "architecture" : MyAgo.Arch, "toolchain_tag" : MyAgo.ToolChain, "build_target" : MyAgo.BuildTarget, "platform_build_directory" : self.PlatformInfo.BuildDir, "module_build_directory" : MyAgo.BuildDir, "module_output_directory" : MyAgo.OutputDir, "module_debug_directory" : MyAgo.DebugDir, "separator" : Separator, "module_tool_definitions" : ToolsDef, "shell_command_code" : list(self._SHELL_CMD_[self._Platform].keys()), "shell_command" : list(self._SHELL_CMD_[self._Platform].values()), "module_entry_point" : ModuleEntryPoint, "image_entry_point" : ImageEntryPoint, "arch_entry_point" : ArchEntryPoint, "remaining_build_target" : self.ResultFileList, "common_dependency_file" : self.CommonFileDependency, "create_directory_command" : self.GetCreateDirectoryCommand(self.IntermediateDirectoryList), "clean_command" : self.GetRemoveDirectoryCommand(["$(OUTPUT_DIR)"]), "cleanall_command" : self.GetRemoveDirectoryCommand(["$(DEBUG_DIR)", "$(OUTPUT_DIR)"]), "dependent_library_build_directory" : self.LibraryBuildDirectoryList, "library_build_command" : LibraryMakeCommandList, "file_macro" : FileMacroList, "file_build_target" : self.BuildTargetList, "backward_compatible_target": BcTargetList, "INCLUDETAG" : "\n".join([self._INCLUDE_CMD_[self._FileType] + " " + os.path.join("$(MODULE_BUILD_DIR)","dependency"), self._INCLUDE_CMD_[self._FileType] + " " + os.path.join("$(MODULE_BUILD_DIR)","deps_target") ]) } return MakefileTemplateDict def ParserGenerateFfsCmd(self): #Add Ffs cmd to self.BuildTargetList OutputFile = '' DepsFileList = [] for Cmd in self.GenFfsList: if Cmd[2]: for CopyCmd in Cmd[2]: Src, Dst = CopyCmd Src = self.ReplaceMacro(Src) Dst = self.ReplaceMacro(Dst) if Dst not in self.ResultFileList: self.ResultFileList.append(Dst) if '%s :' %(Dst) not in self.BuildTargetList: self.BuildTargetList.append("%s : %s" %(Dst,Src)) self.BuildTargetList.append('\t' + self._CP_TEMPLATE_[self._Platform] %{'Src': Src, 'Dst': Dst}) FfsCmdList = Cmd[0] for index, Str in enumerate(FfsCmdList): if '-o' == Str: OutputFile = FfsCmdList[index + 1] if '-i' == Str or "-oi" == Str: if DepsFileList == []: DepsFileList = [FfsCmdList[index + 1]] else: DepsFileList.append(FfsCmdList[index + 1]) DepsFileString = ' '.join(DepsFileList).strip() if DepsFileString == '': continue OutputFile = self.ReplaceMacro(OutputFile) self.ResultFileList.append(OutputFile) DepsFileString = self.ReplaceMacro(DepsFileString) self.BuildTargetList.append('%s : %s' % (OutputFile, DepsFileString)) CmdString = ' '.join(FfsCmdList).strip() CmdString = self.ReplaceMacro(CmdString) self.BuildTargetList.append('\t%s' % CmdString) self.ParseSecCmd(DepsFileList, Cmd[1]) for SecOutputFile, SecDepsFile, SecCmd in self.FfsOutputFileList : self.BuildTargetList.append('%s : %s' % (self.ReplaceMacro(SecOutputFile), self.ReplaceMacro(SecDepsFile))) self.BuildTargetList.append('\t%s' % self.ReplaceMacro(SecCmd)) self.FfsOutputFileList = [] def ParseSecCmd(self, OutputFileList, CmdTuple): for OutputFile in OutputFileList: for SecCmdStr in CmdTuple: SecDepsFileList = [] SecCmdList = SecCmdStr.split() CmdName = SecCmdList[0] for index, CmdItem in enumerate(SecCmdList): if '-o' == CmdItem and OutputFile == SecCmdList[index + 1]: index = index + 1 while index + 1 < len(SecCmdList): if not SecCmdList[index+1].startswith('-'): SecDepsFileList.append(SecCmdList[index + 1]) index = index + 1 if CmdName == 'Trim': SecDepsFileList.append(os.path.join('$(DEBUG_DIR)', os.path.basename(OutputFile).replace('offset', 'efi'))) if OutputFile.endswith('.ui') or OutputFile.endswith('.ver'): SecDepsFileList.append(os.path.join('$(MODULE_DIR)', '$(MODULE_FILE)')) self.FfsOutputFileList.append((OutputFile, ' '.join(SecDepsFileList), SecCmdStr)) if len(SecDepsFileList) > 0: self.ParseSecCmd(SecDepsFileList, CmdTuple) break else: continue def ReplaceMacro(self, str): for Macro in self.MacroList: if self._AutoGenObject.Macros[Macro] and os.path.normcase(self._AutoGenObject.Macros[Macro]) in os.path.normcase(str): replace_dir = str[os.path.normcase(str).index(os.path.normcase(self._AutoGenObject.Macros[Macro])): os.path.normcase(str).index( os.path.normcase(self._AutoGenObject.Macros[Macro])) + len(self._AutoGenObject.Macros[Macro])] str = str.replace(replace_dir, '$(' + Macro + ')') return str def CommandExceedLimit(self): FlagDict = { 'CC' : { 'Macro' : '$(CC_FLAGS)', 'Value' : False}, 'PP' : { 'Macro' : '$(PP_FLAGS)', 'Value' : False}, 'APP' : { 'Macro' : '$(APP_FLAGS)', 'Value' : False}, 'ASLPP' : { 'Macro' : '$(ASLPP_FLAGS)', 'Value' : False}, 'VFRPP' : { 'Macro' : '$(VFRPP_FLAGS)', 'Value' : False}, 'ASM' : { 'Macro' : '$(ASM_FLAGS)', 'Value' : False}, 'ASLCC' : { 'Macro' : '$(ASLCC_FLAGS)', 'Value' : False}, } RespDict = {} FileTypeList = [] IncPrefix = self._INC_FLAG_[self._AutoGenObject.ToolChainFamily] # base on the source files to decide the file type for File in self._AutoGenObject.SourceFileList: for type in self._AutoGenObject.FileTypes: if File in self._AutoGenObject.FileTypes[type]: if type not in FileTypeList: FileTypeList.append(type) # calculate the command-line length if FileTypeList: for type in FileTypeList: BuildTargets = self._AutoGenObject.BuildRules[type].BuildTargets for Target in BuildTargets: CommandList = BuildTargets[Target].Commands for SingleCommand in CommandList: Tool = '' SingleCommandLength = len(SingleCommand) SingleCommandList = SingleCommand.split() if len(SingleCommandList) > 0: for Flag in FlagDict: if '$('+ Flag +')' in SingleCommandList[0]: Tool = Flag break if Tool: if 'PATH' not in self._AutoGenObject.BuildOption[Tool]: EdkLogger.error("build", AUTOGEN_ERROR, "%s_PATH doesn't exist in %s ToolChain and %s Arch." %(Tool, self._AutoGenObject.ToolChain, self._AutoGenObject.Arch), ExtraData="[%s]" % str(self._AutoGenObject)) SingleCommandLength += len(self._AutoGenObject.BuildOption[Tool]['PATH']) for item in SingleCommandList[1:]: if FlagDict[Tool]['Macro'] in item: if 'FLAGS' not in self._AutoGenObject.BuildOption[Tool]: EdkLogger.error("build", AUTOGEN_ERROR, "%s_FLAGS doesn't exist in %s ToolChain and %s Arch." %(Tool, self._AutoGenObject.ToolChain, self._AutoGenObject.Arch), ExtraData="[%s]" % str(self._AutoGenObject)) Str = self._AutoGenObject.BuildOption[Tool]['FLAGS'] for Option in self._AutoGenObject.BuildOption: for Attr in self._AutoGenObject.BuildOption[Option]: if Str.find(Option + '_' + Attr) != -1: Str = Str.replace('$(' + Option + '_' + Attr + ')', self._AutoGenObject.BuildOption[Option][Attr]) while(Str.find('$(') != -1): for macro in self._AutoGenObject.Macros: MacroName = '$('+ macro + ')' if (Str.find(MacroName) != -1): Str = Str.replace(MacroName, self._AutoGenObject.Macros[macro]) break else: break SingleCommandLength += len(Str) elif '$(INC)' in item: SingleCommandLength += self._AutoGenObject.IncludePathLength + len(IncPrefix) * len(self._AutoGenObject.IncludePathList) elif item.find('$(') != -1: Str = item for Option in self._AutoGenObject.BuildOption: for Attr in self._AutoGenObject.BuildOption[Option]: if Str.find(Option + '_' + Attr) != -1: Str = Str.replace('$(' + Option + '_' + Attr + ')', self._AutoGenObject.BuildOption[Option][Attr]) while(Str.find('$(') != -1): for macro in self._AutoGenObject.Macros: MacroName = '$('+ macro + ')' if (Str.find(MacroName) != -1): Str = Str.replace(MacroName, self._AutoGenObject.Macros[macro]) break else: break SingleCommandLength += len(Str) if SingleCommandLength > GlobalData.gCommandMaxLength: FlagDict[Tool]['Value'] = True # generate the response file content by combine the FLAGS and INC for Flag in FlagDict: if FlagDict[Flag]['Value']: Key = Flag + '_RESP' RespMacro = FlagDict[Flag]['Macro'].replace('FLAGS', 'RESP') Value = self._AutoGenObject.BuildOption[Flag]['FLAGS'] for inc in self._AutoGenObject.IncludePathList: Value += ' ' + IncPrefix + inc for Option in self._AutoGenObject.BuildOption: for Attr in self._AutoGenObject.BuildOption[Option]: if Value.find(Option + '_' + Attr) != -1: Value = Value.replace('$(' + Option + '_' + Attr + ')', self._AutoGenObject.BuildOption[Option][Attr]) while (Value.find('$(') != -1): for macro in self._AutoGenObject.Macros: MacroName = '$('+ macro + ')' if (Value.find(MacroName) != -1): Value = Value.replace(MacroName, self._AutoGenObject.Macros[macro]) break else: break if self._AutoGenObject.ToolChainFamily == 'GCC': RespDict[Key] = Value.replace('\\', '/') else: RespDict[Key] = Value for Target in BuildTargets: for i, SingleCommand in enumerate(BuildTargets[Target].Commands): if FlagDict[Flag]['Macro'] in SingleCommand: BuildTargets[Target].Commands[i] = SingleCommand.replace('$(INC)', '').replace(FlagDict[Flag]['Macro'], RespMacro) return RespDict def ProcessBuildTargetList(self, RespFile, ToolsDef): # # Search dependency file list for each source file # ForceIncludedFile = [] for File in self._AutoGenObject.AutoGenFileList: if File.Ext == '.h': ForceIncludedFile.append(File) SourceFileList = [] OutPutFileList = [] for Target in self._AutoGenObject.IntroTargetList: SourceFileList.extend(Target.Inputs) OutPutFileList.extend(Target.Outputs) if OutPutFileList: for Item in OutPutFileList: if Item in SourceFileList: SourceFileList.remove(Item) FileDependencyDict = {item:ForceIncludedFile for item in SourceFileList} for Dependency in FileDependencyDict.values(): self.DependencyHeaderFileSet.update(set(Dependency)) # Get a set of unique package includes from MetaFile parentMetaFileIncludes = set() for aInclude in self._AutoGenObject.PackageIncludePathList: aIncludeName = str(aInclude) parentMetaFileIncludes.add(aIncludeName.lower()) # Check if header files are listed in metafile # Get a set of unique module header source files from MetaFile headerFilesInMetaFileSet = set() for aFile in self._AutoGenObject.SourceFileList: aFileName = str(aFile) if not aFileName.endswith('.h'): continue headerFilesInMetaFileSet.add(aFileName.lower()) # Get a set of unique module autogen files localAutoGenFileSet = set() for aFile in self._AutoGenObject.AutoGenFileList: localAutoGenFileSet.add(str(aFile).lower()) # Get a set of unique module dependency header files # Exclude autogen files and files not in the source directory # and files that are under the package include list headerFileDependencySet = set() localSourceDir = str(self._AutoGenObject.SourceDir).lower() for Dependency in FileDependencyDict.values(): for aFile in Dependency: aFileName = str(aFile).lower() # Exclude non-header files if not aFileName.endswith('.h'): continue # Exclude autogen files if aFileName in localAutoGenFileSet: continue # Exclude include out of local scope if localSourceDir not in aFileName: continue # Exclude files covered by package includes pathNeeded = True for aIncludePath in parentMetaFileIncludes: if aIncludePath in aFileName: pathNeeded = False break if not pathNeeded: continue # Keep the file to be checked headerFileDependencySet.add(aFileName) # Check if a module dependency header file is missing from the module's MetaFile for aFile in headerFileDependencySet: if aFile in headerFilesInMetaFileSet: continue if GlobalData.gUseHashCache: GlobalData.gModuleBuildTracking[self._AutoGenObject] = 'FAIL_METAFILE' EdkLogger.warn("build","Module MetaFile [Sources] is missing local header!", ExtraData = "Local Header: " + aFile + " not found in " + self._AutoGenObject.MetaFile.Path ) for File,Dependency in FileDependencyDict.items(): if not Dependency: continue self._AutoGenObject.AutoGenDepSet |= set(Dependency) CmdSumDict = {} CmdTargetDict = {} CmdCppDict = {} DependencyDict = FileDependencyDict.copy() # Convert target description object to target string in makefile if self._AutoGenObject.BuildRuleFamily == TAB_COMPILER_MSFT and TAB_C_CODE_FILE in self._AutoGenObject.Targets: for T in self._AutoGenObject.Targets[TAB_C_CODE_FILE]: NewFile = self.PlaceMacro(str(T), self.Macros) if not self.ObjTargetDict.get(T.Target.SubDir): self.ObjTargetDict[T.Target.SubDir] = set() self.ObjTargetDict[T.Target.SubDir].add(NewFile) for Type in self._AutoGenObject.Targets: resp_file_number = 0 for T in self._AutoGenObject.Targets[Type]: # Generate related macros if needed if T.GenFileListMacro and T.FileListMacro not in self.FileListMacros: self.FileListMacros[T.FileListMacro] = [] if T.GenListFile and T.ListFileMacro not in self.ListFileMacros: self.ListFileMacros[T.ListFileMacro] = [] if T.GenIncListFile and T.IncListFileMacro not in self.ListFileMacros: self.ListFileMacros[T.IncListFileMacro] = [] Deps = [] CCodeDeps = [] # Add force-dependencies for Dep in T.Dependencies: Deps.append(self.PlaceMacro(str(Dep), self.Macros)) if Dep != '$(MAKE_FILE)': CCodeDeps.append(self.PlaceMacro(str(Dep), self.Macros)) # Add inclusion-dependencies if len(T.Inputs) == 1 and T.Inputs[0] in FileDependencyDict: for F in FileDependencyDict[T.Inputs[0]]: Deps.append(self.PlaceMacro(str(F), self.Macros)) # Add source-dependencies for F in T.Inputs: NewFile = self.PlaceMacro(str(F), self.Macros) # In order to use file list macro as dependency if T.GenListFile: # gnu tools need forward slash path separator, even on Windows self.ListFileMacros[T.ListFileMacro].append(str(F).replace ('\\', '/')) self.FileListMacros[T.FileListMacro].append(NewFile) elif T.GenFileListMacro: self.FileListMacros[T.FileListMacro].append(NewFile) else: Deps.append(NewFile) for key in self.FileListMacros: self.FileListMacros[key].sort() # Use file list macro as dependency if T.GenFileListMacro: Deps.append("$(%s)" % T.FileListMacro) if Type in [TAB_OBJECT_FILE, TAB_STATIC_LIBRARY]: Deps.append("$(%s)" % T.ListFileMacro) if self._AutoGenObject.BuildRuleFamily == TAB_COMPILER_MSFT and Type == TAB_C_CODE_FILE: T, CmdTarget, CmdTargetDict, CmdCppDict = self.ParserCCodeFile(T, Type, CmdSumDict, CmdTargetDict, CmdCppDict, DependencyDict, RespFile, ToolsDef, resp_file_number) resp_file_number += 1 TargetDict = {"target": self.PlaceMacro(T.Target.Path, self.Macros), "cmd": "\n\t".join(T.Commands),"deps": CCodeDeps} CmdLine = self._BUILD_TARGET_TEMPLATE.Replace(TargetDict).rstrip().replace('\t$(OBJLIST', '$(OBJLIST') if T.Commands: CmdLine = '%s%s' %(CmdLine, TAB_LINE_BREAK) if CCodeDeps or CmdLine: self.BuildTargetList.append(CmdLine) else: TargetDict = {"target": self.PlaceMacro(T.Target.Path, self.Macros), "cmd": "\n\t".join(T.Commands),"deps": Deps} self.BuildTargetList.append(self._BUILD_TARGET_TEMPLATE.Replace(TargetDict)) # Add a Makefile rule for targets generating multiple files. # The main output is a prerequisite for the other output files. for i in T.Outputs[1:]: AnnexeTargetDict = {"target": self.PlaceMacro(i.Path, self.Macros), "cmd": "", "deps": self.PlaceMacro(T.Target.Path, self.Macros)} self.BuildTargetList.append(self._BUILD_TARGET_TEMPLATE.Replace(AnnexeTargetDict)) def ParserCCodeFile(self, T, Type, CmdSumDict, CmdTargetDict, CmdCppDict, DependencyDict, RespFile, ToolsDef, resp_file_number): SaveFilePath = os.path.join(RespFile, "cc_resp_%s.txt" % resp_file_number) if not CmdSumDict: for item in self._AutoGenObject.Targets[Type]: CmdSumDict[item.Target.SubDir] = item.Target.BaseName for CppPath in item.Inputs: Path = self.PlaceMacro(CppPath.Path, self.Macros) if CmdCppDict.get(item.Target.SubDir): CmdCppDict[item.Target.SubDir].append(Path) else: CmdCppDict[item.Target.SubDir] = ['$(MAKE_FILE)', Path] if CppPath.Path in DependencyDict: for Temp in DependencyDict[CppPath.Path]: try: Path = self.PlaceMacro(Temp.Path, self.Macros) except: continue if Path not in (self.CommonFileDependency + CmdCppDict[item.Target.SubDir]): CmdCppDict[item.Target.SubDir].append(Path) if T.Commands: CommandList = T.Commands[:] for Item in CommandList[:]: SingleCommandList = Item.split() if len(SingleCommandList) > 0 and self.CheckCCCmd(SingleCommandList): for Temp in SingleCommandList: if Temp.startswith('/Fo'): CmdSign = '%s%s' % (Temp.rsplit(TAB_SLASH, 1)[0], TAB_SLASH) break else: continue if CmdSign not in list(CmdTargetDict.keys()): cmd = Item.replace(Temp, CmdSign) if SingleCommandList[-1] in cmd: CmdTargetDict[CmdSign] = [cmd.replace(SingleCommandList[-1], "").rstrip(), SingleCommandList[-1]] else: # CmdTargetDict[CmdSign] = "%s %s" % (CmdTargetDict[CmdSign], SingleCommandList[-1]) CmdTargetDict[CmdSign].append(SingleCommandList[-1]) Index = CommandList.index(Item) CommandList.pop(Index) if SingleCommandList[-1].endswith("%s%s.c" % (TAB_SLASH, CmdSumDict[CmdSign[3:].rsplit(TAB_SLASH, 1)[0]])): Cpplist = CmdCppDict[T.Target.SubDir] Cpplist.insert(0, '$(OBJLIST_%d): ' % list(self.ObjTargetDict.keys()).index(T.Target.SubDir)) source_files = CmdTargetDict[CmdSign][1:] source_files.insert(0, " ") if len(source_files)>2: SaveFileOnChange(SaveFilePath, " ".join(source_files), False) T.Commands[Index] = '%s\n\t%s $(cc_resp_%s)' % ( ' \\\n\t'.join(Cpplist), CmdTargetDict[CmdSign][0], resp_file_number) ToolsDef.append("cc_resp_%s = @%s" % (resp_file_number, SaveFilePath)) elif len(source_files)<=2 and len(" ".join(CmdTargetDict[CmdSign][:2]))>GlobalData.gCommandMaxLength: SaveFileOnChange(SaveFilePath, " ".join(source_files), False) T.Commands[Index] = '%s\n\t%s $(cc_resp_%s)' % ( ' \\\n\t'.join(Cpplist), CmdTargetDict[CmdSign][0], resp_file_number) ToolsDef.append("cc_resp_%s = @%s" % (resp_file_number, SaveFilePath)) else: T.Commands[Index] = '%s\n\t%s' % (' \\\n\t'.join(Cpplist), " ".join(CmdTargetDict[CmdSign])) else: T.Commands.pop(Index) return T, CmdSumDict, CmdTargetDict, CmdCppDict def CheckCCCmd(self, CommandList): for cmd in CommandList: if '$(CC)' in cmd: return True return False ## For creating makefile targets for dependent libraries def ProcessDependentLibrary(self): for LibraryAutoGen in self._AutoGenObject.LibraryAutoGenList: if not LibraryAutoGen.IsBinaryModule: self.LibraryBuildDirectoryList.append(self.PlaceMacro(LibraryAutoGen.BuildDir, self.Macros)) ## Return a list containing source file's dependencies # # @param FileList The list of source files # @param ForceInculeList The list of files which will be included forcely # @param SearchPathList The list of search path # # @retval dict The mapping between source file path and its dependencies # def GetFileDependency(self, FileList, ForceInculeList, SearchPathList): Dependency = {} for F in FileList: Dependency[F] = GetDependencyList(self._AutoGenObject, self.FileCache, F, ForceInculeList, SearchPathList) return Dependency ## CustomMakefile class # # This class encapsules makefie and its generation for module. It uses template to generate # the content of makefile. The content of makefile will be got from ModuleAutoGen object. # class CustomMakefile(BuildFile): ## template used to generate the makefile for module with custom makefile _TEMPLATE_ = TemplateString('''\ ${makefile_header} # # Platform Macro Definition # PLATFORM_NAME = ${platform_name} PLATFORM_GUID = ${platform_guid} PLATFORM_VERSION = ${platform_version} PLATFORM_RELATIVE_DIR = ${platform_relative_directory} PLATFORM_DIR = ${platform_dir} PLATFORM_OUTPUT_DIR = ${platform_output_directory} # # Module Macro Definition # MODULE_NAME = ${module_name} MODULE_GUID = ${module_guid} MODULE_NAME_GUID = ${module_name_guid} MODULE_VERSION = ${module_version} MODULE_TYPE = ${module_type} MODULE_FILE = ${module_file} MODULE_FILE_BASE_NAME = ${module_file_base_name} BASE_NAME = $(MODULE_NAME) MODULE_RELATIVE_DIR = ${module_relative_directory} MODULE_DIR = ${module_dir} # # Build Configuration Macro Definition # ARCH = ${architecture} TOOLCHAIN = ${toolchain_tag} TOOLCHAIN_TAG = ${toolchain_tag} TARGET = ${build_target} # # Build Directory Macro Definition # # PLATFORM_BUILD_DIR = ${platform_build_directory} BUILD_DIR = ${platform_build_directory} BIN_DIR = $(BUILD_DIR)${separator}${architecture} LIB_DIR = $(BIN_DIR) MODULE_BUILD_DIR = ${module_build_directory} OUTPUT_DIR = ${module_output_directory} DEBUG_DIR = ${module_debug_directory} DEST_DIR_OUTPUT = $(OUTPUT_DIR) DEST_DIR_DEBUG = $(DEBUG_DIR) # # Tools definitions specific to this module # ${BEGIN}${module_tool_definitions} ${END} MAKE_FILE = ${makefile_path} # # Shell Command Macro # ${BEGIN}${shell_command_code} = ${shell_command} ${END} ${custom_makefile_content} # # Target used when called from platform makefile, which will bypass the build of dependent libraries # pbuild: init all # # ModuleTarget # mbuild: init all # # Build Target used in multi-thread build mode, which no init target is needed # tbuild: all # # Initialization target: print build information and create necessary directories # init: \t-@echo Building ... $(MODULE_DIR)${separator}$(MODULE_FILE) [$(ARCH)] ${BEGIN}\t-@${create_directory_command}\n${END}\ ''') ## Constructor of CustomMakefile # # @param ModuleAutoGen Object of ModuleAutoGen class # def __init__(self, ModuleAutoGen): BuildFile.__init__(self, ModuleAutoGen) self.PlatformInfo = self._AutoGenObject.PlatformInfo self.IntermediateDirectoryList = ["$(DEBUG_DIR)", "$(OUTPUT_DIR)"] self.DependencyHeaderFileSet = set() # Compose a dict object containing information used to do replacement in template @property def _TemplateDict(self): Separator = self._SEP_[self._Platform] MyAgo = self._AutoGenObject if self._FileType not in MyAgo.CustomMakefile: EdkLogger.error('build', OPTION_NOT_SUPPORTED, "No custom makefile for %s" % self._FileType, ExtraData="[%s]" % str(MyAgo)) MakefilePath = mws.join( MyAgo.WorkspaceDir, MyAgo.CustomMakefile[self._FileType] ) try: CustomMakefile = open(MakefilePath, 'r').read() except: EdkLogger.error('build', FILE_OPEN_FAILURE, File=str(MyAgo), ExtraData=MyAgo.CustomMakefile[self._FileType]) # tools definitions ToolsDef = [] for Tool in MyAgo.BuildOption: # Don't generate MAKE_FLAGS in makefile. It's put in environment variable. if Tool == "MAKE": continue for Attr in MyAgo.BuildOption[Tool]: if Attr == "FAMILY": continue elif Attr == "PATH": ToolsDef.append("%s = %s" % (Tool, MyAgo.BuildOption[Tool][Attr])) else: ToolsDef.append("%s_%s = %s" % (Tool, Attr, MyAgo.BuildOption[Tool][Attr])) ToolsDef.append("") MakefileName = self.getMakefileName() MakefileTemplateDict = { "makefile_header" : self._FILE_HEADER_[self._FileType], "makefile_path" : os.path.join("$(MODULE_BUILD_DIR)", MakefileName), "platform_name" : self.PlatformInfo.Name, "platform_guid" : self.PlatformInfo.Guid, "platform_version" : self.PlatformInfo.Version, "platform_relative_directory": self.PlatformInfo.SourceDir, "platform_output_directory" : self.PlatformInfo.OutputDir, "platform_dir" : MyAgo.Macros["PLATFORM_DIR"], "module_name" : MyAgo.Name, "module_guid" : MyAgo.Guid, "module_name_guid" : MyAgo.UniqueBaseName, "module_version" : MyAgo.Version, "module_type" : MyAgo.ModuleType, "module_file" : MyAgo.MetaFile, "module_file_base_name" : MyAgo.MetaFile.BaseName, "module_relative_directory" : MyAgo.SourceDir, "module_dir" : mws.join (MyAgo.WorkspaceDir, MyAgo.SourceDir), "architecture" : MyAgo.Arch, "toolchain_tag" : MyAgo.ToolChain, "build_target" : MyAgo.BuildTarget, "platform_build_directory" : self.PlatformInfo.BuildDir, "module_build_directory" : MyAgo.BuildDir, "module_output_directory" : MyAgo.OutputDir, "module_debug_directory" : MyAgo.DebugDir, "separator" : Separator, "module_tool_definitions" : ToolsDef, "shell_command_code" : list(self._SHELL_CMD_[self._Platform].keys()), "shell_command" : list(self._SHELL_CMD_[self._Platform].values()), "create_directory_command" : self.GetCreateDirectoryCommand(self.IntermediateDirectoryList), "custom_makefile_content" : CustomMakefile } return MakefileTemplateDict ## PlatformMakefile class # # This class encapsules makefie and its generation for platform. It uses # template to generate the content of makefile. The content of makefile will be # got from PlatformAutoGen object. # class PlatformMakefile(BuildFile): ## template used to generate the makefile for platform _TEMPLATE_ = TemplateString('''\ ${makefile_header} # # Platform Macro Definition # PLATFORM_NAME = ${platform_name} PLATFORM_GUID = ${platform_guid} PLATFORM_VERSION = ${platform_version} PLATFORM_FILE = ${platform_file} PLATFORM_DIR = ${platform_dir} PLATFORM_OUTPUT_DIR = ${platform_output_directory} # # Build Configuration Macro Definition # TOOLCHAIN = ${toolchain_tag} TOOLCHAIN_TAG = ${toolchain_tag} TARGET = ${build_target} # # Build Directory Macro Definition # BUILD_DIR = ${platform_build_directory} FV_DIR = ${platform_build_directory}${separator}FV # # Shell Command Macro # ${BEGIN}${shell_command_code} = ${shell_command} ${END} MAKE = ${make_path} MAKE_FILE = ${makefile_path} # # Default target # all: init build_libraries build_modules # # Initialization target: print build information and create necessary directories # init: \t-@echo Building ... $(PLATFORM_FILE) [${build_architecture_list}] \t${BEGIN}-@${create_directory_command} \t${END} # # library build target # libraries: init build_libraries # # module build target # modules: init build_libraries build_modules # # Build all libraries: # build_libraries: ${BEGIN}\t@"$(MAKE)" $(MAKE_FLAGS) -f ${library_makefile_list} pbuild ${END}\t@cd $(BUILD_DIR) # # Build all modules: # build_modules: ${BEGIN}\t@"$(MAKE)" $(MAKE_FLAGS) -f ${module_makefile_list} pbuild ${END}\t@cd $(BUILD_DIR) # # Clean intermediate files # clean: \t${BEGIN}-@${library_build_command} clean \t${END}${BEGIN}-@${module_build_command} clean \t${END}@cd $(BUILD_DIR) # # Clean all generated files except to makefile # cleanall: ${BEGIN}\t${cleanall_command} ${END} # # Clean all library files # cleanlib: \t${BEGIN}-@${library_build_command} cleanall \t${END}@cd $(BUILD_DIR)\n ''') ## Constructor of PlatformMakefile # # @param ModuleAutoGen Object of PlatformAutoGen class # def __init__(self, PlatformAutoGen): BuildFile.__init__(self, PlatformAutoGen) self.ModuleBuildCommandList = [] self.ModuleMakefileList = [] self.IntermediateDirectoryList = [] self.ModuleBuildDirectoryList = [] self.LibraryBuildDirectoryList = [] self.LibraryMakeCommandList = [] self.DependencyHeaderFileSet = set() # Compose a dict object containing information used to do replacement in template @property def _TemplateDict(self): Separator = self._SEP_[self._Platform] MyAgo = self._AutoGenObject if "MAKE" not in MyAgo.ToolDefinition or "PATH" not in MyAgo.ToolDefinition["MAKE"]: EdkLogger.error("build", OPTION_MISSING, "No MAKE command defined. Please check your tools_def.txt!", ExtraData="[%s]" % str(MyAgo)) self.IntermediateDirectoryList = ["$(BUILD_DIR)"] self.ModuleBuildDirectoryList = self.GetModuleBuildDirectoryList() self.LibraryBuildDirectoryList = self.GetLibraryBuildDirectoryList() MakefileName = self.getMakefileName() LibraryMakefileList = [] LibraryMakeCommandList = [] for D in self.LibraryBuildDirectoryList: D = self.PlaceMacro(D, {"BUILD_DIR":MyAgo.BuildDir}) Makefile = os.path.join(D, MakefileName) Command = self._MAKE_TEMPLATE_[self._Platform] % {"file":Makefile} LibraryMakefileList.append(Makefile) LibraryMakeCommandList.append(Command) self.LibraryMakeCommandList = LibraryMakeCommandList ModuleMakefileList = [] ModuleMakeCommandList = [] for D in self.ModuleBuildDirectoryList: D = self.PlaceMacro(D, {"BUILD_DIR":MyAgo.BuildDir}) Makefile = os.path.join(D, MakefileName) Command = self._MAKE_TEMPLATE_[self._Platform] % {"file":Makefile} ModuleMakefileList.append(Makefile) ModuleMakeCommandList.append(Command) MakefileTemplateDict = { "makefile_header" : self._FILE_HEADER_[self._FileType], "makefile_path" : os.path.join("$(BUILD_DIR)", MakefileName), "make_path" : MyAgo.ToolDefinition["MAKE"]["PATH"], "makefile_name" : MakefileName, "platform_name" : MyAgo.Name, "platform_guid" : MyAgo.Guid, "platform_version" : MyAgo.Version, "platform_file" : MyAgo.MetaFile, "platform_relative_directory": MyAgo.SourceDir, "platform_output_directory" : MyAgo.OutputDir, "platform_build_directory" : MyAgo.BuildDir, "platform_dir" : MyAgo.Macros["PLATFORM_DIR"], "toolchain_tag" : MyAgo.ToolChain, "build_target" : MyAgo.BuildTarget, "shell_command_code" : list(self._SHELL_CMD_[self._Platform].keys()), "shell_command" : list(self._SHELL_CMD_[self._Platform].values()), "build_architecture_list" : MyAgo.Arch, "architecture" : MyAgo.Arch, "separator" : Separator, "create_directory_command" : self.GetCreateDirectoryCommand(self.IntermediateDirectoryList), "cleanall_command" : self.GetRemoveDirectoryCommand(self.IntermediateDirectoryList), "library_makefile_list" : LibraryMakefileList, "module_makefile_list" : ModuleMakefileList, "library_build_command" : LibraryMakeCommandList, "module_build_command" : ModuleMakeCommandList, } return MakefileTemplateDict ## Get the root directory list for intermediate files of all modules build # # @retval list The list of directory # def GetModuleBuildDirectoryList(self): DirList = [] for ModuleAutoGen in self._AutoGenObject.ModuleAutoGenList: if not ModuleAutoGen.IsBinaryModule: DirList.append(os.path.join(self._AutoGenObject.BuildDir, ModuleAutoGen.BuildDir)) return DirList ## Get the root directory list for intermediate files of all libraries build # # @retval list The list of directory # def GetLibraryBuildDirectoryList(self): DirList = [] for LibraryAutoGen in self._AutoGenObject.LibraryAutoGenList: if not LibraryAutoGen.IsBinaryModule: DirList.append(os.path.join(self._AutoGenObject.BuildDir, LibraryAutoGen.BuildDir)) return DirList ## TopLevelMakefile class # # This class encapsules makefie and its generation for entrance makefile. It # uses template to generate the content of makefile. The content of makefile # will be got from WorkspaceAutoGen object. # class TopLevelMakefile(BuildFile): ## template used to generate toplevel makefile _TEMPLATE_ = TemplateString('''${BEGIN}\tGenFds -f ${fdf_file} --conf=${conf_directory} -o ${platform_build_directory} -t ${toolchain_tag} -b ${build_target} -p ${active_platform} -a ${build_architecture_list} ${extra_options}${END}${BEGIN} -r ${fd} ${END}${BEGIN} -i ${fv} ${END}${BEGIN} -C ${cap} ${END}${BEGIN} -D ${macro} ${END}''') ## Constructor of TopLevelMakefile # # @param Workspace Object of WorkspaceAutoGen class # def __init__(self, Workspace): BuildFile.__init__(self, Workspace) self.IntermediateDirectoryList = [] self.DependencyHeaderFileSet = set() # Compose a dict object containing information used to do replacement in template @property def _TemplateDict(self): Separator = self._SEP_[self._Platform] # any platform autogen object is ok because we just need common information MyAgo = self._AutoGenObject if "MAKE" not in MyAgo.ToolDefinition or "PATH" not in MyAgo.ToolDefinition["MAKE"]: EdkLogger.error("build", OPTION_MISSING, "No MAKE command defined. Please check your tools_def.txt!", ExtraData="[%s]" % str(MyAgo)) for Arch in MyAgo.ArchList: self.IntermediateDirectoryList.append(Separator.join(["$(BUILD_DIR)", Arch])) self.IntermediateDirectoryList.append("$(FV_DIR)") # TRICK: for not generating GenFds call in makefile if no FDF file MacroList = [] if MyAgo.FdfFile is not None and MyAgo.FdfFile != "": FdfFileList = [MyAgo.FdfFile] # macros passed to GenFds MacroDict = {} MacroDict.update(GlobalData.gGlobalDefines) MacroDict.update(GlobalData.gCommandLineDefines) for MacroName in MacroDict: if MacroDict[MacroName] != "": MacroList.append('"%s=%s"' % (MacroName, MacroDict[MacroName].replace('\\', '\\\\'))) else: MacroList.append('"%s"' % MacroName) else: FdfFileList = [] # pass extra common options to external program called in makefile, currently GenFds.exe ExtraOption = '' LogLevel = EdkLogger.GetLevel() if LogLevel == EdkLogger.VERBOSE: ExtraOption += " -v" elif LogLevel <= EdkLogger.DEBUG_9: ExtraOption += " -d %d" % (LogLevel - 1) elif LogLevel == EdkLogger.QUIET: ExtraOption += " -q" if GlobalData.gCaseInsensitive: ExtraOption += " -c" if not GlobalData.gEnableGenfdsMultiThread: ExtraOption += " --no-genfds-multi-thread" if GlobalData.gIgnoreSource: ExtraOption += " --ignore-sources" for pcd in GlobalData.BuildOptionPcd: if pcd[2]: pcdname = '.'.join(pcd[0:3]) else: pcdname = '.'.join(pcd[0:2]) if pcd[3].startswith('{'): ExtraOption += " --pcd " + pcdname + '=' + 'H' + '"' + pcd[3] + '"' else: ExtraOption += " --pcd " + pcdname + '=' + pcd[3] MakefileName = self.getMakefileName() SubBuildCommandList = [] for A in MyAgo.ArchList: Command = self._MAKE_TEMPLATE_[self._Platform] % {"file":os.path.join("$(BUILD_DIR)", A, MakefileName)} SubBuildCommandList.append(Command) MakefileTemplateDict = { "makefile_header" : self._FILE_HEADER_[self._FileType], "makefile_path" : os.path.join("$(BUILD_DIR)", MakefileName), "make_path" : MyAgo.ToolDefinition["MAKE"]["PATH"], "platform_name" : MyAgo.Name, "platform_guid" : MyAgo.Guid, "platform_version" : MyAgo.Version, "platform_build_directory" : MyAgo.BuildDir, "conf_directory" : GlobalData.gConfDirectory, "toolchain_tag" : MyAgo.ToolChain, "build_target" : MyAgo.BuildTarget, "shell_command_code" : list(self._SHELL_CMD_[self._Platform].keys()), "shell_command" : list(self._SHELL_CMD_[self._Platform].values()), 'arch' : list(MyAgo.ArchList), "build_architecture_list" : ','.join(MyAgo.ArchList), "separator" : Separator, "create_directory_command" : self.GetCreateDirectoryCommand(self.IntermediateDirectoryList), "cleanall_command" : self.GetRemoveDirectoryCommand(self.IntermediateDirectoryList), "sub_build_command" : SubBuildCommandList, "fdf_file" : FdfFileList, "active_platform" : str(MyAgo), "fd" : MyAgo.FdTargetList, "fv" : MyAgo.FvTargetList, "cap" : MyAgo.CapTargetList, "extra_options" : ExtraOption, "macro" : MacroList, } return MakefileTemplateDict ## Get the root directory list for intermediate files of all modules build # # @retval list The list of directory # def GetModuleBuildDirectoryList(self): DirList = [] for ModuleAutoGen in self._AutoGenObject.ModuleAutoGenList: if not ModuleAutoGen.IsBinaryModule: DirList.append(os.path.join(self._AutoGenObject.BuildDir, ModuleAutoGen.BuildDir)) return DirList ## Get the root directory list for intermediate files of all libraries build # # @retval list The list of directory # def GetLibraryBuildDirectoryList(self): DirList = [] for LibraryAutoGen in self._AutoGenObject.LibraryAutoGenList: if not LibraryAutoGen.IsBinaryModule: DirList.append(os.path.join(self._AutoGenObject.BuildDir, LibraryAutoGen.BuildDir)) return DirList ## Find dependencies for one source file # # By searching recursively "#include" directive in file, find out all the # files needed by given source file. The dependencies will be only searched # in given search path list. # # @param File The source file # @param ForceInculeList The list of files which will be included forcely # @param SearchPathList The list of search path # # @retval list The list of files the given source file depends on # def GetDependencyList(AutoGenObject, FileCache, File, ForceList, SearchPathList): EdkLogger.debug(EdkLogger.DEBUG_1, "Try to get dependency files for %s" % File) FileStack = [File] + ForceList DependencySet = set() if AutoGenObject.Arch not in gDependencyDatabase: gDependencyDatabase[AutoGenObject.Arch] = {} DepDb = gDependencyDatabase[AutoGenObject.Arch] while len(FileStack) > 0: F = FileStack.pop() FullPathDependList = [] if F in FileCache: for CacheFile in FileCache[F]: FullPathDependList.append(CacheFile) if CacheFile not in DependencySet: FileStack.append(CacheFile) DependencySet.update(FullPathDependList) continue CurrentFileDependencyList = [] if F in DepDb: CurrentFileDependencyList = DepDb[F] else: try: Fd = open(F.Path, 'rb') FileContent = Fd.read() Fd.close() except BaseException as X: EdkLogger.error("build", FILE_OPEN_FAILURE, ExtraData=F.Path + "\n\t" + str(X)) if len(FileContent) == 0: continue try: if FileContent[0] == 0xff or FileContent[0] == 0xfe: FileContent = FileContent.decode('utf-16') else: FileContent = FileContent.decode() except: # The file is not txt file. for example .mcb file continue IncludedFileList = gIncludePattern.findall(FileContent) for Inc in IncludedFileList: Inc = Inc.strip() # if there's macro used to reference header file, expand it HeaderList = gMacroPattern.findall(Inc) if len(HeaderList) == 1 and len(HeaderList[0]) == 2: HeaderType = HeaderList[0][0] HeaderKey = HeaderList[0][1] if HeaderType in gIncludeMacroConversion: Inc = gIncludeMacroConversion[HeaderType] % {"HeaderKey" : HeaderKey} else: # not known macro used in #include, always build the file by # returning a empty dependency FileCache[File] = [] return [] Inc = os.path.normpath(Inc) CurrentFileDependencyList.append(Inc) DepDb[F] = CurrentFileDependencyList CurrentFilePath = F.Dir PathList = [CurrentFilePath] + SearchPathList for Inc in CurrentFileDependencyList: for SearchPath in PathList: FilePath = os.path.join(SearchPath, Inc) if FilePath in gIsFileMap: if not gIsFileMap[FilePath]: continue # If isfile is called too many times, the performance is slow down. elif not os.path.isfile(FilePath): gIsFileMap[FilePath] = False continue else: gIsFileMap[FilePath] = True FilePath = PathClass(FilePath) FullPathDependList.append(FilePath) if FilePath not in DependencySet: FileStack.append(FilePath) break else: EdkLogger.debug(EdkLogger.DEBUG_9, "%s included by %s was not found "\ "in any given path:\n\t%s" % (Inc, F, "\n\t".join(SearchPathList))) FileCache[F] = FullPathDependList DependencySet.update(FullPathDependList) DependencySet.update(ForceList) if File in DependencySet: DependencySet.remove(File) DependencyList = list(DependencySet) # remove duplicate ones return DependencyList # This acts like the main() function for the script, unless it is 'import'ed into another script. if __name__ == '__main__': pass