audk/BaseTools/Source/Python/Workspace/DecBuildData.py

468 lines
22 KiB
Python

## @file
# This file is used to create a database used by build tool
#
# Copyright (c) 2008 - 2018, Intel Corporation. All rights reserved.<BR>
# (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
# This program and the accompanying materials
# are licensed and made available under the terms and conditions of the BSD License
# which accompanies this distribution. The full text of the license may be found at
# http://opensource.org/licenses/bsd-license.php
#
# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#
from Common.StringUtils import *
from Common.DataType import *
from Common.Misc import *
from types import *
from collections import OrderedDict
from Workspace.BuildClassObject import PackageBuildClassObject, StructurePcd, PcdClassObject
## Platform build information from DEC file
#
# This class is used to retrieve information stored in database and convert them
# into PackageBuildClassObject form for easier use for AutoGen.
#
class DecBuildData(PackageBuildClassObject):
# dict used to convert PCD type in database to string used by build tool
_PCD_TYPE_STRING_ = {
MODEL_PCD_FIXED_AT_BUILD : TAB_PCDS_FIXED_AT_BUILD,
MODEL_PCD_PATCHABLE_IN_MODULE : TAB_PCDS_PATCHABLE_IN_MODULE,
MODEL_PCD_FEATURE_FLAG : TAB_PCDS_FEATURE_FLAG,
MODEL_PCD_DYNAMIC : TAB_PCDS_DYNAMIC,
MODEL_PCD_DYNAMIC_DEFAULT : TAB_PCDS_DYNAMIC,
MODEL_PCD_DYNAMIC_HII : TAB_PCDS_DYNAMIC_HII,
MODEL_PCD_DYNAMIC_VPD : TAB_PCDS_DYNAMIC_VPD,
MODEL_PCD_DYNAMIC_EX : TAB_PCDS_DYNAMIC_EX,
MODEL_PCD_DYNAMIC_EX_DEFAULT : TAB_PCDS_DYNAMIC_EX,
MODEL_PCD_DYNAMIC_EX_HII : TAB_PCDS_DYNAMIC_EX_HII,
MODEL_PCD_DYNAMIC_EX_VPD : TAB_PCDS_DYNAMIC_EX_VPD,
}
# dict used to convert part of [Defines] to members of DecBuildData directly
_PROPERTY_ = {
#
# Required Fields
#
TAB_DEC_DEFINES_PACKAGE_NAME : "_PackageName",
TAB_DEC_DEFINES_PACKAGE_GUID : "_Guid",
TAB_DEC_DEFINES_PACKAGE_VERSION : "_Version",
TAB_DEC_DEFINES_PKG_UNI_FILE : "_PkgUniFile",
}
## Constructor of DecBuildData
#
# Initialize object of DecBuildData
#
# @param FilePath The path of package description file
# @param RawData The raw data of DEC file
# @param BuildDataBase Database used to retrieve module information
# @param Arch The target architecture
# @param Platform (not used for DecBuildData)
# @param Macros Macros used for replacement in DSC file
#
def __init__(self, File, RawData, BuildDataBase, Arch=TAB_ARCH_COMMON, Target=None, Toolchain=None):
self.MetaFile = File
self._PackageDir = File.Dir
self._RawData = RawData
self._Bdb = BuildDataBase
self._Arch = Arch
self._Target = Target
self._Toolchain = Toolchain
self._Clear()
## XXX[key] = value
def __setitem__(self, key, value):
self.__dict__[self._PROPERTY_[key]] = value
## value = XXX[key]
def __getitem__(self, key):
return self.__dict__[self._PROPERTY_[key]]
## "in" test support
def __contains__(self, key):
return key in self._PROPERTY_
## Set all internal used members of DecBuildData to None
def _Clear(self):
self._Header = None
self._PackageName = None
self._Guid = None
self._Version = None
self._PkgUniFile = None
self._Protocols = None
self._Ppis = None
self._Guids = None
self._Includes = None
self._CommonIncludes = None
self._LibraryClasses = None
self._Pcds = None
self.__Macros = None
self._PrivateProtocols = None
self._PrivatePpis = None
self._PrivateGuids = None
self._PrivateIncludes = None
## Get current effective macros
def _GetMacros(self):
if self.__Macros is None:
self.__Macros = {}
self.__Macros.update(GlobalData.gGlobalDefines)
return self.__Macros
## Get architecture
def _GetArch(self):
return self._Arch
## Retrieve all information in [Defines] section
#
# (Retriving all [Defines] information in one-shot is just to save time.)
#
def _GetHeaderInfo(self):
RecordList = self._RawData[MODEL_META_DATA_HEADER, self._Arch]
for Record in RecordList:
Name = Record[1]
if Name in self:
self[Name] = Record[2]
self._Header = 'DUMMY'
## Retrieve package name
def _GetPackageName(self):
if self._PackageName is None:
if self._Header is None:
self._GetHeaderInfo()
if self._PackageName is None:
EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "No PACKAGE_NAME", File=self.MetaFile)
return self._PackageName
## Retrieve file guid
def _GetFileGuid(self):
if self._Guid is None:
if self._Header is None:
self._GetHeaderInfo()
if self._Guid is None:
EdkLogger.error("build", ATTRIBUTE_NOT_AVAILABLE, "No PACKAGE_GUID", File=self.MetaFile)
return self._Guid
## Retrieve package version
def _GetVersion(self):
if self._Version is None:
if self._Header is None:
self._GetHeaderInfo()
if self._Version is None:
self._Version = ''
return self._Version
## Retrieve protocol definitions (name/value pairs)
def _GetProtocol(self):
if self._Protocols is None:
#
# tdict is a special kind of dict, used for selecting correct
# protocol defition for given ARCH
#
ProtocolDict = tdict(True)
PrivateProtocolDict = tdict(True)
NameList = []
PrivateNameList = []
PublicNameList = []
# find out all protocol definitions for specific and 'common' arch
RecordList = self._RawData[MODEL_EFI_PROTOCOL, self._Arch]
for Name, Guid, Dummy, Arch, PrivateFlag, ID, LineNo in RecordList:
if PrivateFlag == 'PRIVATE':
if Name not in PrivateNameList:
PrivateNameList.append(Name)
PrivateProtocolDict[Arch, Name] = Guid
if Name in PublicNameList:
EdkLogger.error('build', OPTION_CONFLICT, "Can't determine %s's attribute, it is both defined as Private and non-Private attribute in DEC file." % Name, File=self.MetaFile, Line=LineNo)
else:
if Name not in PublicNameList:
PublicNameList.append(Name)
if Name in PrivateNameList:
EdkLogger.error('build', OPTION_CONFLICT, "Can't determine %s's attribute, it is both defined as Private and non-Private attribute in DEC file." % Name, File=self.MetaFile, Line=LineNo)
if Name not in NameList:
NameList.append(Name)
ProtocolDict[Arch, Name] = Guid
# use OrderedDict to keep the order
self._Protocols = OrderedDict()
self._PrivateProtocols = OrderedDict()
for Name in NameList:
#
# limit the ARCH to self._Arch, if no self._Arch found, tdict
# will automatically turn to 'common' ARCH for trying
#
self._Protocols[Name] = ProtocolDict[self._Arch, Name]
for Name in PrivateNameList:
self._PrivateProtocols[Name] = PrivateProtocolDict[self._Arch, Name]
return self._Protocols
## Retrieve PPI definitions (name/value pairs)
def _GetPpi(self):
if self._Ppis is None:
#
# tdict is a special kind of dict, used for selecting correct
# PPI defition for given ARCH
#
PpiDict = tdict(True)
PrivatePpiDict = tdict(True)
NameList = []
PrivateNameList = []
PublicNameList = []
# find out all PPI definitions for specific arch and 'common' arch
RecordList = self._RawData[MODEL_EFI_PPI, self._Arch]
for Name, Guid, Dummy, Arch, PrivateFlag, ID, LineNo in RecordList:
if PrivateFlag == 'PRIVATE':
if Name not in PrivateNameList:
PrivateNameList.append(Name)
PrivatePpiDict[Arch, Name] = Guid
if Name in PublicNameList:
EdkLogger.error('build', OPTION_CONFLICT, "Can't determine %s's attribute, it is both defined as Private and non-Private attribute in DEC file." % Name, File=self.MetaFile, Line=LineNo)
else:
if Name not in PublicNameList:
PublicNameList.append(Name)
if Name in PrivateNameList:
EdkLogger.error('build', OPTION_CONFLICT, "Can't determine %s's attribute, it is both defined as Private and non-Private attribute in DEC file." % Name, File=self.MetaFile, Line=LineNo)
if Name not in NameList:
NameList.append(Name)
PpiDict[Arch, Name] = Guid
# use OrderedDict to keep the order
self._Ppis = OrderedDict()
self._PrivatePpis = OrderedDict()
for Name in NameList:
#
# limit the ARCH to self._Arch, if no self._Arch found, tdict
# will automatically turn to 'common' ARCH for trying
#
self._Ppis[Name] = PpiDict[self._Arch, Name]
for Name in PrivateNameList:
self._PrivatePpis[Name] = PrivatePpiDict[self._Arch, Name]
return self._Ppis
## Retrieve GUID definitions (name/value pairs)
def _GetGuid(self):
if self._Guids is None:
#
# tdict is a special kind of dict, used for selecting correct
# GUID defition for given ARCH
#
GuidDict = tdict(True)
PrivateGuidDict = tdict(True)
NameList = []
PrivateNameList = []
PublicNameList = []
# find out all protocol definitions for specific and 'common' arch
RecordList = self._RawData[MODEL_EFI_GUID, self._Arch]
for Name, Guid, Dummy, Arch, PrivateFlag, ID, LineNo in RecordList:
if PrivateFlag == 'PRIVATE':
if Name not in PrivateNameList:
PrivateNameList.append(Name)
PrivateGuidDict[Arch, Name] = Guid
if Name in PublicNameList:
EdkLogger.error('build', OPTION_CONFLICT, "Can't determine %s's attribute, it is both defined as Private and non-Private attribute in DEC file." % Name, File=self.MetaFile, Line=LineNo)
else:
if Name not in PublicNameList:
PublicNameList.append(Name)
if Name in PrivateNameList:
EdkLogger.error('build', OPTION_CONFLICT, "Can't determine %s's attribute, it is both defined as Private and non-Private attribute in DEC file." % Name, File=self.MetaFile, Line=LineNo)
if Name not in NameList:
NameList.append(Name)
GuidDict[Arch, Name] = Guid
# use OrderedDict to keep the order
self._Guids = OrderedDict()
self._PrivateGuids = OrderedDict()
for Name in NameList:
#
# limit the ARCH to self._Arch, if no self._Arch found, tdict
# will automatically turn to 'common' ARCH for trying
#
self._Guids[Name] = GuidDict[self._Arch, Name]
for Name in PrivateNameList:
self._PrivateGuids[Name] = PrivateGuidDict[self._Arch, Name]
return self._Guids
## Retrieve public include paths declared in this package
def _GetInclude(self):
if self._Includes is None or self._CommonIncludes is None:
self._CommonIncludes = []
self._Includes = []
self._PrivateIncludes = []
PublicInclues = []
RecordList = self._RawData[MODEL_EFI_INCLUDE, self._Arch]
Macros = self._Macros
Macros["EDK_SOURCE"] = GlobalData.gEcpSource
for Record in RecordList:
File = PathClass(NormPath(Record[0], Macros), self._PackageDir, Arch=self._Arch)
LineNo = Record[-1]
# validate the path
ErrorCode, ErrorInfo = File.Validate()
if ErrorCode != 0:
EdkLogger.error('build', ErrorCode, ExtraData=ErrorInfo, File=self.MetaFile, Line=LineNo)
# avoid duplicate include path
if File not in self._Includes:
self._Includes.append(File)
if Record[4] == 'PRIVATE':
if File not in self._PrivateIncludes:
self._PrivateIncludes.append(File)
if File in PublicInclues:
EdkLogger.error('build', OPTION_CONFLICT, "Can't determine %s's attribute, it is both defined as Private and non-Private attribute in DEC file." % File, File=self.MetaFile, Line=LineNo)
else:
if File not in PublicInclues:
PublicInclues.append(File)
if File in self._PrivateIncludes:
EdkLogger.error('build', OPTION_CONFLICT, "Can't determine %s's attribute, it is both defined as Private and non-Private attribute in DEC file." % File, File=self.MetaFile, Line=LineNo)
if Record[3] == TAB_COMMON:
self._CommonIncludes.append(File)
return self._Includes
## Retrieve library class declarations (not used in build at present)
def _GetLibraryClass(self):
if self._LibraryClasses is None:
#
# tdict is a special kind of dict, used for selecting correct
# library class declaration for given ARCH
#
LibraryClassDict = tdict(True)
LibraryClassSet = set()
RecordList = self._RawData[MODEL_EFI_LIBRARY_CLASS, self._Arch]
Macros = self._Macros
for LibraryClass, File, Dummy, Arch, PrivateFlag, ID, LineNo in RecordList:
File = PathClass(NormPath(File, Macros), self._PackageDir, Arch=self._Arch)
# check the file validation
ErrorCode, ErrorInfo = File.Validate()
if ErrorCode != 0:
EdkLogger.error('build', ErrorCode, ExtraData=ErrorInfo, File=self.MetaFile, Line=LineNo)
LibraryClassSet.add(LibraryClass)
LibraryClassDict[Arch, LibraryClass] = File
self._LibraryClasses = OrderedDict()
for LibraryClass in LibraryClassSet:
self._LibraryClasses[LibraryClass] = LibraryClassDict[self._Arch, LibraryClass]
return self._LibraryClasses
## Retrieve PCD declarations
def _GetPcds(self):
if self._Pcds is None:
self._Pcds = OrderedDict()
self._Pcds.update(self._GetPcd(MODEL_PCD_FIXED_AT_BUILD))
self._Pcds.update(self._GetPcd(MODEL_PCD_PATCHABLE_IN_MODULE))
self._Pcds.update(self._GetPcd(MODEL_PCD_FEATURE_FLAG))
self._Pcds.update(self._GetPcd(MODEL_PCD_DYNAMIC))
self._Pcds.update(self._GetPcd(MODEL_PCD_DYNAMIC_EX))
return self._Pcds
def ProcessStructurePcd(self, StructurePcdRawDataSet):
s_pcd_set = OrderedDict()
for s_pcd, LineNo in StructurePcdRawDataSet:
if s_pcd.TokenSpaceGuidCName not in s_pcd_set:
s_pcd_set[s_pcd.TokenSpaceGuidCName] = []
s_pcd_set[s_pcd.TokenSpaceGuidCName].append((s_pcd, LineNo))
str_pcd_set = []
for pcdname in s_pcd_set:
dep_pkgs = []
struct_pcd = StructurePcd()
for item, LineNo in s_pcd_set[pcdname]:
if "<HeaderFiles>" in item.TokenCName:
struct_pcd.StructuredPcdIncludeFile.append(item.DefaultValue)
elif "<Packages>" in item.TokenCName:
dep_pkgs.append(item.DefaultValue)
elif item.DatumType == item.TokenCName:
struct_pcd.copy(item)
struct_pcd.TokenValue = struct_pcd.TokenValue.strip("{").strip()
struct_pcd.TokenSpaceGuidCName, struct_pcd.TokenCName = pcdname.split(".")
struct_pcd.PcdDefineLineNo = LineNo
struct_pcd.PkgPath = self.MetaFile.File
struct_pcd.SetDecDefaultValue(item.DefaultValue)
else:
struct_pcd.AddDefaultValue(item.TokenCName, item.DefaultValue, self.MetaFile.File, LineNo)
struct_pcd.PackageDecs = dep_pkgs
str_pcd_set.append(struct_pcd)
return str_pcd_set
## Retrieve PCD declarations for given type
def _GetPcd(self, Type):
Pcds = OrderedDict()
#
# tdict is a special kind of dict, used for selecting correct
# PCD declaration for given ARCH
#
PcdDict = tdict(True, 3)
# for summarizing PCD
PcdSet = []
# find out all PCDs of the 'type'
StrPcdSet = []
RecordList = self._RawData[Type, self._Arch]
for TokenSpaceGuid, PcdCName, Setting, Arch, PrivateFlag, Dummy1, Dummy2 in RecordList:
PcdDict[Arch, PcdCName, TokenSpaceGuid] = (Setting, Dummy2)
if not (PcdCName, TokenSpaceGuid) in PcdSet:
PcdSet.append((PcdCName, TokenSpaceGuid))
for PcdCName, TokenSpaceGuid in PcdSet:
#
# limit the ARCH to self._Arch, if no self._Arch found, tdict
# will automatically turn to 'common' ARCH and try again
#
Setting, LineNo = PcdDict[self._Arch, PcdCName, TokenSpaceGuid]
if Setting is None:
continue
DefaultValue, DatumType, TokenNumber = AnalyzePcdData(Setting)
validateranges, validlists, expressions = self._RawData.GetValidExpression(TokenSpaceGuid, PcdCName)
PcdObj = PcdClassObject(
PcdCName,
TokenSpaceGuid,
self._PCD_TYPE_STRING_[Type],
DatumType,
DefaultValue,
TokenNumber,
'',
{},
False,
None,
list(validateranges),
list(validlists),
list(expressions)
)
PcdObj.DefinitionPosition = (self.MetaFile.File, LineNo)
if "." in TokenSpaceGuid:
StrPcdSet.append((PcdObj, LineNo))
else:
Pcds[PcdCName, TokenSpaceGuid, self._PCD_TYPE_STRING_[Type]] = PcdObj
StructurePcds = self.ProcessStructurePcd(StrPcdSet)
for pcd in StructurePcds:
Pcds[pcd.TokenCName, pcd.TokenSpaceGuidCName, self._PCD_TYPE_STRING_[Type]] = pcd
StructPattern = re.compile(r'[_a-zA-Z][0-9A-Za-z_]*$')
for pcd in Pcds.values():
if pcd.DatumType not in [TAB_UINT8, TAB_UINT16, TAB_UINT32, TAB_UINT64, TAB_VOID, "BOOLEAN"]:
if StructPattern.match(pcd.DatumType) is None:
EdkLogger.error('build', FORMAT_INVALID, "DatumType only support BOOLEAN, UINT8, UINT16, UINT32, UINT64, VOID* or a valid struct name.", pcd.DefinitionPosition[0], pcd.DefinitionPosition[1])
for struct_pcd in Pcds.values():
if isinstance(struct_pcd, StructurePcd) and not struct_pcd.StructuredPcdIncludeFile:
EdkLogger.error("build", PCD_STRUCTURE_PCD_ERROR, "The structure Pcd %s.%s header file is not found in %s line %s \n" % (struct_pcd.TokenSpaceGuidCName, struct_pcd.TokenCName, struct_pcd.DefinitionPosition[0], struct_pcd.DefinitionPosition[1] ))
return Pcds
@property
def CommonIncludes(self):
if self._CommonIncludes is None:
self.Includes
return self._CommonIncludes
_Macros = property(_GetMacros)
Arch = property(_GetArch)
PackageName = property(_GetPackageName)
Guid = property(_GetFileGuid)
Version = property(_GetVersion)
Protocols = property(_GetProtocol)
Ppis = property(_GetPpi)
Guids = property(_GetGuid)
Includes = property(_GetInclude)
LibraryClasses = property(_GetLibraryClass)
Pcds = property(_GetPcds)