]>
Commit | Line | Data |
---|---|---|
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 | |
10 | Misc\r | |
11 | '''\r | |
12 | \r | |
13 | ##\r | |
14 | # Import Modules\r | |
15 | #\r | |
16 | import os.path\r | |
17 | from os import access\r | |
18 | from os import F_OK\r | |
19 | from os import makedirs\r | |
20 | from os import getcwd\r | |
21 | from os import chdir\r | |
22 | from os import listdir\r | |
23 | from os import remove\r | |
24 | from os import rmdir\r | |
25 | from os import linesep\r | |
26 | from os import walk\r | |
27 | from os import environ\r | |
28 | import re\r | |
7da3ed89 | 29 | from collections import OrderedDict as Sdict\r |
f51461c8 LG |
30 | \r |
31 | import Logger.Log as Logger\r | |
32 | from Logger import StringTable as ST\r | |
33 | from Logger import ToolError\r | |
34 | from Library import GlobalData\r | |
35 | from Library.DataType import SUP_MODULE_LIST\r | |
36 | from Library.DataType import END_OF_LINE\r | |
37 | from Library.DataType import TAB_SPLIT\r | |
421ccda3 HC |
38 | from Library.DataType import TAB_LANGUAGE_EN_US\r |
39 | from Library.DataType import TAB_LANGUAGE_EN\r | |
40 | from Library.DataType import TAB_LANGUAGE_EN_X\r | |
41 | from Library.DataType import TAB_UNI_FILE_SUFFIXS\r | |
64285f15 | 42 | from Library.StringUtils import GetSplitValueList\r |
f51461c8 LG |
43 | from Library.ParserValidate import IsValidHexVersion\r |
44 | from Library.ParserValidate import IsValidPath\r | |
45 | from Object.POM.CommonObject import TextObject\r | |
421ccda3 | 46 | from Core.FileHook import __FileHookOpen__\r |
8145b63e | 47 | from 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 | |
54 | def 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 | |
69 | def 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 | |
89 | def 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 | |
116 | def 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 | |
130 | def 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 | |
156 | def 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 | |
194 | def 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 |
226 | def 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 | |
244 | def 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 | |
260 | def 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 | |
274 | def 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 | |
301 | def 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 | |
311 | class 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 | 444 | def 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 | |
474 | def 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 | |
490 | def 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 | |
504 | class 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 | |
527 | def 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 | |
542 | def 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 | |
568 | def 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 | |
596 | def 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 | |
618 | def 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 | |
637 | def 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 | |
658 | def 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 | |
681 | def 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 | |
705 | def 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 | |
729 | def 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 | |
780 | def 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 | |
870 | def 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 | |
931 | def 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 |
957 | def 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 |