mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-11 14:28:08 +01:00
b871f2c17a
Signed-off-by: Slice <sergey.slice@gmail.com>
2456 lines
107 KiB
Python
Executable File
2456 lines
107 KiB
Python
Executable File
## @file
|
|
# Create makefile for MS nmake and GNU make
|
|
#
|
|
# Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
|
|
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
#
|
|
from __future__ import absolute_import
|
|
from AutoGen.AutoGen import AutoGen
|
|
from Common.LongFilePathSupport import LongFilePath, CopyLongFilePath
|
|
from Common.BuildToolError import *
|
|
from Common.DataType import *
|
|
from Common.Misc import *
|
|
from Common.StringUtils import NormPath,GetSplitList
|
|
from collections import defaultdict
|
|
from Workspace.WorkspaceCommon import OrderedListDict
|
|
import os.path as path
|
|
import copy
|
|
import hashlib
|
|
from . import InfSectionParser
|
|
from . import GenC
|
|
from . import GenMake
|
|
from . import GenDepex
|
|
from io import BytesIO
|
|
from GenPatchPcdTable.GenPatchPcdTable import parsePcdInfoFromMapFile
|
|
from Workspace.MetaFileCommentParser import UsageList
|
|
from .GenPcdDb import CreatePcdDatabaseCode
|
|
from Common.caching import cached_class_function
|
|
from AutoGen.ModuleAutoGenHelper import PlatformInfo,WorkSpaceInfo
|
|
import json
|
|
import tempfile
|
|
|
|
## Mapping Makefile type
|
|
gMakeTypeMap = {TAB_COMPILER_MSFT:"nmake", "GCC":"gmake"}
|
|
#
|
|
# Regular expression for finding Include Directories, the difference between MSFT and INTEL/GCC/RVCT
|
|
# is the former use /I , the Latter used -I to specify include directories
|
|
#
|
|
gBuildOptIncludePatternMsft = re.compile(r"(?:.*?)/I[ \t]*([^ ]*)", re.MULTILINE | re.DOTALL)
|
|
gBuildOptIncludePatternOther = re.compile(r"(?:.*?)-I[ \t]*([^ ]*)", re.MULTILINE | re.DOTALL)
|
|
|
|
## default file name for AutoGen
|
|
gAutoGenCodeFileName = "AutoGen.c"
|
|
gAutoGenHeaderFileName = "AutoGen.h"
|
|
gAutoGenStringFileName = "%(module_name)sStrDefs.h"
|
|
gAutoGenStringFormFileName = "%(module_name)sStrDefs.hpk"
|
|
gAutoGenDepexFileName = "%(module_name)s.depex"
|
|
gAutoGenImageDefFileName = "%(module_name)sImgDefs.h"
|
|
gAutoGenIdfFileName = "%(module_name)sIdf.hpk"
|
|
gInfSpecVersion = "0x00010017"
|
|
|
|
#
|
|
# Match name = variable
|
|
#
|
|
gEfiVarStoreNamePattern = re.compile(r"\s*name\s*=\s*(\w+)")
|
|
#
|
|
# The format of guid in efivarstore statement likes following and must be correct:
|
|
# guid = {0xA04A27f4, 0xDF00, 0x4D42, {0xB5, 0x52, 0x39, 0x51, 0x13, 0x02, 0x11, 0x3D}}
|
|
#
|
|
gEfiVarStoreGuidPattern = re.compile(r"\s*guid\s*=\s*({.*?{.*?}\s*})")
|
|
|
|
#
|
|
# Template string to generic AsBuilt INF
|
|
#
|
|
gAsBuiltInfHeaderString = TemplateString("""${header_comments}
|
|
|
|
# DO NOT EDIT
|
|
# FILE auto-generated
|
|
|
|
[Defines]
|
|
INF_VERSION = ${module_inf_version}
|
|
BASE_NAME = ${module_name}
|
|
FILE_GUID = ${module_guid}
|
|
MODULE_TYPE = ${module_module_type}${BEGIN}
|
|
VERSION_STRING = ${module_version_string}${END}${BEGIN}
|
|
PCD_IS_DRIVER = ${pcd_is_driver_string}${END}${BEGIN}
|
|
UEFI_SPECIFICATION_VERSION = ${module_uefi_specification_version}${END}${BEGIN}
|
|
PI_SPECIFICATION_VERSION = ${module_pi_specification_version}${END}${BEGIN}
|
|
ENTRY_POINT = ${module_entry_point}${END}${BEGIN}
|
|
UNLOAD_IMAGE = ${module_unload_image}${END}${BEGIN}
|
|
CONSTRUCTOR = ${module_constructor}${END}${BEGIN}
|
|
DESTRUCTOR = ${module_destructor}${END}${BEGIN}
|
|
SHADOW = ${module_shadow}${END}${BEGIN}
|
|
PCI_VENDOR_ID = ${module_pci_vendor_id}${END}${BEGIN}
|
|
PCI_DEVICE_ID = ${module_pci_device_id}${END}${BEGIN}
|
|
PCI_CLASS_CODE = ${module_pci_class_code}${END}${BEGIN}
|
|
PCI_REVISION = ${module_pci_revision}${END}${BEGIN}
|
|
BUILD_NUMBER = ${module_build_number}${END}${BEGIN}
|
|
SPEC = ${module_spec}${END}${BEGIN}
|
|
UEFI_HII_RESOURCE_SECTION = ${module_uefi_hii_resource_section}${END}${BEGIN}
|
|
MODULE_UNI_FILE = ${module_uni_file}${END}
|
|
|
|
[Packages.${module_arch}]${BEGIN}
|
|
${package_item}${END}
|
|
|
|
[Binaries.${module_arch}]${BEGIN}
|
|
${binary_item}${END}
|
|
|
|
[PatchPcd.${module_arch}]${BEGIN}
|
|
${patchablepcd_item}
|
|
${END}
|
|
|
|
[Protocols.${module_arch}]${BEGIN}
|
|
${protocol_item}
|
|
${END}
|
|
|
|
[Ppis.${module_arch}]${BEGIN}
|
|
${ppi_item}
|
|
${END}
|
|
|
|
[Guids.${module_arch}]${BEGIN}
|
|
${guid_item}
|
|
${END}
|
|
|
|
[PcdEx.${module_arch}]${BEGIN}
|
|
${pcd_item}
|
|
${END}
|
|
|
|
[LibraryClasses.${module_arch}]
|
|
## @LIB_INSTANCES${BEGIN}
|
|
# ${libraryclasses_item}${END}
|
|
|
|
${depexsection_item}
|
|
|
|
${userextension_tianocore_item}
|
|
|
|
${tail_comments}
|
|
|
|
[BuildOptions.${module_arch}]
|
|
## @AsBuilt${BEGIN}
|
|
## ${flags_item}${END}
|
|
""")
|
|
#
|
|
# extend lists contained in a dictionary with lists stored in another dictionary
|
|
# if CopyToDict is not derived from DefaultDict(list) then this may raise exception
|
|
#
|
|
def ExtendCopyDictionaryLists(CopyToDict, CopyFromDict):
|
|
for Key in CopyFromDict:
|
|
CopyToDict[Key].extend(CopyFromDict[Key])
|
|
|
|
# Create a directory specified by a set of path elements and return the full path
|
|
def _MakeDir(PathList):
|
|
RetVal = path.join(*PathList)
|
|
CreateDirectory(RetVal)
|
|
return RetVal
|
|
|
|
#
|
|
# Convert string to C format array
|
|
#
|
|
def _ConvertStringToByteArray(Value):
|
|
Value = Value.strip()
|
|
if not Value:
|
|
return None
|
|
if Value[0] == '{':
|
|
if not Value.endswith('}'):
|
|
return None
|
|
Value = Value.replace(' ', '').replace('{', '').replace('}', '')
|
|
ValFields = Value.split(',')
|
|
try:
|
|
for Index in range(len(ValFields)):
|
|
ValFields[Index] = str(int(ValFields[Index], 0))
|
|
except ValueError:
|
|
return None
|
|
Value = '{' + ','.join(ValFields) + '}'
|
|
return Value
|
|
|
|
Unicode = False
|
|
if Value.startswith('L"'):
|
|
if not Value.endswith('"'):
|
|
return None
|
|
Value = Value[1:]
|
|
Unicode = True
|
|
elif not Value.startswith('"') or not Value.endswith('"'):
|
|
return None
|
|
|
|
Value = eval(Value) # translate escape character
|
|
NewValue = '{'
|
|
for Index in range(0, len(Value)):
|
|
if Unicode:
|
|
NewValue = NewValue + str(ord(Value[Index]) % 0x10000) + ','
|
|
else:
|
|
NewValue = NewValue + str(ord(Value[Index]) % 0x100) + ','
|
|
Value = NewValue + '0}'
|
|
return Value
|
|
|
|
## ModuleAutoGen class
|
|
#
|
|
# This class encapsules the AutoGen behaviors for the build tools. In addition to
|
|
# the generation of AutoGen.h and AutoGen.c, it will generate *.depex file according
|
|
# to the [depex] section in module's inf file.
|
|
#
|
|
class ModuleAutoGen(AutoGen):
|
|
# call super().__init__ then call the worker function with different parameter count
|
|
def __init__(self, Workspace, MetaFile, Target, Toolchain, Arch, *args, **kwargs):
|
|
if not hasattr(self, "_Init"):
|
|
self._InitWorker(Workspace, MetaFile, Target, Toolchain, Arch, *args)
|
|
self._Init = True
|
|
|
|
## Cache the timestamps of metafiles of every module in a class attribute
|
|
#
|
|
TimeDict = {}
|
|
|
|
def __new__(cls, Workspace, MetaFile, Target, Toolchain, Arch, *args, **kwargs):
|
|
# check if this module is employed by active platform
|
|
if not PlatformInfo(Workspace, args[0], Target, Toolchain, Arch,args[-1]).ValidModule(MetaFile):
|
|
EdkLogger.verbose("Module [%s] for [%s] is not employed by active platform\n" \
|
|
% (MetaFile, Arch))
|
|
return None
|
|
return super(ModuleAutoGen, cls).__new__(cls, Workspace, MetaFile, Target, Toolchain, Arch, *args, **kwargs)
|
|
|
|
## Initialize ModuleAutoGen
|
|
#
|
|
# @param Workspace EdkIIWorkspaceBuild object
|
|
# @param ModuleFile The path of module file
|
|
# @param Target Build target (DEBUG, RELEASE)
|
|
# @param Toolchain Name of tool chain
|
|
# @param Arch The arch the module supports
|
|
# @param PlatformFile Platform meta-file
|
|
#
|
|
def _InitWorker(self, Workspace, ModuleFile, Target, Toolchain, Arch, PlatformFile,DataPipe):
|
|
EdkLogger.debug(EdkLogger.DEBUG_9, "AutoGen module [%s] [%s]" % (ModuleFile, Arch))
|
|
GlobalData.gProcessingFile = "%s [%s, %s, %s]" % (ModuleFile, Arch, Toolchain, Target)
|
|
|
|
self.Workspace = Workspace
|
|
self.WorkspaceDir = ""
|
|
self.PlatformInfo = None
|
|
self.DataPipe = DataPipe
|
|
self.__init_platform_info__()
|
|
self.MetaFile = ModuleFile
|
|
self.SourceDir = self.MetaFile.SubDir
|
|
self.SourceDir = mws.relpath(self.SourceDir, self.WorkspaceDir)
|
|
|
|
self.ToolChain = Toolchain
|
|
self.BuildTarget = Target
|
|
self.Arch = Arch
|
|
self.ToolChainFamily = self.PlatformInfo.ToolChainFamily
|
|
self.BuildRuleFamily = self.PlatformInfo.BuildRuleFamily
|
|
|
|
self.IsCodeFileCreated = False
|
|
self.IsAsBuiltInfCreated = False
|
|
self.DepexGenerated = False
|
|
|
|
self.BuildDatabase = self.Workspace.BuildDatabase
|
|
self.BuildRuleOrder = None
|
|
self.BuildTime = 0
|
|
|
|
self._GuidComments = OrderedListDict()
|
|
self._ProtocolComments = OrderedListDict()
|
|
self._PpiComments = OrderedListDict()
|
|
self._BuildTargets = None
|
|
self._IntroBuildTargetList = None
|
|
self._FinalBuildTargetList = None
|
|
self._FileTypes = None
|
|
|
|
self.AutoGenDepSet = set()
|
|
self.ReferenceModules = []
|
|
self.ConstPcd = {}
|
|
self.FileDependCache = {}
|
|
|
|
def __init_platform_info__(self):
|
|
pinfo = self.DataPipe.Get("P_Info")
|
|
self.WorkspaceDir = pinfo.get("WorkspaceDir")
|
|
self.PlatformInfo = PlatformInfo(self.Workspace,pinfo.get("ActivePlatform"),pinfo.get("Target"),pinfo.get("ToolChain"),pinfo.get("Arch"),self.DataPipe)
|
|
## hash() operator of ModuleAutoGen
|
|
#
|
|
# The module file path and arch string will be used to represent
|
|
# hash value of this object
|
|
#
|
|
# @retval int Hash value of the module file path and arch
|
|
#
|
|
@cached_class_function
|
|
def __hash__(self):
|
|
return hash((self.MetaFile, self.Arch, self.ToolChain,self.BuildTarget))
|
|
def __repr__(self):
|
|
return "%s [%s]" % (self.MetaFile, self.Arch)
|
|
|
|
# Get FixedAtBuild Pcds of this Module
|
|
@cached_property
|
|
def FixedAtBuildPcds(self):
|
|
RetVal = []
|
|
for Pcd in self.ModulePcdList:
|
|
if Pcd.Type != TAB_PCDS_FIXED_AT_BUILD:
|
|
continue
|
|
if Pcd not in RetVal:
|
|
RetVal.append(Pcd)
|
|
return RetVal
|
|
|
|
@cached_property
|
|
def FixedVoidTypePcds(self):
|
|
RetVal = {}
|
|
for Pcd in self.FixedAtBuildPcds:
|
|
if Pcd.DatumType == TAB_VOID:
|
|
if '.'.join((Pcd.TokenSpaceGuidCName, Pcd.TokenCName)) not in RetVal:
|
|
RetVal['.'.join((Pcd.TokenSpaceGuidCName, Pcd.TokenCName))] = Pcd.DefaultValue
|
|
return RetVal
|
|
|
|
@property
|
|
def UniqueBaseName(self):
|
|
ModuleNames = self.DataPipe.Get("M_Name")
|
|
if not ModuleNames:
|
|
return self.Name
|
|
return ModuleNames.get((self.Name,self.MetaFile),self.Name)
|
|
|
|
# Macros could be used in build_rule.txt (also Makefile)
|
|
@cached_property
|
|
def Macros(self):
|
|
return OrderedDict((
|
|
("WORKSPACE" ,self.WorkspaceDir),
|
|
("MODULE_NAME" ,self.Name),
|
|
("MODULE_NAME_GUID" ,self.UniqueBaseName),
|
|
("MODULE_GUID" ,self.Guid),
|
|
("MODULE_VERSION" ,self.Version),
|
|
("MODULE_TYPE" ,self.ModuleType),
|
|
("MODULE_FILE" ,str(self.MetaFile)),
|
|
("MODULE_FILE_BASE_NAME" ,self.MetaFile.BaseName),
|
|
("MODULE_RELATIVE_DIR" ,self.SourceDir),
|
|
("MODULE_DIR" ,self.SourceDir),
|
|
("BASE_NAME" ,self.Name),
|
|
("ARCH" ,self.Arch),
|
|
("TOOLCHAIN" ,self.ToolChain),
|
|
("TOOLCHAIN_TAG" ,self.ToolChain),
|
|
("TOOL_CHAIN_TAG" ,self.ToolChain),
|
|
("TARGET" ,self.BuildTarget),
|
|
("BUILD_DIR" ,self.PlatformInfo.BuildDir),
|
|
("BIN_DIR" ,os.path.join(self.PlatformInfo.BuildDir, self.Arch)),
|
|
("LIB_DIR" ,os.path.join(self.PlatformInfo.BuildDir, self.Arch)),
|
|
("MODULE_BUILD_DIR" ,self.BuildDir),
|
|
("OUTPUT_DIR" ,self.OutputDir),
|
|
("DEBUG_DIR" ,self.DebugDir),
|
|
("DEST_DIR_OUTPUT" ,self.OutputDir),
|
|
("DEST_DIR_DEBUG" ,self.DebugDir),
|
|
("PLATFORM_NAME" ,self.PlatformInfo.Name),
|
|
("PLATFORM_GUID" ,self.PlatformInfo.Guid),
|
|
("PLATFORM_VERSION" ,self.PlatformInfo.Version),
|
|
("PLATFORM_RELATIVE_DIR" ,self.PlatformInfo.SourceDir),
|
|
("PLATFORM_DIR" ,mws.join(self.WorkspaceDir, self.PlatformInfo.SourceDir)),
|
|
("PLATFORM_OUTPUT_DIR" ,self.PlatformInfo.OutputDir),
|
|
("FFS_OUTPUT_DIR" ,self.FfsOutputDir)
|
|
))
|
|
|
|
## Return the module build data object
|
|
@cached_property
|
|
def Module(self):
|
|
return self.BuildDatabase[self.MetaFile, self.Arch, self.BuildTarget, self.ToolChain]
|
|
|
|
## Return the module name
|
|
@cached_property
|
|
def Name(self):
|
|
return self.Module.BaseName
|
|
|
|
## Return the module DxsFile if exist
|
|
@cached_property
|
|
def DxsFile(self):
|
|
return self.Module.DxsFile
|
|
|
|
## Return the module meta-file GUID
|
|
@cached_property
|
|
def Guid(self):
|
|
#
|
|
# To build same module more than once, the module path with FILE_GUID overridden has
|
|
# the file name FILE_GUIDmodule.inf, but the relative path (self.MetaFile.File) is the real path
|
|
# in DSC. The overridden GUID can be retrieved from file name
|
|
#
|
|
if os.path.basename(self.MetaFile.File) != os.path.basename(self.MetaFile.Path):
|
|
#
|
|
# Length of GUID is 36
|
|
#
|
|
return os.path.basename(self.MetaFile.Path)[:36]
|
|
return self.Module.Guid
|
|
|
|
## Return the module version
|
|
@cached_property
|
|
def Version(self):
|
|
return self.Module.Version
|
|
|
|
## Return the module type
|
|
@cached_property
|
|
def ModuleType(self):
|
|
return self.Module.ModuleType
|
|
|
|
## Return the component type (for Edk.x style of module)
|
|
@cached_property
|
|
def ComponentType(self):
|
|
return self.Module.ComponentType
|
|
|
|
## Return the build type
|
|
@cached_property
|
|
def BuildType(self):
|
|
return self.Module.BuildType
|
|
|
|
## Return the PCD_IS_DRIVER setting
|
|
@cached_property
|
|
def PcdIsDriver(self):
|
|
return self.Module.PcdIsDriver
|
|
|
|
## Return the autogen version, i.e. module meta-file version
|
|
@cached_property
|
|
def AutoGenVersion(self):
|
|
return self.Module.AutoGenVersion
|
|
|
|
## Check if the module is library or not
|
|
@cached_property
|
|
def IsLibrary(self):
|
|
return bool(self.Module.LibraryClass)
|
|
|
|
## Check if the module is binary module or not
|
|
@cached_property
|
|
def IsBinaryModule(self):
|
|
return self.Module.IsBinaryModule
|
|
|
|
## Return the directory to store intermediate files of the module
|
|
@cached_property
|
|
def BuildDir(self):
|
|
return _MakeDir((
|
|
self.PlatformInfo.BuildDir,
|
|
self.Arch,
|
|
self.SourceDir,
|
|
self.MetaFile.BaseName
|
|
))
|
|
|
|
## Return the directory to store the intermediate object files of the module
|
|
@cached_property
|
|
def OutputDir(self):
|
|
return _MakeDir((self.BuildDir, "OUTPUT"))
|
|
|
|
## Return the directory path to store ffs file
|
|
@cached_property
|
|
def FfsOutputDir(self):
|
|
if GlobalData.gFdfParser:
|
|
return path.join(self.PlatformInfo.BuildDir, TAB_FV_DIRECTORY, "Ffs", self.Guid + self.Name)
|
|
return ''
|
|
|
|
## Return the directory to store auto-gened source files of the module
|
|
@cached_property
|
|
def DebugDir(self):
|
|
return _MakeDir((self.BuildDir, "DEBUG"))
|
|
|
|
## Return the path of custom file
|
|
@cached_property
|
|
def CustomMakefile(self):
|
|
RetVal = {}
|
|
for Type in self.Module.CustomMakefile:
|
|
MakeType = gMakeTypeMap[Type] if Type in gMakeTypeMap else 'nmake'
|
|
File = os.path.join(self.SourceDir, self.Module.CustomMakefile[Type])
|
|
RetVal[MakeType] = File
|
|
return RetVal
|
|
|
|
## Return the directory of the makefile
|
|
#
|
|
# @retval string The directory string of module's makefile
|
|
#
|
|
@cached_property
|
|
def MakeFileDir(self):
|
|
return self.BuildDir
|
|
|
|
## Return build command string
|
|
#
|
|
# @retval string Build command string
|
|
#
|
|
@cached_property
|
|
def BuildCommand(self):
|
|
return self.PlatformInfo.BuildCommand
|
|
|
|
## Get Module package and Platform package
|
|
#
|
|
# @retval list The list of package object
|
|
#
|
|
@cached_property
|
|
def PackageList(self):
|
|
PkagList = []
|
|
if self.Module.Packages:
|
|
PkagList.extend(self.Module.Packages)
|
|
Platform = self.BuildDatabase[self.PlatformInfo.MetaFile, self.Arch, self.BuildTarget, self.ToolChain]
|
|
for Package in Platform.Packages:
|
|
if Package in PkagList:
|
|
continue
|
|
PkagList.append(Package)
|
|
return PkagList
|
|
|
|
## Get object list of all packages the module and its dependent libraries belong to and the Platform depends on
|
|
#
|
|
# @retval list The list of package object
|
|
#
|
|
@cached_property
|
|
def DerivedPackageList(self):
|
|
PackageList = []
|
|
PackageList.extend(self.PackageList)
|
|
for M in self.DependentLibraryList:
|
|
for Package in M.Packages:
|
|
if Package in PackageList:
|
|
continue
|
|
PackageList.append(Package)
|
|
return PackageList
|
|
|
|
## Get the depex string
|
|
#
|
|
# @return : a string contain all depex expression.
|
|
def _GetDepexExpresionString(self):
|
|
DepexStr = ''
|
|
DepexList = []
|
|
## DPX_SOURCE IN Define section.
|
|
if self.Module.DxsFile:
|
|
return DepexStr
|
|
for M in [self.Module] + self.DependentLibraryList:
|
|
Filename = M.MetaFile.Path
|
|
InfObj = InfSectionParser.InfSectionParser(Filename)
|
|
DepexExpressionList = InfObj.GetDepexExpresionList()
|
|
for DepexExpression in DepexExpressionList:
|
|
for key in DepexExpression:
|
|
Arch, ModuleType = key
|
|
DepexExpr = [x for x in DepexExpression[key] if not str(x).startswith('#')]
|
|
# the type of build module is USER_DEFINED.
|
|
# All different DEPEX section tags would be copied into the As Built INF file
|
|
# and there would be separate DEPEX section tags
|
|
if self.ModuleType.upper() == SUP_MODULE_USER_DEFINED or self.ModuleType.upper() == SUP_MODULE_HOST_APPLICATION:
|
|
if (Arch.upper() == self.Arch.upper()) and (ModuleType.upper() != TAB_ARCH_COMMON):
|
|
DepexList.append({(Arch, ModuleType): DepexExpr})
|
|
else:
|
|
if Arch.upper() == TAB_ARCH_COMMON or \
|
|
(Arch.upper() == self.Arch.upper() and \
|
|
ModuleType.upper() in [TAB_ARCH_COMMON, self.ModuleType.upper()]):
|
|
DepexList.append({(Arch, ModuleType): DepexExpr})
|
|
|
|
#the type of build module is USER_DEFINED.
|
|
if self.ModuleType.upper() == SUP_MODULE_USER_DEFINED or self.ModuleType.upper() == SUP_MODULE_HOST_APPLICATION:
|
|
for Depex in DepexList:
|
|
for key in Depex:
|
|
DepexStr += '[Depex.%s.%s]\n' % key
|
|
DepexStr += '\n'.join('# '+ val for val in Depex[key])
|
|
DepexStr += '\n\n'
|
|
if not DepexStr:
|
|
return '[Depex.%s]\n' % self.Arch
|
|
return DepexStr
|
|
|
|
#the type of build module not is USER_DEFINED.
|
|
Count = 0
|
|
for Depex in DepexList:
|
|
Count += 1
|
|
if DepexStr != '':
|
|
DepexStr += ' AND '
|
|
DepexStr += '('
|
|
for D in Depex.values():
|
|
DepexStr += ' '.join(val for val in D)
|
|
Index = DepexStr.find('END')
|
|
if Index > -1 and Index == len(DepexStr) - 3:
|
|
DepexStr = DepexStr[:-3]
|
|
DepexStr = DepexStr.strip()
|
|
DepexStr += ')'
|
|
if Count == 1:
|
|
DepexStr = DepexStr.lstrip('(').rstrip(')').strip()
|
|
if not DepexStr:
|
|
return '[Depex.%s]\n' % self.Arch
|
|
return '[Depex.%s]\n# ' % self.Arch + DepexStr
|
|
|
|
## Merge dependency expression
|
|
#
|
|
# @retval list The token list of the dependency expression after parsed
|
|
#
|
|
@cached_property
|
|
def DepexList(self):
|
|
if self.DxsFile or self.IsLibrary or TAB_DEPENDENCY_EXPRESSION_FILE in self.FileTypes:
|
|
return {}
|
|
|
|
DepexList = []
|
|
#
|
|
# Append depex from dependent libraries, if not "BEFORE", "AFTER" expression
|
|
#
|
|
FixedVoidTypePcds = {}
|
|
for M in [self] + self.LibraryAutoGenList:
|
|
FixedVoidTypePcds.update(M.FixedVoidTypePcds)
|
|
for M in [self] + self.LibraryAutoGenList:
|
|
Inherited = False
|
|
for D in M.Module.Depex[self.Arch, self.ModuleType]:
|
|
if DepexList != []:
|
|
DepexList.append('AND')
|
|
DepexList.append('(')
|
|
#replace D with value if D is FixedAtBuild PCD
|
|
NewList = []
|
|
for item in D:
|
|
if '.' not in item:
|
|
NewList.append(item)
|
|
else:
|
|
try:
|
|
Value = FixedVoidTypePcds[item]
|
|
if len(Value.split(',')) != 16:
|
|
EdkLogger.error("build", FORMAT_INVALID,
|
|
"{} used in [Depex] section should be used as FixedAtBuild type and VOID* datum type and 16 bytes in the module.".format(item))
|
|
NewList.append(Value)
|
|
except:
|
|
EdkLogger.error("build", FORMAT_INVALID, "{} used in [Depex] section should be used as FixedAtBuild type and VOID* datum type in the module.".format(item))
|
|
|
|
DepexList.extend(NewList)
|
|
if DepexList[-1] == 'END': # no need of a END at this time
|
|
DepexList.pop()
|
|
DepexList.append(')')
|
|
Inherited = True
|
|
if Inherited:
|
|
EdkLogger.verbose("DEPEX[%s] (+%s) = %s" % (self.Name, M.Module.BaseName, DepexList))
|
|
if 'BEFORE' in DepexList or 'AFTER' in DepexList:
|
|
break
|
|
if len(DepexList) > 0:
|
|
EdkLogger.verbose('')
|
|
return {self.ModuleType:DepexList}
|
|
|
|
## Merge dependency expression
|
|
#
|
|
# @retval list The token list of the dependency expression after parsed
|
|
#
|
|
@cached_property
|
|
def DepexExpressionDict(self):
|
|
if self.DxsFile or self.IsLibrary or TAB_DEPENDENCY_EXPRESSION_FILE in self.FileTypes:
|
|
return {}
|
|
|
|
DepexExpressionString = ''
|
|
#
|
|
# Append depex from dependent libraries, if not "BEFORE", "AFTER" expresion
|
|
#
|
|
for M in [self.Module] + self.DependentLibraryList:
|
|
Inherited = False
|
|
for D in M.DepexExpression[self.Arch, self.ModuleType]:
|
|
if DepexExpressionString != '':
|
|
DepexExpressionString += ' AND '
|
|
DepexExpressionString += '('
|
|
DepexExpressionString += D
|
|
DepexExpressionString = DepexExpressionString.rstrip('END').strip()
|
|
DepexExpressionString += ')'
|
|
Inherited = True
|
|
if Inherited:
|
|
EdkLogger.verbose("DEPEX[%s] (+%s) = %s" % (self.Name, M.BaseName, DepexExpressionString))
|
|
if 'BEFORE' in DepexExpressionString or 'AFTER' in DepexExpressionString:
|
|
break
|
|
if len(DepexExpressionString) > 0:
|
|
EdkLogger.verbose('')
|
|
|
|
return {self.ModuleType:DepexExpressionString}
|
|
|
|
# Get the tiano core user extension, it is contain dependent library.
|
|
# @retval: a list contain tiano core userextension.
|
|
#
|
|
def _GetTianoCoreUserExtensionList(self):
|
|
TianoCoreUserExtentionList = []
|
|
for M in [self.Module] + self.DependentLibraryList:
|
|
Filename = M.MetaFile.Path
|
|
InfObj = InfSectionParser.InfSectionParser(Filename)
|
|
TianoCoreUserExtenList = InfObj.GetUserExtensionTianoCore()
|
|
for TianoCoreUserExtent in TianoCoreUserExtenList:
|
|
for Section in TianoCoreUserExtent:
|
|
ItemList = Section.split(TAB_SPLIT)
|
|
Arch = self.Arch
|
|
if len(ItemList) == 4:
|
|
Arch = ItemList[3]
|
|
if Arch.upper() == TAB_ARCH_COMMON or Arch.upper() == self.Arch.upper():
|
|
TianoCoreList = []
|
|
TianoCoreList.extend([TAB_SECTION_START + Section + TAB_SECTION_END])
|
|
TianoCoreList.extend(TianoCoreUserExtent[Section][:])
|
|
TianoCoreList.append('\n')
|
|
TianoCoreUserExtentionList.append(TianoCoreList)
|
|
|
|
return TianoCoreUserExtentionList
|
|
|
|
## Return the list of specification version required for the module
|
|
#
|
|
# @retval list The list of specification defined in module file
|
|
#
|
|
@cached_property
|
|
def Specification(self):
|
|
return self.Module.Specification
|
|
|
|
## Tool option for the module build
|
|
#
|
|
# @param PlatformInfo The object of PlatformBuildInfo
|
|
# @retval dict The dict containing valid options
|
|
#
|
|
@cached_property
|
|
def BuildOption(self):
|
|
RetVal, self.BuildRuleOrder = self.PlatformInfo.ApplyBuildOption(self.Module)
|
|
if self.BuildRuleOrder:
|
|
self.BuildRuleOrder = ['.%s' % Ext for Ext in self.BuildRuleOrder.split()]
|
|
return RetVal
|
|
|
|
## Get include path list from tool option for the module build
|
|
#
|
|
# @retval list The include path list
|
|
#
|
|
@cached_property
|
|
def BuildOptionIncPathList(self):
|
|
#
|
|
# Regular expression for finding Include Directories, the difference between MSFT and INTEL/GCC/RVCT
|
|
# is the former use /I , the Latter used -I to specify include directories
|
|
#
|
|
if self.PlatformInfo.ToolChainFamily in (TAB_COMPILER_MSFT):
|
|
BuildOptIncludeRegEx = gBuildOptIncludePatternMsft
|
|
elif self.PlatformInfo.ToolChainFamily in ('INTEL', 'GCC', 'RVCT'):
|
|
BuildOptIncludeRegEx = gBuildOptIncludePatternOther
|
|
else:
|
|
#
|
|
# New ToolChainFamily, don't known whether there is option to specify include directories
|
|
#
|
|
return []
|
|
|
|
RetVal = []
|
|
for Tool in ('CC', 'PP', 'VFRPP', 'ASLPP', 'ASLCC', 'APP', 'ASM'):
|
|
try:
|
|
FlagOption = self.BuildOption[Tool]['FLAGS']
|
|
except KeyError:
|
|
FlagOption = ''
|
|
|
|
if self.ToolChainFamily != 'RVCT':
|
|
IncPathList = [NormPath(Path, self.Macros) for Path in BuildOptIncludeRegEx.findall(FlagOption)]
|
|
else:
|
|
#
|
|
# RVCT may specify a list of directory seperated by commas
|
|
#
|
|
IncPathList = []
|
|
for Path in BuildOptIncludeRegEx.findall(FlagOption):
|
|
PathList = GetSplitList(Path, TAB_COMMA_SPLIT)
|
|
IncPathList.extend(NormPath(PathEntry, self.Macros) for PathEntry in PathList)
|
|
|
|
#
|
|
# EDK II modules must not reference header files outside of the packages they depend on or
|
|
# within the module's directory tree. Report error if violation.
|
|
#
|
|
if GlobalData.gDisableIncludePathCheck == False:
|
|
for Path in IncPathList:
|
|
if (Path not in self.IncludePathList) and (CommonPath([Path, self.MetaFile.Dir]) != self.MetaFile.Dir):
|
|
ErrMsg = "The include directory for the EDK II module in this line is invalid %s specified in %s FLAGS '%s'" % (Path, Tool, FlagOption)
|
|
EdkLogger.error("build",
|
|
PARAMETER_INVALID,
|
|
ExtraData=ErrMsg,
|
|
File=str(self.MetaFile))
|
|
RetVal += IncPathList
|
|
return RetVal
|
|
|
|
## Return a list of files which can be built from source
|
|
#
|
|
# What kind of files can be built is determined by build rules in
|
|
# $(CONF_DIRECTORY)/build_rule.txt and toolchain family.
|
|
#
|
|
@cached_property
|
|
def SourceFileList(self):
|
|
RetVal = []
|
|
ToolChainTagSet = {"", TAB_STAR, self.ToolChain}
|
|
ToolChainFamilySet = {"", TAB_STAR, self.ToolChainFamily, self.BuildRuleFamily}
|
|
for F in self.Module.Sources:
|
|
# match tool chain
|
|
if F.TagName not in ToolChainTagSet:
|
|
EdkLogger.debug(EdkLogger.DEBUG_9, "The toolchain [%s] for processing file [%s] is found, "
|
|
"but [%s] is currently used" % (F.TagName, str(F), self.ToolChain))
|
|
continue
|
|
# match tool chain family or build rule family
|
|
if F.ToolChainFamily not in ToolChainFamilySet:
|
|
EdkLogger.debug(
|
|
EdkLogger.DEBUG_0,
|
|
"The file [%s] must be built by tools of [%s], " \
|
|
"but current toolchain family is [%s], buildrule family is [%s]" \
|
|
% (str(F), F.ToolChainFamily, self.ToolChainFamily, self.BuildRuleFamily))
|
|
continue
|
|
|
|
# add the file path into search path list for file including
|
|
if F.Dir not in self.IncludePathList:
|
|
self.IncludePathList.insert(0, F.Dir)
|
|
RetVal.append(F)
|
|
|
|
self._MatchBuildRuleOrder(RetVal)
|
|
|
|
for F in RetVal:
|
|
self._ApplyBuildRule(F, TAB_UNKNOWN_FILE)
|
|
return RetVal
|
|
|
|
def _MatchBuildRuleOrder(self, FileList):
|
|
Order_Dict = {}
|
|
self.BuildOption
|
|
for SingleFile in FileList:
|
|
if self.BuildRuleOrder and SingleFile.Ext in self.BuildRuleOrder and SingleFile.Ext in self.BuildRules:
|
|
key = SingleFile.Path.rsplit(SingleFile.Ext,1)[0]
|
|
if key in Order_Dict:
|
|
Order_Dict[key].append(SingleFile.Ext)
|
|
else:
|
|
Order_Dict[key] = [SingleFile.Ext]
|
|
|
|
RemoveList = []
|
|
for F in Order_Dict:
|
|
if len(Order_Dict[F]) > 1:
|
|
Order_Dict[F].sort(key=lambda i: self.BuildRuleOrder.index(i))
|
|
for Ext in Order_Dict[F][1:]:
|
|
RemoveList.append(F + Ext)
|
|
|
|
for item in RemoveList:
|
|
FileList.remove(item)
|
|
|
|
return FileList
|
|
|
|
## Return the list of unicode files
|
|
@cached_property
|
|
def UnicodeFileList(self):
|
|
return self.FileTypes.get(TAB_UNICODE_FILE,[])
|
|
|
|
## Return the list of vfr files
|
|
@cached_property
|
|
def VfrFileList(self):
|
|
return self.FileTypes.get(TAB_VFR_FILE, [])
|
|
|
|
## Return the list of Image Definition files
|
|
@cached_property
|
|
def IdfFileList(self):
|
|
return self.FileTypes.get(TAB_IMAGE_FILE,[])
|
|
|
|
## Return a list of files which can be built from binary
|
|
#
|
|
# "Build" binary files are just to copy them to build directory.
|
|
#
|
|
# @retval list The list of files which can be built later
|
|
#
|
|
@cached_property
|
|
def BinaryFileList(self):
|
|
RetVal = []
|
|
for F in self.Module.Binaries:
|
|
if F.Target not in [TAB_ARCH_COMMON, TAB_STAR] and F.Target != self.BuildTarget:
|
|
continue
|
|
RetVal.append(F)
|
|
self._ApplyBuildRule(F, F.Type, BinaryFileList=RetVal)
|
|
return RetVal
|
|
|
|
@cached_property
|
|
def BuildRules(self):
|
|
RetVal = {}
|
|
BuildRuleDatabase = self.PlatformInfo.BuildRule
|
|
for Type in BuildRuleDatabase.FileTypeList:
|
|
#first try getting build rule by BuildRuleFamily
|
|
RuleObject = BuildRuleDatabase[Type, self.BuildType, self.Arch, self.BuildRuleFamily]
|
|
if not RuleObject:
|
|
# build type is always module type, but ...
|
|
if self.ModuleType != self.BuildType:
|
|
RuleObject = BuildRuleDatabase[Type, self.ModuleType, self.Arch, self.BuildRuleFamily]
|
|
#second try getting build rule by ToolChainFamily
|
|
if not RuleObject:
|
|
RuleObject = BuildRuleDatabase[Type, self.BuildType, self.Arch, self.ToolChainFamily]
|
|
if not RuleObject:
|
|
# build type is always module type, but ...
|
|
if self.ModuleType != self.BuildType:
|
|
RuleObject = BuildRuleDatabase[Type, self.ModuleType, self.Arch, self.ToolChainFamily]
|
|
if not RuleObject:
|
|
continue
|
|
RuleObject = RuleObject.Instantiate(self.Macros)
|
|
RetVal[Type] = RuleObject
|
|
for Ext in RuleObject.SourceFileExtList:
|
|
RetVal[Ext] = RuleObject
|
|
return RetVal
|
|
|
|
def _ApplyBuildRule(self, File, FileType, BinaryFileList=None):
|
|
if self._BuildTargets is None:
|
|
self._IntroBuildTargetList = set()
|
|
self._FinalBuildTargetList = set()
|
|
self._BuildTargets = defaultdict(set)
|
|
self._FileTypes = defaultdict(set)
|
|
|
|
if not BinaryFileList:
|
|
BinaryFileList = self.BinaryFileList
|
|
|
|
SubDirectory = os.path.join(self.OutputDir, File.SubDir)
|
|
if not os.path.exists(SubDirectory):
|
|
CreateDirectory(SubDirectory)
|
|
TargetList = set()
|
|
FinalTargetName = set()
|
|
RuleChain = set()
|
|
SourceList = [File]
|
|
Index = 0
|
|
#
|
|
# Make sure to get build rule order value
|
|
#
|
|
self.BuildOption
|
|
|
|
while Index < len(SourceList):
|
|
# Reset the FileType if not the first iteration.
|
|
if Index > 0:
|
|
FileType = TAB_UNKNOWN_FILE
|
|
Source = SourceList[Index]
|
|
Index = Index + 1
|
|
|
|
if Source != File:
|
|
CreateDirectory(Source.Dir)
|
|
|
|
if File.IsBinary and File == Source and File in BinaryFileList:
|
|
# Skip all files that are not binary libraries
|
|
if not self.IsLibrary:
|
|
continue
|
|
RuleObject = self.BuildRules[TAB_DEFAULT_BINARY_FILE]
|
|
elif FileType in self.BuildRules:
|
|
RuleObject = self.BuildRules[FileType]
|
|
elif Source.Ext in self.BuildRules:
|
|
RuleObject = self.BuildRules[Source.Ext]
|
|
else:
|
|
# No more rule to apply: Source is a final target.
|
|
FinalTargetName.add(Source)
|
|
continue
|
|
|
|
FileType = RuleObject.SourceFileType
|
|
self._FileTypes[FileType].add(Source)
|
|
|
|
# stop at STATIC_LIBRARY for library
|
|
if self.IsLibrary and FileType == TAB_STATIC_LIBRARY:
|
|
FinalTargetName.add(Source)
|
|
continue
|
|
|
|
Target = RuleObject.Apply(Source, self.BuildRuleOrder)
|
|
if not Target:
|
|
# No Target: Source is a final target.
|
|
FinalTargetName.add(Source)
|
|
continue
|
|
|
|
TargetList.add(Target)
|
|
self._BuildTargets[FileType].add(Target)
|
|
|
|
if not Source.IsBinary and Source == File:
|
|
self._IntroBuildTargetList.add(Target)
|
|
|
|
# to avoid cyclic rule
|
|
if FileType in RuleChain:
|
|
EdkLogger.error("build", ERROR_STATEMENT, "Cyclic dependency detected while generating rule for %s" % str(Source))
|
|
|
|
RuleChain.add(FileType)
|
|
SourceList.extend(Target.Outputs)
|
|
|
|
# For each final target name, retrieve the corresponding TargetDescBlock instance.
|
|
for FTargetName in FinalTargetName:
|
|
for Target in TargetList:
|
|
if FTargetName == Target.Target:
|
|
self._FinalBuildTargetList.add(Target)
|
|
|
|
@cached_property
|
|
def Targets(self):
|
|
if self._BuildTargets is None:
|
|
self._IntroBuildTargetList = set()
|
|
self._FinalBuildTargetList = set()
|
|
self._BuildTargets = defaultdict(set)
|
|
self._FileTypes = defaultdict(set)
|
|
|
|
#TRICK: call SourceFileList property to apply build rule for source files
|
|
self.SourceFileList
|
|
|
|
#TRICK: call _GetBinaryFileList to apply build rule for binary files
|
|
self.BinaryFileList
|
|
|
|
return self._BuildTargets
|
|
|
|
@cached_property
|
|
def IntroTargetList(self):
|
|
self.Targets
|
|
return self._IntroBuildTargetList
|
|
|
|
@cached_property
|
|
def CodaTargetList(self):
|
|
self.Targets
|
|
return self._FinalBuildTargetList
|
|
|
|
@cached_property
|
|
def FileTypes(self):
|
|
self.Targets
|
|
return self._FileTypes
|
|
|
|
## Get the list of package object the module depends on and the Platform depends on
|
|
#
|
|
# @retval list The package object list
|
|
#
|
|
@cached_property
|
|
def DependentPackageList(self):
|
|
return self.PackageList
|
|
|
|
## Return the list of auto-generated code file
|
|
#
|
|
# @retval list The list of auto-generated file
|
|
#
|
|
@cached_property
|
|
def AutoGenFileList(self):
|
|
AutoGenUniIdf = self.BuildType != 'UEFI_HII'
|
|
UniStringBinBuffer = BytesIO()
|
|
IdfGenBinBuffer = BytesIO()
|
|
RetVal = {}
|
|
AutoGenC = TemplateString()
|
|
AutoGenH = TemplateString()
|
|
StringH = TemplateString()
|
|
StringIdf = TemplateString()
|
|
GenC.CreateCode(self, AutoGenC, AutoGenH, StringH, AutoGenUniIdf, UniStringBinBuffer, StringIdf, AutoGenUniIdf, IdfGenBinBuffer)
|
|
#
|
|
# AutoGen.c is generated if there are library classes in inf, or there are object files
|
|
#
|
|
if str(AutoGenC) != "" and (len(self.Module.LibraryClasses) > 0
|
|
or TAB_OBJECT_FILE in self.FileTypes):
|
|
AutoFile = PathClass(gAutoGenCodeFileName, self.DebugDir)
|
|
RetVal[AutoFile] = str(AutoGenC)
|
|
self._ApplyBuildRule(AutoFile, TAB_UNKNOWN_FILE)
|
|
if str(AutoGenH) != "":
|
|
AutoFile = PathClass(gAutoGenHeaderFileName, self.DebugDir)
|
|
RetVal[AutoFile] = str(AutoGenH)
|
|
self._ApplyBuildRule(AutoFile, TAB_UNKNOWN_FILE)
|
|
if str(StringH) != "":
|
|
AutoFile = PathClass(gAutoGenStringFileName % {"module_name":self.Name}, self.DebugDir)
|
|
RetVal[AutoFile] = str(StringH)
|
|
self._ApplyBuildRule(AutoFile, TAB_UNKNOWN_FILE)
|
|
if UniStringBinBuffer is not None and UniStringBinBuffer.getvalue() != b"":
|
|
AutoFile = PathClass(gAutoGenStringFormFileName % {"module_name":self.Name}, self.OutputDir)
|
|
RetVal[AutoFile] = UniStringBinBuffer.getvalue()
|
|
AutoFile.IsBinary = True
|
|
self._ApplyBuildRule(AutoFile, TAB_UNKNOWN_FILE)
|
|
if UniStringBinBuffer is not None:
|
|
UniStringBinBuffer.close()
|
|
if str(StringIdf) != "":
|
|
AutoFile = PathClass(gAutoGenImageDefFileName % {"module_name":self.Name}, self.DebugDir)
|
|
RetVal[AutoFile] = str(StringIdf)
|
|
self._ApplyBuildRule(AutoFile, TAB_UNKNOWN_FILE)
|
|
if IdfGenBinBuffer is not None and IdfGenBinBuffer.getvalue() != b"":
|
|
AutoFile = PathClass(gAutoGenIdfFileName % {"module_name":self.Name}, self.OutputDir)
|
|
RetVal[AutoFile] = IdfGenBinBuffer.getvalue()
|
|
AutoFile.IsBinary = True
|
|
self._ApplyBuildRule(AutoFile, TAB_UNKNOWN_FILE)
|
|
if IdfGenBinBuffer is not None:
|
|
IdfGenBinBuffer.close()
|
|
return RetVal
|
|
|
|
## Return the list of library modules explicitly or implicitly used by this module
|
|
@cached_property
|
|
def DependentLibraryList(self):
|
|
# only merge library classes and PCD for non-library module
|
|
if self.IsLibrary:
|
|
return []
|
|
return self.PlatformInfo.ApplyLibraryInstance(self.Module)
|
|
|
|
## Get the list of PCDs from current module
|
|
#
|
|
# @retval list The list of PCD
|
|
#
|
|
@cached_property
|
|
def ModulePcdList(self):
|
|
# apply PCD settings from platform
|
|
RetVal = self.PlatformInfo.ApplyPcdSetting(self, self.Module.Pcds)
|
|
|
|
return RetVal
|
|
@cached_property
|
|
def _PcdComments(self):
|
|
ReVal = OrderedListDict()
|
|
ExtendCopyDictionaryLists(ReVal, self.Module.PcdComments)
|
|
if not self.IsLibrary:
|
|
for Library in self.DependentLibraryList:
|
|
ExtendCopyDictionaryLists(ReVal, Library.PcdComments)
|
|
return ReVal
|
|
|
|
## Get the list of PCDs from dependent libraries
|
|
#
|
|
# @retval list The list of PCD
|
|
#
|
|
@cached_property
|
|
def LibraryPcdList(self):
|
|
if self.IsLibrary:
|
|
return []
|
|
RetVal = []
|
|
Pcds = set()
|
|
# get PCDs from dependent libraries
|
|
for Library in self.DependentLibraryList:
|
|
PcdsInLibrary = OrderedDict()
|
|
for Key in Library.Pcds:
|
|
# skip duplicated PCDs
|
|
if Key in self.Module.Pcds or Key in Pcds:
|
|
continue
|
|
Pcds.add(Key)
|
|
PcdsInLibrary[Key] = copy.copy(Library.Pcds[Key])
|
|
RetVal.extend(self.PlatformInfo.ApplyPcdSetting(self, PcdsInLibrary, Library=Library))
|
|
return RetVal
|
|
|
|
## Get the GUID value mapping
|
|
#
|
|
# @retval dict The mapping between GUID cname and its value
|
|
#
|
|
@cached_property
|
|
def GuidList(self):
|
|
RetVal = self.Module.Guids
|
|
for Library in self.DependentLibraryList:
|
|
RetVal.update(Library.Guids)
|
|
ExtendCopyDictionaryLists(self._GuidComments, Library.GuidComments)
|
|
ExtendCopyDictionaryLists(self._GuidComments, self.Module.GuidComments)
|
|
return RetVal
|
|
|
|
@cached_property
|
|
def GetGuidsUsedByPcd(self):
|
|
RetVal = OrderedDict(self.Module.GetGuidsUsedByPcd())
|
|
for Library in self.DependentLibraryList:
|
|
RetVal.update(Library.GetGuidsUsedByPcd())
|
|
return RetVal
|
|
## Get the protocol value mapping
|
|
#
|
|
# @retval dict The mapping between protocol cname and its value
|
|
#
|
|
@cached_property
|
|
def ProtocolList(self):
|
|
RetVal = OrderedDict(self.Module.Protocols)
|
|
for Library in self.DependentLibraryList:
|
|
RetVal.update(Library.Protocols)
|
|
ExtendCopyDictionaryLists(self._ProtocolComments, Library.ProtocolComments)
|
|
ExtendCopyDictionaryLists(self._ProtocolComments, self.Module.ProtocolComments)
|
|
return RetVal
|
|
|
|
## Get the PPI value mapping
|
|
#
|
|
# @retval dict The mapping between PPI cname and its value
|
|
#
|
|
@cached_property
|
|
def PpiList(self):
|
|
RetVal = OrderedDict(self.Module.Ppis)
|
|
for Library in self.DependentLibraryList:
|
|
RetVal.update(Library.Ppis)
|
|
ExtendCopyDictionaryLists(self._PpiComments, Library.PpiComments)
|
|
ExtendCopyDictionaryLists(self._PpiComments, self.Module.PpiComments)
|
|
return RetVal
|
|
|
|
## Get the list of include search path
|
|
#
|
|
# @retval list The list path
|
|
#
|
|
@cached_property
|
|
def IncludePathList(self):
|
|
RetVal = []
|
|
RetVal.append(self.MetaFile.Dir)
|
|
RetVal.append(self.DebugDir)
|
|
|
|
for Package in self.PackageList:
|
|
PackageDir = mws.join(self.WorkspaceDir, Package.MetaFile.Dir)
|
|
if PackageDir not in RetVal:
|
|
RetVal.append(PackageDir)
|
|
IncludesList = Package.Includes
|
|
if Package._PrivateIncludes:
|
|
if not self.MetaFile.OriginalPath.Path.startswith(PackageDir):
|
|
IncludesList = list(set(Package.Includes).difference(set(Package._PrivateIncludes)))
|
|
for Inc in IncludesList:
|
|
if Inc not in RetVal:
|
|
RetVal.append(str(Inc))
|
|
RetVal.extend(self.IncPathFromBuildOptions)
|
|
return RetVal
|
|
|
|
@cached_property
|
|
def IncPathFromBuildOptions(self):
|
|
IncPathList = []
|
|
for tool in self.BuildOption:
|
|
if 'FLAGS' in self.BuildOption[tool]:
|
|
flags = self.BuildOption[tool]['FLAGS']
|
|
whitespace = False
|
|
for flag in flags.split(" "):
|
|
flag = flag.strip()
|
|
if flag.startswith(("/I","-I")):
|
|
if len(flag)>2:
|
|
if os.path.exists(flag[2:]):
|
|
IncPathList.append(flag[2:])
|
|
else:
|
|
whitespace = True
|
|
continue
|
|
if whitespace and flag:
|
|
if os.path.exists(flag):
|
|
IncPathList.append(flag)
|
|
whitespace = False
|
|
return IncPathList
|
|
|
|
@cached_property
|
|
def IncludePathLength(self):
|
|
return sum(len(inc)+1 for inc in self.IncludePathList)
|
|
|
|
## Get the list of include paths from the packages
|
|
#
|
|
# @IncludesList list The list path
|
|
#
|
|
@cached_property
|
|
def PackageIncludePathList(self):
|
|
IncludesList = []
|
|
for Package in self.PackageList:
|
|
PackageDir = mws.join(self.WorkspaceDir, Package.MetaFile.Dir)
|
|
IncludesList = Package.Includes
|
|
if Package._PrivateIncludes:
|
|
if not self.MetaFile.Path.startswith(PackageDir):
|
|
IncludesList = list(set(Package.Includes).difference(set(Package._PrivateIncludes)))
|
|
return IncludesList
|
|
|
|
## Get HII EX PCDs which maybe used by VFR
|
|
#
|
|
# efivarstore used by VFR may relate with HII EX PCDs
|
|
# Get the variable name and GUID from efivarstore and HII EX PCD
|
|
# List the HII EX PCDs in As Built INF if both name and GUID match.
|
|
#
|
|
# @retval list HII EX PCDs
|
|
#
|
|
def _GetPcdsMaybeUsedByVfr(self):
|
|
if not self.SourceFileList:
|
|
return []
|
|
|
|
NameGuids = set()
|
|
for SrcFile in self.SourceFileList:
|
|
if SrcFile.Ext.lower() != '.vfr':
|
|
continue
|
|
Vfri = os.path.join(self.OutputDir, SrcFile.BaseName + '.i')
|
|
if not os.path.exists(Vfri):
|
|
continue
|
|
VfriFile = open(Vfri, 'r')
|
|
Content = VfriFile.read()
|
|
VfriFile.close()
|
|
Pos = Content.find('efivarstore')
|
|
while Pos != -1:
|
|
#
|
|
# Make sure 'efivarstore' is the start of efivarstore statement
|
|
# In case of the value of 'name' (name = efivarstore) is equal to 'efivarstore'
|
|
#
|
|
Index = Pos - 1
|
|
while Index >= 0 and Content[Index] in ' \t\r\n':
|
|
Index -= 1
|
|
if Index >= 0 and Content[Index] != ';':
|
|
Pos = Content.find('efivarstore', Pos + len('efivarstore'))
|
|
continue
|
|
#
|
|
# 'efivarstore' must be followed by name and guid
|
|
#
|
|
Name = gEfiVarStoreNamePattern.search(Content, Pos)
|
|
if not Name:
|
|
break
|
|
Guid = gEfiVarStoreGuidPattern.search(Content, Pos)
|
|
if not Guid:
|
|
break
|
|
NameArray = _ConvertStringToByteArray('L"' + Name.group(1) + '"')
|
|
NameGuids.add((NameArray, GuidStructureStringToGuidString(Guid.group(1))))
|
|
Pos = Content.find('efivarstore', Name.end())
|
|
if not NameGuids:
|
|
return []
|
|
HiiExPcds = []
|
|
for Pcd in self.PlatformInfo.Pcds.values():
|
|
if Pcd.Type != TAB_PCDS_DYNAMIC_EX_HII:
|
|
continue
|
|
for SkuInfo in Pcd.SkuInfoList.values():
|
|
Value = GuidValue(SkuInfo.VariableGuid, self.PlatformInfo.PackageList, self.MetaFile.Path)
|
|
if not Value:
|
|
continue
|
|
Name = _ConvertStringToByteArray(SkuInfo.VariableName)
|
|
Guid = GuidStructureStringToGuidString(Value)
|
|
if (Name, Guid) in NameGuids and Pcd not in HiiExPcds:
|
|
HiiExPcds.append(Pcd)
|
|
break
|
|
|
|
return HiiExPcds
|
|
|
|
def _GenOffsetBin(self):
|
|
VfrUniBaseName = {}
|
|
for SourceFile in self.Module.Sources:
|
|
if SourceFile.Type.upper() == ".VFR" :
|
|
#
|
|
# search the .map file to find the offset of vfr binary in the PE32+/TE file.
|
|
#
|
|
VfrUniBaseName[SourceFile.BaseName] = (SourceFile.BaseName + "Bin")
|
|
elif SourceFile.Type.upper() == ".UNI" :
|
|
#
|
|
# search the .map file to find the offset of Uni strings binary in the PE32+/TE file.
|
|
#
|
|
VfrUniBaseName["UniOffsetName"] = (self.Name + "Strings")
|
|
|
|
if not VfrUniBaseName:
|
|
return None
|
|
MapFileName = os.path.join(self.OutputDir, self.Name + ".map")
|
|
EfiFileName = os.path.join(self.OutputDir, self.Name + ".efi")
|
|
VfrUniOffsetList = GetVariableOffset(MapFileName, EfiFileName, list(VfrUniBaseName.values()))
|
|
if not VfrUniOffsetList:
|
|
return None
|
|
|
|
OutputName = '%sOffset.bin' % self.Name
|
|
UniVfrOffsetFileName = os.path.join( self.OutputDir, OutputName)
|
|
|
|
try:
|
|
fInputfile = open(UniVfrOffsetFileName, "wb+", 0)
|
|
except:
|
|
EdkLogger.error("build", FILE_OPEN_FAILURE, "File open failed for %s" % UniVfrOffsetFileName, None)
|
|
|
|
# Use a instance of BytesIO to cache data
|
|
fStringIO = BytesIO()
|
|
|
|
for Item in VfrUniOffsetList:
|
|
if (Item[0].find("Strings") != -1):
|
|
#
|
|
# UNI offset in image.
|
|
# GUID + Offset
|
|
# { 0x8913c5e0, 0x33f6, 0x4d86, { 0x9b, 0xf1, 0x43, 0xef, 0x89, 0xfc, 0x6, 0x66 } }
|
|
#
|
|
UniGuid = b'\xe0\xc5\x13\x89\xf63\x86M\x9b\xf1C\xef\x89\xfc\x06f'
|
|
fStringIO.write(UniGuid)
|
|
UniValue = pack ('Q', int (Item[1], 16))
|
|
fStringIO.write (UniValue)
|
|
else:
|
|
#
|
|
# VFR binary offset in image.
|
|
# GUID + Offset
|
|
# { 0xd0bc7cb4, 0x6a47, 0x495f, { 0xaa, 0x11, 0x71, 0x7, 0x46, 0xda, 0x6, 0xa2 } };
|
|
#
|
|
VfrGuid = b'\xb4|\xbc\xd0Gj_I\xaa\x11q\x07F\xda\x06\xa2'
|
|
fStringIO.write(VfrGuid)
|
|
VfrValue = pack ('Q', int (Item[1], 16))
|
|
fStringIO.write (VfrValue)
|
|
#
|
|
# write data into file.
|
|
#
|
|
try :
|
|
fInputfile.write (fStringIO.getvalue())
|
|
except:
|
|
EdkLogger.error("build", FILE_WRITE_FAILURE, "Write data to file %s failed, please check whether the "
|
|
"file been locked or using by other applications." %UniVfrOffsetFileName, None)
|
|
|
|
fStringIO.close ()
|
|
fInputfile.close ()
|
|
return OutputName
|
|
|
|
@cached_property
|
|
def OutputFile(self):
|
|
retVal = set()
|
|
|
|
for Root, Dirs, Files in os.walk(self.BuildDir):
|
|
for File in Files:
|
|
# lib file is already added through above CodaTargetList, skip it here
|
|
if not (File.lower().endswith('.obj') or File.lower().endswith('.debug')):
|
|
NewFile = path.join(Root, File)
|
|
retVal.add(NewFile)
|
|
|
|
for Root, Dirs, Files in os.walk(self.FfsOutputDir):
|
|
for File in Files:
|
|
NewFile = path.join(Root, File)
|
|
retVal.add(NewFile)
|
|
|
|
return retVal
|
|
|
|
## Create AsBuilt INF file the module
|
|
#
|
|
def CreateAsBuiltInf(self):
|
|
|
|
if self.IsAsBuiltInfCreated:
|
|
return
|
|
|
|
# Skip INF file generation for libraries
|
|
if self.IsLibrary:
|
|
return
|
|
|
|
# Skip the following code for modules with no source files
|
|
if not self.SourceFileList:
|
|
return
|
|
|
|
# Skip the following code for modules without any binary files
|
|
if self.BinaryFileList:
|
|
return
|
|
|
|
### TODO: How to handles mixed source and binary modules
|
|
|
|
# Find all DynamicEx and PatchableInModule PCDs used by this module and dependent libraries
|
|
# Also find all packages that the DynamicEx PCDs depend on
|
|
Pcds = []
|
|
PatchablePcds = []
|
|
Packages = []
|
|
PcdCheckList = []
|
|
PcdTokenSpaceList = []
|
|
for Pcd in self.ModulePcdList + self.LibraryPcdList:
|
|
if Pcd.Type == TAB_PCDS_PATCHABLE_IN_MODULE:
|
|
PatchablePcds.append(Pcd)
|
|
PcdCheckList.append((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, TAB_PCDS_PATCHABLE_IN_MODULE))
|
|
elif Pcd.Type in PCD_DYNAMIC_EX_TYPE_SET:
|
|
if Pcd not in Pcds:
|
|
Pcds.append(Pcd)
|
|
PcdCheckList.append((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, TAB_PCDS_DYNAMIC_EX))
|
|
PcdCheckList.append((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, TAB_PCDS_DYNAMIC))
|
|
PcdTokenSpaceList.append(Pcd.TokenSpaceGuidCName)
|
|
GuidList = OrderedDict(self.GuidList)
|
|
for TokenSpace in self.GetGuidsUsedByPcd:
|
|
# If token space is not referred by patch PCD or Ex PCD, remove the GUID from GUID list
|
|
# The GUIDs in GUIDs section should really be the GUIDs in source INF or referred by Ex an patch PCDs
|
|
if TokenSpace not in PcdTokenSpaceList and TokenSpace in GuidList:
|
|
GuidList.pop(TokenSpace)
|
|
CheckList = (GuidList, self.PpiList, self.ProtocolList, PcdCheckList)
|
|
for Package in self.DerivedPackageList:
|
|
if Package in Packages:
|
|
continue
|
|
BeChecked = (Package.Guids, Package.Ppis, Package.Protocols, Package.Pcds)
|
|
Found = False
|
|
for Index in range(len(BeChecked)):
|
|
for Item in CheckList[Index]:
|
|
if Item in BeChecked[Index]:
|
|
Packages.append(Package)
|
|
Found = True
|
|
break
|
|
if Found:
|
|
break
|
|
|
|
VfrPcds = self._GetPcdsMaybeUsedByVfr()
|
|
for Pkg in self.PlatformInfo.PackageList:
|
|
if Pkg in Packages:
|
|
continue
|
|
for VfrPcd in VfrPcds:
|
|
if ((VfrPcd.TokenCName, VfrPcd.TokenSpaceGuidCName, TAB_PCDS_DYNAMIC_EX) in Pkg.Pcds or
|
|
(VfrPcd.TokenCName, VfrPcd.TokenSpaceGuidCName, TAB_PCDS_DYNAMIC) in Pkg.Pcds):
|
|
Packages.append(Pkg)
|
|
break
|
|
|
|
ModuleType = SUP_MODULE_DXE_DRIVER if self.ModuleType == SUP_MODULE_UEFI_DRIVER and self.DepexGenerated else self.ModuleType
|
|
DriverType = self.PcdIsDriver if self.PcdIsDriver else ''
|
|
Guid = self.Guid
|
|
MDefs = self.Module.Defines
|
|
|
|
AsBuiltInfDict = {
|
|
'module_name' : self.Name,
|
|
'module_guid' : Guid,
|
|
'module_module_type' : ModuleType,
|
|
'module_version_string' : [MDefs['VERSION_STRING']] if 'VERSION_STRING' in MDefs else [],
|
|
'pcd_is_driver_string' : [],
|
|
'module_uefi_specification_version' : [],
|
|
'module_pi_specification_version' : [],
|
|
'module_entry_point' : self.Module.ModuleEntryPointList,
|
|
'module_unload_image' : self.Module.ModuleUnloadImageList,
|
|
'module_constructor' : self.Module.ConstructorList,
|
|
'module_destructor' : self.Module.DestructorList,
|
|
'module_shadow' : [MDefs['SHADOW']] if 'SHADOW' in MDefs else [],
|
|
'module_pci_vendor_id' : [MDefs['PCI_VENDOR_ID']] if 'PCI_VENDOR_ID' in MDefs else [],
|
|
'module_pci_device_id' : [MDefs['PCI_DEVICE_ID']] if 'PCI_DEVICE_ID' in MDefs else [],
|
|
'module_pci_class_code' : [MDefs['PCI_CLASS_CODE']] if 'PCI_CLASS_CODE' in MDefs else [],
|
|
'module_pci_revision' : [MDefs['PCI_REVISION']] if 'PCI_REVISION' in MDefs else [],
|
|
'module_build_number' : [MDefs['BUILD_NUMBER']] if 'BUILD_NUMBER' in MDefs else [],
|
|
'module_spec' : [MDefs['SPEC']] if 'SPEC' in MDefs else [],
|
|
'module_uefi_hii_resource_section' : [MDefs['UEFI_HII_RESOURCE_SECTION']] if 'UEFI_HII_RESOURCE_SECTION' in MDefs else [],
|
|
'module_uni_file' : [MDefs['MODULE_UNI_FILE']] if 'MODULE_UNI_FILE' in MDefs else [],
|
|
'module_arch' : self.Arch,
|
|
'package_item' : [Package.MetaFile.File.replace('\\', '/') for Package in Packages],
|
|
'binary_item' : [],
|
|
'patchablepcd_item' : [],
|
|
'pcd_item' : [],
|
|
'protocol_item' : [],
|
|
'ppi_item' : [],
|
|
'guid_item' : [],
|
|
'flags_item' : [],
|
|
'libraryclasses_item' : []
|
|
}
|
|
|
|
if 'MODULE_UNI_FILE' in MDefs:
|
|
UNIFile = os.path.join(self.MetaFile.Dir, MDefs['MODULE_UNI_FILE'])
|
|
if os.path.isfile(UNIFile):
|
|
shutil.copy2(UNIFile, self.OutputDir)
|
|
|
|
if self.AutoGenVersion > int(gInfSpecVersion, 0):
|
|
AsBuiltInfDict['module_inf_version'] = '0x%08x' % self.AutoGenVersion
|
|
else:
|
|
AsBuiltInfDict['module_inf_version'] = gInfSpecVersion
|
|
|
|
if DriverType:
|
|
AsBuiltInfDict['pcd_is_driver_string'].append(DriverType)
|
|
|
|
if 'UEFI_SPECIFICATION_VERSION' in self.Specification:
|
|
AsBuiltInfDict['module_uefi_specification_version'].append(self.Specification['UEFI_SPECIFICATION_VERSION'])
|
|
if 'PI_SPECIFICATION_VERSION' in self.Specification:
|
|
AsBuiltInfDict['module_pi_specification_version'].append(self.Specification['PI_SPECIFICATION_VERSION'])
|
|
|
|
OutputDir = self.OutputDir.replace('\\', '/').strip('/')
|
|
DebugDir = self.DebugDir.replace('\\', '/').strip('/')
|
|
for Item in self.CodaTargetList:
|
|
File = Item.Target.Path.replace('\\', '/').strip('/').replace(DebugDir, '').replace(OutputDir, '').strip('/')
|
|
if os.path.isabs(File):
|
|
File = File.replace('\\', '/').strip('/').replace(OutputDir, '').strip('/')
|
|
if Item.Target.Ext.lower() == '.aml':
|
|
AsBuiltInfDict['binary_item'].append('ASL|' + File)
|
|
elif Item.Target.Ext.lower() == '.acpi':
|
|
AsBuiltInfDict['binary_item'].append('ACPI|' + File)
|
|
elif Item.Target.Ext.lower() == '.efi':
|
|
AsBuiltInfDict['binary_item'].append('PE32|' + self.Name + '.efi')
|
|
else:
|
|
AsBuiltInfDict['binary_item'].append('BIN|' + File)
|
|
if not self.DepexGenerated:
|
|
DepexFile = os.path.join(self.OutputDir, self.Name + '.depex')
|
|
if os.path.exists(DepexFile):
|
|
self.DepexGenerated = True
|
|
if self.DepexGenerated:
|
|
if self.ModuleType in [SUP_MODULE_PEIM]:
|
|
AsBuiltInfDict['binary_item'].append('PEI_DEPEX|' + self.Name + '.depex')
|
|
elif self.ModuleType in [SUP_MODULE_DXE_DRIVER, SUP_MODULE_DXE_RUNTIME_DRIVER, SUP_MODULE_DXE_SAL_DRIVER, SUP_MODULE_UEFI_DRIVER]:
|
|
AsBuiltInfDict['binary_item'].append('DXE_DEPEX|' + self.Name + '.depex')
|
|
elif self.ModuleType in [SUP_MODULE_DXE_SMM_DRIVER]:
|
|
AsBuiltInfDict['binary_item'].append('SMM_DEPEX|' + self.Name + '.depex')
|
|
|
|
Bin = self._GenOffsetBin()
|
|
if Bin:
|
|
AsBuiltInfDict['binary_item'].append('BIN|%s' % Bin)
|
|
|
|
for Root, Dirs, Files in os.walk(OutputDir):
|
|
for File in Files:
|
|
if File.lower().endswith('.pdb'):
|
|
AsBuiltInfDict['binary_item'].append('DISPOSABLE|' + File)
|
|
HeaderComments = self.Module.HeaderComments
|
|
StartPos = 0
|
|
for Index in range(len(HeaderComments)):
|
|
if HeaderComments[Index].find('@BinaryHeader') != -1:
|
|
HeaderComments[Index] = HeaderComments[Index].replace('@BinaryHeader', '@file')
|
|
StartPos = Index
|
|
break
|
|
AsBuiltInfDict['header_comments'] = '\n'.join(HeaderComments[StartPos:]).replace(':#', '://')
|
|
AsBuiltInfDict['tail_comments'] = '\n'.join(self.Module.TailComments)
|
|
|
|
GenList = [
|
|
(self.ProtocolList, self._ProtocolComments, 'protocol_item'),
|
|
(self.PpiList, self._PpiComments, 'ppi_item'),
|
|
(GuidList, self._GuidComments, 'guid_item')
|
|
]
|
|
for Item in GenList:
|
|
for CName in Item[0]:
|
|
Comments = '\n '.join(Item[1][CName]) if CName in Item[1] else ''
|
|
Entry = Comments + '\n ' + CName if Comments else CName
|
|
AsBuiltInfDict[Item[2]].append(Entry)
|
|
PatchList = parsePcdInfoFromMapFile(
|
|
os.path.join(self.OutputDir, self.Name + '.map'),
|
|
os.path.join(self.OutputDir, self.Name + '.efi')
|
|
)
|
|
if PatchList:
|
|
for Pcd in PatchablePcds:
|
|
TokenCName = Pcd.TokenCName
|
|
for PcdItem in GlobalData.MixedPcd:
|
|
if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName) in GlobalData.MixedPcd[PcdItem]:
|
|
TokenCName = PcdItem[0]
|
|
break
|
|
for PatchPcd in PatchList:
|
|
if TokenCName == PatchPcd[0]:
|
|
break
|
|
else:
|
|
continue
|
|
PcdValue = ''
|
|
if Pcd.DatumType == 'BOOLEAN':
|
|
BoolValue = Pcd.DefaultValue.upper()
|
|
if BoolValue == 'TRUE':
|
|
Pcd.DefaultValue = '1'
|
|
elif BoolValue == 'FALSE':
|
|
Pcd.DefaultValue = '0'
|
|
|
|
if Pcd.DatumType in TAB_PCD_NUMERIC_TYPES:
|
|
HexFormat = '0x%02x'
|
|
if Pcd.DatumType == TAB_UINT16:
|
|
HexFormat = '0x%04x'
|
|
elif Pcd.DatumType == TAB_UINT32:
|
|
HexFormat = '0x%08x'
|
|
elif Pcd.DatumType == TAB_UINT64:
|
|
HexFormat = '0x%016x'
|
|
PcdValue = HexFormat % int(Pcd.DefaultValue, 0)
|
|
else:
|
|
if Pcd.MaxDatumSize is None or Pcd.MaxDatumSize == '':
|
|
EdkLogger.error("build", AUTOGEN_ERROR,
|
|
"Unknown [MaxDatumSize] of PCD [%s.%s]" % (Pcd.TokenSpaceGuidCName, TokenCName)
|
|
)
|
|
ArraySize = int(Pcd.MaxDatumSize, 0)
|
|
PcdValue = Pcd.DefaultValue
|
|
if PcdValue[0] != '{':
|
|
Unicode = False
|
|
if PcdValue[0] == 'L':
|
|
Unicode = True
|
|
PcdValue = PcdValue.lstrip('L')
|
|
PcdValue = eval(PcdValue)
|
|
NewValue = '{'
|
|
for Index in range(0, len(PcdValue)):
|
|
if Unicode:
|
|
CharVal = ord(PcdValue[Index])
|
|
NewValue = NewValue + '0x%02x' % (CharVal & 0x00FF) + ', ' \
|
|
+ '0x%02x' % (CharVal >> 8) + ', '
|
|
else:
|
|
NewValue = NewValue + '0x%02x' % (ord(PcdValue[Index]) % 0x100) + ', '
|
|
Padding = '0x00, '
|
|
if Unicode:
|
|
Padding = Padding * 2
|
|
ArraySize = ArraySize // 2
|
|
if ArraySize < (len(PcdValue) + 1):
|
|
if Pcd.MaxSizeUserSet:
|
|
EdkLogger.error("build", AUTOGEN_ERROR,
|
|
"The maximum size of VOID* type PCD '%s.%s' is less than its actual size occupied." % (Pcd.TokenSpaceGuidCName, TokenCName)
|
|
)
|
|
else:
|
|
ArraySize = len(PcdValue) + 1
|
|
if ArraySize > len(PcdValue) + 1:
|
|
NewValue = NewValue + Padding * (ArraySize - len(PcdValue) - 1)
|
|
PcdValue = NewValue + Padding.strip().rstrip(',') + '}'
|
|
elif len(PcdValue.split(',')) <= ArraySize:
|
|
PcdValue = PcdValue.rstrip('}') + ', 0x00' * (ArraySize - len(PcdValue.split(',')))
|
|
PcdValue += '}'
|
|
else:
|
|
if Pcd.MaxSizeUserSet:
|
|
EdkLogger.error("build", AUTOGEN_ERROR,
|
|
"The maximum size of VOID* type PCD '%s.%s' is less than its actual size occupied." % (Pcd.TokenSpaceGuidCName, TokenCName)
|
|
)
|
|
else:
|
|
ArraySize = len(PcdValue) + 1
|
|
PcdItem = '%s.%s|%s|0x%X' % \
|
|
(Pcd.TokenSpaceGuidCName, TokenCName, PcdValue, PatchPcd[1])
|
|
PcdComments = ''
|
|
if (Pcd.TokenSpaceGuidCName, Pcd.TokenCName) in self._PcdComments:
|
|
PcdComments = '\n '.join(self._PcdComments[Pcd.TokenSpaceGuidCName, Pcd.TokenCName])
|
|
if PcdComments:
|
|
PcdItem = PcdComments + '\n ' + PcdItem
|
|
AsBuiltInfDict['patchablepcd_item'].append(PcdItem)
|
|
|
|
for Pcd in Pcds + VfrPcds:
|
|
PcdCommentList = []
|
|
HiiInfo = ''
|
|
TokenCName = Pcd.TokenCName
|
|
for PcdItem in GlobalData.MixedPcd:
|
|
if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName) in GlobalData.MixedPcd[PcdItem]:
|
|
TokenCName = PcdItem[0]
|
|
break
|
|
if Pcd.Type == TAB_PCDS_DYNAMIC_EX_HII:
|
|
for SkuName in Pcd.SkuInfoList:
|
|
SkuInfo = Pcd.SkuInfoList[SkuName]
|
|
HiiInfo = '## %s|%s|%s' % (SkuInfo.VariableName, SkuInfo.VariableGuid, SkuInfo.VariableOffset)
|
|
break
|
|
if (Pcd.TokenSpaceGuidCName, Pcd.TokenCName) in self._PcdComments:
|
|
PcdCommentList = self._PcdComments[Pcd.TokenSpaceGuidCName, Pcd.TokenCName][:]
|
|
if HiiInfo:
|
|
UsageIndex = -1
|
|
UsageStr = ''
|
|
for Index, Comment in enumerate(PcdCommentList):
|
|
for Usage in UsageList:
|
|
if Comment.find(Usage) != -1:
|
|
UsageStr = Usage
|
|
UsageIndex = Index
|
|
break
|
|
if UsageIndex != -1:
|
|
PcdCommentList[UsageIndex] = '## %s %s %s' % (UsageStr, HiiInfo, PcdCommentList[UsageIndex].replace(UsageStr, ''))
|
|
else:
|
|
PcdCommentList.append('## UNDEFINED ' + HiiInfo)
|
|
PcdComments = '\n '.join(PcdCommentList)
|
|
PcdEntry = Pcd.TokenSpaceGuidCName + '.' + TokenCName
|
|
if PcdComments:
|
|
PcdEntry = PcdComments + '\n ' + PcdEntry
|
|
AsBuiltInfDict['pcd_item'].append(PcdEntry)
|
|
for Item in self.BuildOption:
|
|
if 'FLAGS' in self.BuildOption[Item]:
|
|
AsBuiltInfDict['flags_item'].append('%s:%s_%s_%s_%s_FLAGS = %s' % (self.ToolChainFamily, self.BuildTarget, self.ToolChain, self.Arch, Item, self.BuildOption[Item]['FLAGS'].strip()))
|
|
|
|
# Generated LibraryClasses section in comments.
|
|
for Library in self.LibraryAutoGenList:
|
|
AsBuiltInfDict['libraryclasses_item'].append(Library.MetaFile.File.replace('\\', '/'))
|
|
|
|
# Generated UserExtensions TianoCore section.
|
|
# All tianocore user extensions are copied.
|
|
UserExtStr = ''
|
|
for TianoCore in self._GetTianoCoreUserExtensionList():
|
|
UserExtStr += '\n'.join(TianoCore)
|
|
ExtensionFile = os.path.join(self.MetaFile.Dir, TianoCore[1])
|
|
if os.path.isfile(ExtensionFile):
|
|
shutil.copy2(ExtensionFile, self.OutputDir)
|
|
AsBuiltInfDict['userextension_tianocore_item'] = UserExtStr
|
|
|
|
# Generated depex expression section in comments.
|
|
DepexExpression = self._GetDepexExpresionString()
|
|
AsBuiltInfDict['depexsection_item'] = DepexExpression if DepexExpression else ''
|
|
|
|
AsBuiltInf = TemplateString()
|
|
AsBuiltInf.Append(gAsBuiltInfHeaderString.Replace(AsBuiltInfDict))
|
|
|
|
SaveFileOnChange(os.path.join(self.OutputDir, self.Name + '.inf'), str(AsBuiltInf), False)
|
|
|
|
self.IsAsBuiltInfCreated = True
|
|
|
|
def CacheCopyFile(self, DestDir, SourceDir, File):
|
|
if os.path.isdir(File):
|
|
return
|
|
|
|
sub_dir = os.path.relpath(File, SourceDir)
|
|
destination_file = os.path.join(DestDir, sub_dir)
|
|
destination_dir = os.path.dirname(destination_file)
|
|
CreateDirectory(destination_dir)
|
|
try:
|
|
CopyFileOnChange(File, destination_dir)
|
|
except:
|
|
EdkLogger.quiet("[cache warning]: fail to copy file:%s to folder:%s" % (File, destination_dir))
|
|
return
|
|
|
|
def CopyModuleToCache(self):
|
|
# Find the MakeHashStr and PreMakeHashStr from latest MakeHashFileList
|
|
# and PreMakeHashFileList files
|
|
MakeHashStr = None
|
|
PreMakeHashStr = None
|
|
MakeTimeStamp = 0
|
|
PreMakeTimeStamp = 0
|
|
Files = [f for f in os.listdir(LongFilePath(self.BuildDir)) if path.isfile(LongFilePath(path.join(self.BuildDir, f)))]
|
|
for File in Files:
|
|
if ".MakeHashFileList." in File:
|
|
#find lastest file through time stamp
|
|
FileTimeStamp = os.stat(LongFilePath(path.join(self.BuildDir, File)))[8]
|
|
if FileTimeStamp > MakeTimeStamp:
|
|
MakeTimeStamp = FileTimeStamp
|
|
MakeHashStr = File.split('.')[-1]
|
|
if len(MakeHashStr) != 32:
|
|
EdkLogger.quiet("[cache error]: wrong MakeHashFileList file:%s" % (File))
|
|
if ".PreMakeHashFileList." in File:
|
|
FileTimeStamp = os.stat(LongFilePath(path.join(self.BuildDir, File)))[8]
|
|
if FileTimeStamp > PreMakeTimeStamp:
|
|
PreMakeTimeStamp = FileTimeStamp
|
|
PreMakeHashStr = File.split('.')[-1]
|
|
if len(PreMakeHashStr) != 32:
|
|
EdkLogger.quiet("[cache error]: wrong PreMakeHashFileList file:%s" % (File))
|
|
|
|
if not MakeHashStr:
|
|
EdkLogger.quiet("[cache error]: No MakeHashFileList file for module:%s[%s]" % (self.MetaFile.Path, self.Arch))
|
|
return
|
|
if not PreMakeHashStr:
|
|
EdkLogger.quiet("[cache error]: No PreMakeHashFileList file for module:%s[%s]" % (self.MetaFile.Path, self.Arch))
|
|
return
|
|
|
|
# Create Cache destination dirs
|
|
FileDir = path.join(GlobalData.gBinCacheDest, self.PlatformInfo.OutputDir, self.BuildTarget + "_" + self.ToolChain, self.Arch, self.SourceDir, self.MetaFile.BaseName)
|
|
FfsDir = path.join(GlobalData.gBinCacheDest, self.PlatformInfo.OutputDir, self.BuildTarget + "_" + self.ToolChain, TAB_FV_DIRECTORY, "Ffs", self.Guid + self.Name)
|
|
CacheFileDir = path.join(FileDir, MakeHashStr)
|
|
CacheFfsDir = path.join(FfsDir, MakeHashStr)
|
|
CreateDirectory (CacheFileDir)
|
|
CreateDirectory (CacheFfsDir)
|
|
|
|
# Create ModuleHashPair file to support multiple version cache together
|
|
ModuleHashPair = path.join(FileDir, self.Name + ".ModuleHashPair")
|
|
ModuleHashPairList = [] # tuple list: [tuple(PreMakefileHash, MakeHash)]
|
|
if os.path.exists(ModuleHashPair):
|
|
with open(ModuleHashPair, 'r') as f:
|
|
ModuleHashPairList = json.load(f)
|
|
if not (PreMakeHashStr, MakeHashStr) in set(map(tuple, ModuleHashPairList)):
|
|
ModuleHashPairList.insert(0, (PreMakeHashStr, MakeHashStr))
|
|
with open(ModuleHashPair, 'w') as f:
|
|
json.dump(ModuleHashPairList, f, indent=2)
|
|
|
|
# Copy files to Cache destination dirs
|
|
if not self.OutputFile:
|
|
Ma = self.BuildDatabase[self.MetaFile, self.Arch, self.BuildTarget, self.ToolChain]
|
|
self.OutputFile = Ma.Binaries
|
|
for File in self.OutputFile:
|
|
if File.startswith(os.path.abspath(self.FfsOutputDir)+os.sep):
|
|
self.CacheCopyFile(CacheFfsDir, self.FfsOutputDir, File)
|
|
else:
|
|
if self.Name + ".autogen.hash." in File or \
|
|
self.Name + ".autogen.hashchain." in File or \
|
|
self.Name + ".hash." in File or \
|
|
self.Name + ".hashchain." in File or \
|
|
self.Name + ".PreMakeHashFileList." in File or \
|
|
self.Name + ".MakeHashFileList." in File:
|
|
self.CacheCopyFile(FileDir, self.BuildDir, File)
|
|
else:
|
|
self.CacheCopyFile(CacheFileDir, self.BuildDir, File)
|
|
## Create makefile for the module and its dependent libraries
|
|
#
|
|
# @param CreateLibraryMakeFile Flag indicating if or not the makefiles of
|
|
# dependent libraries will be created
|
|
#
|
|
@cached_class_function
|
|
def CreateMakeFile(self, CreateLibraryMakeFile=True, GenFfsList = []):
|
|
|
|
# nest this function inside it's only caller.
|
|
def CreateTimeStamp():
|
|
FileSet = {self.MetaFile.Path}
|
|
|
|
for SourceFile in self.Module.Sources:
|
|
FileSet.add (SourceFile.Path)
|
|
|
|
for Lib in self.DependentLibraryList:
|
|
FileSet.add (Lib.MetaFile.Path)
|
|
|
|
for f in self.AutoGenDepSet:
|
|
FileSet.add (f.Path)
|
|
|
|
if os.path.exists (self.TimeStampPath):
|
|
os.remove (self.TimeStampPath)
|
|
|
|
SaveFileOnChange(self.TimeStampPath, "\n".join(FileSet), False)
|
|
|
|
# Ignore generating makefile when it is a binary module
|
|
if self.IsBinaryModule:
|
|
return
|
|
|
|
self.GenFfsList = GenFfsList
|
|
|
|
if not self.IsLibrary and CreateLibraryMakeFile:
|
|
for LibraryAutoGen in self.LibraryAutoGenList:
|
|
LibraryAutoGen.CreateMakeFile()
|
|
|
|
# CanSkip uses timestamps to determine build skipping
|
|
if self.CanSkip():
|
|
return
|
|
|
|
if len(self.CustomMakefile) == 0:
|
|
Makefile = GenMake.ModuleMakefile(self)
|
|
else:
|
|
Makefile = GenMake.CustomMakefile(self)
|
|
if Makefile.Generate():
|
|
EdkLogger.debug(EdkLogger.DEBUG_9, "Generated makefile for module %s [%s]" %
|
|
(self.Name, self.Arch))
|
|
else:
|
|
EdkLogger.debug(EdkLogger.DEBUG_9, "Skipped the generation of makefile for module %s [%s]" %
|
|
(self.Name, self.Arch))
|
|
|
|
CreateTimeStamp()
|
|
|
|
MakefileType = Makefile._FileType
|
|
MakefileName = Makefile._FILE_NAME_[MakefileType]
|
|
MakefilePath = os.path.join(self.MakeFileDir, MakefileName)
|
|
FilePath = path.join(self.BuildDir, self.Name + ".makefile")
|
|
SaveFileOnChange(FilePath, MakefilePath, False)
|
|
|
|
def CopyBinaryFiles(self):
|
|
for File in self.Module.Binaries:
|
|
SrcPath = File.Path
|
|
DstPath = os.path.join(self.OutputDir, os.path.basename(SrcPath))
|
|
CopyLongFilePath(SrcPath, DstPath)
|
|
## Create autogen code for the module and its dependent libraries
|
|
#
|
|
# @param CreateLibraryCodeFile Flag indicating if or not the code of
|
|
# dependent libraries will be created
|
|
#
|
|
def CreateCodeFile(self, CreateLibraryCodeFile=True):
|
|
|
|
if self.IsCodeFileCreated:
|
|
return
|
|
|
|
# Need to generate PcdDatabase even PcdDriver is binarymodule
|
|
if self.IsBinaryModule and self.PcdIsDriver != '':
|
|
CreatePcdDatabaseCode(self, TemplateString(), TemplateString())
|
|
return
|
|
if self.IsBinaryModule:
|
|
if self.IsLibrary:
|
|
self.CopyBinaryFiles()
|
|
return
|
|
|
|
if not self.IsLibrary and CreateLibraryCodeFile:
|
|
for LibraryAutoGen in self.LibraryAutoGenList:
|
|
LibraryAutoGen.CreateCodeFile()
|
|
|
|
self.LibraryAutoGenList
|
|
AutoGenList = []
|
|
IgoredAutoGenList = []
|
|
|
|
for File in self.AutoGenFileList:
|
|
if GenC.Generate(File.Path, self.AutoGenFileList[File], File.IsBinary):
|
|
AutoGenList.append(str(File))
|
|
else:
|
|
IgoredAutoGenList.append(str(File))
|
|
|
|
|
|
for ModuleType in self.DepexList:
|
|
# Ignore empty [depex] section or [depex] section for SUP_MODULE_USER_DEFINED module
|
|
if len(self.DepexList[ModuleType]) == 0 or ModuleType == SUP_MODULE_USER_DEFINED or ModuleType == SUP_MODULE_HOST_APPLICATION:
|
|
continue
|
|
|
|
Dpx = GenDepex.DependencyExpression(self.DepexList[ModuleType], ModuleType, True)
|
|
DpxFile = gAutoGenDepexFileName % {"module_name" : self.Name}
|
|
|
|
if len(Dpx.PostfixNotation) != 0:
|
|
self.DepexGenerated = True
|
|
|
|
if Dpx.Generate(path.join(self.OutputDir, DpxFile)):
|
|
AutoGenList.append(str(DpxFile))
|
|
else:
|
|
IgoredAutoGenList.append(str(DpxFile))
|
|
|
|
if IgoredAutoGenList == []:
|
|
EdkLogger.debug(EdkLogger.DEBUG_9, "Generated [%s] files for module %s [%s]" %
|
|
(" ".join(AutoGenList), self.Name, self.Arch))
|
|
elif AutoGenList == []:
|
|
EdkLogger.debug(EdkLogger.DEBUG_9, "Skipped the generation of [%s] files for module %s [%s]" %
|
|
(" ".join(IgoredAutoGenList), self.Name, self.Arch))
|
|
else:
|
|
EdkLogger.debug(EdkLogger.DEBUG_9, "Generated [%s] (skipped %s) files for module %s [%s]" %
|
|
(" ".join(AutoGenList), " ".join(IgoredAutoGenList), self.Name, self.Arch))
|
|
|
|
self.IsCodeFileCreated = True
|
|
|
|
return AutoGenList
|
|
|
|
## Summarize the ModuleAutoGen objects of all libraries used by this module
|
|
@cached_property
|
|
def LibraryAutoGenList(self):
|
|
RetVal = []
|
|
for Library in self.DependentLibraryList:
|
|
La = ModuleAutoGen(
|
|
self.Workspace,
|
|
Library.MetaFile,
|
|
self.BuildTarget,
|
|
self.ToolChain,
|
|
self.Arch,
|
|
self.PlatformInfo.MetaFile,
|
|
self.DataPipe
|
|
)
|
|
La.IsLibrary = True
|
|
if La not in RetVal:
|
|
RetVal.append(La)
|
|
for Lib in La.CodaTargetList:
|
|
self._ApplyBuildRule(Lib.Target, TAB_UNKNOWN_FILE)
|
|
return RetVal
|
|
|
|
def GenCMakeHash(self):
|
|
# GenCMakeHash can only be called in --binary-destination
|
|
# Never called in multiprocessing and always directly save result in main process,
|
|
# so no need remote dict to share the gCMakeHashFile result with main process
|
|
|
|
DependencyFileSet = set()
|
|
# Add AutoGen files
|
|
if self.AutoGenFileList:
|
|
for File in set(self.AutoGenFileList):
|
|
DependencyFileSet.add(File)
|
|
|
|
# Add Makefile
|
|
abspath = path.join(self.BuildDir, self.Name + ".makefile")
|
|
try:
|
|
with open(LongFilePath(abspath),"r") as fd:
|
|
lines = fd.readlines()
|
|
except Exception as e:
|
|
EdkLogger.error("build",FILE_NOT_FOUND, "%s doesn't exist" % abspath, ExtraData=str(e), RaiseError=False)
|
|
if lines:
|
|
DependencyFileSet.update(lines)
|
|
|
|
# Caculate all above dependency files hash
|
|
# Initialze hash object
|
|
FileList = []
|
|
m = hashlib.md5()
|
|
for File in sorted(DependencyFileSet, key=lambda x: str(x)):
|
|
if not path.exists(LongFilePath(str(File))):
|
|
EdkLogger.quiet("[cache warning]: header file %s is missing for module: %s[%s]" % (File, self.MetaFile.Path, self.Arch))
|
|
continue
|
|
with open(LongFilePath(str(File)), 'rb') as f:
|
|
Content = f.read()
|
|
m.update(Content)
|
|
FileList.append((str(File), hashlib.md5(Content).hexdigest()))
|
|
|
|
HashChainFile = path.join(self.BuildDir, self.Name + ".autogen.hashchain." + m.hexdigest())
|
|
GlobalData.gCMakeHashFile[(self.MetaFile.Path, self.Arch)] = HashChainFile
|
|
try:
|
|
with open(LongFilePath(HashChainFile), 'w') as f:
|
|
json.dump(FileList, f, indent=2)
|
|
except:
|
|
EdkLogger.quiet("[cache warning]: fail to save hashchain file:%s" % HashChainFile)
|
|
return False
|
|
|
|
def GenModuleHash(self):
|
|
# GenModuleHash only called after autogen phase
|
|
# Never called in multiprocessing and always directly save result in main process,
|
|
# so no need remote dict to share the gModuleHashFile result with main process
|
|
#
|
|
# GenPreMakefileHashList consume no dict.
|
|
# GenPreMakefileHashList produce local gModuleHashFile dict.
|
|
|
|
DependencyFileSet = set()
|
|
# Add Module Meta file
|
|
DependencyFileSet.add(self.MetaFile.Path)
|
|
|
|
# Add Module's source files
|
|
if self.SourceFileList:
|
|
for File in set(self.SourceFileList):
|
|
DependencyFileSet.add(File.Path)
|
|
|
|
# Add modules's include header files
|
|
# Directly use the deps.txt file in the module BuildDir
|
|
abspath = path.join(self.BuildDir, "deps.txt")
|
|
rt = None
|
|
try:
|
|
with open(LongFilePath(abspath),"r") as fd:
|
|
lines = fd.readlines()
|
|
if lines:
|
|
rt = set([item.lstrip().strip("\n") for item in lines if item.strip("\n").endswith(".h")])
|
|
except Exception as e:
|
|
EdkLogger.error("build",FILE_NOT_FOUND, "%s doesn't exist" % abspath, ExtraData=str(e), RaiseError=False)
|
|
|
|
if rt:
|
|
DependencyFileSet.update(rt)
|
|
|
|
|
|
# Caculate all above dependency files hash
|
|
# Initialze hash object
|
|
FileList = []
|
|
m = hashlib.md5()
|
|
BuildDirStr = path.abspath(self.BuildDir).lower()
|
|
for File in sorted(DependencyFileSet, key=lambda x: str(x)):
|
|
# Skip the AutoGen files in BuildDir which already been
|
|
# included in .autogen.hash. file
|
|
if BuildDirStr in path.abspath(File).lower():
|
|
continue
|
|
if not path.exists(LongFilePath(File)):
|
|
EdkLogger.quiet("[cache warning]: header file %s is missing for module: %s[%s]" % (File, self.MetaFile.Path, self.Arch))
|
|
continue
|
|
with open(LongFilePath(File), 'rb') as f:
|
|
Content = f.read()
|
|
m.update(Content)
|
|
FileList.append((File, hashlib.md5(Content).hexdigest()))
|
|
|
|
HashChainFile = path.join(self.BuildDir, self.Name + ".hashchain." + m.hexdigest())
|
|
GlobalData.gModuleHashFile[(self.MetaFile.Path, self.Arch)] = HashChainFile
|
|
try:
|
|
with open(LongFilePath(HashChainFile), 'w') as f:
|
|
json.dump(FileList, f, indent=2)
|
|
except:
|
|
EdkLogger.quiet("[cache warning]: fail to save hashchain file:%s" % HashChainFile)
|
|
return False
|
|
|
|
def GenPreMakefileHashList(self):
|
|
# GenPreMakefileHashList consume below dicts:
|
|
# gPlatformHashFile
|
|
# gPackageHashFile
|
|
# gModuleHashFile
|
|
# GenPreMakefileHashList produce no dict.
|
|
# gModuleHashFile items might be produced in multiprocessing, so
|
|
# need check gModuleHashFile remote dict
|
|
|
|
# skip binary module
|
|
if self.IsBinaryModule:
|
|
return
|
|
|
|
FileList = []
|
|
m = hashlib.md5()
|
|
# Add Platform level hash
|
|
HashFile = GlobalData.gPlatformHashFile
|
|
if path.exists(LongFilePath(HashFile)):
|
|
FileList.append(HashFile)
|
|
m.update(HashFile.encode('utf-8'))
|
|
else:
|
|
EdkLogger.quiet("[cache warning]: No Platform HashFile: %s" % HashFile)
|
|
|
|
# Add Package level hash
|
|
if self.DependentPackageList:
|
|
for Pkg in sorted(self.DependentPackageList, key=lambda x: x.PackageName):
|
|
if not (Pkg.PackageName, Pkg.Arch) in GlobalData.gPackageHashFile:
|
|
EdkLogger.quiet("[cache warning]:No Package %s for module %s[%s]" % (Pkg.PackageName, self.MetaFile.Path, self.Arch))
|
|
continue
|
|
HashFile = GlobalData.gPackageHashFile[(Pkg.PackageName, Pkg.Arch)]
|
|
if path.exists(LongFilePath(HashFile)):
|
|
FileList.append(HashFile)
|
|
m.update(HashFile.encode('utf-8'))
|
|
else:
|
|
EdkLogger.quiet("[cache warning]:No Package HashFile: %s" % HashFile)
|
|
|
|
# Add Module self
|
|
# GenPreMakefileHashList needed in both --binary-destination
|
|
# and --hash. And --hash might save ModuleHashFile in remote dict
|
|
# during multiprocessing.
|
|
if (self.MetaFile.Path, self.Arch) in GlobalData.gModuleHashFile:
|
|
HashFile = GlobalData.gModuleHashFile[(self.MetaFile.Path, self.Arch)]
|
|
else:
|
|
EdkLogger.quiet("[cache error]:No ModuleHashFile for module: %s[%s]" % (self.MetaFile.Path, self.Arch))
|
|
if path.exists(LongFilePath(HashFile)):
|
|
FileList.append(HashFile)
|
|
m.update(HashFile.encode('utf-8'))
|
|
else:
|
|
EdkLogger.quiet("[cache warning]:No Module HashFile: %s" % HashFile)
|
|
|
|
# Add Library hash
|
|
if self.LibraryAutoGenList:
|
|
for Lib in sorted(self.LibraryAutoGenList, key=lambda x: x.MetaFile.Path):
|
|
|
|
if (Lib.MetaFile.Path, Lib.Arch) in GlobalData.gModuleHashFile:
|
|
HashFile = GlobalData.gModuleHashFile[(Lib.MetaFile.Path, Lib.Arch)]
|
|
else:
|
|
EdkLogger.quiet("[cache error]:No ModuleHashFile for lib: %s[%s]" % (Lib.MetaFile.Path, Lib.Arch))
|
|
if path.exists(LongFilePath(HashFile)):
|
|
FileList.append(HashFile)
|
|
m.update(HashFile.encode('utf-8'))
|
|
else:
|
|
EdkLogger.quiet("[cache warning]:No Lib HashFile: %s" % HashFile)
|
|
|
|
# Save PreMakeHashFileList
|
|
FilePath = path.join(self.BuildDir, self.Name + ".PreMakeHashFileList." + m.hexdigest())
|
|
try:
|
|
with open(LongFilePath(FilePath), 'w') as f:
|
|
json.dump(FileList, f, indent=0)
|
|
except:
|
|
EdkLogger.quiet("[cache warning]: fail to save PreMake HashFileList: %s" % FilePath)
|
|
|
|
def GenMakefileHashList(self):
|
|
# GenMakefileHashList only need in --binary-destination which will
|
|
# everything in local dict. So don't need check remote dict.
|
|
|
|
# skip binary module
|
|
if self.IsBinaryModule:
|
|
return
|
|
|
|
FileList = []
|
|
m = hashlib.md5()
|
|
# Add AutoGen hash
|
|
HashFile = GlobalData.gCMakeHashFile[(self.MetaFile.Path, self.Arch)]
|
|
if path.exists(LongFilePath(HashFile)):
|
|
FileList.append(HashFile)
|
|
m.update(HashFile.encode('utf-8'))
|
|
else:
|
|
EdkLogger.quiet("[cache warning]:No AutoGen HashFile: %s" % HashFile)
|
|
|
|
# Add Module self
|
|
if (self.MetaFile.Path, self.Arch) in GlobalData.gModuleHashFile:
|
|
HashFile = GlobalData.gModuleHashFile[(self.MetaFile.Path, self.Arch)]
|
|
else:
|
|
EdkLogger.quiet("[cache error]:No ModuleHashFile for module: %s[%s]" % (self.MetaFile.Path, self.Arch))
|
|
if path.exists(LongFilePath(HashFile)):
|
|
FileList.append(HashFile)
|
|
m.update(HashFile.encode('utf-8'))
|
|
else:
|
|
EdkLogger.quiet("[cache warning]:No Module HashFile: %s" % HashFile)
|
|
|
|
# Add Library hash
|
|
if self.LibraryAutoGenList:
|
|
for Lib in sorted(self.LibraryAutoGenList, key=lambda x: x.MetaFile.Path):
|
|
if (Lib.MetaFile.Path, Lib.Arch) in GlobalData.gModuleHashFile:
|
|
HashFile = GlobalData.gModuleHashFile[(Lib.MetaFile.Path, Lib.Arch)]
|
|
else:
|
|
EdkLogger.quiet("[cache error]:No ModuleHashFile for lib: %s[%s]" % (Lib.MetaFile.Path, Lib.Arch))
|
|
if path.exists(LongFilePath(HashFile)):
|
|
FileList.append(HashFile)
|
|
m.update(HashFile.encode('utf-8'))
|
|
else:
|
|
EdkLogger.quiet("[cache warning]:No Lib HashFile: %s" % HashFile)
|
|
|
|
# Save MakeHashFileList
|
|
FilePath = path.join(self.BuildDir, self.Name + ".MakeHashFileList." + m.hexdigest())
|
|
try:
|
|
with open(LongFilePath(FilePath), 'w') as f:
|
|
json.dump(FileList, f, indent=0)
|
|
except:
|
|
EdkLogger.quiet("[cache warning]: fail to save Make HashFileList: %s" % FilePath)
|
|
|
|
def CheckHashChainFile(self, HashChainFile):
|
|
# Assume the HashChainFile basename format is the 'x.hashchain.16BytesHexStr'
|
|
# The x is module name and the 16BytesHexStr is md5 hexdigest of
|
|
# all hashchain files content
|
|
HashStr = HashChainFile.split('.')[-1]
|
|
if len(HashStr) != 32:
|
|
EdkLogger.quiet("[cache error]: wrong format HashChainFile:%s" % (File))
|
|
return False
|
|
|
|
try:
|
|
with open(LongFilePath(HashChainFile), 'r') as f:
|
|
HashChainList = json.load(f)
|
|
except:
|
|
EdkLogger.quiet("[cache error]: fail to load HashChainFile: %s" % HashChainFile)
|
|
return False
|
|
|
|
# Print the different file info
|
|
# print(HashChainFile)
|
|
for idx, (SrcFile, SrcHash) in enumerate (HashChainList):
|
|
if SrcFile in GlobalData.gFileHashDict:
|
|
DestHash = GlobalData.gFileHashDict[SrcFile]
|
|
else:
|
|
try:
|
|
with open(LongFilePath(SrcFile), 'rb') as f:
|
|
Content = f.read()
|
|
DestHash = hashlib.md5(Content).hexdigest()
|
|
GlobalData.gFileHashDict[SrcFile] = DestHash
|
|
except IOError as X:
|
|
# cache miss if SrcFile is removed in new version code
|
|
GlobalData.gFileHashDict[SrcFile] = 0
|
|
EdkLogger.quiet("[cache insight]: first cache miss file in %s is %s" % (HashChainFile, SrcFile))
|
|
return False
|
|
if SrcHash != DestHash:
|
|
EdkLogger.quiet("[cache insight]: first cache miss file in %s is %s" % (HashChainFile, SrcFile))
|
|
return False
|
|
|
|
return True
|
|
|
|
## Decide whether we can skip the left autogen and make process
|
|
def CanSkipbyMakeCache(self):
|
|
# For --binary-source only
|
|
# CanSkipbyMakeCache consume below dicts:
|
|
# gModuleMakeCacheStatus
|
|
# gHashChainStatus
|
|
# GenPreMakefileHashList produce gModuleMakeCacheStatus, gModuleHashFile dict.
|
|
# all these dicts might be produced in multiprocessing, so
|
|
# need check these remote dict
|
|
|
|
if not GlobalData.gBinCacheSource:
|
|
return False
|
|
|
|
if (self.MetaFile.Path, self.Arch) in GlobalData.gModuleMakeCacheStatus:
|
|
return GlobalData.gModuleMakeCacheStatus[(self.MetaFile.Path, self.Arch)]
|
|
|
|
# If Module is binary, which has special build rule, do not skip by cache.
|
|
if self.IsBinaryModule:
|
|
print("[cache miss]: MakeCache: Skip BinaryModule:", self.MetaFile.Path, self.Arch)
|
|
GlobalData.gModuleMakeCacheStatus[(self.MetaFile.Path, self.Arch)] = False
|
|
return False
|
|
|
|
# see .inc as binary file, do not skip by hash
|
|
for f_ext in self.SourceFileList:
|
|
if '.inc' in str(f_ext):
|
|
print("[cache miss]: MakeCache: Skip '.inc' File:", self.MetaFile.Path, self.Arch)
|
|
GlobalData.gModuleMakeCacheStatus[(self.MetaFile.Path, self.Arch)] = False
|
|
return False
|
|
|
|
ModuleCacheDir = path.join(GlobalData.gBinCacheSource, self.PlatformInfo.OutputDir, self.BuildTarget + "_" + self.ToolChain, self.Arch, self.SourceDir, self.MetaFile.BaseName)
|
|
FfsDir = path.join(GlobalData.gBinCacheSource, self.PlatformInfo.OutputDir, self.BuildTarget + "_" + self.ToolChain, TAB_FV_DIRECTORY, "Ffs", self.Guid + self.Name)
|
|
|
|
ModuleHashPairList = [] # tuple list: [tuple(PreMakefileHash, MakeHash)]
|
|
ModuleHashPair = path.join(ModuleCacheDir, self.Name + ".ModuleHashPair")
|
|
try:
|
|
with open(LongFilePath(ModuleHashPair), 'r') as f:
|
|
ModuleHashPairList = json.load(f)
|
|
except:
|
|
# ModuleHashPair might not exist for new added module
|
|
GlobalData.gModuleMakeCacheStatus[(self.MetaFile.Path, self.Arch)] = False
|
|
EdkLogger.quiet("[cache warning]: fail to load ModuleHashPair file: %s" % ModuleHashPair)
|
|
print("[cache miss]: MakeCache:", self.MetaFile.Path, self.Arch)
|
|
return False
|
|
|
|
# Check the PreMakeHash in ModuleHashPairList one by one
|
|
for idx, (PreMakefileHash, MakeHash) in enumerate (ModuleHashPairList):
|
|
SourceHashDir = path.join(ModuleCacheDir, MakeHash)
|
|
SourceFfsHashDir = path.join(FfsDir, MakeHash)
|
|
PreMakeHashFileList_FilePah = path.join(ModuleCacheDir, self.Name + ".PreMakeHashFileList." + PreMakefileHash)
|
|
MakeHashFileList_FilePah = path.join(ModuleCacheDir, self.Name + ".MakeHashFileList." + MakeHash)
|
|
|
|
try:
|
|
with open(LongFilePath(MakeHashFileList_FilePah), 'r') as f:
|
|
MakeHashFileList = json.load(f)
|
|
except:
|
|
EdkLogger.quiet("[cache error]: fail to load MakeHashFileList file: %s" % MakeHashFileList_FilePah)
|
|
continue
|
|
|
|
HashMiss = False
|
|
for HashChainFile in MakeHashFileList:
|
|
HashChainStatus = None
|
|
if HashChainFile in GlobalData.gHashChainStatus:
|
|
HashChainStatus = GlobalData.gHashChainStatus[HashChainFile]
|
|
if HashChainStatus == False:
|
|
HashMiss = True
|
|
break
|
|
elif HashChainStatus == True:
|
|
continue
|
|
# Convert to path start with cache source dir
|
|
RelativePath = os.path.relpath(HashChainFile, self.WorkspaceDir)
|
|
NewFilePath = os.path.join(GlobalData.gBinCacheSource, RelativePath)
|
|
if self.CheckHashChainFile(NewFilePath):
|
|
GlobalData.gHashChainStatus[HashChainFile] = True
|
|
# Save the module self HashFile for GenPreMakefileHashList later usage
|
|
if self.Name + ".hashchain." in HashChainFile:
|
|
GlobalData.gModuleHashFile[(self.MetaFile.Path, self.Arch)] = HashChainFile
|
|
else:
|
|
GlobalData.gHashChainStatus[HashChainFile] = False
|
|
HashMiss = True
|
|
break
|
|
|
|
if HashMiss:
|
|
continue
|
|
|
|
# PreMakefile cache hit, restore the module build result
|
|
for root, dir, files in os.walk(SourceHashDir):
|
|
for f in files:
|
|
File = path.join(root, f)
|
|
self.CacheCopyFile(self.BuildDir, SourceHashDir, File)
|
|
if os.path.exists(SourceFfsHashDir):
|
|
for root, dir, files in os.walk(SourceFfsHashDir):
|
|
for f in files:
|
|
File = path.join(root, f)
|
|
self.CacheCopyFile(self.FfsOutputDir, SourceFfsHashDir, File)
|
|
|
|
if self.Name == "PcdPeim" or self.Name == "PcdDxe":
|
|
CreatePcdDatabaseCode(self, TemplateString(), TemplateString())
|
|
|
|
print("[cache hit]: MakeCache:", self.MetaFile.Path, self.Arch)
|
|
GlobalData.gModuleMakeCacheStatus[(self.MetaFile.Path, self.Arch)] = True
|
|
return True
|
|
|
|
print("[cache miss]: MakeCache:", self.MetaFile.Path, self.Arch)
|
|
GlobalData.gModuleMakeCacheStatus[(self.MetaFile.Path, self.Arch)] = False
|
|
return False
|
|
|
|
## Decide whether we can skip the left autogen and make process
|
|
def CanSkipbyPreMakeCache(self):
|
|
# CanSkipbyPreMakeCache consume below dicts:
|
|
# gModulePreMakeCacheStatus
|
|
# gHashChainStatus
|
|
# gModuleHashFile
|
|
# GenPreMakefileHashList produce gModulePreMakeCacheStatus dict.
|
|
# all these dicts might be produced in multiprocessing, so
|
|
# need check these remote dicts
|
|
|
|
if not GlobalData.gUseHashCache or GlobalData.gBinCacheDest:
|
|
return False
|
|
|
|
if (self.MetaFile.Path, self.Arch) in GlobalData.gModulePreMakeCacheStatus:
|
|
return GlobalData.gModulePreMakeCacheStatus[(self.MetaFile.Path, self.Arch)]
|
|
|
|
# If Module is binary, which has special build rule, do not skip by cache.
|
|
if self.IsBinaryModule:
|
|
print("[cache miss]: PreMakeCache: Skip BinaryModule:", self.MetaFile.Path, self.Arch)
|
|
GlobalData.gModulePreMakeCacheStatus[(self.MetaFile.Path, self.Arch)] = False
|
|
return False
|
|
|
|
# see .inc as binary file, do not skip by hash
|
|
for f_ext in self.SourceFileList:
|
|
if '.inc' in str(f_ext):
|
|
print("[cache miss]: PreMakeCache: Skip '.inc' File:", self.MetaFile.Path, self.Arch)
|
|
GlobalData.gModulePreMakeCacheStatus[(self.MetaFile.Path, self.Arch)] = False
|
|
return False
|
|
|
|
# For --hash only in the incremental build
|
|
if not GlobalData.gBinCacheSource:
|
|
Files = [path.join(self.BuildDir, f) for f in os.listdir(self.BuildDir) if path.isfile(path.join(self.BuildDir, f))]
|
|
PreMakeHashFileList_FilePah = None
|
|
MakeTimeStamp = 0
|
|
# Find latest PreMakeHashFileList file in self.BuildDir folder
|
|
for File in Files:
|
|
if ".PreMakeHashFileList." in File:
|
|
FileTimeStamp = os.stat(path.join(self.BuildDir, File))[8]
|
|
if FileTimeStamp > MakeTimeStamp:
|
|
MakeTimeStamp = FileTimeStamp
|
|
PreMakeHashFileList_FilePah = File
|
|
if not PreMakeHashFileList_FilePah:
|
|
GlobalData.gModulePreMakeCacheStatus[(self.MetaFile.Path, self.Arch)] = False
|
|
return False
|
|
|
|
try:
|
|
with open(LongFilePath(PreMakeHashFileList_FilePah), 'r') as f:
|
|
PreMakeHashFileList = json.load(f)
|
|
except:
|
|
EdkLogger.quiet("[cache error]: fail to load PreMakeHashFileList file: %s" % PreMakeHashFileList_FilePah)
|
|
print("[cache miss]: PreMakeCache:", self.MetaFile.Path, self.Arch)
|
|
GlobalData.gModulePreMakeCacheStatus[(self.MetaFile.Path, self.Arch)] = False
|
|
return False
|
|
|
|
HashMiss = False
|
|
for HashChainFile in PreMakeHashFileList:
|
|
HashChainStatus = None
|
|
if HashChainFile in GlobalData.gHashChainStatus:
|
|
HashChainStatus = GlobalData.gHashChainStatus[HashChainFile]
|
|
if HashChainStatus == False:
|
|
HashMiss = True
|
|
break
|
|
elif HashChainStatus == True:
|
|
continue
|
|
if self.CheckHashChainFile(HashChainFile):
|
|
GlobalData.gHashChainStatus[HashChainFile] = True
|
|
# Save the module self HashFile for GenPreMakefileHashList later usage
|
|
if self.Name + ".hashchain." in HashChainFile:
|
|
GlobalData.gModuleHashFile[(self.MetaFile.Path, self.Arch)] = HashChainFile
|
|
else:
|
|
GlobalData.gHashChainStatus[HashChainFile] = False
|
|
HashMiss = True
|
|
break
|
|
|
|
if HashMiss:
|
|
print("[cache miss]: PreMakeCache:", self.MetaFile.Path, self.Arch)
|
|
GlobalData.gModulePreMakeCacheStatus[(self.MetaFile.Path, self.Arch)] = False
|
|
return False
|
|
else:
|
|
print("[cache hit]: PreMakeCache:", self.MetaFile.Path, self.Arch)
|
|
GlobalData.gModulePreMakeCacheStatus[(self.MetaFile.Path, self.Arch)] = True
|
|
return True
|
|
|
|
ModuleCacheDir = path.join(GlobalData.gBinCacheSource, self.PlatformInfo.OutputDir, self.BuildTarget + "_" + self.ToolChain, self.Arch, self.SourceDir, self.MetaFile.BaseName)
|
|
FfsDir = path.join(GlobalData.gBinCacheSource, self.PlatformInfo.OutputDir, self.BuildTarget + "_" + self.ToolChain, TAB_FV_DIRECTORY, "Ffs", self.Guid + self.Name)
|
|
|
|
ModuleHashPairList = [] # tuple list: [tuple(PreMakefileHash, MakeHash)]
|
|
ModuleHashPair = path.join(ModuleCacheDir, self.Name + ".ModuleHashPair")
|
|
try:
|
|
with open(LongFilePath(ModuleHashPair), 'r') as f:
|
|
ModuleHashPairList = json.load(f)
|
|
except:
|
|
# ModuleHashPair might not exist for new added module
|
|
GlobalData.gModulePreMakeCacheStatus[(self.MetaFile.Path, self.Arch)] = False
|
|
EdkLogger.quiet("[cache warning]: fail to load ModuleHashPair file: %s" % ModuleHashPair)
|
|
print("[cache miss]: PreMakeCache:", self.MetaFile.Path, self.Arch)
|
|
return False
|
|
|
|
# Check the PreMakeHash in ModuleHashPairList one by one
|
|
for idx, (PreMakefileHash, MakeHash) in enumerate (ModuleHashPairList):
|
|
SourceHashDir = path.join(ModuleCacheDir, MakeHash)
|
|
SourceFfsHashDir = path.join(FfsDir, MakeHash)
|
|
PreMakeHashFileList_FilePah = path.join(ModuleCacheDir, self.Name + ".PreMakeHashFileList." + PreMakefileHash)
|
|
MakeHashFileList_FilePah = path.join(ModuleCacheDir, self.Name + ".MakeHashFileList." + MakeHash)
|
|
|
|
try:
|
|
with open(LongFilePath(PreMakeHashFileList_FilePah), 'r') as f:
|
|
PreMakeHashFileList = json.load(f)
|
|
except:
|
|
EdkLogger.quiet("[cache error]: fail to load PreMakeHashFileList file: %s" % PreMakeHashFileList_FilePah)
|
|
continue
|
|
|
|
HashMiss = False
|
|
for HashChainFile in PreMakeHashFileList:
|
|
HashChainStatus = None
|
|
if HashChainFile in GlobalData.gHashChainStatus:
|
|
HashChainStatus = GlobalData.gHashChainStatus[HashChainFile]
|
|
if HashChainStatus == False:
|
|
HashMiss = True
|
|
break
|
|
elif HashChainStatus == True:
|
|
continue
|
|
# Convert to path start with cache source dir
|
|
RelativePath = os.path.relpath(HashChainFile, self.WorkspaceDir)
|
|
NewFilePath = os.path.join(GlobalData.gBinCacheSource, RelativePath)
|
|
if self.CheckHashChainFile(NewFilePath):
|
|
GlobalData.gHashChainStatus[HashChainFile] = True
|
|
else:
|
|
GlobalData.gHashChainStatus[HashChainFile] = False
|
|
HashMiss = True
|
|
break
|
|
|
|
if HashMiss:
|
|
continue
|
|
|
|
# PreMakefile cache hit, restore the module build result
|
|
for root, dir, files in os.walk(SourceHashDir):
|
|
for f in files:
|
|
File = path.join(root, f)
|
|
self.CacheCopyFile(self.BuildDir, SourceHashDir, File)
|
|
if os.path.exists(SourceFfsHashDir):
|
|
for root, dir, files in os.walk(SourceFfsHashDir):
|
|
for f in files:
|
|
File = path.join(root, f)
|
|
self.CacheCopyFile(self.FfsOutputDir, SourceFfsHashDir, File)
|
|
|
|
if self.Name == "PcdPeim" or self.Name == "PcdDxe":
|
|
CreatePcdDatabaseCode(self, TemplateString(), TemplateString())
|
|
|
|
print("[cache hit]: PreMakeCache:", self.MetaFile.Path, self.Arch)
|
|
GlobalData.gModulePreMakeCacheStatus[(self.MetaFile.Path, self.Arch)] = True
|
|
return True
|
|
|
|
print("[cache miss]: PreMakeCache:", self.MetaFile.Path, self.Arch)
|
|
GlobalData.gModulePreMakeCacheStatus[(self.MetaFile.Path, self.Arch)] = False
|
|
return False
|
|
|
|
## Decide whether we can skip the Module build
|
|
def CanSkipbyCache(self, gHitSet):
|
|
# Hashing feature is off
|
|
if not GlobalData.gBinCacheSource:
|
|
return False
|
|
|
|
if self in gHitSet:
|
|
return True
|
|
|
|
return False
|
|
|
|
## Decide whether we can skip the ModuleAutoGen process
|
|
# If any source file is newer than the module than we cannot skip
|
|
#
|
|
def CanSkip(self):
|
|
# Don't skip if cache feature enabled
|
|
if GlobalData.gUseHashCache or GlobalData.gBinCacheDest or GlobalData.gBinCacheSource:
|
|
return False
|
|
if self.MakeFileDir in GlobalData.gSikpAutoGenCache:
|
|
return True
|
|
if not os.path.exists(self.TimeStampPath):
|
|
return False
|
|
#last creation time of the module
|
|
DstTimeStamp = os.stat(self.TimeStampPath)[8]
|
|
|
|
SrcTimeStamp = self.Workspace._SrcTimeStamp
|
|
if SrcTimeStamp > DstTimeStamp:
|
|
return False
|
|
|
|
with open(self.TimeStampPath,'r') as f:
|
|
for source in f:
|
|
source = source.rstrip('\n')
|
|
if not os.path.exists(source):
|
|
return False
|
|
if source not in ModuleAutoGen.TimeDict :
|
|
ModuleAutoGen.TimeDict[source] = os.stat(source)[8]
|
|
if ModuleAutoGen.TimeDict[source] > DstTimeStamp:
|
|
return False
|
|
GlobalData.gSikpAutoGenCache.add(self.MakeFileDir)
|
|
return True
|
|
|
|
@cached_property
|
|
def TimeStampPath(self):
|
|
return os.path.join(self.MakeFileDir, 'AutoGenTimeStamp')
|