## @file # This file is used to create a database used by build tool # # Copyright (c) 2008 - 2018, Intel Corporation. All rights reserved.
# (C) Copyright 2016 Hewlett Packard Enterprise Development LP
# 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._MacroDict = None self._PrivateProtocols = None self._PrivatePpis = None self._PrivateGuids = None self._PrivateIncludes = None ## Get current effective macros @property def _Macros(self): if self._MacroDict is None: self._MacroDict = dict(GlobalData.gGlobalDefines) return self._MacroDict ## Get architecture @property def Arch(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 @property def PackageName(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 @property def PackageName(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 @property def Version(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) @property def Protocols(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) @property def Ppis(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) @property def Guids(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 @property def Includes(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) @property def LibraryClasses(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 @property def Pcds(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 ParsePcdName(self,TokenCName): TokenCName = TokenCName.strip() if TokenCName.startswith("["): if "." in TokenCName: Demesionattr = TokenCName[:TokenCName.index(".")] Fields = TokenCName[TokenCName.index(".")+1:] else: Demesionattr = TokenCName Fields = "" else: Demesionattr = "" Fields = TokenCName return Demesionattr,Fields 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 not item.TokenCName: continue if "" in item.TokenCName: struct_pcd.StructuredPcdIncludeFile.append(item.DefaultValue) elif "" 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: DemesionAttr, Fields = self.ParsePcdName(item.TokenCName) struct_pcd.AddDefaultValue(Fields, item.DefaultValue, self.MetaFile.File, LineNo,DemesionAttr) 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)) DefinitionPosition = {} 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) ) DefinitionPosition[PcdObj] = (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 for pcd in Pcds.values(): if pcd.DatumType not in [TAB_UINT8, TAB_UINT16, TAB_UINT32, TAB_UINT64, TAB_VOID, "BOOLEAN"]: if not pcd.IsAggregateDatumType(): EdkLogger.error('build', FORMAT_INVALID, "DatumType only support BOOLEAN, UINT8, UINT16, UINT32, UINT64, VOID* or a valid struct name.", DefinitionPosition[pcd][0], DefinitionPosition[pcd][1]) elif not pcd.IsArray() and not pcd.StructuredPcdIncludeFile: EdkLogger.error("build", PCD_STRUCTURE_PCD_ERROR, "The structure Pcd %s.%s header file is not found in %s line %s \n" % (pcd.TokenSpaceGuidCName, pcd.TokenCName, pcd.DefinitionPosition[0], pcd.DefinitionPosition[1] )) return Pcds @property def CommonIncludes(self): if self._CommonIncludes is None: self.Includes return self._CommonIncludes