## @file | |
# Common routines used by all tools | |
# | |
# Copyright (c) 2011 - 2019, Intel Corporation. All rights reserved.<BR> | |
# | |
# SPDX-License-Identifier: BSD-2-Clause-Patent | |
# | |
''' | |
Misc | |
''' | |
## | |
# Import Modules | |
# | |
import os.path | |
from os import access | |
from os import F_OK | |
from os import makedirs | |
from os import getcwd | |
from os import chdir | |
from os import listdir | |
from os import remove | |
from os import rmdir | |
from os import linesep | |
from os import walk | |
from os import environ | |
import re | |
from collections import OrderedDict as Sdict | |
import Logger.Log as Logger | |
from Logger import StringTable as ST | |
from Logger import ToolError | |
from Library import GlobalData | |
from Library.DataType import SUP_MODULE_LIST | |
from Library.DataType import END_OF_LINE | |
from Library.DataType import TAB_SPLIT | |
from Library.DataType import TAB_LANGUAGE_EN_US | |
from Library.DataType import TAB_LANGUAGE_EN | |
from Library.DataType import TAB_LANGUAGE_EN_X | |
from Library.DataType import TAB_UNI_FILE_SUFFIXS | |
from Library.StringUtils import GetSplitValueList | |
from Library.ParserValidate import IsValidHexVersion | |
from Library.ParserValidate import IsValidPath | |
from Object.POM.CommonObject import TextObject | |
from Core.FileHook import __FileHookOpen__ | |
from Common.MultipleWorkspace import MultipleWorkspace as mws | |
## Convert GUID string in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx style to C | |
# structure style | |
# | |
# @param Guid: The GUID string | |
# | |
def GuidStringToGuidStructureString(Guid): | |
GuidList = Guid.split('-') | |
Result = '{' | |
for Index in range(0, 3, 1): | |
Result = Result + '0x' + GuidList[Index] + ', ' | |
Result = Result + '{0x' + GuidList[3][0:2] + ', 0x' + GuidList[3][2:4] | |
for Index in range(0, 12, 2): | |
Result = Result + ', 0x' + GuidList[4][Index:Index + 2] | |
Result += '}}' | |
return Result | |
## Check whether GUID string is of format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | |
# | |
# @param GuidValue: The GUID value | |
# | |
def CheckGuidRegFormat(GuidValue): | |
## Regular expression used to find out register format of GUID | |
# | |
RegFormatGuidPattern = re.compile("^\s*([0-9a-fA-F]){8}-" | |
"([0-9a-fA-F]){4}-" | |
"([0-9a-fA-F]){4}-" | |
"([0-9a-fA-F]){4}-" | |
"([0-9a-fA-F]){12}\s*$") | |
if RegFormatGuidPattern.match(GuidValue): | |
return True | |
else: | |
return False | |
## Convert GUID string in C structure style to | |
# xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | |
# | |
# @param GuidValue: The GUID value in C structure format | |
# | |
def GuidStructureStringToGuidString(GuidValue): | |
GuidValueString = GuidValue.lower().replace("{", "").replace("}", "").\ | |
replace(" ", "").replace(";", "") | |
GuidValueList = GuidValueString.split(",") | |
if len(GuidValueList) != 11: | |
return '' | |
try: | |
return "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % ( | |
int(GuidValueList[0], 16), | |
int(GuidValueList[1], 16), | |
int(GuidValueList[2], 16), | |
int(GuidValueList[3], 16), | |
int(GuidValueList[4], 16), | |
int(GuidValueList[5], 16), | |
int(GuidValueList[6], 16), | |
int(GuidValueList[7], 16), | |
int(GuidValueList[8], 16), | |
int(GuidValueList[9], 16), | |
int(GuidValueList[10], 16) | |
) | |
except BaseException: | |
return '' | |
## Create directories | |
# | |
# @param Directory: The directory name | |
# | |
def CreateDirectory(Directory): | |
if Directory is None or Directory.strip() == "": | |
return True | |
try: | |
if not access(Directory, F_OK): | |
makedirs(Directory) | |
except BaseException: | |
return False | |
return True | |
## Remove directories, including files and sub-directories in it | |
# | |
# @param Directory: The directory name | |
# | |
def RemoveDirectory(Directory, Recursively=False): | |
if Directory is None or Directory.strip() == "" or not \ | |
os.path.exists(Directory): | |
return | |
if Recursively: | |
CurrentDirectory = getcwd() | |
chdir(Directory) | |
for File in listdir("."): | |
if os.path.isdir(File): | |
RemoveDirectory(File, Recursively) | |
else: | |
remove(File) | |
chdir(CurrentDirectory) | |
rmdir(Directory) | |
## Store content in file | |
# | |
# This method is used to save file only when its content is changed. This is | |
# quite useful for "make" system to decide what will be re-built and what | |
# won't. | |
# | |
# @param File: The path of file | |
# @param Content: The new content of the file | |
# @param IsBinaryFile: The flag indicating if the file is binary file | |
# or not | |
# | |
def SaveFileOnChange(File, Content, IsBinaryFile=True): | |
if os.path.exists(File): | |
if IsBinaryFile: | |
try: | |
if Content == __FileHookOpen__(File, "rb").read(): | |
return False | |
except BaseException: | |
Logger.Error(None, ToolError.FILE_OPEN_FAILURE, ExtraData=File) | |
else: | |
try: | |
if Content == __FileHookOpen__(File, "r").read(): | |
return False | |
except BaseException: | |
Logger.Error(None, ToolError.FILE_OPEN_FAILURE, ExtraData=File) | |
CreateDirectory(os.path.dirname(File)) | |
if IsBinaryFile: | |
try: | |
FileFd = __FileHookOpen__(File, "wb") | |
FileFd.write(Content) | |
FileFd.close() | |
except BaseException: | |
Logger.Error(None, ToolError.FILE_CREATE_FAILURE, ExtraData=File) | |
else: | |
try: | |
FileFd = __FileHookOpen__(File, "w") | |
FileFd.write(Content) | |
FileFd.close() | |
except BaseException: | |
Logger.Error(None, ToolError.FILE_CREATE_FAILURE, ExtraData=File) | |
return True | |
## Get all files of a directory | |
# | |
# @param Root: Root dir | |
# @param SkipList : The files need be skipped | |
# | |
def GetFiles(Root, SkipList=None, FullPath=True): | |
OriPath = os.path.normpath(Root) | |
FileList = [] | |
for Root, Dirs, Files in walk(Root): | |
if SkipList: | |
for Item in SkipList: | |
if Item in Dirs: | |
Dirs.remove(Item) | |
if Item in Files: | |
Files.remove(Item) | |
for Dir in Dirs: | |
if Dir.startswith('.'): | |
Dirs.remove(Dir) | |
for File in Files: | |
if File.startswith('.'): | |
continue | |
File = os.path.normpath(os.path.join(Root, File)) | |
if not FullPath: | |
File = File[len(OriPath) + 1:] | |
FileList.append(File) | |
return FileList | |
## Get all non-metadata files of a directory | |
# | |
# @param Root: Root Dir | |
# @param SkipList : List of path need be skipped | |
# @param FullPath: True if the returned file should be full path | |
# @param PrefixPath: the path that need to be added to the files found | |
# @return: the list of files found | |
# | |
def GetNonMetaDataFiles(Root, SkipList, FullPath, PrefixPath): | |
FileList = GetFiles(Root, SkipList, FullPath) | |
NewFileList = [] | |
for File in FileList: | |
ExtName = os.path.splitext(File)[1] | |
# | |
# skip '.dec', '.inf', '.dsc', '.fdf' files | |
# | |
if ExtName.lower() not in ['.dec', '.inf', '.dsc', '.fdf']: | |
NewFileList.append(os.path.normpath(os.path.join(PrefixPath, File))) | |
return NewFileList | |
## Check if given file exists or not | |
# | |
# @param File: File name or path to be checked | |
# @param Dir: The directory the file is relative to | |
# | |
def ValidFile(File, Ext=None): | |
File = File.replace('\\', '/') | |
if Ext is not None: | |
FileExt = os.path.splitext(File)[1] | |
if FileExt.lower() != Ext.lower(): | |
return False | |
if not os.path.exists(File): | |
return False | |
return True | |
## RealPath | |
# | |
# @param File: File name or path to be checked | |
# @param Dir: The directory the file is relative to | |
# @param OverrideDir: The override directory | |
# | |
def RealPath(File, Dir='', OverrideDir=''): | |
NewFile = os.path.normpath(os.path.join(Dir, File)) | |
NewFile = GlobalData.gALL_FILES[NewFile] | |
if not NewFile and OverrideDir: | |
NewFile = os.path.normpath(os.path.join(OverrideDir, File)) | |
NewFile = GlobalData.gALL_FILES[NewFile] | |
return NewFile | |
## RealPath2 | |
# | |
# @param File: File name or path to be checked | |
# @param Dir: The directory the file is relative to | |
# @param OverrideDir: The override directory | |
# | |
def RealPath2(File, Dir='', OverrideDir=''): | |
if OverrideDir: | |
NewFile = GlobalData.gALL_FILES[os.path.normpath(os.path.join\ | |
(OverrideDir, File))] | |
if NewFile: | |
if OverrideDir[-1] == os.path.sep: | |
return NewFile[len(OverrideDir):], NewFile[0:len(OverrideDir)] | |
else: | |
return NewFile[len(OverrideDir) + 1:], \ | |
NewFile[0:len(OverrideDir)] | |
NewFile = GlobalData.gALL_FILES[os.path.normpath(os.path.join(Dir, File))] | |
if NewFile: | |
if Dir: | |
if Dir[-1] == os.path.sep: | |
return NewFile[len(Dir):], NewFile[0:len(Dir)] | |
else: | |
return NewFile[len(Dir) + 1:], NewFile[0:len(Dir)] | |
else: | |
return NewFile, '' | |
return None, None | |
## CommonPath | |
# | |
# @param PathList: PathList | |
# | |
def CommonPath(PathList): | |
Path1 = min(PathList).split(os.path.sep) | |
Path2 = max(PathList).split(os.path.sep) | |
for Index in range(min(len(Path1), len(Path2))): | |
if Path1[Index] != Path2[Index]: | |
return os.path.sep.join(Path1[:Index]) | |
return os.path.sep.join(Path1) | |
## PathClass | |
# | |
class PathClass(object): | |
def __init__(self, File='', Root='', AlterRoot='', Type='', IsBinary=False, | |
Arch='COMMON', ToolChainFamily='', Target='', TagName='', \ | |
ToolCode=''): | |
self.Arch = Arch | |
self.File = str(File) | |
if os.path.isabs(self.File): | |
self.Root = '' | |
self.AlterRoot = '' | |
else: | |
self.Root = str(Root) | |
self.AlterRoot = str(AlterRoot) | |
# | |
# Remove any '.' and '..' in path | |
# | |
if self.Root: | |
self.Path = os.path.normpath(os.path.join(self.Root, self.File)) | |
self.Root = os.path.normpath(CommonPath([self.Root, self.Path])) | |
# | |
# eliminate the side-effect of 'C:' | |
# | |
if self.Root[-1] == ':': | |
self.Root += os.path.sep | |
# | |
# file path should not start with path separator | |
# | |
if self.Root[-1] == os.path.sep: | |
self.File = self.Path[len(self.Root):] | |
else: | |
self.File = self.Path[len(self.Root) + 1:] | |
else: | |
self.Path = os.path.normpath(self.File) | |
self.SubDir, self.Name = os.path.split(self.File) | |
self.BaseName, self.Ext = os.path.splitext(self.Name) | |
if self.Root: | |
if self.SubDir: | |
self.Dir = os.path.join(self.Root, self.SubDir) | |
else: | |
self.Dir = self.Root | |
else: | |
self.Dir = self.SubDir | |
if IsBinary: | |
self.Type = Type | |
else: | |
self.Type = self.Ext.lower() | |
self.IsBinary = IsBinary | |
self.Target = Target | |
self.TagName = TagName | |
self.ToolCode = ToolCode | |
self.ToolChainFamily = ToolChainFamily | |
self._Key = None | |
## Convert the object of this class to a string | |
# | |
# Convert member Path of the class to a string | |
# | |
def __str__(self): | |
return self.Path | |
## Override __eq__ function | |
# | |
# Check whether PathClass are the same | |
# | |
def __eq__(self, Other): | |
if isinstance(Other, type(self)): | |
return self.Path == Other.Path | |
else: | |
return self.Path == str(Other) | |
## Override __hash__ function | |
# | |
# Use Path as key in hash table | |
# | |
def __hash__(self): | |
return hash(self.Path) | |
## _GetFileKey | |
# | |
def _GetFileKey(self): | |
if self._Key is None: | |
self._Key = self.Path.upper() | |
return self._Key | |
## Validate | |
# | |
def Validate(self, Type='', CaseSensitive=True): | |
if GlobalData.gCASE_INSENSITIVE: | |
CaseSensitive = False | |
if Type and Type.lower() != self.Type: | |
return ToolError.FILE_TYPE_MISMATCH, '%s (expect %s but got %s)' % \ | |
(self.File, Type, self.Type) | |
RealFile, RealRoot = RealPath2(self.File, self.Root, self.AlterRoot) | |
if not RealRoot and not RealFile: | |
RealFile = self.File | |
if self.AlterRoot: | |
RealFile = os.path.join(self.AlterRoot, self.File) | |
elif self.Root: | |
RealFile = os.path.join(self.Root, self.File) | |
return ToolError.FILE_NOT_FOUND, os.path.join(self.AlterRoot, RealFile) | |
ErrorCode = 0 | |
ErrorInfo = '' | |
if RealRoot != self.Root or RealFile != self.File: | |
if CaseSensitive and (RealFile != self.File or \ | |
(RealRoot != self.Root and RealRoot != \ | |
self.AlterRoot)): | |
ErrorCode = ToolError.FILE_CASE_MISMATCH | |
ErrorInfo = self.File + '\n\t' + RealFile + \ | |
" [in file system]" | |
self.SubDir, self.Name = os.path.split(RealFile) | |
self.BaseName, self.Ext = os.path.splitext(self.Name) | |
if self.SubDir: | |
self.Dir = os.path.join(RealRoot, self.SubDir) | |
else: | |
self.Dir = RealRoot | |
self.File = RealFile | |
self.Root = RealRoot | |
self.Path = os.path.join(RealRoot, RealFile) | |
return ErrorCode, ErrorInfo | |
Key = property(_GetFileKey) | |
## Get current workspace | |
# | |
# get WORKSPACE from environment variable if present,if not use current working directory as WORKSPACE | |
# | |
def GetWorkspace(): | |
# | |
# check WORKSPACE | |
# | |
if "WORKSPACE" in environ: | |
WorkspaceDir = os.path.normpath(environ["WORKSPACE"]) | |
if not os.path.exists(WorkspaceDir): | |
Logger.Error("UPT", | |
ToolError.UPT_ENVIRON_MISSING_ERROR, | |
ST.ERR_WORKSPACE_NOTEXIST, | |
ExtraData="%s" % WorkspaceDir) | |
else: | |
WorkspaceDir = os.getcwd() | |
if WorkspaceDir[-1] == ':': | |
WorkspaceDir += os.sep | |
PackagesPath = os.environ.get("PACKAGES_PATH") | |
mws.setWs(WorkspaceDir, PackagesPath) | |
return WorkspaceDir, mws.PACKAGES_PATH | |
## Get relative path | |
# | |
# use full path and workspace to get relative path | |
# the destination of this function is mainly to resolve the root path issue(like c: or c:\) | |
# | |
# @param Fullpath: a string of fullpath | |
# @param Workspace: a string of workspace | |
# | |
def GetRelativePath(Fullpath, Workspace): | |
RelativePath = '' | |
if Workspace.endswith(os.sep): | |
RelativePath = Fullpath[Fullpath.upper().find(Workspace.upper())+len(Workspace):] | |
else: | |
RelativePath = Fullpath[Fullpath.upper().find(Workspace.upper())+len(Workspace)+1:] | |
return RelativePath | |
## Check whether all module types are in list | |
# | |
# check whether all module types (SUP_MODULE_LIST) are in list | |
# | |
# @param ModuleList: a list of ModuleType | |
# | |
def IsAllModuleList(ModuleList): | |
NewModuleList = [Module.upper() for Module in ModuleList] | |
for Module in SUP_MODULE_LIST: | |
if Module not in NewModuleList: | |
return False | |
else: | |
return True | |
## Dictionary that use comment(GenericComment, TailComment) as value, | |
# if a new comment which key already in the dic is inserted, then the | |
# comment will be merged. | |
# Key is (Statement, SupArch), when TailComment is added, it will ident | |
# according to Statement | |
# | |
class MergeCommentDict(dict): | |
## []= operator | |
# | |
def __setitem__(self, Key, CommentVal): | |
GenericComment, TailComment = CommentVal | |
if Key in self: | |
OrigVal1, OrigVal2 = dict.__getitem__(self, Key) | |
Statement = Key[0] | |
dict.__setitem__(self, Key, (OrigVal1 + GenericComment, OrigVal2 \ | |
+ len(Statement) * ' ' + TailComment)) | |
else: | |
dict.__setitem__(self, Key, (GenericComment, TailComment)) | |
## =[] operator | |
# | |
def __getitem__(self, Key): | |
return dict.__getitem__(self, Key) | |
## GenDummyHelpTextObj | |
# | |
# @retval HelpTxt: Generated dummy help text object | |
# | |
def GenDummyHelpTextObj(): | |
HelpTxt = TextObject() | |
HelpTxt.SetLang(TAB_LANGUAGE_EN_US) | |
HelpTxt.SetString(' ') | |
return HelpTxt | |
## ConvertVersionToDecimal, the minor version should be within 0 - 99 | |
# <HexVersion> ::= "0x" <Major> <Minor> | |
# <Major> ::= (a-fA-F0-9){4} | |
# <Minor> ::= (a-fA-F0-9){4} | |
# <DecVersion> ::= (0-65535) ["." (0-99)] | |
# | |
# @param StringIn: The string contains version defined in INF file. | |
# It can be Decimal or Hex | |
# | |
def ConvertVersionToDecimal(StringIn): | |
if IsValidHexVersion(StringIn): | |
Value = int(StringIn, 16) | |
Major = Value >> 16 | |
Minor = Value & 0xFFFF | |
MinorStr = str(Minor) | |
if len(MinorStr) == 1: | |
MinorStr = '0' + MinorStr | |
return str(Major) + '.' + MinorStr | |
else: | |
if StringIn.find(TAB_SPLIT) != -1: | |
return StringIn | |
elif StringIn: | |
return StringIn + '.0' | |
else: | |
# | |
# when StringIn is '', return it directly | |
# | |
return StringIn | |
## GetHelpStringByRemoveHashKey | |
# | |
# Remove hash key at the header of string and return the remain. | |
# | |
# @param String: The string need to be processed. | |
# | |
def GetHelpStringByRemoveHashKey(String): | |
ReturnString = '' | |
PattenRemoveHashKey = re.compile(r"^[#+\s]+", re.DOTALL) | |
String = String.strip() | |
if String == '': | |
return String | |
LineList = GetSplitValueList(String, END_OF_LINE) | |
for Line in LineList: | |
ValueList = PattenRemoveHashKey.split(Line) | |
if len(ValueList) == 1: | |
ReturnString += ValueList[0] + END_OF_LINE | |
else: | |
ReturnString += ValueList[1] + END_OF_LINE | |
if ReturnString.endswith('\n') and not ReturnString.endswith('\n\n') and ReturnString != '\n': | |
ReturnString = ReturnString[:-1] | |
return ReturnString | |
## ConvPathFromAbsToRel | |
# | |
# Get relative file path from absolute path. | |
# | |
# @param Path: The string contain file absolute path. | |
# @param Root: The string contain the parent path of Path in. | |
# | |
# | |
def ConvPathFromAbsToRel(Path, Root): | |
Path = os.path.normpath(Path) | |
Root = os.path.normpath(Root) | |
FullPath = os.path.normpath(os.path.join(Root, Path)) | |
# | |
# If Path is absolute path. | |
# It should be in Root. | |
# | |
if os.path.isabs(Path): | |
return FullPath[FullPath.find(Root) + len(Root) + 1:] | |
else: | |
return Path | |
## ConvertPath | |
# | |
# Convert special characters to '_', '\' to '/' | |
# return converted path: Test!1.inf -> Test_1.inf | |
# | |
# @param Path: Path to be converted | |
# | |
def ConvertPath(Path): | |
RetPath = '' | |
for Char in Path.strip(): | |
if Char.isalnum() or Char in '.-_/': | |
RetPath = RetPath + Char | |
elif Char == '\\': | |
RetPath = RetPath + '/' | |
else: | |
RetPath = RetPath + '_' | |
return RetPath | |
## ConvertSpec | |
# | |
# during install, convert the Spec string extract from UPD into INF allowable definition, | |
# the difference is period is allowed in the former (not the first letter) but not in the latter. | |
# return converted Spec string | |
# | |
# @param SpecStr: SpecStr to be converted | |
# | |
def ConvertSpec(SpecStr): | |
RetStr = '' | |
for Char in SpecStr: | |
if Char.isalnum() or Char == '_': | |
RetStr = RetStr + Char | |
else: | |
RetStr = RetStr + '_' | |
return RetStr | |
## IsEqualList | |
# | |
# Judge two lists are identical(contain same item). | |
# The rule is elements in List A are in List B and elements in List B are in List A. | |
# | |
# @param ListA, ListB Lists need to be judged. | |
# | |
# @return True ListA and ListB are identical | |
# @return False ListA and ListB are different with each other | |
# | |
def IsEqualList(ListA, ListB): | |
if ListA == ListB: | |
return True | |
for ItemA in ListA: | |
if not ItemA in ListB: | |
return False | |
for ItemB in ListB: | |
if not ItemB in ListA: | |
return False | |
return True | |
## ConvertArchList | |
# | |
# Convert item in ArchList if the start character is lower case. | |
# In UDP spec, Arch is only allowed as: [A-Z]([a-zA-Z0-9])* | |
# | |
# @param ArchList The ArchList need to be converted. | |
# | |
# @return NewList The ArchList been converted. | |
# | |
def ConvertArchList(ArchList): | |
NewArchList = [] | |
if not ArchList: | |
return NewArchList | |
if isinstance(ArchList, list): | |
for Arch in ArchList: | |
Arch = Arch.upper() | |
NewArchList.append(Arch) | |
elif isinstance(ArchList, str): | |
ArchList = ArchList.upper() | |
NewArchList.append(ArchList) | |
return NewArchList | |
## ProcessLineExtender | |
# | |
# Process the LineExtender of Line in LineList. | |
# If one line ends with a line extender, then it will be combined together with next line. | |
# | |
# @param LineList The LineList need to be processed. | |
# | |
# @return NewList The ArchList been processed. | |
# | |
def ProcessLineExtender(LineList): | |
NewList = [] | |
Count = 0 | |
while Count < len(LineList): | |
if LineList[Count].strip().endswith("\\") and Count + 1 < len(LineList): | |
NewList.append(LineList[Count].strip()[:-2] + LineList[Count + 1]) | |
Count = Count + 1 | |
else: | |
NewList.append(LineList[Count]) | |
Count = Count + 1 | |
return NewList | |
## ProcessEdkComment | |
# | |
# Process EDK style comment in LineList: c style /* */ comment or cpp style // comment | |
# | |
# | |
# @param LineList The LineList need to be processed. | |
# | |
# @return LineList The LineList been processed. | |
# @return FirstPos Where Edk comment is first found, -1 if not found | |
# | |
def ProcessEdkComment(LineList): | |
FindEdkBlockComment = False | |
Count = 0 | |
StartPos = -1 | |
EndPos = -1 | |
FirstPos = -1 | |
while(Count < len(LineList)): | |
Line = LineList[Count].strip() | |
if Line.startswith("/*"): | |
# | |
# handling c style comment | |
# | |
StartPos = Count | |
while Count < len(LineList): | |
Line = LineList[Count].strip() | |
if Line.endswith("*/"): | |
if (Count == StartPos) and Line.strip() == '/*/': | |
Count = Count + 1 | |
continue | |
EndPos = Count | |
FindEdkBlockComment = True | |
break | |
Count = Count + 1 | |
if FindEdkBlockComment: | |
if FirstPos == -1: | |
FirstPos = StartPos | |
for Index in range(StartPos, EndPos+1): | |
LineList[Index] = '' | |
FindEdkBlockComment = False | |
elif Line.find("//") != -1 and not Line.startswith("#"): | |
# | |
# handling cpp style comment | |
# | |
LineList[Count] = Line.replace("//", '#') | |
if FirstPos == -1: | |
FirstPos = Count | |
Count = Count + 1 | |
return LineList, FirstPos | |
## GetLibInstanceInfo | |
# | |
# Get the information from Library Instance INF file. | |
# | |
# @param string. A string start with # and followed by INF file path | |
# @param WorkSpace. The WorkSpace directory used to combined with INF file path. | |
# | |
# @return GUID, Version | |
def GetLibInstanceInfo(String, WorkSpace, LineNo): | |
FileGuidString = "" | |
VerString = "" | |
OriginalString = String | |
String = String.strip() | |
if not String: | |
return None, None | |
# | |
# Remove "#" characters at the beginning | |
# | |
String = GetHelpStringByRemoveHashKey(String) | |
String = String.strip() | |
# | |
# Validate file name exist. | |
# | |
FullFileName = os.path.normpath(os.path.realpath(os.path.join(WorkSpace, String))) | |
if not (ValidFile(FullFileName)): | |
Logger.Error("InfParser", | |
ToolError.FORMAT_INVALID, | |
ST.ERR_FILELIST_EXIST % (String), | |
File=GlobalData.gINF_MODULE_NAME, | |
Line=LineNo, | |
ExtraData=OriginalString) | |
# | |
# Validate file exist/format. | |
# | |
if IsValidPath(String, WorkSpace): | |
IsValidFileFlag = True | |
else: | |
Logger.Error("InfParser", | |
ToolError.FORMAT_INVALID, | |
ST.ERR_INF_PARSER_FILE_NOT_EXIST_OR_NAME_INVALID % (String), | |
File=GlobalData.gINF_MODULE_NAME, | |
Line=LineNo, | |
ExtraData=OriginalString) | |
return False | |
if IsValidFileFlag: | |
FileLinesList = [] | |
try: | |
FInputfile = open(FullFileName, "r") | |
try: | |
FileLinesList = FInputfile.readlines() | |
except BaseException: | |
Logger.Error("InfParser", | |
ToolError.FILE_READ_FAILURE, | |
ST.ERR_FILE_OPEN_FAILURE, | |
File=FullFileName) | |
finally: | |
FInputfile.close() | |
except BaseException: | |
Logger.Error("InfParser", | |
ToolError.FILE_READ_FAILURE, | |
ST.ERR_FILE_OPEN_FAILURE, | |
File=FullFileName) | |
ReFileGuidPattern = re.compile("^\s*FILE_GUID\s*=.*$") | |
ReVerStringPattern = re.compile("^\s*VERSION_STRING\s*=.*$") | |
FileLinesList = ProcessLineExtender(FileLinesList) | |
for Line in FileLinesList: | |
if ReFileGuidPattern.match(Line): | |
FileGuidString = Line | |
if ReVerStringPattern.match(Line): | |
VerString = Line | |
if FileGuidString: | |
FileGuidString = GetSplitValueList(FileGuidString, '=', 1)[1] | |
if VerString: | |
VerString = GetSplitValueList(VerString, '=', 1)[1] | |
return FileGuidString, VerString | |
## GetLocalValue | |
# | |
# Generate the local value for INF and DEC file. If Lang attribute not present, then use this value. | |
# If present, and there is no element without the Lang attribute, and one of the elements has the rfc1766 code is | |
# "en-x-tianocore", or "en-US" if "en-x-tianocore" was not found, or "en" if "en-US" was not found, or startswith 'en' | |
# if 'en' was not found, then use this value. | |
# If multiple entries of a tag exist which have the same language code, use the last entry. | |
# | |
# @param ValueList A list need to be processed. | |
# @param UseFirstValue: True to use the first value, False to use the last value | |
# | |
# @return LocalValue | |
def GetLocalValue(ValueList, UseFirstValue=False): | |
Value1 = '' | |
Value2 = '' | |
Value3 = '' | |
Value4 = '' | |
Value5 = '' | |
for (Key, Value) in ValueList: | |
if Key == TAB_LANGUAGE_EN_X: | |
if UseFirstValue: | |
if not Value1: | |
Value1 = Value | |
else: | |
Value1 = Value | |
if Key == TAB_LANGUAGE_EN_US: | |
if UseFirstValue: | |
if not Value2: | |
Value2 = Value | |
else: | |
Value2 = Value | |
if Key == TAB_LANGUAGE_EN: | |
if UseFirstValue: | |
if not Value3: | |
Value3 = Value | |
else: | |
Value3 = Value | |
if Key.startswith(TAB_LANGUAGE_EN): | |
if UseFirstValue: | |
if not Value4: | |
Value4 = Value | |
else: | |
Value4 = Value | |
if Key == '': | |
if UseFirstValue: | |
if not Value5: | |
Value5 = Value | |
else: | |
Value5 = Value | |
if Value1: | |
return Value1 | |
if Value2: | |
return Value2 | |
if Value3: | |
return Value3 | |
if Value4: | |
return Value4 | |
if Value5: | |
return Value5 | |
return '' | |
## GetCharIndexOutStr | |
# | |
# Get comment character index outside a string | |
# | |
# @param Line: The string to be checked | |
# @param CommentCharacter: Comment char, used to ignore comment content | |
# | |
# @retval Index | |
# | |
def GetCharIndexOutStr(CommentCharacter, Line): | |
# | |
# remove whitespace | |
# | |
Line = Line.strip() | |
# | |
# Check whether comment character is in a string | |
# | |
InString = False | |
for Index in range(0, len(Line)): | |
if Line[Index] == '"': | |
InString = not InString | |
elif Line[Index] == CommentCharacter and InString : | |
pass | |
elif Line[Index] == CommentCharacter and (Index +1) < len(Line) and Line[Index+1] == CommentCharacter \ | |
and not InString : | |
return Index | |
return -1 | |
## ValidateUNIFilePath | |
# | |
# Check the UNI file path | |
# | |
# @param FilePath: The UNI file path | |
# | |
def ValidateUNIFilePath(Path): | |
Suffix = Path[Path.rfind(TAB_SPLIT):] | |
# | |
# Check if the suffix is one of the '.uni', '.UNI', '.Uni' | |
# | |
if Suffix not in TAB_UNI_FILE_SUFFIXS: | |
Logger.Error("Unicode File Parser", | |
ToolError.FORMAT_INVALID, | |
Message=ST.ERR_UNI_FILE_SUFFIX_WRONG, | |
ExtraData=Path) | |
# | |
# Check if '..' in the file name(without suffix) | |
# | |
if (TAB_SPLIT + TAB_SPLIT) in Path: | |
Logger.Error("Unicode File Parser", | |
ToolError.FORMAT_INVALID, | |
Message=ST.ERR_UNI_FILE_NAME_INVALID, | |
ExtraData=Path) | |
# | |
# Check if the file name is valid according to the DEC and INF specification | |
# | |
Pattern = '[a-zA-Z0-9_][a-zA-Z0-9_\-\.]*' | |
FileName = Path.replace(Suffix, '') | |
InvalidCh = re.sub(Pattern, '', FileName) | |
if InvalidCh: | |
Logger.Error("Unicode File Parser", | |
ToolError.FORMAT_INVALID, | |
Message=ST.ERR_INF_PARSER_FILE_NOT_EXIST_OR_NAME_INVALID, | |
ExtraData=Path) | |