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