## @file # # Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.
# # SPDX-License-Identifier: BSD-2-Clause-Patent from plugins.EdkPlugins.basemodel import ini from plugins.EdkPlugins.edk2.model import dsc from plugins.EdkPlugins.edk2.model import inf from plugins.EdkPlugins.edk2.model import dec import os from plugins.EdkPlugins.basemodel.message import * class SurfaceObject(object): _objs = {} def __new__(cls, *args, **kwargs): """Maintain only a single instance of this object @return: instance of this class """ obj = object.__new__(cls) if "None" not in cls._objs: cls._objs["None"] = [] cls._objs["None"].append(obj) return obj def __init__(self, parent, workspace): self._parent = parent self._fileObj = None self._workspace = workspace self._isModify = False self._modifiedObjs = [] def __del__(self): pass def Destroy(self): key = self.GetRelativeFilename() self.GetFileObj().Destroy(self) del self._fileObj # dereference self from _objs arrary assert key in self._objs, "when destory, object is not in obj list" assert self in self._objs[key], "when destory, object is not in obj list" self._objs[key].remove(self) if len(self._objs[key]) == 0: del self._objs[key] def GetParent(self): return self._parent def GetWorkspace(self): return self._workspace def GetFileObjectClass(self): return ini.BaseINIFile def GetFilename(self): return self.GetFileObj().GetFilename() def GetFileObj(self): return self._fileObj def GetRelativeFilename(self): fullPath = self.GetFilename() return fullPath[len(self._workspace) + 1:] def Load(self, relativePath): # if has been loaded, directly return if self._fileObj is not None: return True relativePath = os.path.normpath(relativePath) fullPath = os.path.join(self._workspace, relativePath) fullPath = os.path.normpath(fullPath) if not os.path.exists(fullPath): ErrorMsg("file does not exist!", fullPath) return False self._fileObj = self.GetFileObjectClass()(fullPath, self) if not self._fileObj.Parse(): ErrorMsg("Fail to parse file!", fullPath) return False # remove self from None list to list with filename as key cls = self.__class__ if self not in cls._objs["None"]: ErrorMsg("Sufrace object does not be create into None list") cls._objs["None"].remove(self) if relativePath not in cls._objs: cls._objs[relativePath] = [] cls._objs[relativePath].append(self) return True def Reload(self, force=False): ret = True # whether require must be update if force: ret = self.GetFileObj().Reload(True) else: if self.IsModified(): if self.GetFileObj().IsModified(): ret = self.GetFileObj().Reload() return ret def Modify(self, modify=True, modifiedObj=None): if modify: #LogMsg("%s is modified, modified object is %s" % (self.GetFilename(), modifiedObj)) if issubclass(modifiedObj.__class__, ini.BaseINIFile) and self._isModify: return self._isModify = modify self.GetParent().Modify(modify, self) else: self._isModify = modify def IsModified(self): return self._isModify def GetModifiedObjs(self): return self._modifiedObjs def FilterObjsByArch(self, objs, arch): arr = [] for obj in objs: if obj.GetArch().lower() == 'common': arr.append(obj) continue if obj.GetArch().lower() == arch.lower(): arr.append(obj) continue return arr class Platform(SurfaceObject): def __init__(self, parent, workspace): SurfaceObject.__init__(self, parent, workspace) self._modules = [] self._packages = [] def Destroy(self): for module in self._modules: module.Destroy() del self._modules[:] del self._packages[:] SurfaceObject.Destroy(self) def GetName(self): return self.GetFileObj().GetDefine("PLATFORM_NAME") def GetFileObjectClass(self): return dsc.DSCFile def GetModuleCount(self): if self.GetFileObj() is None: ErrorMsg("Fail to get module count because DSC file has not been load!") return len(self.GetFileObj().GetComponents()) def GetSupportArchs(self): return self.GetFileObj().GetDefine("SUPPORTED_ARCHITECTURES").strip().split('#')[0].split('|') def LoadModules(self, precallback=None, postcallback=None): for obj in self.GetFileObj().GetComponents(): mFilename = obj.GetFilename() if precallback is not None: precallback(self, mFilename) arch = obj.GetArch() if arch.lower() == 'common': archarr = self.GetSupportArchs() else: archarr = [arch] for arch in archarr: module = Module(self, self.GetWorkspace()) if module.Load(mFilename, arch, obj.GetOveridePcds(), obj.GetOverideLibs()): self._modules.append(module) if postcallback is not None: postcallback(self, module) else: del module ErrorMsg("Fail to load module %s" % mFilename) def GetModules(self): return self._modules def GetLibraryPath(self, classname, arch, type): objs = self.GetFileObj().GetSectionObjectsByName("libraryclasses") for obj in objs: if classname.lower() != obj.GetClass().lower(): continue if obj.GetArch().lower() != 'common' and \ obj.GetArch().lower() != arch.lower(): continue if obj.GetModuleType().lower() != 'common' and \ obj.GetModuleType().lower() != type.lower(): continue return obj.GetInstance() ErrorMsg("Fail to get library class %s [%s][%s] from platform %s" % (classname, arch, type, self.GetFilename())) return None def GetPackage(self, path): package = self.GetParent().GetPackage(path) if package not in self._packages: self._packages.append(package) return package def GetPcdBuildObjs(self, name, arch=None): arr = [] objs = self.GetFileObj().GetSectionObjectsByName('pcds') for obj in objs: if obj.GetPcdName().lower() == name.lower(): arr.append(obj) if arch is not None: arr = self.FilterObjsByArch(arr, arch) return arr def Reload(self, callback=None): # do not care force paramter for platform object isFileChanged = self.GetFileObj().IsModified() ret = SurfaceObject.Reload(self, False) if not ret: return False if isFileChanged: # destroy all modules and reload them again for obj in self._modules: obj.Destroy() del self._modules[:] del self._packages[:] self.LoadModules(callback) else: for obj in self._modules: callback(self, obj.GetFilename()) obj.Reload() self.Modify(False) return True def Modify(self, modify=True, modifiedObj=None): if modify: #LogMsg("%s is modified, modified object is %s" % (self.GetFilename(), modifiedObj)) if issubclass(modifiedObj.__class__, ini.BaseINIFile) and self._isModify: return self._isModify = modify self.GetParent().Modify(modify, self) else: if self.GetFileObj().IsModified(): return for obj in self._modules: if obj.IsModified(): return self._isModify = modify self.GetParent().Modify(modify, self) def GetModuleObject(self, relativePath, arch): path = os.path.normpath(relativePath) for obj in self._modules: if obj.GetRelativeFilename() == path: if arch.lower() == 'common': return obj if obj.GetArch() == arch: return obj return None def GenerateFullReferenceDsc(self): oldDsc = self.GetFileObj() newDsc = dsc.DSCFile() newDsc.CopySectionsByName(oldDsc, 'defines') newDsc.CopySectionsByName(oldDsc, 'SkuIds') # # Dynamic common section should also be copied # newDsc.CopySectionsByName(oldDsc, 'PcdsDynamicDefault') newDsc.CopySectionsByName(oldDsc, 'PcdsDynamicHii') newDsc.CopySectionsByName(oldDsc, 'PcdsDynamicVpd') newDsc.CopySectionsByName(oldDsc, 'PcdsDynamicEx') sects = oldDsc.GetSectionByName('Components') for oldSect in sects: newSect = newDsc.AddNewSection(oldSect.GetName()) for oldComObj in oldSect.GetObjects(): module = self.GetModuleObject(oldComObj.GetFilename(), oldSect.GetArch()) if module is None: continue newComObj = dsc.DSCComponentObject(newSect) newComObj.SetFilename(oldComObj.GetFilename()) # add all library instance for override section libdict = module.GetLibraries() for libclass in libdict.keys(): if libdict[libclass] is not None: newComObj.AddOverideLib(libclass, libdict[libclass].GetRelativeFilename().replace('\\', '/')) # add all pcds for override section pcddict = module.GetPcds() for pcd in pcddict.values(): buildPcd = pcd.GetBuildObj() buildType = buildPcd.GetPcdType() buildValue = None if buildType.lower() == 'pcdsdynamichii' or \ buildType.lower() == 'pcdsdynamicvpd' or \ buildType.lower() == 'pcdsdynamicdefault': buildType = 'PcdsDynamic' if buildType != 'PcdsDynamic': buildValue = buildPcd.GetPcdValue() newComObj.AddOveridePcd(buildPcd.GetPcdName(), buildType, buildValue) newSect.AddObject(newComObj) return newDsc class Module(SurfaceObject): def __init__(self, parent, workspace): SurfaceObject.__init__(self, parent, workspace) self._arch = 'common' self._parent = parent self._overidePcds = {} self._overideLibs = {} self._libs = {} self._pcds = {} self._ppis = [] self._protocols = [] self._depexs = [] self._guids = [] self._packages = [] def Destroy(self): for lib in self._libs.values(): if lib is not None: lib.Destroy() self._libs.clear() for pcd in self._pcds.values(): pcd.Destroy() self._pcds.clear() for ppi in self._ppis: ppi.DeRef(self) del self._ppis[:] for protocol in self._protocols: if protocol is not None: protocol.DeRef(self) del self._protocols[:] for guid in self._guids: if guid is not None: guid.DeRef(self) del self._guids[:] del self._packages[:] del self._depexs[:] SurfaceObject.Destroy(self) def GetFileObjectClass(self): return inf.INFFile def GetLibraries(self): return self._libs def Load(self, filename, arch='common', overidePcds=None, overideLibs=None): if not SurfaceObject.Load(self, filename): return False self._arch = arch if overidePcds is not None: self._overideLibs = overideLibs if overideLibs is not None: self._overidePcds = overidePcds self._SearchLibraries() self._SearchPackage() self._SearchSurfaceItems() return True def GetArch(self): return self._arch def GetModuleName(self): return self.GetFileObj().GetDefine("BASE_NAME") def GetModuleType(self): return self.GetFileObj().GetDefine("MODULE_TYPE") def GetPlatform(self): return self.GetParent() def GetModuleObj(self): return self def GetPcds(self): pcds = self._pcds.copy() for lib in self._libs.values(): if lib is None: continue for name in lib._pcds.keys(): pcds[name] = lib._pcds[name] return pcds def GetPpis(self): ppis = [] ppis += self._ppis for lib in self._libs.values(): if lib is None: continue ppis += lib._ppis return ppis def GetProtocols(self): pros = [] pros = self._protocols for lib in self._libs.values(): if lib is None: continue pros += lib._protocols return pros def GetGuids(self): guids = [] guids += self._guids for lib in self._libs.values(): if lib is None: continue guids += lib._guids return guids def GetDepexs(self): deps = [] deps += self._depexs for lib in self._libs.values(): if lib is None: continue deps += lib._depexs return deps def IsLibrary(self): return self.GetFileObj().GetDefine("LIBRARY_CLASS") is not None def GetLibraryInstance(self, classname, arch, type): if classname not in self._libs.keys(): # find in overide lib firstly if classname in self._overideLibs.keys(): self._libs[classname] = Library(self, self.GetWorkspace()) self._libs[classname].Load(self._overideLibs[classname]) return self._libs[classname] parent = self.GetParent() if issubclass(parent.__class__, Platform): path = parent.GetLibraryPath(classname, arch, type) if path is None: ErrorMsg('Fail to get library instance for %s' % classname, self.GetFilename()) return None self._libs[classname] = Library(self, self.GetWorkspace()) if not self._libs[classname].Load(path, self.GetArch()): self._libs[classname] = None else: self._libs[classname] = parent.GetLibraryInstance(classname, arch, type) return self._libs[classname] def GetSourceObjs(self): return self.GetFileObj().GetSectionObjectsByName('source') def _SearchLibraries(self): objs = self.GetFileObj().GetSectionObjectsByName('libraryclasses') arch = self.GetArch() type = self.GetModuleType() for obj in objs: if obj.GetArch().lower() != 'common' and \ obj.GetArch().lower() not in self.GetPlatform().GetSupportArchs(): continue classname = obj.GetClass() instance = self.GetLibraryInstance(classname, arch, type) if not self.IsLibrary() and instance is not None: instance._isInherit = False if classname not in self._libs.keys(): self._libs[classname] = instance def _SearchSurfaceItems(self): # get surface item from self's inf pcds = [] ppis = [] pros = [] deps = [] guids = [] if self.GetFileObj() is not None: pcds = self.FilterObjsByArch(self.GetFileObj().GetSectionObjectsByName('pcd'), self.GetArch()) for pcd in pcds: if pcd.GetPcdName() not in self._pcds.keys(): pcdItem = PcdItem(pcd.GetPcdName(), self, pcd) self._pcds[pcd.GetPcdName()] = ModulePcd(self, pcd.GetPcdName(), pcd, pcdItem) ppis += self.FilterObjsByArch(self.GetFileObj().GetSectionObjectsByName('ppis'), self.GetArch()) for ppi in ppis: item = PpiItem(ppi.GetName(), self, ppi) if item not in self._ppis: self._ppis.append(item) pros += self.FilterObjsByArch(self.GetFileObj().GetSectionObjectsByName('protocols'), self.GetArch()) for pro in pros: item = ProtocolItem(pro.GetName(), self, pro) if item not in self._protocols: self._protocols.append(item) deps += self.FilterObjsByArch(self.GetFileObj().GetSectionObjectsByName('depex'), self.GetArch()) for dep in deps: item = DepexItem(self, dep) self._depexs.append(item) guids += self.FilterObjsByArch(self.GetFileObj().GetSectionObjectsByName('guids'), self.GetArch()) for guid in guids: item = GuidItem(guid.GetName(), self, guid) if item not in self._guids: self._guids.append(item) def _SearchPackage(self): objs = self.GetFileObj().GetSectionObjectsByName('packages') for obj in objs: package = self.GetPlatform().GetPackage(obj.GetPath()) if package is not None: self._packages.append(package) def GetPackages(self): return self._packages def GetPcdObjects(self): if self.GetFileObj() is None: return [] return self.GetFileObj().GetSectionObjectsByName('pcd') def GetLibraryClassHeaderFilePath(self): lcname = self.GetFileObj().GetProduceLibraryClass() if lcname is None: return None pkgs = self.GetPackages() for package in pkgs: path = package.GetLibraryClassHeaderPathByName(lcname) if path is not None: return os.path.realpath(os.path.join(package.GetFileObj().GetPackageRootPath(), path)) return None def Reload(self, force=False, callback=None): if callback is not None: callback(self, "Starting reload...") ret = SurfaceObject.Reload(self, force) if not ret: return False if not force and not self.IsModified(): return True for lib in self._libs.values(): if lib is not None: lib.Destroy() self._libs.clear() for pcd in self._pcds.values(): pcd.Destroy() self._pcds.clear() for ppi in self._ppis: ppi.DeRef(self) del self._ppis[:] for protocol in self._protocols: protocol.DeRef(self) del self._protocols[:] for guid in self._guids: guid.DeRef(self) del self._guids[:] del self._packages[:] del self._depexs[:] if callback is not None: callback(self, "Searching libraries...") self._SearchLibraries() if callback is not None: callback(self, "Searching packages...") self._SearchPackage() if callback is not None: callback(self, "Searching surface items...") self._SearchSurfaceItems() self.Modify(False) return True def Modify(self, modify=True, modifiedObj=None): if modify: #LogMsg("%s is modified, modified object is %s" % (self.GetFilename(), modifiedObj)) if issubclass(modifiedObj.__class__, ini.BaseINIFile) and self._isModify: return self._isModify = modify self.GetParent().Modify(modify, self) else: if self.GetFileObj().IsModified(): return self._isModify = modify self.GetParent().Modify(modify, self) class Library(Module): def __init__(self, parent, workspace): Module.__init__(self, parent, workspace) self._isInherit = True def IsInherit(self): return self._isInherit def GetModuleType(self): return self.GetParent().GetModuleType() def GetPlatform(self): return self.GetParent().GetParent() def GetModuleObj(self): return self.GetParent() def GetArch(self): return self.GetParent().GetArch() def Destroy(self): self._libs.clear() self._pcds.clear() SurfaceObject.Destroy(self) class Package(SurfaceObject): def __init__(self, parent, workspace): SurfaceObject.__init__(self, parent, workspace) self._pcds = {} self._guids = {} self._protocols = {} self._ppis = {} def GetPcds(self): return self._pcds def GetPpis(self): return list(self._ppis.values()) def GetProtocols(self): return list(self._protocols.values()) def GetGuids(self): return list(self._guids.values()) def Destroy(self): for pcd in self._pcds.values(): if pcd is not None: pcd.Destroy() for guid in self._guids.values(): if guid is not None: guid.Destroy() for protocol in self._protocols.values(): if protocol is not None: protocol.Destroy() for ppi in self._ppis.values(): if ppi is not None: ppi.Destroy() self._pcds.clear() self._guids.clear() self._protocols.clear() self._ppis.clear() self._pcds.clear() SurfaceObject.Destroy(self) def Load(self, relativePath): ret = SurfaceObject.Load(self, relativePath) if not ret: return False pcds = self.GetFileObj().GetSectionObjectsByName('pcds') for pcd in pcds: if pcd.GetPcdName() in self._pcds.keys(): if self._pcds[pcd.GetPcdName()] is not None: self._pcds[pcd.GetPcdName()].AddDecObj(pcd) else: self._pcds[pcd.GetPcdName()] = PcdItem(pcd.GetPcdName(), self, pcd) guids = self.GetFileObj().GetSectionObjectsByName('guids') for guid in guids: if guid.GetName() not in self._guids.keys(): self._guids[guid.GetName()] = GuidItem(guid.GetName(), self, guid) else: WarnMsg("Duplicate definition for %s" % guid.GetName()) ppis = self.GetFileObj().GetSectionObjectsByName('ppis') for ppi in ppis: if ppi.GetName() not in self._ppis.keys(): self._ppis[ppi.GetName()] = PpiItem(ppi.GetName(), self, ppi) else: WarnMsg("Duplicate definition for %s" % ppi.GetName()) protocols = self.GetFileObj().GetSectionObjectsByName('protocols') for protocol in protocols: if protocol.GetName() not in self._protocols.keys(): self._protocols[protocol.GetName()] = ProtocolItem(protocol.GetName(), self, protocol) else: WarnMsg("Duplicate definition for %s" % protocol.GetName()) return True def GetFileObjectClass(self): return dec.DECFile def GetName(self): return self.GetFileObj().GetDefine("PACKAGE_NAME") def GetPcdDefineObjs(self, name=None): arr = [] objs = self.GetFileObj().GetSectionObjectsByName('pcds') if name is None: return objs for obj in objs: if obj.GetPcdName().lower() == name.lower(): arr.append(obj) return arr def GetLibraryClassObjs(self): return self.GetFileObj().GetSectionObjectsByName('libraryclasses') def Modify(self, modify=True, modifiedObj=None): if modify: self._isModify = modify self.GetParent().Modify(modify, self) else: if self.GetFileObj().IsModified(): return self._isModify = modify self.GetParent().Modify(modify, self) def GetLibraryClassHeaderPathByName(self, clsname): objs = self.GetLibraryClassObjs() for obj in objs: if obj.GetClassName() == clsname: return obj.GetHeaderFile() return None class DepexItem(object): def __init__(self, parent, infObj): self._parent = parent self._infObj = infObj def GetDepexString(self): return str(self._infObj) def GetInfObject(self): return self._infObj class ModulePcd(object): _type_mapping = {'FeaturePcd': 'PcdsFeatureFlag', 'FixedPcd': 'PcdsFixedAtBuild', 'PatchPcd': 'PcdsPatchableInModule'} def __init__(self, parent, name, infObj, pcdItem): assert issubclass(parent.__class__, Module), "Module's PCD's parent must be module!" assert pcdItem is not None, 'Pcd %s does not in some package!' % name self._name = name self._parent = parent self._pcdItem = pcdItem self._infObj = infObj def GetName(self): return self._name def GetParent(self): return self._name def GetArch(self): return self._parent.GetArch() def Destroy(self): self._pcdItem.DeRef(self._parent) self._infObj = None def GetBuildObj(self): platformInfos = self._parent.GetPlatform().GetPcdBuildObjs(self._name, self.GetArch()) modulePcdType = self._infObj.GetPcdType() # if platform do not gives pcd's value, get default value from package if len(platformInfos) == 0: if modulePcdType.lower() == 'pcd': return self._pcdItem.GetDecObject() else: for obj in self._pcdItem.GetDecObjects(): if modulePcdType not in self._type_mapping.keys(): ErrorMsg("Invalid PCD type %s" % modulePcdType) return None if self._type_mapping[modulePcdType] == obj.GetPcdType(): return obj ErrorMsg ('Module PCD type %s does not in valied range [%s] in package!' % \ (modulePcdType)) else: if modulePcdType.lower() == 'pcd': if len(platformInfos) > 1: WarnMsg("Find more than one value for PCD %s in platform %s" % \ (self._name, self._parent.GetPlatform().GetFilename())) return platformInfos[0] else: for obj in platformInfos: if modulePcdType not in self._type_mapping.keys(): ErrorMsg("Invalid PCD type %s" % modulePcdType) return None if self._type_mapping[modulePcdType] == obj.GetPcdType(): return obj ErrorMsg('Can not find value for pcd %s in pcd type %s' % \ (self._name, modulePcdType)) return None class SurfaceItem(object): _objs = {} def __new__(cls, *args, **kwargs): """Maintain only a single instance of this object @return: instance of this class """ name = args[0] parent = args[1] fileObj = args[2] if issubclass(parent.__class__, Package): if name in cls._objs.keys(): ErrorMsg("%s item is duplicated defined in packages: %s and %s" % (name, parent.GetFilename(), cls._objs[name].GetParent().GetFilename())) return None obj = object.__new__(cls) cls._objs[name] = obj return obj elif issubclass(parent.__class__, Module): if name not in cls._objs.keys(): ErrorMsg("%s item does not defined in any package! It is used by module %s" % \ (name, parent.GetFilename())) return None return cls._objs[name] return None def __init__(self, name, parent, fileObj): if issubclass(parent.__class__, Package): self._name = name self._parent = parent self._decObj = [fileObj] self._refMods = {} else: self.RefModule(parent, fileObj) @classmethod def GetObjectDict(cls): return cls._objs def GetParent(self): return self._parent def GetReference(self): return self._refMods def RefModule(self, mObj, infObj): if mObj in self._refMods.keys(): return self._refMods[mObj] = infObj def DeRef(self, mObj): if mObj not in self._refMods.keys(): WarnMsg("%s is not referenced by module %s" % (self._name, mObj.GetFilename())) return del self._refMods[mObj] def Destroy(self): self._refMods.clear() cls = self.__class__ del cls._objs[self._name] def GetName(self): return self._name def GetDecObject(self): return self._decObj[0] def GetDecObjects(self): return self._decObj class PcdItem(SurfaceItem): def AddDecObj(self, fileObj): for decObj in self._decObj: if decObj.GetFilename() != fileObj.GetFilename(): ErrorMsg("Pcd %s defined in more than one packages : %s and %s" % \ (self._name, decObj.GetFilename(), fileObj.GetFilename())) return if decObj.GetPcdType() == fileObj.GetPcdType() and \ decObj.GetArch().lower() == fileObj.GetArch(): ErrorMsg("Pcd %s is duplicated defined in pcd type %s in package %s" % \ (self._name, decObj.GetPcdType(), decObj.GetFilename())) return self._decObj.append(fileObj) def GetValidPcdType(self): types = [] for obj in self._decObj: if obj.GetPcdType() not in types: types += obj.GetPcdType() return types class GuidItem(SurfaceItem): pass class PpiItem(SurfaceItem): pass class ProtocolItem(SurfaceItem): pass