## @file | |
# Create makefile for MS nmake and GNU make | |
# | |
# Copyright (c) 2007 - 2021, Intel Corporation. All rights reserved.<BR> | |
# Copyright (c) 2020 - 2021, Arm Limited. All rights reserved.<BR> | |
# 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", "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: | |
MakePath = AutoGenObject.ToolDefinition.get('MAKE', {}).get('PATH') | |
if "nmake" in MakePath: | |
self._FileType = NMAKE_FILETYPE | |
else: | |
self._FileType = GMAKE_FILETYPE | |
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 |