1## @file 2# Common routines used by all tools 3# 4# Copyright (c) 2011 - 2019, Intel Corporation. All rights reserved.<BR> 5# 6# SPDX-License-Identifier: BSD-2-Clause-Patent 7# 8 9''' 10Misc 11''' 12 13## 14# Import Modules 15# 16import os.path 17from os import access 18from os import F_OK 19from os import makedirs 20from os import getcwd 21from os import chdir 22from os import listdir 23from os import remove 24from os import rmdir 25from os import linesep 26from os import walk 27from os import environ 28import re 29from collections import OrderedDict as Sdict 30 31import Logger.Log as Logger 32from Logger import StringTable as ST 33from Logger import ToolError 34from Library import GlobalData 35from Library.DataType import SUP_MODULE_LIST 36from Library.DataType import END_OF_LINE 37from Library.DataType import TAB_SPLIT 38from Library.DataType import TAB_LANGUAGE_EN_US 39from Library.DataType import TAB_LANGUAGE_EN 40from Library.DataType import TAB_LANGUAGE_EN_X 41from Library.DataType import TAB_UNI_FILE_SUFFIXS 42from Library.StringUtils import GetSplitValueList 43from Library.ParserValidate import IsValidHexVersion 44from Library.ParserValidate import IsValidPath 45from Object.POM.CommonObject import TextObject 46from Core.FileHook import __FileHookOpen__ 47from Common.MultipleWorkspace import MultipleWorkspace as mws 48 49## Convert GUID string in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx style to C 50# structure style 51# 52# @param Guid: The GUID string 53# 54def GuidStringToGuidStructureString(Guid): 55 GuidList = Guid.split('-') 56 Result = '{' 57 for Index in range(0, 3, 1): 58 Result = Result + '0x' + GuidList[Index] + ', ' 59 Result = Result + '{0x' + GuidList[3][0:2] + ', 0x' + GuidList[3][2:4] 60 for Index in range(0, 12, 2): 61 Result = Result + ', 0x' + GuidList[4][Index:Index + 2] 62 Result += '}}' 63 return Result 64 65## Check whether GUID string is of format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 66# 67# @param GuidValue: The GUID value 68# 69def CheckGuidRegFormat(GuidValue): 70 ## Regular expression used to find out register format of GUID 71 # 72 RegFormatGuidPattern = re.compile("^\s*([0-9a-fA-F]){8}-" 73 "([0-9a-fA-F]){4}-" 74 "([0-9a-fA-F]){4}-" 75 "([0-9a-fA-F]){4}-" 76 "([0-9a-fA-F]){12}\s*$") 77 78 if RegFormatGuidPattern.match(GuidValue): 79 return True 80 else: 81 return False 82 83 84## Convert GUID string in C structure style to 85# xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 86# 87# @param GuidValue: The GUID value in C structure format 88# 89def GuidStructureStringToGuidString(GuidValue): 90 GuidValueString = GuidValue.lower().replace("{", "").replace("}", "").\ 91 replace(" ", "").replace(";", "") 92 GuidValueList = GuidValueString.split(",") 93 if len(GuidValueList) != 11: 94 return '' 95 try: 96 return "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % ( 97 int(GuidValueList[0], 16), 98 int(GuidValueList[1], 16), 99 int(GuidValueList[2], 16), 100 int(GuidValueList[3], 16), 101 int(GuidValueList[4], 16), 102 int(GuidValueList[5], 16), 103 int(GuidValueList[6], 16), 104 int(GuidValueList[7], 16), 105 int(GuidValueList[8], 16), 106 int(GuidValueList[9], 16), 107 int(GuidValueList[10], 16) 108 ) 109 except BaseException: 110 return '' 111 112## Create directories 113# 114# @param Directory: The directory name 115# 116def CreateDirectory(Directory): 117 if Directory is None or Directory.strip() == "": 118 return True 119 try: 120 if not access(Directory, F_OK): 121 makedirs(Directory) 122 except BaseException: 123 return False 124 return True 125 126## Remove directories, including files and sub-directories in it 127# 128# @param Directory: The directory name 129# 130def RemoveDirectory(Directory, Recursively=False): 131 if Directory is None or Directory.strip() == "" or not \ 132 os.path.exists(Directory): 133 return 134 if Recursively: 135 CurrentDirectory = getcwd() 136 chdir(Directory) 137 for File in listdir("."): 138 if os.path.isdir(File): 139 RemoveDirectory(File, Recursively) 140 else: 141 remove(File) 142 chdir(CurrentDirectory) 143 rmdir(Directory) 144 145## Store content in file 146# 147# This method is used to save file only when its content is changed. This is 148# quite useful for "make" system to decide what will be re-built and what 149# won't. 150# 151# @param File: The path of file 152# @param Content: The new content of the file 153# @param IsBinaryFile: The flag indicating if the file is binary file 154# or not 155# 156def SaveFileOnChange(File, Content, IsBinaryFile=True): 157 if os.path.exists(File): 158 if IsBinaryFile: 159 try: 160 if Content == __FileHookOpen__(File, "rb").read(): 161 return False 162 except BaseException: 163 Logger.Error(None, ToolError.FILE_OPEN_FAILURE, ExtraData=File) 164 else: 165 try: 166 if Content == __FileHookOpen__(File, "r").read(): 167 return False 168 except BaseException: 169 Logger.Error(None, ToolError.FILE_OPEN_FAILURE, ExtraData=File) 170 171 CreateDirectory(os.path.dirname(File)) 172 if IsBinaryFile: 173 try: 174 FileFd = __FileHookOpen__(File, "wb") 175 FileFd.write(Content) 176 FileFd.close() 177 except BaseException: 178 Logger.Error(None, ToolError.FILE_CREATE_FAILURE, ExtraData=File) 179 else: 180 try: 181 FileFd = __FileHookOpen__(File, "w") 182 FileFd.write(Content) 183 FileFd.close() 184 except BaseException: 185 Logger.Error(None, ToolError.FILE_CREATE_FAILURE, ExtraData=File) 186 187 return True 188 189## Get all files of a directory 190# 191# @param Root: Root dir 192# @param SkipList : The files need be skipped 193# 194def GetFiles(Root, SkipList=None, FullPath=True): 195 OriPath = os.path.normpath(Root) 196 FileList = [] 197 for Root, Dirs, Files in walk(Root): 198 if SkipList: 199 for Item in SkipList: 200 if Item in Dirs: 201 Dirs.remove(Item) 202 if Item in Files: 203 Files.remove(Item) 204 for Dir in Dirs: 205 if Dir.startswith('.'): 206 Dirs.remove(Dir) 207 208 for File in Files: 209 if File.startswith('.'): 210 continue 211 File = os.path.normpath(os.path.join(Root, File)) 212 if not FullPath: 213 File = File[len(OriPath) + 1:] 214 FileList.append(File) 215 216 return FileList 217 218## Get all non-metadata files of a directory 219# 220# @param Root: Root Dir 221# @param SkipList : List of path need be skipped 222# @param FullPath: True if the returned file should be full path 223# @param PrefixPath: the path that need to be added to the files found 224# @return: the list of files found 225# 226def GetNonMetaDataFiles(Root, SkipList, FullPath, PrefixPath): 227 FileList = GetFiles(Root, SkipList, FullPath) 228 NewFileList = [] 229 for File in FileList: 230 ExtName = os.path.splitext(File)[1] 231 # 232 # skip '.dec', '.inf', '.dsc', '.fdf' files 233 # 234 if ExtName.lower() not in ['.dec', '.inf', '.dsc', '.fdf']: 235 NewFileList.append(os.path.normpath(os.path.join(PrefixPath, File))) 236 237 return NewFileList 238 239## Check if given file exists or not 240# 241# @param File: File name or path to be checked 242# @param Dir: The directory the file is relative to 243# 244def ValidFile(File, Ext=None): 245 File = File.replace('\\', '/') 246 if Ext is not None: 247 FileExt = os.path.splitext(File)[1] 248 if FileExt.lower() != Ext.lower(): 249 return False 250 if not os.path.exists(File): 251 return False 252 return True 253 254## RealPath 255# 256# @param File: File name or path to be checked 257# @param Dir: The directory the file is relative to 258# @param OverrideDir: The override directory 259# 260def RealPath(File, Dir='', OverrideDir=''): 261 NewFile = os.path.normpath(os.path.join(Dir, File)) 262 NewFile = GlobalData.gALL_FILES[NewFile] 263 if not NewFile and OverrideDir: 264 NewFile = os.path.normpath(os.path.join(OverrideDir, File)) 265 NewFile = GlobalData.gALL_FILES[NewFile] 266 return NewFile 267 268## RealPath2 269# 270# @param File: File name or path to be checked 271# @param Dir: The directory the file is relative to 272# @param OverrideDir: The override directory 273# 274def RealPath2(File, Dir='', OverrideDir=''): 275 if OverrideDir: 276 NewFile = GlobalData.gALL_FILES[os.path.normpath(os.path.join\ 277 (OverrideDir, File))] 278 if NewFile: 279 if OverrideDir[-1] == os.path.sep: 280 return NewFile[len(OverrideDir):], NewFile[0:len(OverrideDir)] 281 else: 282 return NewFile[len(OverrideDir) + 1:], \ 283 NewFile[0:len(OverrideDir)] 284 285 NewFile = GlobalData.gALL_FILES[os.path.normpath(os.path.join(Dir, File))] 286 if NewFile: 287 if Dir: 288 if Dir[-1] == os.path.sep: 289 return NewFile[len(Dir):], NewFile[0:len(Dir)] 290 else: 291 return NewFile[len(Dir) + 1:], NewFile[0:len(Dir)] 292 else: 293 return NewFile, '' 294 295 return None, None 296 297## CommonPath 298# 299# @param PathList: PathList 300# 301def CommonPath(PathList): 302 Path1 = min(PathList).split(os.path.sep) 303 Path2 = max(PathList).split(os.path.sep) 304 for Index in range(min(len(Path1), len(Path2))): 305 if Path1[Index] != Path2[Index]: 306 return os.path.sep.join(Path1[:Index]) 307 return os.path.sep.join(Path1) 308 309## PathClass 310# 311class PathClass(object): 312 def __init__(self, File='', Root='', AlterRoot='', Type='', IsBinary=False, 313 Arch='COMMON', ToolChainFamily='', Target='', TagName='', \ 314 ToolCode=''): 315 self.Arch = Arch 316 self.File = str(File) 317 if os.path.isabs(self.File): 318 self.Root = '' 319 self.AlterRoot = '' 320 else: 321 self.Root = str(Root) 322 self.AlterRoot = str(AlterRoot) 323 324 # 325 # Remove any '.' and '..' in path 326 # 327 if self.Root: 328 self.Path = os.path.normpath(os.path.join(self.Root, self.File)) 329 self.Root = os.path.normpath(CommonPath([self.Root, self.Path])) 330 # 331 # eliminate the side-effect of 'C:' 332 # 333 if self.Root[-1] == ':': 334 self.Root += os.path.sep 335 # 336 # file path should not start with path separator 337 # 338 if self.Root[-1] == os.path.sep: 339 self.File = self.Path[len(self.Root):] 340 else: 341 self.File = self.Path[len(self.Root) + 1:] 342 else: 343 self.Path = os.path.normpath(self.File) 344 345 self.SubDir, self.Name = os.path.split(self.File) 346 self.BaseName, self.Ext = os.path.splitext(self.Name) 347 348 if self.Root: 349 if self.SubDir: 350 self.Dir = os.path.join(self.Root, self.SubDir) 351 else: 352 self.Dir = self.Root 353 else: 354 self.Dir = self.SubDir 355 356 if IsBinary: 357 self.Type = Type 358 else: 359 self.Type = self.Ext.lower() 360 361 self.IsBinary = IsBinary 362 self.Target = Target 363 self.TagName = TagName 364 self.ToolCode = ToolCode 365 self.ToolChainFamily = ToolChainFamily 366 367 self._Key = None 368 369 ## Convert the object of this class to a string 370 # 371 # Convert member Path of the class to a string 372 # 373 def __str__(self): 374 return self.Path 375 376 ## Override __eq__ function 377 # 378 # Check whether PathClass are the same 379 # 380 def __eq__(self, Other): 381 if isinstance(Other, type(self)): 382 return self.Path == Other.Path 383 else: 384 return self.Path == str(Other) 385 386 ## Override __hash__ function 387 # 388 # Use Path as key in hash table 389 # 390 def __hash__(self): 391 return hash(self.Path) 392 393 ## _GetFileKey 394 # 395 def _GetFileKey(self): 396 if self._Key is None: 397 self._Key = self.Path.upper() 398 return self._Key 399 ## Validate 400 # 401 def Validate(self, Type='', CaseSensitive=True): 402 if GlobalData.gCASE_INSENSITIVE: 403 CaseSensitive = False 404 if Type and Type.lower() != self.Type: 405 return ToolError.FILE_TYPE_MISMATCH, '%s (expect %s but got %s)' % \ 406 (self.File, Type, self.Type) 407 408 RealFile, RealRoot = RealPath2(self.File, self.Root, self.AlterRoot) 409 if not RealRoot and not RealFile: 410 RealFile = self.File 411 if self.AlterRoot: 412 RealFile = os.path.join(self.AlterRoot, self.File) 413 elif self.Root: 414 RealFile = os.path.join(self.Root, self.File) 415 return ToolError.FILE_NOT_FOUND, os.path.join(self.AlterRoot, RealFile) 416 417 ErrorCode = 0 418 ErrorInfo = '' 419 if RealRoot != self.Root or RealFile != self.File: 420 if CaseSensitive and (RealFile != self.File or \ 421 (RealRoot != self.Root and RealRoot != \ 422 self.AlterRoot)): 423 ErrorCode = ToolError.FILE_CASE_MISMATCH 424 ErrorInfo = self.File + '\n\t' + RealFile + \ 425 " [in file system]" 426 427 self.SubDir, self.Name = os.path.split(RealFile) 428 self.BaseName, self.Ext = os.path.splitext(self.Name) 429 if self.SubDir: 430 self.Dir = os.path.join(RealRoot, self.SubDir) 431 else: 432 self.Dir = RealRoot 433 self.File = RealFile 434 self.Root = RealRoot 435 self.Path = os.path.join(RealRoot, RealFile) 436 return ErrorCode, ErrorInfo 437 438 Key = property(_GetFileKey) 439 440## Get current workspace 441# 442# get WORKSPACE from environment variable if present,if not use current working directory as WORKSPACE 443# 444def GetWorkspace(): 445 # 446 # check WORKSPACE 447 # 448 if "WORKSPACE" in environ: 449 WorkspaceDir = os.path.normpath(environ["WORKSPACE"]) 450 if not os.path.exists(WorkspaceDir): 451 Logger.Error("UPT", 452 ToolError.UPT_ENVIRON_MISSING_ERROR, 453 ST.ERR_WORKSPACE_NOTEXIST, 454 ExtraData="%s" % WorkspaceDir) 455 else: 456 WorkspaceDir = os.getcwd() 457 458 if WorkspaceDir[-1] == ':': 459 WorkspaceDir += os.sep 460 461 PackagesPath = os.environ.get("PACKAGES_PATH") 462 mws.setWs(WorkspaceDir, PackagesPath) 463 464 return WorkspaceDir, mws.PACKAGES_PATH 465 466## Get relative path 467# 468# use full path and workspace to get relative path 469# the destination of this function is mainly to resolve the root path issue(like c: or c:\) 470# 471# @param Fullpath: a string of fullpath 472# @param Workspace: a string of workspace 473# 474def GetRelativePath(Fullpath, Workspace): 475 476 RelativePath = '' 477 if Workspace.endswith(os.sep): 478 RelativePath = Fullpath[Fullpath.upper().find(Workspace.upper())+len(Workspace):] 479 else: 480 RelativePath = Fullpath[Fullpath.upper().find(Workspace.upper())+len(Workspace)+1:] 481 482 return RelativePath 483 484## Check whether all module types are in list 485# 486# check whether all module types (SUP_MODULE_LIST) are in list 487# 488# @param ModuleList: a list of ModuleType 489# 490def IsAllModuleList(ModuleList): 491 NewModuleList = [Module.upper() for Module in ModuleList] 492 for Module in SUP_MODULE_LIST: 493 if Module not in NewModuleList: 494 return False 495 else: 496 return True 497 498## Dictionary that use comment(GenericComment, TailComment) as value, 499# if a new comment which key already in the dic is inserted, then the 500# comment will be merged. 501# Key is (Statement, SupArch), when TailComment is added, it will ident 502# according to Statement 503# 504class MergeCommentDict(dict): 505 ## []= operator 506 # 507 def __setitem__(self, Key, CommentVal): 508 GenericComment, TailComment = CommentVal 509 if Key in self: 510 OrigVal1, OrigVal2 = dict.__getitem__(self, Key) 511 Statement = Key[0] 512 dict.__setitem__(self, Key, (OrigVal1 + GenericComment, OrigVal2 \ 513 + len(Statement) * ' ' + TailComment)) 514 else: 515 dict.__setitem__(self, Key, (GenericComment, TailComment)) 516 517 ## =[] operator 518 # 519 def __getitem__(self, Key): 520 return dict.__getitem__(self, Key) 521 522 523## GenDummyHelpTextObj 524# 525# @retval HelpTxt: Generated dummy help text object 526# 527def GenDummyHelpTextObj(): 528 HelpTxt = TextObject() 529 HelpTxt.SetLang(TAB_LANGUAGE_EN_US) 530 HelpTxt.SetString(' ') 531 return HelpTxt 532 533## ConvertVersionToDecimal, the minor version should be within 0 - 99 534# <HexVersion> ::= "0x" <Major> <Minor> 535# <Major> ::= (a-fA-F0-9){4} 536# <Minor> ::= (a-fA-F0-9){4} 537# <DecVersion> ::= (0-65535) ["." (0-99)] 538# 539# @param StringIn: The string contains version defined in INF file. 540# It can be Decimal or Hex 541# 542def ConvertVersionToDecimal(StringIn): 543 if IsValidHexVersion(StringIn): 544 Value = int(StringIn, 16) 545 Major = Value >> 16 546 Minor = Value & 0xFFFF 547 MinorStr = str(Minor) 548 if len(MinorStr) == 1: 549 MinorStr = '0' + MinorStr 550 return str(Major) + '.' + MinorStr 551 else: 552 if StringIn.find(TAB_SPLIT) != -1: 553 return StringIn 554 elif StringIn: 555 return StringIn + '.0' 556 else: 557 # 558 # when StringIn is '', return it directly 559 # 560 return StringIn 561 562## GetHelpStringByRemoveHashKey 563# 564# Remove hash key at the header of string and return the remain. 565# 566# @param String: The string need to be processed. 567# 568def GetHelpStringByRemoveHashKey(String): 569 ReturnString = '' 570 PattenRemoveHashKey = re.compile(r"^[#+\s]+", re.DOTALL) 571 String = String.strip() 572 if String == '': 573 return String 574 575 LineList = GetSplitValueList(String, END_OF_LINE) 576 for Line in LineList: 577 ValueList = PattenRemoveHashKey.split(Line) 578 if len(ValueList) == 1: 579 ReturnString += ValueList[0] + END_OF_LINE 580 else: 581 ReturnString += ValueList[1] + END_OF_LINE 582 583 if ReturnString.endswith('\n') and not ReturnString.endswith('\n\n') and ReturnString != '\n': 584 ReturnString = ReturnString[:-1] 585 586 return ReturnString 587 588## ConvPathFromAbsToRel 589# 590# Get relative file path from absolute path. 591# 592# @param Path: The string contain file absolute path. 593# @param Root: The string contain the parent path of Path in. 594# 595# 596def ConvPathFromAbsToRel(Path, Root): 597 Path = os.path.normpath(Path) 598 Root = os.path.normpath(Root) 599 FullPath = os.path.normpath(os.path.join(Root, Path)) 600 601 # 602 # If Path is absolute path. 603 # It should be in Root. 604 # 605 if os.path.isabs(Path): 606 return FullPath[FullPath.find(Root) + len(Root) + 1:] 607 608 else: 609 return Path 610 611## ConvertPath 612# 613# Convert special characters to '_', '\' to '/' 614# return converted path: Test!1.inf -> Test_1.inf 615# 616# @param Path: Path to be converted 617# 618def ConvertPath(Path): 619 RetPath = '' 620 for Char in Path.strip(): 621 if Char.isalnum() or Char in '.-_/': 622 RetPath = RetPath + Char 623 elif Char == '\\': 624 RetPath = RetPath + '/' 625 else: 626 RetPath = RetPath + '_' 627 return RetPath 628 629## ConvertSpec 630# 631# during install, convert the Spec string extract from UPD into INF allowable definition, 632# the difference is period is allowed in the former (not the first letter) but not in the latter. 633# return converted Spec string 634# 635# @param SpecStr: SpecStr to be converted 636# 637def ConvertSpec(SpecStr): 638 RetStr = '' 639 for Char in SpecStr: 640 if Char.isalnum() or Char == '_': 641 RetStr = RetStr + Char 642 else: 643 RetStr = RetStr + '_' 644 645 return RetStr 646 647 648## IsEqualList 649# 650# Judge two lists are identical(contain same item). 651# The rule is elements in List A are in List B and elements in List B are in List A. 652# 653# @param ListA, ListB Lists need to be judged. 654# 655# @return True ListA and ListB are identical 656# @return False ListA and ListB are different with each other 657# 658def IsEqualList(ListA, ListB): 659 if ListA == ListB: 660 return True 661 662 for ItemA in ListA: 663 if not ItemA in ListB: 664 return False 665 666 for ItemB in ListB: 667 if not ItemB in ListA: 668 return False 669 670 return True 671 672## ConvertArchList 673# 674# Convert item in ArchList if the start character is lower case. 675# In UDP spec, Arch is only allowed as: [A-Z]([a-zA-Z0-9])* 676# 677# @param ArchList The ArchList need to be converted. 678# 679# @return NewList The ArchList been converted. 680# 681def ConvertArchList(ArchList): 682 NewArchList = [] 683 if not ArchList: 684 return NewArchList 685 686 if isinstance(ArchList, list): 687 for Arch in ArchList: 688 Arch = Arch.upper() 689 NewArchList.append(Arch) 690 elif isinstance(ArchList, str): 691 ArchList = ArchList.upper() 692 NewArchList.append(ArchList) 693 694 return NewArchList 695 696## ProcessLineExtender 697# 698# Process the LineExtender of Line in LineList. 699# If one line ends with a line extender, then it will be combined together with next line. 700# 701# @param LineList The LineList need to be processed. 702# 703# @return NewList The ArchList been processed. 704# 705def ProcessLineExtender(LineList): 706 NewList = [] 707 Count = 0 708 while Count < len(LineList): 709 if LineList[Count].strip().endswith("\\") and Count + 1 < len(LineList): 710 NewList.append(LineList[Count].strip()[:-2] + LineList[Count + 1]) 711 Count = Count + 1 712 else: 713 NewList.append(LineList[Count]) 714 715 Count = Count + 1 716 717 return NewList 718 719## ProcessEdkComment 720# 721# Process EDK style comment in LineList: c style /* */ comment or cpp style // comment 722# 723# 724# @param LineList The LineList need to be processed. 725# 726# @return LineList The LineList been processed. 727# @return FirstPos Where Edk comment is first found, -1 if not found 728# 729def ProcessEdkComment(LineList): 730 FindEdkBlockComment = False 731 Count = 0 732 StartPos = -1 733 EndPos = -1 734 FirstPos = -1 735 736 while(Count < len(LineList)): 737 Line = LineList[Count].strip() 738 if Line.startswith("/*"): 739 # 740 # handling c style comment 741 # 742 StartPos = Count 743 while Count < len(LineList): 744 Line = LineList[Count].strip() 745 if Line.endswith("*/"): 746 if (Count == StartPos) and Line.strip() == '/*/': 747 Count = Count + 1 748 continue 749 EndPos = Count 750 FindEdkBlockComment = True 751 break 752 Count = Count + 1 753 754 if FindEdkBlockComment: 755 if FirstPos == -1: 756 FirstPos = StartPos 757 for Index in range(StartPos, EndPos+1): 758 LineList[Index] = '' 759 FindEdkBlockComment = False 760 elif Line.find("//") != -1 and not Line.startswith("#"): 761 # 762 # handling cpp style comment 763 # 764 LineList[Count] = Line.replace("//", '#') 765 if FirstPos == -1: 766 FirstPos = Count 767 768 Count = Count + 1 769 770 return LineList, FirstPos 771 772## GetLibInstanceInfo 773# 774# Get the information from Library Instance INF file. 775# 776# @param string. A string start with # and followed by INF file path 777# @param WorkSpace. The WorkSpace directory used to combined with INF file path. 778# 779# @return GUID, Version 780def GetLibInstanceInfo(String, WorkSpace, LineNo): 781 782 FileGuidString = "" 783 VerString = "" 784 785 OriginalString = String 786 String = String.strip() 787 if not String: 788 return None, None 789 # 790 # Remove "#" characters at the beginning 791 # 792 String = GetHelpStringByRemoveHashKey(String) 793 String = String.strip() 794 795 # 796 # Validate file name exist. 797 # 798 FullFileName = os.path.normpath(os.path.realpath(os.path.join(WorkSpace, String))) 799 if not (ValidFile(FullFileName)): 800 Logger.Error("InfParser", 801 ToolError.FORMAT_INVALID, 802 ST.ERR_FILELIST_EXIST % (String), 803 File=GlobalData.gINF_MODULE_NAME, 804 Line=LineNo, 805 ExtraData=OriginalString) 806 807 # 808 # Validate file exist/format. 809 # 810 if IsValidPath(String, WorkSpace): 811 IsValidFileFlag = True 812 else: 813 Logger.Error("InfParser", 814 ToolError.FORMAT_INVALID, 815 ST.ERR_INF_PARSER_FILE_NOT_EXIST_OR_NAME_INVALID % (String), 816 File=GlobalData.gINF_MODULE_NAME, 817 Line=LineNo, 818 ExtraData=OriginalString) 819 return False 820 if IsValidFileFlag: 821 FileLinesList = [] 822 823 try: 824 FInputfile = open(FullFileName, "r") 825 try: 826 FileLinesList = FInputfile.readlines() 827 except BaseException: 828 Logger.Error("InfParser", 829 ToolError.FILE_READ_FAILURE, 830 ST.ERR_FILE_OPEN_FAILURE, 831 File=FullFileName) 832 finally: 833 FInputfile.close() 834 except BaseException: 835 Logger.Error("InfParser", 836 ToolError.FILE_READ_FAILURE, 837 ST.ERR_FILE_OPEN_FAILURE, 838 File=FullFileName) 839 840 ReFileGuidPattern = re.compile("^\s*FILE_GUID\s*=.*$") 841 ReVerStringPattern = re.compile("^\s*VERSION_STRING\s*=.*$") 842 843 FileLinesList = ProcessLineExtender(FileLinesList) 844 845 for Line in FileLinesList: 846 if ReFileGuidPattern.match(Line): 847 FileGuidString = Line 848 if ReVerStringPattern.match(Line): 849 VerString = Line 850 851 if FileGuidString: 852 FileGuidString = GetSplitValueList(FileGuidString, '=', 1)[1] 853 if VerString: 854 VerString = GetSplitValueList(VerString, '=', 1)[1] 855 856 return FileGuidString, VerString 857 858## GetLocalValue 859# 860# Generate the local value for INF and DEC file. If Lang attribute not present, then use this value. 861# If present, and there is no element without the Lang attribute, and one of the elements has the rfc1766 code is 862# "en-x-tianocore", or "en-US" if "en-x-tianocore" was not found, or "en" if "en-US" was not found, or startswith 'en' 863# if 'en' was not found, then use this value. 864# If multiple entries of a tag exist which have the same language code, use the last entry. 865# 866# @param ValueList A list need to be processed. 867# @param UseFirstValue: True to use the first value, False to use the last value 868# 869# @return LocalValue 870def GetLocalValue(ValueList, UseFirstValue=False): 871 Value1 = '' 872 Value2 = '' 873 Value3 = '' 874 Value4 = '' 875 Value5 = '' 876 for (Key, Value) in ValueList: 877 if Key == TAB_LANGUAGE_EN_X: 878 if UseFirstValue: 879 if not Value1: 880 Value1 = Value 881 else: 882 Value1 = Value 883 if Key == TAB_LANGUAGE_EN_US: 884 if UseFirstValue: 885 if not Value2: 886 Value2 = Value 887 else: 888 Value2 = Value 889 if Key == TAB_LANGUAGE_EN: 890 if UseFirstValue: 891 if not Value3: 892 Value3 = Value 893 else: 894 Value3 = Value 895 if Key.startswith(TAB_LANGUAGE_EN): 896 if UseFirstValue: 897 if not Value4: 898 Value4 = Value 899 else: 900 Value4 = Value 901 if Key == '': 902 if UseFirstValue: 903 if not Value5: 904 Value5 = Value 905 else: 906 Value5 = Value 907 908 if Value1: 909 return Value1 910 if Value2: 911 return Value2 912 if Value3: 913 return Value3 914 if Value4: 915 return Value4 916 if Value5: 917 return Value5 918 919 return '' 920 921 922## GetCharIndexOutStr 923# 924# Get comment character index outside a string 925# 926# @param Line: The string to be checked 927# @param CommentCharacter: Comment char, used to ignore comment content 928# 929# @retval Index 930# 931def GetCharIndexOutStr(CommentCharacter, Line): 932 # 933 # remove whitespace 934 # 935 Line = Line.strip() 936 937 # 938 # Check whether comment character is in a string 939 # 940 InString = False 941 for Index in range(0, len(Line)): 942 if Line[Index] == '"': 943 InString = not InString 944 elif Line[Index] == CommentCharacter and InString : 945 pass 946 elif Line[Index] == CommentCharacter and (Index +1) < len(Line) and Line[Index+1] == CommentCharacter \ 947 and not InString : 948 return Index 949 return -1 950 951## ValidateUNIFilePath 952# 953# Check the UNI file path 954# 955# @param FilePath: The UNI file path 956# 957def ValidateUNIFilePath(Path): 958 Suffix = Path[Path.rfind(TAB_SPLIT):] 959 960 # 961 # Check if the suffix is one of the '.uni', '.UNI', '.Uni' 962 # 963 if Suffix not in TAB_UNI_FILE_SUFFIXS: 964 Logger.Error("Unicode File Parser", 965 ToolError.FORMAT_INVALID, 966 Message=ST.ERR_UNI_FILE_SUFFIX_WRONG, 967 ExtraData=Path) 968 969 # 970 # Check if '..' in the file name(without suffix) 971 # 972 if (TAB_SPLIT + TAB_SPLIT) in Path: 973 Logger.Error("Unicode File Parser", 974 ToolError.FORMAT_INVALID, 975 Message=ST.ERR_UNI_FILE_NAME_INVALID, 976 ExtraData=Path) 977 978 # 979 # Check if the file name is valid according to the DEC and INF specification 980 # 981 Pattern = '[a-zA-Z0-9_][a-zA-Z0-9_\-\.]*' 982 FileName = Path.replace(Suffix, '') 983 InvalidCh = re.sub(Pattern, '', FileName) 984 if InvalidCh: 985 Logger.Error("Unicode File Parser", 986 ToolError.FORMAT_INVALID, 987 Message=ST.ERR_INF_PARSER_FILE_NOT_EXIST_OR_NAME_INVALID, 988 ExtraData=Path) 989 990