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