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