## @file
# Create makefile for MS nmake and GNU make
#
# Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.
# This program and the accompanying materials
# are licensed and made available under the terms and conditions of the BSD License
# which accompanies this distribution. The full text of the license may be found at
# http://opensource.org/licenses/bsd-license.php
#
# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#
## Import Modules
#
import Common.LongFilePathOs as os
import sys
import string
import re
import os.path as path
from Common.LongFilePathSupport import OpenLongFilePath as open
from Common.BuildToolError import *
from Common.Misc import *
from Common.String import *
from BuildEngine import *
import Common.GlobalData as GlobalData
## 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,
}
## default makefile type
gMakeType = ""
if sys.platform == "win32":
gMakeType = "nmake"
else:
gMakeType = "gmake"
## 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" : "Makefile",
"gmake" : "GNUmakefile"
}
## 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" : _MAKEFILE_HEADER % _FILE_NAME_["nmake"],
"gmake" : _MAKEFILE_HEADER % _FILE_NAME_["gmake"]
}
## 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_ = {
"nmake" : {
"CP" : "copy /y",
"MV" : "move /y",
"RM" : "del /f /q",
"MD" : "mkdir",
"RD" : "rmdir /s /q",
},
"gmake" : {
"CP" : "cp -f",
"MV" : "mv -f",
"RM" : "rm -f",
"MD" : "mkdir -p",
"RD" : "rm -r -f",
}
}
## directory separator
_SEP_ = {
"nmake" : "\\",
"gmake" : "/"
}
## directory creation template
_MD_TEMPLATE_ = {
"nmake" : 'if not exist %(dir)s $(MD) %(dir)s',
"gmake" : "$(MD) %(dir)s"
}
## directory removal template
_RD_TEMPLATE_ = {
"nmake" : 'if exist %(dir)s $(RD) %(dir)s',
"gmake" : "$(RD) %(dir)s"
}
_CD_TEMPLATE_ = {
"nmake" : 'if exist %(dir)s cd %(dir)s',
"gmake" : "test -e %(dir)s && cd %(dir)s"
}
_MAKE_TEMPLATE_ = {
"nmake" : 'if exist %(file)s "$(MAKE)" $(MAKE_FLAGS) -f %(file)s',
"gmake" : 'test -e %(file)s && "$(MAKE)" $(MAKE_FLAGS) -f %(file)s'
}
_INCLUDE_CMD_ = {
"nmake" : '!INCLUDE',
"gmake" : "include"
}
_INC_FLAG_ = {"MSFT" : "/I", "GCC" : "-I", "INTEL" : "-I", "RVCT" : "-I"}
## Constructor of BuildFile
#
# @param AutoGenObject Object of AutoGen class
#
def __init__(self, AutoGenObject):
self._AutoGenObject = AutoGenObject
self._FileType = gMakeType
## Create build file
#
# @param FileType Type of build file. Only nmake and gmake are supported now.
#
# @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, FileType=gMakeType):
if FileType not in self._FILE_NAME_:
EdkLogger.error("build", PARAMETER_INVALID, "Invalid build type [%s]" % FileType,
ExtraData="[%s]" % str(self._AutoGenObject))
self._FileType = FileType
FileContent = self._TEMPLATE_.Replace(self._TemplateDict)
FileName = self._FILE_NAME_[FileType]
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._FileType] % {'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._FileType] % {'dir':Dir} for Dir in DirList]
def PlaceMacro(self, Path, MacroDefinitions={}):
if Path.startswith("$("):
return Path
else:
PathLength = len(Path)
for MacroName in MacroDefinitions:
MacroValue = MacroDefinitions[MacroName]
MacroValueLength = len(MacroValue)
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 = $(WORKSPACE)${separator}${platform_relative_directory}
PLATFORM_OUTPUT_DIR = ${platform_output_directory}
#
# Module Macro Definition
#
MODULE_NAME = ${module_name}
MODULE_GUID = ${module_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 = $(WORKSPACE)${separator}${module_relative_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}
COMMON_DEPS = ${BEGIN}${common_dependency_file} \\
${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)
#
# Individual Object Build Targets
#
${BEGIN}${file_build_target}
${END}
#
# clean all intermediate files
#
clean:
\t${BEGIN}${clean_command}
\t${END}
#
# 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
#
# 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.SourceFileDatabase = {} # {file type : file path}
self.DestFileDatabase = {} # {file type : file path}
self.FileBuildTargetList = [] # [(src, target string)]
self.BuildTargetList = [] # [target string]
self.PendingBuildTargetList = [] # [FileBuildRule objects]
self.CommonFileDependency = []
self.FileListMacros = {}
self.ListFileMacros = {}
self.FileCache = {}
self.FileDependency = []
self.LibraryBuildCommandList = []
self.LibraryFileList = []
self.LibraryMakefileList = []
self.LibraryBuildDirectoryList = []
self.SystemLibraryList = []
self.Macros = sdict()
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"]
# Compose a dict object containing information used to do replacement in template
def _CreateTemplateDict(self):
if self._FileType not in self._SEP_:
EdkLogger.error("build", PARAMETER_INVALID, "Invalid Makefile type [%s]" % self._FileType,
ExtraData="[%s]" % str(self._AutoGenObject))
Separator = self._SEP_[self._FileType]
# break build if no source files and binary files are found
if len(self._AutoGenObject.SourceFileList) == 0 and len(self._AutoGenObject.BinaryFileList) == 0:
EdkLogger.error("build", AUTOGEN_ERROR, "No files to be built in module [%s, %s, %s]"
% (self._AutoGenObject.BuildTarget, self._AutoGenObject.ToolChain, self._AutoGenObject.Arch),
ExtraData="[%s]" % str(self._AutoGenObject))
# convert dependent libraries to build command
self.ProcessDependentLibrary()
if len(self._AutoGenObject.Module.ModuleEntryPointList) > 0:
ModuleEntryPoint = self._AutoGenObject.Module.ModuleEntryPointList[0]
else:
ModuleEntryPoint = "_ModuleEntryPoint"
# Intel EBC compiler enforces EfiMain
if self._AutoGenObject.AutoGenVersion < 0x00010005 and self._AutoGenObject.Arch == "EBC":
ArchEntryPoint = "EfiMain"
else:
ArchEntryPoint = ModuleEntryPoint
if self._AutoGenObject.Arch == "EBC":
# EBC compiler always use "EfiStart" as entry point. Only applies to EdkII modules
ImageEntryPoint = "EfiStart"
elif self._AutoGenObject.AutoGenVersion < 0x00010005:
# Edk modules use entry point specified in INF file
ImageEntryPoint = ModuleEntryPoint
else:
# EdkII modules always use "_ModuleEntryPoint" as entry point
ImageEntryPoint = "_ModuleEntryPoint"
# tools definitions
ToolsDef = []
IncPrefix = self._INC_FLAG_[self._AutoGenObject.ToolChainFamily]
for Tool in self._AutoGenObject.BuildOption:
for Attr in self._AutoGenObject.BuildOption[Tool]:
Value = self._AutoGenObject.BuildOption[Tool][Attr]
if Attr == "FAMILY":
continue
elif Attr == "PATH":
ToolsDef.append("%s = %s" % (Tool, Value))
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, self._AutoGenObject.IncludePathList)
ToolsDef.append("%s_%s = %s" % (Tool, Attr, Value))
ToolsDef.append("")
# convert source files and binary files to build targets
self.ResultFileList = [str(T.Target) for T in self._AutoGenObject.CodaTargetList]
if len(self.ResultFileList) == 0 and len(self._AutoGenObject.SourceFileList) <> 0:
EdkLogger.error("build", AUTOGEN_ERROR, "Nothing to build",
ExtraData="[%s]" % str(self._AutoGenObject))
self.ProcessBuildTargetList()
# 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 self._AutoGenObject.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)
# Generate macros used to represent files containing list of input files
for ListFileMacro in self.ListFileMacros:
ListFileName = os.path.join(self._AutoGenObject.OutputDir, "%s.lst" % ListFileMacro.lower()[:len(ListFileMacro)-5])
FileMacroList.append("%s = %s" % (ListFileMacro, ListFileName))
SaveFileOnChange(
ListFileName,
"\n".join(self.ListFileMacros[ListFileMacro]),
False
)
# Edk modules need StrDefs.h for string ID
#if self._AutoGenObject.AutoGenVersion < 0x00010005 and len(self._AutoGenObject.UnicodeFileList) > 0:
# BcTargetList = ['strdefs']
#else:
# BcTargetList = []
BcTargetList = []
MakefileName = self._FILE_NAME_[self._FileType]
LibraryMakeCommandList = []
for D in self.LibraryBuildDirectoryList:
Command = self._MAKE_TEMPLATE_[self._FileType] % {"file":os.path.join(D, MakefileName)}
LibraryMakeCommandList.append(Command)
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,
"module_name" : self._AutoGenObject.Name,
"module_guid" : self._AutoGenObject.Guid,
"module_version" : self._AutoGenObject.Version,
"module_type" : self._AutoGenObject.ModuleType,
"module_file" : self._AutoGenObject.MetaFile.Name,
"module_file_base_name" : self._AutoGenObject.MetaFile.BaseName,
"module_relative_directory" : self._AutoGenObject.SourceDir,
"module_extra_defines" : ["%s = %s" % (k, v) for k,v in self._AutoGenObject.Module.Defines.iteritems()],
"architecture" : self._AutoGenObject.Arch,
"toolchain_tag" : self._AutoGenObject.ToolChain,
"build_target" : self._AutoGenObject.BuildTarget,
"platform_build_directory" : self.PlatformInfo.BuildDir,
"module_build_directory" : self._AutoGenObject.BuildDir,
"module_output_directory" : self._AutoGenObject.OutputDir,
"module_debug_directory" : self._AutoGenObject.DebugDir,
"separator" : Separator,
"module_tool_definitions" : ToolsDef,
"shell_command_code" : self._SHELL_CMD_[self._FileType].keys(),
"shell_command" : self._SHELL_CMD_[self._FileType].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,
}
return MakefileTemplateDict
def ProcessBuildTargetList(self):
#
# Search dependency file list for each source file
#
ForceIncludedFile = []
for File in self._AutoGenObject.AutoGenFileList:
if File.Ext == '.h':
ForceIncludedFile.append(File)
SourceFileList = []
for Target in self._AutoGenObject.IntroTargetList:
SourceFileList.extend(Target.Inputs)
self.FileDependency = self.GetFileDependency(
SourceFileList,
ForceIncludedFile,
self._AutoGenObject.IncludePathList + self._AutoGenObject.BuildOptionIncPathList
)
DepSet = None
for File in self.FileDependency:
if not self.FileDependency[File]:
self.FileDependency[File] = ['$(FORCE_REBUILD)']
continue
# skip non-C files
if File.Ext not in [".c", ".C"] or File.Name == "AutoGen.c":
continue
elif DepSet == None:
DepSet = set(self.FileDependency[File])
else:
DepSet &= set(self.FileDependency[File])
# in case nothing in SourceFileList
if DepSet == None:
DepSet = set()
#
# Extract common files list in the dependency files
#
for File in DepSet:
self.CommonFileDependency.append(self.PlaceMacro(File.Path, self.Macros))
for File in self.FileDependency:
# skip non-C files
if File.Ext not in [".c", ".C"] or File.Name == "AutoGen.c":
continue
NewDepSet = set(self.FileDependency[File])
NewDepSet -= DepSet
self.FileDependency[File] = ["$(COMMON_DEPS)"] + list(NewDepSet)
# Convert target description object to target string in makefile
for Type in self._AutoGenObject.Targets:
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 = []
# Add force-dependencies
for Dep in T.Dependencies:
Deps.append(self.PlaceMacro(str(Dep), self.Macros))
# Add inclusion-dependencies
if len(T.Inputs) == 1 and T.Inputs[0] in self.FileDependency:
for F in self.FileDependency[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:
self.ListFileMacros[T.ListFileMacro].append(str(F))
self.FileListMacros[T.FileListMacro].append(NewFile)
elif T.GenFileListMacro:
self.FileListMacros[T.FileListMacro].append(NewFile)
else:
Deps.append(NewFile)
# Use file list macro as dependency
if T.GenFileListMacro:
Deps.append("$(%s)" % T.FileListMacro)
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))
## For creating makefile targets for dependent libraries
def ProcessDependentLibrary(self):
for LibraryAutoGen in self._AutoGenObject.LibraryAutoGenList:
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] = self.GetDependencyList(F, ForceInculeList, SearchPathList)
return Dependency
## Find dependencies for one source file
#
# By searching recursively "#include" directive in file, find out all the
# files needed by given source file. The dependecies 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(self, File, ForceList, SearchPathList):
EdkLogger.debug(EdkLogger.DEBUG_1, "Try to get dependency files for %s" % File)
FileStack = [File] + ForceList
DependencySet = set()
if self._AutoGenObject.Arch not in gDependencyDatabase:
gDependencyDatabase[self._AutoGenObject.Arch] = {}
DepDb = gDependencyDatabase[self._AutoGenObject.Arch]
while len(FileStack) > 0:
F = FileStack.pop()
FullPathDependList = []
if F in self.FileCache:
for CacheFile in self.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, 'r')
except BaseException, X:
EdkLogger.error("build", FILE_OPEN_FAILURE, ExtraData=F.Path+"\n\t"+str(X))
FileContent = Fd.read()
Fd.close()
if len(FileContent) == 0:
continue
if FileContent[0] == 0xff or FileContent[0] == 0xfe:
FileContent = unicode(FileContent, "utf-16")
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
self.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)))
self.FileCache[F] = FullPathDependList
DependencySet.update(FullPathDependList)
DependencySet.update(ForceList)
if File in DependencySet:
DependencySet.remove(File)
DependencyList = list(DependencySet) # remove duplicate ones
return DependencyList
_TemplateDict = property(_CreateTemplateDict)
## 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 = $(WORKSPACE)${separator}${platform_relative_directory}
PLATFORM_OUTPUT_DIR = ${platform_output_directory}
#
# Module Macro Definition
#
MODULE_NAME = ${module_name}
MODULE_GUID = ${module_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 = $(WORKSPACE)${separator}${module_relative_directory}
#
# 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)"]
# Compose a dict object containing information used to do replacement in template
def _CreateTemplateDict(self):
Separator = self._SEP_[self._FileType]
if self._FileType not in self._AutoGenObject.CustomMakefile:
EdkLogger.error('build', OPTION_NOT_SUPPORTED, "No custom makefile for %s" % self._FileType,
ExtraData="[%s]" % str(self._AutoGenObject))
MakefilePath = os.path.join(
self._AutoGenObject.WorkspaceDir,
self._AutoGenObject.CustomMakefile[self._FileType]
)
try:
CustomMakefile = open(MakefilePath, 'r').read()
except:
EdkLogger.error('build', FILE_OPEN_FAILURE, File=str(self._AutoGenObject),
ExtraData=self._AutoGenObject.CustomMakefile[self._FileType])
# tools definitions
ToolsDef = []
for Tool in self._AutoGenObject.BuildOption:
# Don't generate MAKE_FLAGS in makefile. It's put in environment variable.
if Tool == "MAKE":
continue
for Attr in self._AutoGenObject.BuildOption[Tool]:
if Attr == "FAMILY":
continue
elif Attr == "PATH":
ToolsDef.append("%s = %s" % (Tool, self._AutoGenObject.BuildOption[Tool][Attr]))
else:
ToolsDef.append("%s_%s = %s" % (Tool, Attr, self._AutoGenObject.BuildOption[Tool][Attr]))
ToolsDef.append("")
MakefileName = self._FILE_NAME_[self._FileType]
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,
"module_name" : self._AutoGenObject.Name,
"module_guid" : self._AutoGenObject.Guid,
"module_version" : self._AutoGenObject.Version,
"module_type" : self._AutoGenObject.ModuleType,
"module_file" : self._AutoGenObject.MetaFile,
"module_file_base_name" : self._AutoGenObject.MetaFile.BaseName,
"module_relative_directory" : self._AutoGenObject.SourceDir,
"architecture" : self._AutoGenObject.Arch,
"toolchain_tag" : self._AutoGenObject.ToolChain,
"build_target" : self._AutoGenObject.BuildTarget,
"platform_build_directory" : self.PlatformInfo.BuildDir,
"module_build_directory" : self._AutoGenObject.BuildDir,
"module_output_directory" : self._AutoGenObject.OutputDir,
"module_debug_directory" : self._AutoGenObject.DebugDir,
"separator" : Separator,
"module_tool_definitions" : ToolsDef,
"shell_command_code" : self._SHELL_CMD_[self._FileType].keys(),
"shell_command" : self._SHELL_CMD_[self._FileType].values(),
"create_directory_command" : self.GetCreateDirectoryCommand(self.IntermediateDirectoryList),
"custom_makefile_content" : CustomMakefile
}
return MakefileTemplateDict
_TemplateDict = property(_CreateTemplateDict)
## 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 = $(WORKSPACE)${separator}${platform_relative_directory}
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 = []
# Compose a dict object containing information used to do replacement in template
def _CreateTemplateDict(self):
Separator = self._SEP_[self._FileType]
PlatformInfo = self._AutoGenObject
if "MAKE" not in PlatformInfo.ToolDefinition or "PATH" not in PlatformInfo.ToolDefinition["MAKE"]:
EdkLogger.error("build", OPTION_MISSING, "No MAKE command defined. Please check your tools_def.txt!",
ExtraData="[%s]" % str(self._AutoGenObject))
self.IntermediateDirectoryList = ["$(BUILD_DIR)"]
self.ModuleBuildDirectoryList = self.GetModuleBuildDirectoryList()
self.LibraryBuildDirectoryList = self.GetLibraryBuildDirectoryList()
MakefileName = self._FILE_NAME_[self._FileType]
LibraryMakefileList = []
LibraryMakeCommandList = []
for D in self.LibraryBuildDirectoryList:
D = self.PlaceMacro(D, {"BUILD_DIR":PlatformInfo.BuildDir})
Makefile = os.path.join(D, MakefileName)
Command = self._MAKE_TEMPLATE_[self._FileType] % {"file":Makefile}
LibraryMakefileList.append(Makefile)
LibraryMakeCommandList.append(Command)
self.LibraryMakeCommandList = LibraryMakeCommandList
ModuleMakefileList = []
ModuleMakeCommandList = []
for D in self.ModuleBuildDirectoryList:
D = self.PlaceMacro(D, {"BUILD_DIR":PlatformInfo.BuildDir})
Makefile = os.path.join(D, MakefileName)
Command = self._MAKE_TEMPLATE_[self._FileType] % {"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" : PlatformInfo.ToolDefinition["MAKE"]["PATH"],
"makefile_name" : MakefileName,
"platform_name" : PlatformInfo.Name,
"platform_guid" : PlatformInfo.Guid,
"platform_version" : PlatformInfo.Version,
"platform_file" : self._AutoGenObject.MetaFile,
"platform_relative_directory": PlatformInfo.SourceDir,
"platform_output_directory" : PlatformInfo.OutputDir,
"platform_build_directory" : PlatformInfo.BuildDir,
"toolchain_tag" : PlatformInfo.ToolChain,
"build_target" : PlatformInfo.BuildTarget,
"shell_command_code" : self._SHELL_CMD_[self._FileType].keys(),
"shell_command" : self._SHELL_CMD_[self._FileType].values(),
"build_architecture_list" : self._AutoGenObject.Arch,
"architecture" : self._AutoGenObject.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:
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:
DirList.append(os.path.join(self._AutoGenObject.BuildDir, LibraryAutoGen.BuildDir))
return DirList
_TemplateDict = property(_CreateTemplateDict)
## 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} -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 = []
# Compose a dict object containing information used to do replacement in template
def _CreateTemplateDict(self):
Separator = self._SEP_[self._FileType]
# any platform autogen object is ok because we just need common information
PlatformInfo = self._AutoGenObject
if "MAKE" not in PlatformInfo.ToolDefinition or "PATH" not in PlatformInfo.ToolDefinition["MAKE"]:
EdkLogger.error("build", OPTION_MISSING, "No MAKE command defined. Please check your tools_def.txt!",
ExtraData="[%s]" % str(self._AutoGenObject))
for Arch in PlatformInfo.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 PlatformInfo.FdfFile != None and PlatformInfo.FdfFile != "":
FdfFileList = [PlatformInfo.FdfFile]
# macros passed to GenFds
MacroList.append('"%s=%s"' % ("EFI_SOURCE", GlobalData.gEfiSource.replace('\\', '\\\\')))
MacroList.append('"%s=%s"' % ("EDK_SOURCE", GlobalData.gEdkSource.replace('\\', '\\\\')))
MacroDict = {}
MacroDict.update(GlobalData.gGlobalDefines)
MacroDict.update(GlobalData.gCommandLineDefines)
MacroDict.pop("EFI_SOURCE", "dummy")
MacroDict.pop("EDK_SOURCE", "dummy")
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"
MakefileName = self._FILE_NAME_[self._FileType]
SubBuildCommandList = []
for A in PlatformInfo.ArchList:
Command = self._MAKE_TEMPLATE_[self._FileType] % {"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" : PlatformInfo.ToolDefinition["MAKE"]["PATH"],
"platform_name" : PlatformInfo.Name,
"platform_guid" : PlatformInfo.Guid,
"platform_version" : PlatformInfo.Version,
"platform_build_directory" : PlatformInfo.BuildDir,
"toolchain_tag" : PlatformInfo.ToolChain,
"build_target" : PlatformInfo.BuildTarget,
"shell_command_code" : self._SHELL_CMD_[self._FileType].keys(),
"shell_command" : self._SHELL_CMD_[self._FileType].values(),
'arch' : list(PlatformInfo.ArchList),
"build_architecture_list" : ','.join(PlatformInfo.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(PlatformInfo),
"fd" : PlatformInfo.FdTargetList,
"fv" : PlatformInfo.FvTargetList,
"cap" : PlatformInfo.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:
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:
DirList.append(os.path.join(self._AutoGenObject.BuildDir, LibraryAutoGen.BuildDir))
return DirList
_TemplateDict = property(_CreateTemplateDict)
# This acts like the main() function for the script, unless it is 'import'ed into another script.
if __name__ == '__main__':
pass