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