]> git.proxmox.com Git - mirror_edk2.git/blame - BaseTools/Source/Python/Common/Misc.py
ShellPkg: acpiview: DBG2: Remove redundant forward declarations
[mirror_edk2.git] / BaseTools / Source / Python / Common / Misc.py
CommitLineData
f51461c8
LG
1## @file\r
2# Common routines used by all tools\r
3#\r
2b95556c 4# Copyright (c) 2007 - 2019, Intel Corporation. All rights reserved.<BR>\r
2e351cbe 5# SPDX-License-Identifier: BSD-2-Clause-Patent\r
f51461c8
LG
6#\r
7\r
8##\r
9# Import Modules\r
10#\r
1ccc4d89 11from __future__ import absolute_import\r
3aaacb2d 12\r
f51461c8
LG
13import sys\r
14import string\r
f51461c8
LG
15import threading\r
16import time\r
17import re\r
3a0c1bf6 18import pickle\r
f51461c8 19import array\r
97fa0ee9 20import shutil\r
2378ea55 21import filecmp\r
d3d97b37 22from random import sample\r
a3251d84 23from struct import pack\r
3aaacb2d
CJ
24import uuid\r
25import subprocess\r
0b836855 26import tempfile\r
3aaacb2d 27from collections import OrderedDict\r
f51461c8 28\r
3aaacb2d 29import Common.LongFilePathOs as os\r
f51461c8
LG
30from Common import EdkLogger as EdkLogger\r
31from Common import GlobalData as GlobalData\r
3aaacb2d
CJ
32from Common.DataType import *\r
33from Common.BuildToolError import *\r
f51461c8 34from CommonDataClass.DataClass import *\r
3aaacb2d 35from Common.Parsing import GetSplitValueList\r
1be2ed90 36from Common.LongFilePathSupport import OpenLongFilePath as open\r
05cc51ad 37from Common.MultipleWorkspace import MultipleWorkspace as mws\r
726c501c 38from CommonDataClass.Exceptions import BadExpression\r
6c204ed4 39from Common.caching import cached_property\r
3aaacb2d 40\r
afe8c411 41ArrayIndex = re.compile("\[\s*[0-9a-fA-FxX]*\s*\]")\r
f51461c8 42## Regular expression used to find out place holders in string template\r
47fea6af 43gPlaceholderPattern = re.compile("\$\{([^$()\s]+)\}", re.MULTILINE | re.UNICODE)\r
f51461c8 44\r
1eb72acd
CJ
45## regular expressions for map file processing\r
46startPatternGeneral = re.compile("^Start[' ']+Length[' ']+Name[' ']+Class")\r
47addressPatternGeneral = re.compile("^Address[' ']+Publics by Value[' ']+Rva\+Base")\r
48valuePatternGcc = re.compile('^([\w_\.]+) +([\da-fA-Fx]+) +([\da-fA-Fx]+)$')\r
49pcdPatternGcc = re.compile('^([\da-fA-Fx]+) +([\da-fA-Fx]+)')\r
50secReGeneral = re.compile('^([\da-fA-F]+):([\da-fA-F]+) +([\da-fA-F]+)[Hh]? +([.\w\$]+) +(\w+)', re.UNICODE)\r
51\r
ff4d0f85
YZ
52StructPattern = re.compile(r'[_a-zA-Z][0-9A-Za-z_]*$')\r
53\r
f51461c8
LG
54## Dictionary used to store dependencies of files\r
55gDependencyDatabase = {} # arch : {file path : [dependent files list]}\r
56\r
2b5c643a
JC
57#\r
58# If a module is built more than once with different PCDs or library classes\r
59# a temporary INF file with same content is created, the temporary file is removed\r
60# when build exits.\r
61#\r
62_TempInfs = []\r
63\r
22a99b87 64def GetVariableOffset(mapfilepath, efifilepath, varnames):\r
f7496d71 65 """ Parse map file to get variable offset in current EFI file\r
22a99b87
YL
66 @param mapfilepath Map file absolution path\r
67 @param efifilepath: EFI binary file full path\r
68 @param varnames iteratable container whose elements are variable names to be searched\r
f7496d71 69\r
22a99b87
YL
70 @return List whos elements are tuple with variable name and raw offset\r
71 """\r
72 lines = []\r
73 try:\r
74 f = open(mapfilepath, 'r')\r
75 lines = f.readlines()\r
76 f.close()\r
77 except:\r
78 return None\r
f7496d71 79\r
22a99b87
YL
80 if len(lines) == 0: return None\r
81 firstline = lines[0].strip()\r
82 if (firstline.startswith("Archive member included ") and\r
83 firstline.endswith(" file (symbol)")):\r
84 return _parseForGCC(lines, efifilepath, varnames)\r
14239ee0
YZ
85 if firstline.startswith("# Path:"):\r
86 return _parseForXcode(lines, efifilepath, varnames)\r
22a99b87
YL
87 return _parseGeneral(lines, efifilepath, varnames)\r
88\r
14239ee0
YZ
89def _parseForXcode(lines, efifilepath, varnames):\r
90 status = 0\r
91 ret = []\r
e52aed0d 92 for line in lines:\r
14239ee0
YZ
93 line = line.strip()\r
94 if status == 0 and line == "# Symbols:":\r
95 status = 1\r
96 continue\r
97 if status == 1 and len(line) != 0:\r
98 for varname in varnames:\r
99 if varname in line:\r
6686d9a1 100 # cannot pregenerate this RegEx since it uses varname from varnames.\r
14239ee0 101 m = re.match('^([\da-fA-FxX]+)([\s\S]*)([_]*%s)$' % varname, line)\r
4231a819 102 if m is not None:\r
14239ee0
YZ
103 ret.append((varname, m.group(1)))\r
104 return ret\r
105\r
22a99b87
YL
106def _parseForGCC(lines, efifilepath, varnames):\r
107 """ Parse map file generated by GCC linker """\r
108 status = 0\r
109 sections = []\r
110 varoffset = []\r
3e7e8571 111 for index, line in enumerate(lines):\r
22a99b87
YL
112 line = line.strip()\r
113 # status machine transection\r
114 if status == 0 and line == "Memory Configuration":\r
115 status = 1\r
116 continue\r
117 elif status == 1 and line == 'Linker script and memory map':\r
118 status = 2\r
119 continue\r
120 elif status ==2 and line == 'START GROUP':\r
121 status = 3\r
122 continue\r
123\r
124 # status handler\r
3e7e8571 125 if status == 3:\r
1eb72acd 126 m = valuePatternGcc.match(line)\r
4231a819 127 if m is not None:\r
22a99b87
YL
128 sections.append(m.groups(0))\r
129 for varname in varnames:\r
d03c056b
YZ
130 Str = ''\r
131 m = re.match("^.data.(%s)" % varname, line)\r
4231a819 132 if m is not None:\r
d03c056b 133 m = re.match(".data.(%s)$" % varname, line)\r
4231a819 134 if m is not None:\r
d03c056b
YZ
135 Str = lines[index + 1]\r
136 else:\r
137 Str = line[len(".data.%s" % varname):]\r
138 if Str:\r
1eb72acd 139 m = pcdPatternGcc.match(Str.strip())\r
4231a819 140 if m is not None:\r
ccaa7754 141 varoffset.append((varname, int(m.groups(0)[0], 16), int(sections[-1][1], 16), sections[-1][0]))\r
22a99b87
YL
142\r
143 if not varoffset:\r
144 return []\r
145 # get section information from efi file\r
146 efisecs = PeImageClass(efifilepath).SectionHeaderList\r
4231a819 147 if efisecs is None or len(efisecs) == 0:\r
22a99b87
YL
148 return []\r
149 #redirection\r
150 redirection = 0\r
151 for efisec in efisecs:\r
152 for section in sections:\r
153 if section[0].strip() == efisec[0].strip() and section[0].strip() == '.text':\r
154 redirection = int(section[1], 16) - efisec[1]\r
155\r
156 ret = []\r
157 for var in varoffset:\r
158 for efisec in efisecs:\r
159 if var[1] >= efisec[1] and var[1] < efisec[1]+efisec[3]:\r
160 ret.append((var[0], hex(efisec[2] + var[1] - efisec[1] - redirection)))\r
161 return ret\r
162\r
163def _parseGeneral(lines, efifilepath, varnames):\r
164 status = 0 #0 - beginning of file; 1 - PE section definition; 2 - symbol table\r
165 secs = [] # key = section name\r
166 varoffset = []\r
22a99b87
YL
167 symRe = re.compile('^([\da-fA-F]+):([\da-fA-F]+) +([\.:\\\\\w\?@\$]+) +([\da-fA-F]+)', re.UNICODE)\r
168\r
169 for line in lines:\r
170 line = line.strip()\r
1eb72acd 171 if startPatternGeneral.match(line):\r
22a99b87
YL
172 status = 1\r
173 continue\r
1eb72acd 174 if addressPatternGeneral.match(line):\r
22a99b87
YL
175 status = 2\r
176 continue\r
6686d9a1 177 if line.startswith("entry point at"):\r
22a99b87 178 status = 3\r
f7496d71 179 continue\r
22a99b87 180 if status == 1 and len(line) != 0:\r
1eb72acd 181 m = secReGeneral.match(line)\r
4231a819 182 assert m is not None, "Fail to parse the section in map file , line is %s" % line\r
22a99b87
YL
183 sec_no, sec_start, sec_length, sec_name, sec_class = m.groups(0)\r
184 secs.append([int(sec_no, 16), int(sec_start, 16), int(sec_length, 16), sec_name, sec_class])\r
185 if status == 2 and len(line) != 0:\r
186 for varname in varnames:\r
187 m = symRe.match(line)\r
4231a819 188 assert m is not None, "Fail to parse the symbol in map file, line is %s" % line\r
22a99b87
YL
189 sec_no, sym_offset, sym_name, vir_addr = m.groups(0)\r
190 sec_no = int(sec_no, 16)\r
191 sym_offset = int(sym_offset, 16)\r
192 vir_addr = int(vir_addr, 16)\r
6686d9a1 193 # cannot pregenerate this RegEx since it uses varname from varnames.\r
22a99b87 194 m2 = re.match('^[_]*(%s)' % varname, sym_name)\r
4231a819 195 if m2 is not None:\r
22a99b87
YL
196 # fond a binary pcd entry in map file\r
197 for sec in secs:\r
198 if sec[0] == sec_no and (sym_offset >= sec[1] and sym_offset < sec[1] + sec[2]):\r
199 varoffset.append([varname, sec[3], sym_offset, vir_addr, sec_no])\r
200\r
201 if not varoffset: return []\r
202\r
203 # get section information from efi file\r
204 efisecs = PeImageClass(efifilepath).SectionHeaderList\r
4231a819 205 if efisecs is None or len(efisecs) == 0:\r
22a99b87
YL
206 return []\r
207\r
208 ret = []\r
209 for var in varoffset:\r
210 index = 0\r
211 for efisec in efisecs:\r
212 index = index + 1\r
213 if var[1].strip() == efisec[0].strip():\r
214 ret.append((var[0], hex(efisec[2] + var[2])))\r
215 elif var[4] == index:\r
216 ret.append((var[0], hex(efisec[2] + var[2])))\r
217\r
218 return ret\r
219\r
97fa0ee9
YL
220## Routine to process duplicated INF\r
221#\r
222# This function is called by following two cases:\r
223# Case 1 in DSC:\r
224# [components.arch]\r
225# Pkg/module/module.inf\r
226# Pkg/module/module.inf {\r
227# <Defines>\r
228# FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836\r
229# }\r
230# Case 2 in FDF:\r
231# INF Pkg/module/module.inf\r
232# INF FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836 Pkg/module/module.inf\r
233#\r
234# This function copies Pkg/module/module.inf to\r
235# Conf/.cache/0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf\r
236#\r
237# @param Path Original PathClass object\r
238# @param BaseName New file base name\r
239#\r
240# @retval return the new PathClass object\r
241#\r
242def ProcessDuplicatedInf(Path, BaseName, Workspace):\r
243 Filename = os.path.split(Path.File)[1]\r
244 if '.' in Filename:\r
245 Filename = BaseName + Path.BaseName + Filename[Filename.rfind('.'):]\r
246 else:\r
247 Filename = BaseName + Path.BaseName\r
248\r
249 #\r
250 # If -N is specified on command line, cache is disabled\r
251 # The directory has to be created\r
252 #\r
253 DbDir = os.path.split(GlobalData.gDatabasePath)[0]\r
254 if not os.path.exists(DbDir):\r
255 os.makedirs(DbDir)\r
256 #\r
257 # A temporary INF is copied to database path which must have write permission\r
258 # The temporary will be removed at the end of build\r
f7496d71 259 # In case of name conflict, the file name is\r
97fa0ee9
YL
260 # FILE_GUIDBaseName (0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf)\r
261 #\r
262 TempFullPath = os.path.join(DbDir,\r
263 Filename)\r
264 RtPath = PathClass(Path.File, Workspace)\r
265 #\r
266 # Modify the full path to temporary path, keep other unchanged\r
267 #\r
268 # To build same module more than once, the module path with FILE_GUID overridden has\r
269 # the file name FILE_GUIDmodule.inf, but the relative path (self.MetaFile.File) is the real path\r
f7496d71 270 # in DSC which is used as relative path by C files and other files in INF.\r
97fa0ee9
YL
271 # A trick was used: all module paths are PathClass instances, after the initialization\r
272 # of PathClass, the PathClass.Path is overridden by the temporary INF path.\r
273 #\r
274 # The reason for creating a temporary INF is:\r
275 # Platform.Modules which is the base to create ModuleAutoGen objects is a dictionary,\r
276 # the key is the full path of INF, the value is an object to save overridden library instances, PCDs.\r
277 # A different key for the same module is needed to create different output directory,\r
278 # retrieve overridden PCDs, library instances.\r
279 #\r
280 # The BaseName is the FILE_GUID which is also the output directory name.\r
281 #\r
282 #\r
283 RtPath.Path = TempFullPath\r
284 RtPath.BaseName = BaseName\r
66b845ae 285 RtPath.OriginalPath = Path\r
97fa0ee9
YL
286 #\r
287 # If file exists, compare contents\r
288 #\r
289 if os.path.exists(TempFullPath):\r
f7496d71 290 with open(str(Path), 'rb') as f1, open(TempFullPath, 'rb') as f2:\r
2b5c643a
JC
291 if f1.read() == f2.read():\r
292 return RtPath\r
293 _TempInfs.append(TempFullPath)\r
97fa0ee9
YL
294 shutil.copy2(str(Path), TempFullPath)\r
295 return RtPath\r
296\r
2b5c643a 297## Remove temporary created INFs whose paths were saved in _TempInfs\r
97fa0ee9
YL
298#\r
299def ClearDuplicatedInf():\r
2b5c643a
JC
300 while _TempInfs:\r
301 File = _TempInfs.pop()\r
97fa0ee9
YL
302 if os.path.exists(File):\r
303 os.remove(File)\r
304\r
f51461c8
LG
305## Convert GUID string in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx style to C structure style\r
306#\r
307# @param Guid The GUID string\r
308#\r
309# @retval string The GUID string in C structure style\r
310#\r
311def GuidStringToGuidStructureString(Guid):\r
312 GuidList = Guid.split('-')\r
313 Result = '{'\r
47fea6af 314 for Index in range(0, 3, 1):\r
f51461c8
LG
315 Result = Result + '0x' + GuidList[Index] + ', '\r
316 Result = Result + '{0x' + GuidList[3][0:2] + ', 0x' + GuidList[3][2:4]\r
47fea6af
YZ
317 for Index in range(0, 12, 2):\r
318 Result = Result + ', 0x' + GuidList[4][Index:Index + 2]\r
f51461c8
LG
319 Result += '}}'\r
320 return Result\r
321\r
322## Convert GUID structure in byte array to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\r
323#\r
324# @param GuidValue The GUID value in byte array\r
325#\r
326# @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format\r
327#\r
328def GuidStructureByteArrayToGuidString(GuidValue):\r
329 guidValueString = GuidValue.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")\r
330 guidValueList = guidValueString.split(",")\r
331 if len(guidValueList) != 16:\r
332 return ''\r
333 #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)\r
334 try:\r
335 return "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (\r
336 int(guidValueList[3], 16),\r
337 int(guidValueList[2], 16),\r
338 int(guidValueList[1], 16),\r
339 int(guidValueList[0], 16),\r
340 int(guidValueList[5], 16),\r
341 int(guidValueList[4], 16),\r
342 int(guidValueList[7], 16),\r
343 int(guidValueList[6], 16),\r
344 int(guidValueList[8], 16),\r
345 int(guidValueList[9], 16),\r
346 int(guidValueList[10], 16),\r
347 int(guidValueList[11], 16),\r
348 int(guidValueList[12], 16),\r
349 int(guidValueList[13], 16),\r
350 int(guidValueList[14], 16),\r
351 int(guidValueList[15], 16)\r
352 )\r
353 except:\r
354 return ''\r
355\r
356## Convert GUID string in C structure style to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\r
357#\r
358# @param GuidValue The GUID value in C structure format\r
359#\r
360# @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format\r
361#\r
362def GuidStructureStringToGuidString(GuidValue):\r
85e5d3cf
FY
363 if not GlobalData.gGuidCFormatPattern.match(GuidValue):\r
364 return ''\r
f51461c8
LG
365 guidValueString = GuidValue.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")\r
366 guidValueList = guidValueString.split(",")\r
367 if len(guidValueList) != 11:\r
368 return ''\r
369 #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)\r
370 try:\r
371 return "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (\r
372 int(guidValueList[0], 16),\r
373 int(guidValueList[1], 16),\r
374 int(guidValueList[2], 16),\r
375 int(guidValueList[3], 16),\r
376 int(guidValueList[4], 16),\r
377 int(guidValueList[5], 16),\r
378 int(guidValueList[6], 16),\r
379 int(guidValueList[7], 16),\r
380 int(guidValueList[8], 16),\r
381 int(guidValueList[9], 16),\r
382 int(guidValueList[10], 16)\r
383 )\r
384 except:\r
385 return ''\r
386\r
387## Convert GUID string in C structure style to xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx\r
388#\r
389# @param GuidValue The GUID value in C structure format\r
390#\r
391# @retval string The GUID value in xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx format\r
392#\r
393def GuidStructureStringToGuidValueName(GuidValue):\r
394 guidValueString = GuidValue.lower().replace("{", "").replace("}", "").replace(" ", "")\r
395 guidValueList = guidValueString.split(",")\r
396 if len(guidValueList) != 11:\r
397 EdkLogger.error(None, FORMAT_INVALID, "Invalid GUID value string [%s]" % GuidValue)\r
398 return "%08x_%04x_%04x_%02x%02x_%02x%02x%02x%02x%02x%02x" % (\r
399 int(guidValueList[0], 16),\r
400 int(guidValueList[1], 16),\r
401 int(guidValueList[2], 16),\r
402 int(guidValueList[3], 16),\r
403 int(guidValueList[4], 16),\r
404 int(guidValueList[5], 16),\r
405 int(guidValueList[6], 16),\r
406 int(guidValueList[7], 16),\r
407 int(guidValueList[8], 16),\r
408 int(guidValueList[9], 16),\r
409 int(guidValueList[10], 16)\r
410 )\r
411\r
412## Create directories\r
413#\r
414# @param Directory The directory name\r
415#\r
416def CreateDirectory(Directory):\r
4231a819 417 if Directory is None or Directory.strip() == "":\r
f51461c8
LG
418 return True\r
419 try:\r
420 if not os.access(Directory, os.F_OK):\r
421 os.makedirs(Directory)\r
422 except:\r
423 return False\r
424 return True\r
425\r
426## Remove directories, including files and sub-directories in it\r
427#\r
428# @param Directory The directory name\r
429#\r
430def RemoveDirectory(Directory, Recursively=False):\r
4231a819 431 if Directory is None or Directory.strip() == "" or not os.path.exists(Directory):\r
f51461c8
LG
432 return\r
433 if Recursively:\r
434 CurrentDirectory = os.getcwd()\r
435 os.chdir(Directory)\r
436 for File in os.listdir("."):\r
437 if os.path.isdir(File):\r
438 RemoveDirectory(File, Recursively)\r
439 else:\r
440 os.remove(File)\r
441 os.chdir(CurrentDirectory)\r
442 os.rmdir(Directory)\r
443\r
f51461c8
LG
444## Store content in file\r
445#\r
446# This method is used to save file only when its content is changed. This is\r
447# quite useful for "make" system to decide what will be re-built and what won't.\r
448#\r
449# @param File The path of file\r
450# @param Content The new content of the file\r
451# @param IsBinaryFile The flag indicating if the file is binary file or not\r
452#\r
453# @retval True If the file content is changed and the file is renewed\r
454# @retval False If the file content is the same\r
455#\r
456def SaveFileOnChange(File, Content, IsBinaryFile=True):\r
1ccc4d89 457\r
f51461c8 458 if os.path.exists(File):\r
d943b0c3
FB
459 if IsBinaryFile:\r
460 try:\r
461 with open(File, "rb") as f:\r
462 if Content == f.read():\r
463 return False\r
464 except:\r
465 EdkLogger.error(None, FILE_OPEN_FAILURE, ExtraData=File)\r
466 else:\r
467 try:\r
468 with open(File, "r") as f:\r
469 if Content == f.read():\r
470 return False\r
471 except:\r
472 EdkLogger.error(None, FILE_OPEN_FAILURE, ExtraData=File)\r
f51461c8
LG
473\r
474 DirName = os.path.dirname(File)\r
475 if not CreateDirectory(DirName):\r
476 EdkLogger.error(None, FILE_CREATE_FAILURE, "Could not create directory %s" % DirName)\r
477 else:\r
478 if DirName == '':\r
479 DirName = os.getcwd()\r
480 if not os.access(DirName, os.W_OK):\r
481 EdkLogger.error(None, PERMISSION_FAILURE, "Do not have write permission on directory %s" % DirName)\r
482\r
0b836855 483 OpenMode = "w"\r
d943b0c3 484 if IsBinaryFile:\r
0b836855
YF
485 OpenMode = "wb"\r
486\r
487 if GlobalData.gIsWindows and not os.path.exists(File):\r
488 # write temp file, then rename the temp file to the real file\r
489 # to make sure the file be immediate saved to disk\r
490 with tempfile.NamedTemporaryFile(OpenMode, dir=os.path.dirname(File), delete=False) as tf:\r
491 tf.write(Content)\r
492 tempname = tf.name\r
d943b0c3 493 try:\r
0b836855
YF
494 os.rename(tempname, File)\r
495 except:\r
d943b0c3
FB
496 EdkLogger.error(None, FILE_CREATE_FAILURE, ExtraData='IOError %s' % X)\r
497 else:\r
498 try:\r
0b836855 499 with open(File, OpenMode) as Fd:\r
d943b0c3
FB
500 Fd.write(Content)\r
501 except IOError as X:\r
502 EdkLogger.error(None, FILE_CREATE_FAILURE, ExtraData='IOError %s' % X)\r
f51461c8
LG
503\r
504 return True\r
505\r
2378ea55
SS
506## Copy source file only if it is different from the destination file\r
507#\r
508# This method is used to copy file only if the source file and destination\r
509# file content are different. This is quite useful to avoid duplicated\r
510# file writing.\r
511#\r
512# @param SrcFile The path of source file\r
513# @param Dst The path of destination file or folder\r
514#\r
515# @retval True The two files content are different and the file is copied\r
516# @retval False No copy really happen\r
517#\r
518def CopyFileOnChange(SrcFile, Dst):\r
519 if not os.path.exists(SrcFile):\r
520 return False\r
521\r
522 if os.path.isdir(Dst):\r
523 DstFile = os.path.join(Dst, os.path.basename(SrcFile))\r
524 else:\r
525 DstFile = Dst\r
526\r
527 if os.path.exists(DstFile) and filecmp.cmp(SrcFile, DstFile, shallow=False):\r
528 return False\r
529\r
530 DirName = os.path.dirname(DstFile)\r
531 if not CreateDirectory(DirName):\r
532 EdkLogger.error(None, FILE_CREATE_FAILURE, "Could not create directory %s" % DirName)\r
533 else:\r
534 if DirName == '':\r
535 DirName = os.getcwd()\r
536 if not os.access(DirName, os.W_OK):\r
537 EdkLogger.error(None, PERMISSION_FAILURE, "Do not have write permission on directory %s" % DirName)\r
538\r
539 # os.replace and os.rename are the atomic operations in python 3 and 2.\r
540 # we use these two atomic operations to ensure the file copy is atomic:\r
541 # copy the src to a temp file in the dst same folder firstly, then\r
542 # replace or rename the temp file to the destination file.\r
543 with tempfile.NamedTemporaryFile(dir=DirName, delete=False) as tf:\r
544 shutil.copy(SrcFile, tf.name)\r
545 tempname = tf.name\r
546 try:\r
547 if hasattr(os, 'replace'):\r
548 os.replace(tempname, DstFile)\r
549 else:\r
550 # os.rename reqire to remove the dst on Windows, otherwise OSError will be raised.\r
551 if GlobalData.gIsWindows and os.path.exists(DstFile):\r
552 os.remove(DstFile)\r
553 os.rename(tempname, DstFile)\r
554\r
555 except IOError as X:\r
556 EdkLogger.error(None, FILE_COPY_FAILURE, ExtraData='IOError %s' % X)\r
557\r
558 return True\r
559\r
f51461c8
LG
560## Retrieve and cache the real path name in file system\r
561#\r
562# @param Root The root directory of path relative to\r
563#\r
564# @retval str The path string if the path exists\r
565# @retval None If path doesn't exist\r
566#\r
567class DirCache:\r
568 _CACHE_ = set()\r
569 _UPPER_CACHE_ = {}\r
570\r
571 def __init__(self, Root):\r
572 self._Root = Root\r
573 for F in os.listdir(Root):\r
574 self._CACHE_.add(F)\r
575 self._UPPER_CACHE_[F.upper()] = F\r
576\r
577 # =[] operator\r
578 def __getitem__(self, Path):\r
579 Path = Path[len(os.path.commonprefix([Path, self._Root])):]\r
580 if not Path:\r
581 return self._Root\r
582 if Path and Path[0] == os.path.sep:\r
583 Path = Path[1:]\r
584 if Path in self._CACHE_:\r
585 return os.path.join(self._Root, Path)\r
586 UpperPath = Path.upper()\r
587 if UpperPath in self._UPPER_CACHE_:\r
588 return os.path.join(self._Root, self._UPPER_CACHE_[UpperPath])\r
589\r
590 IndexList = []\r
591 LastSepIndex = -1\r
592 SepIndex = Path.find(os.path.sep)\r
593 while SepIndex > -1:\r
594 Parent = UpperPath[:SepIndex]\r
595 if Parent not in self._UPPER_CACHE_:\r
596 break\r
597 LastSepIndex = SepIndex\r
598 SepIndex = Path.find(os.path.sep, LastSepIndex + 1)\r
599\r
600 if LastSepIndex == -1:\r
601 return None\r
602\r
603 Cwd = os.getcwd()\r
604 os.chdir(self._Root)\r
605 SepIndex = LastSepIndex\r
606 while SepIndex > -1:\r
607 Parent = Path[:SepIndex]\r
608 ParentKey = UpperPath[:SepIndex]\r
609 if ParentKey not in self._UPPER_CACHE_:\r
610 os.chdir(Cwd)\r
611 return None\r
612\r
613 if Parent in self._CACHE_:\r
614 ParentDir = Parent\r
615 else:\r
616 ParentDir = self._UPPER_CACHE_[ParentKey]\r
617 for F in os.listdir(ParentDir):\r
618 Dir = os.path.join(ParentDir, F)\r
619 self._CACHE_.add(Dir)\r
620 self._UPPER_CACHE_[Dir.upper()] = Dir\r
621\r
622 SepIndex = Path.find(os.path.sep, SepIndex + 1)\r
623\r
624 os.chdir(Cwd)\r
625 if Path in self._CACHE_:\r
626 return os.path.join(self._Root, Path)\r
627 elif UpperPath in self._UPPER_CACHE_:\r
628 return os.path.join(self._Root, self._UPPER_CACHE_[UpperPath])\r
629 return None\r
630\r
f51461c8
LG
631def RealPath(File, Dir='', OverrideDir=''):\r
632 NewFile = os.path.normpath(os.path.join(Dir, File))\r
633 NewFile = GlobalData.gAllFiles[NewFile]\r
634 if not NewFile and OverrideDir:\r
635 NewFile = os.path.normpath(os.path.join(OverrideDir, File))\r
636 NewFile = GlobalData.gAllFiles[NewFile]\r
637 return NewFile\r
638\r
f51461c8
LG
639## Get GUID value from given packages\r
640#\r
641# @param CName The CName of the GUID\r
642# @param PackageList List of packages looking-up in\r
c28d2e10 643# @param Inffile The driver file\r
f51461c8
LG
644#\r
645# @retval GuidValue if the CName is found in any given package\r
646# @retval None if the CName is not found in all given packages\r
647#\r
c28d2e10 648def GuidValue(CName, PackageList, Inffile = None):\r
f51461c8 649 for P in PackageList:\r
f8d11e5a 650 GuidKeys = list(P.Guids.keys())\r
c28d2e10
YZ
651 if Inffile and P._PrivateGuids:\r
652 if not Inffile.startswith(P.MetaFile.Dir):\r
175a4b5d 653 GuidKeys = [x for x in P.Guids if x not in P._PrivateGuids]\r
c28d2e10 654 if CName in GuidKeys:\r
f51461c8
LG
655 return P.Guids[CName]\r
656 return None\r
f8d11e5a 657 return None\r
f51461c8 658\r
f51461c8
LG
659## A string template class\r
660#\r
661# This class implements a template for string replacement. A string template\r
662# looks like following\r
663#\r
664# ${BEGIN} other_string ${placeholder_name} other_string ${END}\r
665#\r
666# The string between ${BEGIN} and ${END} will be repeated as many times as the\r
667# length of "placeholder_name", which is a list passed through a dict. The\r
668# "placeholder_name" is the key name of the dict. The ${BEGIN} and ${END} can\r
669# be not used and, in this case, the "placeholder_name" must not a list and it\r
670# will just be replaced once.\r
671#\r
672class TemplateString(object):\r
673 _REPEAT_START_FLAG = "BEGIN"\r
674 _REPEAT_END_FLAG = "END"\r
675\r
676 class Section(object):\r
677 _LIST_TYPES = [type([]), type(set()), type((0,))]\r
678\r
679 def __init__(self, TemplateSection, PlaceHolderList):\r
680 self._Template = TemplateSection\r
681 self._PlaceHolderList = []\r
682\r
683 # Split the section into sub-sections according to the position of placeholders\r
684 if PlaceHolderList:\r
685 self._SubSectionList = []\r
686 SubSectionStart = 0\r
687 #\r
688 # The placeholders passed in must be in the format of\r
689 #\r
690 # PlaceHolderName, PlaceHolderStartPoint, PlaceHolderEndPoint\r
691 #\r
47fea6af 692 for PlaceHolder, Start, End in PlaceHolderList:\r
f51461c8
LG
693 self._SubSectionList.append(TemplateSection[SubSectionStart:Start])\r
694 self._SubSectionList.append(TemplateSection[Start:End])\r
695 self._PlaceHolderList.append(PlaceHolder)\r
696 SubSectionStart = End\r
697 if SubSectionStart < len(TemplateSection):\r
698 self._SubSectionList.append(TemplateSection[SubSectionStart:])\r
699 else:\r
700 self._SubSectionList = [TemplateSection]\r
701\r
702 def __str__(self):\r
703 return self._Template + " : " + str(self._PlaceHolderList)\r
704\r
705 def Instantiate(self, PlaceHolderValues):\r
706 RepeatTime = -1\r
707 RepeatPlaceHolders = {}\r
708 NonRepeatPlaceHolders = {}\r
709\r
710 for PlaceHolder in self._PlaceHolderList:\r
711 if PlaceHolder not in PlaceHolderValues:\r
712 continue\r
713 Value = PlaceHolderValues[PlaceHolder]\r
714 if type(Value) in self._LIST_TYPES:\r
715 if RepeatTime < 0:\r
716 RepeatTime = len(Value)\r
717 elif RepeatTime != len(Value):\r
718 EdkLogger.error(\r
719 "TemplateString",\r
720 PARAMETER_INVALID,\r
721 "${%s} has different repeat time from others!" % PlaceHolder,\r
722 ExtraData=str(self._Template)\r
723 )\r
724 RepeatPlaceHolders["${%s}" % PlaceHolder] = Value\r
725 else:\r
726 NonRepeatPlaceHolders["${%s}" % PlaceHolder] = Value\r
727\r
728 if NonRepeatPlaceHolders:\r
729 StringList = []\r
730 for S in self._SubSectionList:\r
731 if S not in NonRepeatPlaceHolders:\r
732 StringList.append(S)\r
733 else:\r
734 StringList.append(str(NonRepeatPlaceHolders[S]))\r
735 else:\r
736 StringList = self._SubSectionList\r
737\r
738 if RepeatPlaceHolders:\r
739 TempStringList = []\r
740 for Index in range(RepeatTime):\r
741 for S in StringList:\r
742 if S not in RepeatPlaceHolders:\r
743 TempStringList.append(S)\r
744 else:\r
745 TempStringList.append(str(RepeatPlaceHolders[S][Index]))\r
746 StringList = TempStringList\r
747\r
748 return "".join(StringList)\r
749\r
750 ## Constructor\r
751 def __init__(self, Template=None):\r
4e375707 752 self.String = []\r
f51461c8
LG
753 self.IsBinary = False\r
754 self._Template = Template\r
755 self._TemplateSectionList = self._Parse(Template)\r
756\r
757 ## str() operator\r
758 #\r
759 # @retval string The string replaced\r
760 #\r
761 def __str__(self):\r
4e375707 762 return "".join(self.String)\r
f51461c8
LG
763\r
764 ## Split the template string into fragments per the ${BEGIN} and ${END} flags\r
765 #\r
766 # @retval list A list of TemplateString.Section objects\r
767 #\r
768 def _Parse(self, Template):\r
769 SectionStart = 0\r
770 SearchFrom = 0\r
771 MatchEnd = 0\r
772 PlaceHolderList = []\r
773 TemplateSectionList = []\r
774 while Template:\r
775 MatchObj = gPlaceholderPattern.search(Template, SearchFrom)\r
776 if not MatchObj:\r
777 if MatchEnd <= len(Template):\r
778 TemplateSection = TemplateString.Section(Template[SectionStart:], PlaceHolderList)\r
779 TemplateSectionList.append(TemplateSection)\r
780 break\r
781\r
782 MatchString = MatchObj.group(1)\r
783 MatchStart = MatchObj.start()\r
784 MatchEnd = MatchObj.end()\r
785\r
786 if MatchString == self._REPEAT_START_FLAG:\r
787 if MatchStart > SectionStart:\r
788 TemplateSection = TemplateString.Section(Template[SectionStart:MatchStart], PlaceHolderList)\r
789 TemplateSectionList.append(TemplateSection)\r
790 SectionStart = MatchEnd\r
791 PlaceHolderList = []\r
792 elif MatchString == self._REPEAT_END_FLAG:\r
793 TemplateSection = TemplateString.Section(Template[SectionStart:MatchStart], PlaceHolderList)\r
794 TemplateSectionList.append(TemplateSection)\r
795 SectionStart = MatchEnd\r
796 PlaceHolderList = []\r
797 else:\r
798 PlaceHolderList.append((MatchString, MatchStart - SectionStart, MatchEnd - SectionStart))\r
799 SearchFrom = MatchEnd\r
800 return TemplateSectionList\r
801\r
802 ## Replace the string template with dictionary of placeholders and append it to previous one\r
803 #\r
804 # @param AppendString The string template to append\r
805 # @param Dictionary The placeholder dictionaries\r
806 #\r
807 def Append(self, AppendString, Dictionary=None):\r
808 if Dictionary:\r
809 SectionList = self._Parse(AppendString)\r
4e375707 810 self.String.append( "".join(S.Instantiate(Dictionary) for S in SectionList))\r
f51461c8 811 else:\r
4e375707
B
812 if isinstance(AppendString,list):\r
813 self.String.extend(AppendString)\r
814 else:\r
815 self.String.append(AppendString)\r
f51461c8
LG
816\r
817 ## Replace the string template with dictionary of placeholders\r
818 #\r
819 # @param Dictionary The placeholder dictionaries\r
820 #\r
821 # @retval str The string replaced with placeholder values\r
822 #\r
823 def Replace(self, Dictionary=None):\r
8252e6bf 824 return "".join(S.Instantiate(Dictionary) for S in self._TemplateSectionList)\r
f51461c8
LG
825\r
826## Progress indicator class\r
827#\r
828# This class makes use of thread to print progress on console.\r
829#\r
830class Progressor:\r
831 # for avoiding deadloop\r
832 _StopFlag = None\r
833 _ProgressThread = None\r
834 _CheckInterval = 0.25\r
835\r
836 ## Constructor\r
837 #\r
fb0b35e0
AC
838 # @param OpenMessage The string printed before progress characters\r
839 # @param CloseMessage The string printed after progress characters\r
840 # @param ProgressChar The character used to indicate the progress\r
841 # @param Interval The interval in seconds between two progress characters\r
f51461c8
LG
842 #\r
843 def __init__(self, OpenMessage="", CloseMessage="", ProgressChar='.', Interval=1.0):\r
844 self.PromptMessage = OpenMessage\r
845 self.CodaMessage = CloseMessage\r
846 self.ProgressChar = ProgressChar\r
847 self.Interval = Interval\r
4231a819 848 if Progressor._StopFlag is None:\r
f51461c8
LG
849 Progressor._StopFlag = threading.Event()\r
850\r
fb0b35e0 851 ## Start to print progress character\r
f51461c8 852 #\r
fb0b35e0 853 # @param OpenMessage The string printed before progress characters\r
f51461c8
LG
854 #\r
855 def Start(self, OpenMessage=None):\r
4231a819 856 if OpenMessage is not None:\r
f51461c8
LG
857 self.PromptMessage = OpenMessage\r
858 Progressor._StopFlag.clear()\r
4231a819 859 if Progressor._ProgressThread is None:\r
f51461c8
LG
860 Progressor._ProgressThread = threading.Thread(target=self._ProgressThreadEntry)\r
861 Progressor._ProgressThread.setDaemon(False)\r
862 Progressor._ProgressThread.start()\r
863\r
fb0b35e0 864 ## Stop printing progress character\r
f51461c8 865 #\r
fb0b35e0 866 # @param CloseMessage The string printed after progress characters\r
f51461c8
LG
867 #\r
868 def Stop(self, CloseMessage=None):\r
869 OriginalCodaMessage = self.CodaMessage\r
4231a819 870 if CloseMessage is not None:\r
f51461c8
LG
871 self.CodaMessage = CloseMessage\r
872 self.Abort()\r
873 self.CodaMessage = OriginalCodaMessage\r
874\r
875 ## Thread entry method\r
876 def _ProgressThreadEntry(self):\r
877 sys.stdout.write(self.PromptMessage + " ")\r
878 sys.stdout.flush()\r
879 TimeUp = 0.0\r
880 while not Progressor._StopFlag.isSet():\r
881 if TimeUp <= 0.0:\r
882 sys.stdout.write(self.ProgressChar)\r
883 sys.stdout.flush()\r
884 TimeUp = self.Interval\r
885 time.sleep(self._CheckInterval)\r
886 TimeUp -= self._CheckInterval\r
887 sys.stdout.write(" " + self.CodaMessage + "\n")\r
888 sys.stdout.flush()\r
889\r
890 ## Abort the progress display\r
891 @staticmethod\r
892 def Abort():\r
4231a819 893 if Progressor._StopFlag is not None:\r
f51461c8 894 Progressor._StopFlag.set()\r
4231a819 895 if Progressor._ProgressThread is not None:\r
f51461c8
LG
896 Progressor._ProgressThread.join()\r
897 Progressor._ProgressThread = None\r
898\r
f51461c8 899\r
f51461c8
LG
900## Dictionary using prioritized list as key\r
901#\r
902class tdict:\r
903 _ListType = type([])\r
904 _TupleType = type(())\r
905 _Wildcard = 'COMMON'\r
bc39c5cb 906 _ValidWildcardList = ['COMMON', 'DEFAULT', 'ALL', TAB_STAR, 'PLATFORM']\r
f51461c8
LG
907\r
908 def __init__(self, _Single_=False, _Level_=2):\r
909 self._Level_ = _Level_\r
910 self.data = {}\r
911 self._Single_ = _Single_\r
912\r
913 # =[] operator\r
914 def __getitem__(self, key):\r
915 KeyType = type(key)\r
916 RestKeys = None\r
917 if KeyType == self._ListType or KeyType == self._TupleType:\r
918 FirstKey = key[0]\r
919 if len(key) > 1:\r
920 RestKeys = key[1:]\r
921 elif self._Level_ > 1:\r
47fea6af 922 RestKeys = [self._Wildcard for i in range(0, self._Level_ - 1)]\r
f51461c8
LG
923 else:\r
924 FirstKey = key\r
925 if self._Level_ > 1:\r
47fea6af 926 RestKeys = [self._Wildcard for i in range(0, self._Level_ - 1)]\r
f51461c8 927\r
4231a819 928 if FirstKey is None or str(FirstKey).upper() in self._ValidWildcardList:\r
f51461c8
LG
929 FirstKey = self._Wildcard\r
930\r
931 if self._Single_:\r
932 return self._GetSingleValue(FirstKey, RestKeys)\r
933 else:\r
934 return self._GetAllValues(FirstKey, RestKeys)\r
935\r
936 def _GetSingleValue(self, FirstKey, RestKeys):\r
937 Value = None\r
938 #print "%s-%s" % (FirstKey, self._Level_) ,\r
939 if self._Level_ > 1:\r
940 if FirstKey == self._Wildcard:\r
941 if FirstKey in self.data:\r
942 Value = self.data[FirstKey][RestKeys]\r
4231a819 943 if Value is None:\r
f51461c8
LG
944 for Key in self.data:\r
945 Value = self.data[Key][RestKeys]\r
4231a819 946 if Value is not None: break\r
f51461c8
LG
947 else:\r
948 if FirstKey in self.data:\r
949 Value = self.data[FirstKey][RestKeys]\r
4231a819 950 if Value is None and self._Wildcard in self.data:\r
f51461c8
LG
951 #print "Value=None"\r
952 Value = self.data[self._Wildcard][RestKeys]\r
953 else:\r
954 if FirstKey == self._Wildcard:\r
955 if FirstKey in self.data:\r
956 Value = self.data[FirstKey]\r
4231a819 957 if Value is None:\r
f51461c8
LG
958 for Key in self.data:\r
959 Value = self.data[Key]\r
4231a819 960 if Value is not None: break\r
f51461c8
LG
961 else:\r
962 if FirstKey in self.data:\r
963 Value = self.data[FirstKey]\r
964 elif self._Wildcard in self.data:\r
965 Value = self.data[self._Wildcard]\r
966 return Value\r
967\r
968 def _GetAllValues(self, FirstKey, RestKeys):\r
969 Value = []\r
970 if self._Level_ > 1:\r
971 if FirstKey == self._Wildcard:\r
972 for Key in self.data:\r
973 Value += self.data[Key][RestKeys]\r
974 else:\r
975 if FirstKey in self.data:\r
976 Value += self.data[FirstKey][RestKeys]\r
977 if self._Wildcard in self.data:\r
978 Value += self.data[self._Wildcard][RestKeys]\r
979 else:\r
980 if FirstKey == self._Wildcard:\r
981 for Key in self.data:\r
982 Value.append(self.data[Key])\r
983 else:\r
984 if FirstKey in self.data:\r
985 Value.append(self.data[FirstKey])\r
986 if self._Wildcard in self.data:\r
987 Value.append(self.data[self._Wildcard])\r
988 return Value\r
989\r
990 ## []= operator\r
991 def __setitem__(self, key, value):\r
992 KeyType = type(key)\r
993 RestKeys = None\r
994 if KeyType == self._ListType or KeyType == self._TupleType:\r
995 FirstKey = key[0]\r
996 if len(key) > 1:\r
997 RestKeys = key[1:]\r
998 else:\r
47fea6af 999 RestKeys = [self._Wildcard for i in range(0, self._Level_ - 1)]\r
f51461c8
LG
1000 else:\r
1001 FirstKey = key\r
1002 if self._Level_ > 1:\r
47fea6af 1003 RestKeys = [self._Wildcard for i in range(0, self._Level_ - 1)]\r
f51461c8
LG
1004\r
1005 if FirstKey in self._ValidWildcardList:\r
1006 FirstKey = self._Wildcard\r
1007\r
1008 if FirstKey not in self.data and self._Level_ > 0:\r
1009 self.data[FirstKey] = tdict(self._Single_, self._Level_ - 1)\r
1010\r
1011 if self._Level_ > 1:\r
1012 self.data[FirstKey][RestKeys] = value\r
1013 else:\r
1014 self.data[FirstKey] = value\r
1015\r
1016 def SetGreedyMode(self):\r
1017 self._Single_ = False\r
1018 if self._Level_ > 1:\r
1019 for Key in self.data:\r
1020 self.data[Key].SetGreedyMode()\r
1021\r
1022 def SetSingleMode(self):\r
1023 self._Single_ = True\r
1024 if self._Level_ > 1:\r
1025 for Key in self.data:\r
1026 self.data[Key].SetSingleMode()\r
1027\r
1028 def GetKeys(self, KeyIndex=0):\r
1029 assert KeyIndex >= 0\r
1030 if KeyIndex == 0:\r
1031 return set(self.data.keys())\r
1032 else:\r
1033 keys = set()\r
1034 for Key in self.data:\r
1035 keys |= self.data[Key].GetKeys(KeyIndex - 1)\r
1036 return keys\r
1037\r
67e11e4d 1038def AnalyzePcdExpression(Setting):\r
d3d97b37 1039 RanStr = ''.join(sample(string.ascii_letters + string.digits, 8))\r
1040 Setting = Setting.replace('\\\\', RanStr).strip()\r
ea927d2f
FY
1041 # There might be escaped quote in a string: \", \\\" , \', \\\'\r
1042 Data = Setting\r
f51461c8
LG
1043 # There might be '|' in string and in ( ... | ... ), replace it with '-'\r
1044 NewStr = ''\r
ea927d2f
FY
1045 InSingleQuoteStr = False\r
1046 InDoubleQuoteStr = False\r
f51461c8 1047 Pair = 0\r
ea927d2f
FY
1048 for Index, ch in enumerate(Data):\r
1049 if ch == '"' and not InSingleQuoteStr:\r
1050 if Data[Index - 1] != '\\':\r
1051 InDoubleQuoteStr = not InDoubleQuoteStr\r
1052 elif ch == "'" and not InDoubleQuoteStr:\r
1053 if Data[Index - 1] != '\\':\r
1054 InSingleQuoteStr = not InSingleQuoteStr\r
1055 elif ch == '(' and not (InSingleQuoteStr or InDoubleQuoteStr):\r
f51461c8 1056 Pair += 1\r
ea927d2f 1057 elif ch == ')' and not (InSingleQuoteStr or InDoubleQuoteStr):\r
f51461c8 1058 Pair -= 1\r
47fea6af 1059\r
ea927d2f 1060 if (Pair > 0 or InSingleQuoteStr or InDoubleQuoteStr) and ch == TAB_VALUE_SPLIT:\r
f51461c8
LG
1061 NewStr += '-'\r
1062 else:\r
1063 NewStr += ch\r
1064 FieldList = []\r
1065 StartPos = 0\r
1066 while True:\r
1067 Pos = NewStr.find(TAB_VALUE_SPLIT, StartPos)\r
1068 if Pos < 0:\r
1069 FieldList.append(Setting[StartPos:].strip())\r
1070 break\r
1071 FieldList.append(Setting[StartPos:Pos].strip())\r
1072 StartPos = Pos + 1\r
d3d97b37 1073 for i, ch in enumerate(FieldList):\r
1074 if RanStr in ch:\r
1075 FieldList[i] = ch.replace(RanStr,'\\\\')\r
67e11e4d
YZ
1076 return FieldList\r
1077\r
42bd1750
CJ
1078def ParseFieldValue (Value):\r
1079 def ParseDevPathValue (Value):\r
1080 if '\\' in Value:\r
1081 Value.replace('\\', '/').replace(' ', '')\r
7dbc50bd 1082\r
42bd1750
CJ
1083 Cmd = 'DevicePath ' + '"' + Value + '"'\r
1084 try:\r
1085 p = subprocess.Popen(Cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)\r
1086 out, err = p.communicate()\r
1087 except Exception as X:\r
1088 raise BadExpression("DevicePath: %s" % (str(X)) )\r
1089 finally:\r
1090 subprocess._cleanup()\r
1091 p.stdout.close()\r
1092 p.stderr.close()\r
1093 if err:\r
1094 raise BadExpression("DevicePath: %s" % str(err))\r
1c27ec42 1095 out = out.decode(encoding='utf-8', errors='ignore')\r
42bd1750
CJ
1096 Size = len(out.split())\r
1097 out = ','.join(out.split())\r
1098 return '{' + out + '}', Size\r
726c501c 1099\r
72a1d776 1100 if "{CODE(" in Value:\r
1101 return Value, len(Value.split(","))\r
0d1f5b2b 1102 if isinstance(Value, type(0)):\r
b3e94a06 1103 return Value, (Value.bit_length() + 7) // 8\r
0d1f5b2b 1104 if not isinstance(Value, type('')):\r
726c501c
YZ
1105 raise BadExpression('Type %s is %s' %(Value, type(Value)))\r
1106 Value = Value.strip()\r
656d2539 1107 if Value.startswith(TAB_UINT8) and Value.endswith(')'):\r
726c501c
YZ
1108 Value, Size = ParseFieldValue(Value.split('(', 1)[1][:-1])\r
1109 if Size > 1:\r
1110 raise BadExpression('Value (%s) Size larger than %d' %(Value, Size))\r
1111 return Value, 1\r
656d2539 1112 if Value.startswith(TAB_UINT16) and Value.endswith(')'):\r
726c501c
YZ
1113 Value, Size = ParseFieldValue(Value.split('(', 1)[1][:-1])\r
1114 if Size > 2:\r
1115 raise BadExpression('Value (%s) Size larger than %d' %(Value, Size))\r
1116 return Value, 2\r
656d2539 1117 if Value.startswith(TAB_UINT32) and Value.endswith(')'):\r
726c501c
YZ
1118 Value, Size = ParseFieldValue(Value.split('(', 1)[1][:-1])\r
1119 if Size > 4:\r
1120 raise BadExpression('Value (%s) Size larger than %d' %(Value, Size))\r
1121 return Value, 4\r
656d2539 1122 if Value.startswith(TAB_UINT64) and Value.endswith(')'):\r
726c501c
YZ
1123 Value, Size = ParseFieldValue(Value.split('(', 1)[1][:-1])\r
1124 if Size > 8:\r
1125 raise BadExpression('Value (%s) Size larger than %d' % (Value, Size))\r
1126 return Value, 8\r
91fa33ee 1127 if Value.startswith(TAB_GUID) and Value.endswith(')'):\r
726c501c
YZ
1128 Value = Value.split('(', 1)[1][:-1].strip()\r
1129 if Value[0] == '{' and Value[-1] == '}':\r
4344a788 1130 TmpValue = GuidStructureStringToGuidString(Value)\r
85e5d3cf 1131 if not TmpValue:\r
4344a788
FY
1132 raise BadExpression("Invalid GUID value string %s" % Value)\r
1133 Value = TmpValue\r
726c501c
YZ
1134 if Value[0] == '"' and Value[-1] == '"':\r
1135 Value = Value[1:-1]\r
1136 try:\r
d943b0c3
FB
1137 Value = str(uuid.UUID(Value).bytes_le)\r
1138 if Value.startswith("b'"):\r
1139 Value = Value[2:-1]\r
1140 Value = "'" + Value + "'"\r
5b0671c1 1141 except ValueError as Message:\r
caf74495 1142 raise BadExpression(Message)\r
726c501c
YZ
1143 Value, Size = ParseFieldValue(Value)\r
1144 return Value, 16\r
1145 if Value.startswith('L"') and Value.endswith('"'):\r
1146 # Unicode String\r
4faf1322
FY
1147 # translate escape character\r
1148 Value = Value[1:]\r
1149 try:\r
1150 Value = eval(Value)\r
1151 except:\r
1152 Value = Value[1:-1]\r
1153 List = list(Value)\r
726c501c
YZ
1154 List.reverse()\r
1155 Value = 0\r
1156 for Char in List:\r
1157 Value = (Value << 16) | ord(Char)\r
1158 return Value, (len(List) + 1) * 2\r
1159 if Value.startswith('"') and Value.endswith('"'):\r
1160 # ASCII String\r
4faf1322
FY
1161 # translate escape character\r
1162 try:\r
1163 Value = eval(Value)\r
1164 except:\r
1165 Value = Value[1:-1]\r
1166 List = list(Value)\r
726c501c
YZ
1167 List.reverse()\r
1168 Value = 0\r
1169 for Char in List:\r
1170 Value = (Value << 8) | ord(Char)\r
1171 return Value, len(List) + 1\r
1172 if Value.startswith("L'") and Value.endswith("'"):\r
1173 # Unicode Character Constant\r
4faf1322
FY
1174 # translate escape character\r
1175 Value = Value[1:]\r
1176 try:\r
1177 Value = eval(Value)\r
1178 except:\r
1179 Value = Value[1:-1]\r
1180 List = list(Value)\r
0e6b8673
FY
1181 if len(List) == 0:\r
1182 raise BadExpression('Length %s is %s' % (Value, len(List)))\r
726c501c
YZ
1183 List.reverse()\r
1184 Value = 0\r
1185 for Char in List:\r
1186 Value = (Value << 16) | ord(Char)\r
1187 return Value, len(List) * 2\r
1188 if Value.startswith("'") and Value.endswith("'"):\r
1189 # Character constant\r
4faf1322
FY
1190 # translate escape character\r
1191 try:\r
1192 Value = eval(Value)\r
1193 except:\r
1194 Value = Value[1:-1]\r
1195 List = list(Value)\r
0e6b8673
FY
1196 if len(List) == 0:\r
1197 raise BadExpression('Length %s is %s' % (Value, len(List)))\r
726c501c
YZ
1198 List.reverse()\r
1199 Value = 0\r
1200 for Char in List:\r
1201 Value = (Value << 8) | ord(Char)\r
1202 return Value, len(List)\r
1203 if Value.startswith('{') and Value.endswith('}'):\r
1204 # Byte array\r
1205 Value = Value[1:-1]\r
1206 List = [Item.strip() for Item in Value.split(',')]\r
1207 List.reverse()\r
1208 Value = 0\r
1209 RetSize = 0\r
1210 for Item in List:\r
1211 ItemValue, Size = ParseFieldValue(Item)\r
1212 RetSize += Size\r
1213 for I in range(Size):\r
1214 Value = (Value << 8) | ((ItemValue >> 8 * I) & 0xff)\r
1215 return Value, RetSize\r
1216 if Value.startswith('DEVICE_PATH(') and Value.endswith(')'):\r
8ad5f10a
FY
1217 Value = Value.replace("DEVICE_PATH(", '').rstrip(')')\r
1218 Value = Value.strip().strip('"')\r
726c501c
YZ
1219 return ParseDevPathValue(Value)\r
1220 if Value.lower().startswith('0x'):\r
b62cbfb7 1221 try:\r
1222 Value = int(Value, 16)\r
1223 except:\r
1224 raise BadExpression("invalid hex value: %s" % Value)\r
726c501c
YZ
1225 if Value == 0:\r
1226 return 0, 1\r
b3e94a06 1227 return Value, (Value.bit_length() + 7) // 8\r
726c501c
YZ
1228 if Value[0].isdigit():\r
1229 Value = int(Value, 10)\r
1230 if Value == 0:\r
1231 return 0, 1\r
b3e94a06 1232 return Value, (Value.bit_length() + 7) // 8\r
726c501c
YZ
1233 if Value.lower() == 'true':\r
1234 return 1, 1\r
1235 if Value.lower() == 'false':\r
1236 return 0, 1\r
ae7b6df8 1237 return Value, 1\r
ae7b6df8 1238\r
67e11e4d
YZ
1239## AnalyzeDscPcd\r
1240#\r
1241# Analyze DSC PCD value, since there is no data type info in DSC\r
f2cc33d8 1242# This function is used to match functions (AnalyzePcdData) used for retrieving PCD value from database\r
67e11e4d 1243# 1. Feature flag: TokenSpace.PcdCName|PcdValue\r
f2cc33d8 1244# 2. Fix and Patch:TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]\r
67e11e4d
YZ
1245# 3. Dynamic default:\r
1246# TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]\r
1247# TokenSpace.PcdCName|PcdValue\r
1248# 4. Dynamic VPD:\r
1249# TokenSpace.PcdCName|VpdOffset[|VpdValue]\r
1250# TokenSpace.PcdCName|VpdOffset[|MaxSize[|VpdValue]]\r
1251# 5. Dynamic HII:\r
f2cc33d8 1252# TokenSpace.PcdCName|HiiString|VariableGuid|VariableOffset[|HiiValue]\r
67e11e4d
YZ
1253# PCD value needs to be located in such kind of string, and the PCD value might be an expression in which\r
1254# there might have "|" operator, also in string value.\r
1255#\r
1256# @param Setting: String contain information described above with "TokenSpace.PcdCName|" stripped\r
1257# @param PcdType: PCD type: feature, fixed, dynamic default VPD HII\r
1258# @param DataType: The datum type of PCD: VOID*, UNIT, BOOL\r
1259# @retval:\r
1260# ValueList: A List contain fields described above\r
1261# IsValid: True if conforming EBNF, otherwise False\r
1262# Index: The index where PcdValue is in ValueList\r
1263#\r
1264def AnalyzeDscPcd(Setting, PcdType, DataType=''):\r
1265 FieldList = AnalyzePcdExpression(Setting)\r
1266\r
f51461c8 1267 IsValid = True\r
f2cc33d8 1268 if PcdType in (MODEL_PCD_FIXED_AT_BUILD, MODEL_PCD_PATCHABLE_IN_MODULE, MODEL_PCD_DYNAMIC_DEFAULT, MODEL_PCD_DYNAMIC_EX_DEFAULT):\r
f51461c8
LG
1269 Value = FieldList[0]\r
1270 Size = ''\r
f2cc33d8 1271 if len(FieldList) > 1 and FieldList[1]:\r
1272 DataType = FieldList[1]\r
ff4d0f85 1273 if FieldList[1] != TAB_VOID and StructPattern.match(FieldList[1]) is None:\r
520365de 1274 IsValid = False\r
f51461c8
LG
1275 if len(FieldList) > 2:\r
1276 Size = FieldList[2]\r
f2cc33d8 1277 if IsValid:\r
1278 if DataType == "":\r
1279 IsValid = (len(FieldList) <= 1)\r
1280 else:\r
1281 IsValid = (len(FieldList) <= 3)\r
520365de
B
1282\r
1283 if Size:\r
1284 try:\r
ccaa7754 1285 int(Size, 16) if Size.upper().startswith("0X") else int(Size)\r
520365de
B
1286 except:\r
1287 IsValid = False\r
1288 Size = -1\r
f2cc33d8 1289 return [str(Value), DataType, str(Size)], IsValid, 0\r
1290 elif PcdType == MODEL_PCD_FEATURE_FLAG:\r
1291 Value = FieldList[0]\r
1292 Size = ''\r
1293 IsValid = (len(FieldList) <= 1)\r
1294 return [Value, DataType, str(Size)], IsValid, 0\r
f51461c8
LG
1295 elif PcdType in (MODEL_PCD_DYNAMIC_VPD, MODEL_PCD_DYNAMIC_EX_VPD):\r
1296 VpdOffset = FieldList[0]\r
1297 Value = Size = ''\r
656d2539 1298 if not DataType == TAB_VOID:\r
f51461c8
LG
1299 if len(FieldList) > 1:\r
1300 Value = FieldList[1]\r
1301 else:\r
1302 if len(FieldList) > 1:\r
1303 Size = FieldList[1]\r
1304 if len(FieldList) > 2:\r
1305 Value = FieldList[2]\r
ae7b6df8
LG
1306 if DataType == "":\r
1307 IsValid = (len(FieldList) <= 1)\r
f51461c8 1308 else:\r
ae7b6df8 1309 IsValid = (len(FieldList) <= 3)\r
520365de
B
1310 if Size:\r
1311 try:\r
ccaa7754 1312 int(Size, 16) if Size.upper().startswith("0X") else int(Size)\r
520365de
B
1313 except:\r
1314 IsValid = False\r
1315 Size = -1\r
1316 return [VpdOffset, str(Size), Value], IsValid, 2\r
f51461c8 1317 elif PcdType in (MODEL_PCD_DYNAMIC_HII, MODEL_PCD_DYNAMIC_EX_HII):\r
24bd035c 1318 IsValid = (3 <= len(FieldList) <= 5)\r
f51461c8 1319 HiiString = FieldList[0]\r
82a6a960 1320 Guid = Offset = Value = Attribute = ''\r
f51461c8
LG
1321 if len(FieldList) > 1:\r
1322 Guid = FieldList[1]\r
1323 if len(FieldList) > 2:\r
1324 Offset = FieldList[2]\r
1325 if len(FieldList) > 3:\r
1326 Value = FieldList[3]\r
82a6a960
BF
1327 if len(FieldList) > 4:\r
1328 Attribute = FieldList[4]\r
82a6a960 1329 return [HiiString, Guid, Offset, Value, Attribute], IsValid, 3\r
f51461c8
LG
1330 return [], False, 0\r
1331\r
1332## AnalyzePcdData\r
1333#\r
1334# Analyze the pcd Value, Datum type and TokenNumber.\r
1335# Used to avoid split issue while the value string contain "|" character\r
1336#\r
1337# @param[in] Setting: A String contain value/datum type/token number information;\r
f7496d71
LG
1338#\r
1339# @retval ValueList: A List contain value, datum type and toke number.\r
f51461c8 1340#\r
47fea6af
YZ
1341def AnalyzePcdData(Setting):\r
1342 ValueList = ['', '', '']\r
1343\r
1344 ValueRe = re.compile(r'^\s*L?\".*\|.*\"')\r
f51461c8 1345 PtrValue = ValueRe.findall(Setting)\r
f7496d71 1346\r
f51461c8 1347 ValueUpdateFlag = False\r
f7496d71 1348\r
f51461c8
LG
1349 if len(PtrValue) >= 1:\r
1350 Setting = re.sub(ValueRe, '', Setting)\r
47fea6af 1351 ValueUpdateFlag = True\r
f51461c8
LG
1352\r
1353 TokenList = Setting.split(TAB_VALUE_SPLIT)\r
1354 ValueList[0:len(TokenList)] = TokenList\r
f7496d71 1355\r
f51461c8
LG
1356 if ValueUpdateFlag:\r
1357 ValueList[0] = PtrValue[0]\r
f7496d71
LG
1358\r
1359 return ValueList\r
1360\r
f51461c8
LG
1361## check format of PCD value against its the datum type\r
1362#\r
1363# For PCD value setting\r
1364#\r
1365def CheckPcdDatum(Type, Value):\r
656d2539 1366 if Type == TAB_VOID:\r
47fea6af 1367 ValueRe = re.compile(r'\s*L?\".*\"\s*$')\r
f51461c8 1368 if not (((Value.startswith('L"') or Value.startswith('"')) and Value.endswith('"'))\r
d5988a8a 1369 or (Value.startswith('{') and Value.endswith('}')) or (Value.startswith("L'") or Value.startswith("'") and Value.endswith("'"))\r
f51461c8
LG
1370 ):\r
1371 return False, "Invalid value [%s] of type [%s]; must be in the form of {...} for array"\\r
d5988a8a 1372 ", \"...\" or \'...\' for string, L\"...\" or L\'...\' for unicode string" % (Value, Type)\r
f51461c8
LG
1373 elif ValueRe.match(Value):\r
1374 # Check the chars in UnicodeString or CString is printable\r
1375 if Value.startswith("L"):\r
1376 Value = Value[2:-1]\r
1377 else:\r
1378 Value = Value[1:-1]\r
1379 Printset = set(string.printable)\r
1380 Printset.remove(TAB_PRINTCHAR_VT)\r
1381 Printset.add(TAB_PRINTCHAR_BS)\r
1382 Printset.add(TAB_PRINTCHAR_NUL)\r
1383 if not set(Value).issubset(Printset):\r
0d1f5b2b 1384 PrintList = sorted(Printset)\r
f51461c8
LG
1385 return False, "Invalid PCD string value of type [%s]; must be printable chars %s." % (Type, PrintList)\r
1386 elif Type == 'BOOLEAN':\r
1387 if Value not in ['TRUE', 'True', 'true', '0x1', '0x01', '1', 'FALSE', 'False', 'false', '0x0', '0x00', '0']:\r
1388 return False, "Invalid value [%s] of type [%s]; must be one of TRUE, True, true, 0x1, 0x01, 1"\\r
1389 ", FALSE, False, false, 0x0, 0x00, 0" % (Value, Type)\r
1390 elif Type in [TAB_UINT8, TAB_UINT16, TAB_UINT32, TAB_UINT64]:\r
94c91295 1391 if Value.startswith('0') and not Value.lower().startswith('0x') and len(Value) > 1 and Value.lstrip('0'):\r
1590d123 1392 Value = Value.lstrip('0')\r
f51461c8 1393 try:\r
1590d123
ZF
1394 if Value and int(Value, 0) < 0:\r
1395 return False, "PCD can't be set to negative value[%s] for datum type [%s]" % (Value, Type)\r
af881abc 1396 Value = int(Value, 0)\r
1ccc4d89
LG
1397 if Value > MAX_VAL_TYPE[Type]:\r
1398 return False, "Too large PCD value[%s] for datum type [%s]" % (Value, Type)\r
f51461c8 1399 except:\r
1ccc4d89
LG
1400 return False, "Invalid value [%s] of type [%s];"\\r
1401 " must be a hexadecimal, decimal or octal in C language format." % (Value, Type)\r
f51461c8 1402 else:\r
ae7b6df8 1403 return True, "StructurePcd"\r
f51461c8
LG
1404\r
1405 return True, ""\r
1406\r
f51461c8
LG
1407def CommonPath(PathList):\r
1408 P1 = min(PathList).split(os.path.sep)\r
1409 P2 = max(PathList).split(os.path.sep)\r
e77e59c9 1410 for Index in range(min(len(P1), len(P2))):\r
f51461c8
LG
1411 if P1[Index] != P2[Index]:\r
1412 return os.path.sep.join(P1[:Index])\r
1413 return os.path.sep.join(P1)\r
1414\r
1415class PathClass(object):\r
1416 def __init__(self, File='', Root='', AlterRoot='', Type='', IsBinary=False,\r
1417 Arch='COMMON', ToolChainFamily='', Target='', TagName='', ToolCode=''):\r
1418 self.Arch = Arch\r
1419 self.File = str(File)\r
1420 if os.path.isabs(self.File):\r
1421 self.Root = ''\r
1422 self.AlterRoot = ''\r
1423 else:\r
1424 self.Root = str(Root)\r
1425 self.AlterRoot = str(AlterRoot)\r
1426\r
1427 # Remove any '.' and '..' in path\r
1428 if self.Root:\r
05cc51ad 1429 self.Root = mws.getWs(self.Root, self.File)\r
f51461c8
LG
1430 self.Path = os.path.normpath(os.path.join(self.Root, self.File))\r
1431 self.Root = os.path.normpath(CommonPath([self.Root, self.Path]))\r
1432 # eliminate the side-effect of 'C:'\r
1433 if self.Root[-1] == ':':\r
1434 self.Root += os.path.sep\r
1435 # file path should not start with path separator\r
1436 if self.Root[-1] == os.path.sep:\r
1437 self.File = self.Path[len(self.Root):]\r
1438 else:\r
47fea6af 1439 self.File = self.Path[len(self.Root) + 1:]\r
f51461c8
LG
1440 else:\r
1441 self.Path = os.path.normpath(self.File)\r
1442\r
1443 self.SubDir, self.Name = os.path.split(self.File)\r
1444 self.BaseName, self.Ext = os.path.splitext(self.Name)\r
1445\r
1446 if self.Root:\r
1447 if self.SubDir:\r
1448 self.Dir = os.path.join(self.Root, self.SubDir)\r
1449 else:\r
1450 self.Dir = self.Root\r
1451 else:\r
1452 self.Dir = self.SubDir\r
1453\r
1454 if IsBinary:\r
1455 self.Type = Type\r
1456 else:\r
1457 self.Type = self.Ext.lower()\r
1458\r
1459 self.IsBinary = IsBinary\r
1460 self.Target = Target\r
1461 self.TagName = TagName\r
1462 self.ToolCode = ToolCode\r
1463 self.ToolChainFamily = ToolChainFamily\r
66b845ae 1464 self.OriginalPath = self\r
f51461c8 1465\r
f51461c8
LG
1466 ## Convert the object of this class to a string\r
1467 #\r
1468 # Convert member Path of the class to a string\r
1469 #\r
1470 # @retval string Formatted String\r
1471 #\r
1472 def __str__(self):\r
1473 return self.Path\r
1474\r
1475 ## Override __eq__ function\r
1476 #\r
1477 # Check whether PathClass are the same\r
1478 #\r
1479 # @retval False The two PathClass are different\r
1480 # @retval True The two PathClass are the same\r
1481 #\r
1482 def __eq__(self, Other):\r
4e375707 1483 return self.Path == str(Other)\r
f51461c8
LG
1484\r
1485 ## Override __cmp__ function\r
1486 #\r
fb0b35e0 1487 # Customize the comparison operation of two PathClass\r
f51461c8
LG
1488 #\r
1489 # @retval 0 The two PathClass are different\r
1490 # @retval -1 The first PathClass is less than the second PathClass\r
1491 # @retval 1 The first PathClass is Bigger than the second PathClass\r
1492 def __cmp__(self, Other):\r
4e375707 1493 OtherKey = str(Other)\r
f7496d71 1494\r
f51461c8
LG
1495 SelfKey = self.Path\r
1496 if SelfKey == OtherKey:\r
1497 return 0\r
1498 elif SelfKey > OtherKey:\r
1499 return 1\r
1500 else:\r
1501 return -1\r
1502\r
1503 ## Override __hash__ function\r
1504 #\r
1505 # Use Path as key in hash table\r
1506 #\r
1507 # @retval string Key for hash table\r
1508 #\r
1509 def __hash__(self):\r
1510 return hash(self.Path)\r
1511\r
6c204ed4
CJ
1512 @cached_property\r
1513 def Key(self):\r
1514 return self.Path.upper()\r
f51461c8 1515\r
6c204ed4
CJ
1516 @property\r
1517 def TimeStamp(self):\r
f51461c8
LG
1518 return os.stat(self.Path)[8]\r
1519\r
1520 def Validate(self, Type='', CaseSensitive=True):\r
42bd1750
CJ
1521 def RealPath2(File, Dir='', OverrideDir=''):\r
1522 NewFile = None\r
1523 if OverrideDir:\r
1524 NewFile = GlobalData.gAllFiles[os.path.normpath(os.path.join(OverrideDir, File))]\r
1525 if NewFile:\r
1526 if OverrideDir[-1] == os.path.sep:\r
1527 return NewFile[len(OverrideDir):], NewFile[0:len(OverrideDir)]\r
1528 else:\r
1529 return NewFile[len(OverrideDir) + 1:], NewFile[0:len(OverrideDir)]\r
1530 if GlobalData.gAllFiles:\r
1531 NewFile = GlobalData.gAllFiles[os.path.normpath(os.path.join(Dir, File))]\r
1532 if not NewFile:\r
1533 NewFile = os.path.normpath(os.path.join(Dir, File))\r
1534 if not os.path.exists(NewFile):\r
1535 return None, None\r
1536 if NewFile:\r
1537 if Dir:\r
1538 if Dir[-1] == os.path.sep:\r
1539 return NewFile[len(Dir):], NewFile[0:len(Dir)]\r
1540 else:\r
1541 return NewFile[len(Dir) + 1:], NewFile[0:len(Dir)]\r
1542 else:\r
1543 return NewFile, ''\r
1544\r
1545 return None, None\r
1546\r
f51461c8
LG
1547 if GlobalData.gCaseInsensitive:\r
1548 CaseSensitive = False\r
1549 if Type and Type.lower() != self.Type:\r
1550 return FILE_TYPE_MISMATCH, '%s (expect %s but got %s)' % (self.File, Type, self.Type)\r
1551\r
1552 RealFile, RealRoot = RealPath2(self.File, self.Root, self.AlterRoot)\r
1553 if not RealRoot and not RealFile:\r
1554 RealFile = self.File\r
1555 if self.AlterRoot:\r
1556 RealFile = os.path.join(self.AlterRoot, self.File)\r
1557 elif self.Root:\r
1558 RealFile = os.path.join(self.Root, self.File)\r
05cc51ad
LY
1559 if len (mws.getPkgPath()) == 0:\r
1560 return FILE_NOT_FOUND, os.path.join(self.AlterRoot, RealFile)\r
1561 else:\r
1562 return FILE_NOT_FOUND, "%s is not found in packages path:\n\t%s" % (self.File, '\n\t'.join(mws.getPkgPath()))\r
f51461c8
LG
1563\r
1564 ErrorCode = 0\r
1565 ErrorInfo = ''\r
1566 if RealRoot != self.Root or RealFile != self.File:\r
1567 if CaseSensitive and (RealFile != self.File or (RealRoot != self.Root and RealRoot != self.AlterRoot)):\r
1568 ErrorCode = FILE_CASE_MISMATCH\r
1569 ErrorInfo = self.File + '\n\t' + RealFile + " [in file system]"\r
1570\r
1571 self.SubDir, self.Name = os.path.split(RealFile)\r
1572 self.BaseName, self.Ext = os.path.splitext(self.Name)\r
1573 if self.SubDir:\r
1574 self.Dir = os.path.join(RealRoot, self.SubDir)\r
1575 else:\r
1576 self.Dir = RealRoot\r
1577 self.File = RealFile\r
1578 self.Root = RealRoot\r
1579 self.Path = os.path.join(RealRoot, RealFile)\r
1580 return ErrorCode, ErrorInfo\r
1581\r
fb0b35e0 1582## Parse PE image to get the required PE information.\r
f51461c8
LG
1583#\r
1584class PeImageClass():\r
1585 ## Constructor\r
1586 #\r
1587 # @param File FilePath of PeImage\r
1588 #\r
1589 def __init__(self, PeFile):\r
1590 self.FileName = PeFile\r
1591 self.IsValid = False\r
1592 self.Size = 0\r
1593 self.EntryPoint = 0\r
1594 self.SectionAlignment = 0\r
1595 self.SectionHeaderList = []\r
1596 self.ErrorInfo = ''\r
1597 try:\r
1598 PeObject = open(PeFile, 'rb')\r
1599 except:\r
1600 self.ErrorInfo = self.FileName + ' can not be found\n'\r
1601 return\r
1602 # Read DOS header\r
1603 ByteArray = array.array('B')\r
1604 ByteArray.fromfile(PeObject, 0x3E)\r
1605 ByteList = ByteArray.tolist()\r
1606 # DOS signature should be 'MZ'\r
1607 if self._ByteListToStr (ByteList[0x0:0x2]) != 'MZ':\r
1608 self.ErrorInfo = self.FileName + ' has no valid DOS signature MZ'\r
1609 return\r
1610\r
1611 # Read 4 byte PE Signature\r
1612 PeOffset = self._ByteListToInt(ByteList[0x3C:0x3E])\r
1613 PeObject.seek(PeOffset)\r
1614 ByteArray = array.array('B')\r
1615 ByteArray.fromfile(PeObject, 4)\r
1616 # PE signature should be 'PE\0\0'\r
d943b0c3 1617 if ByteArray.tostring() != b'PE\0\0':\r
f51461c8
LG
1618 self.ErrorInfo = self.FileName + ' has no valid PE signature PE00'\r
1619 return\r
1620\r
1621 # Read PE file header\r
1622 ByteArray = array.array('B')\r
1623 ByteArray.fromfile(PeObject, 0x14)\r
1624 ByteList = ByteArray.tolist()\r
1625 SecNumber = self._ByteListToInt(ByteList[0x2:0x4])\r
1626 if SecNumber == 0:\r
1627 self.ErrorInfo = self.FileName + ' has no section header'\r
1628 return\r
1629\r
1630 # Read PE optional header\r
1631 OptionalHeaderSize = self._ByteListToInt(ByteArray[0x10:0x12])\r
1632 ByteArray = array.array('B')\r
1633 ByteArray.fromfile(PeObject, OptionalHeaderSize)\r
1634 ByteList = ByteArray.tolist()\r
1635 self.EntryPoint = self._ByteListToInt(ByteList[0x10:0x14])\r
1636 self.SectionAlignment = self._ByteListToInt(ByteList[0x20:0x24])\r
1637 self.Size = self._ByteListToInt(ByteList[0x38:0x3C])\r
1638\r
1639 # Read each Section Header\r
1640 for Index in range(SecNumber):\r
1641 ByteArray = array.array('B')\r
1642 ByteArray.fromfile(PeObject, 0x28)\r
1643 ByteList = ByteArray.tolist()\r
1644 SecName = self._ByteListToStr(ByteList[0:8])\r
1645 SecVirtualSize = self._ByteListToInt(ByteList[8:12])\r
1646 SecRawAddress = self._ByteListToInt(ByteList[20:24])\r
1647 SecVirtualAddress = self._ByteListToInt(ByteList[12:16])\r
1648 self.SectionHeaderList.append((SecName, SecVirtualAddress, SecRawAddress, SecVirtualSize))\r
1649 self.IsValid = True\r
1650 PeObject.close()\r
1651\r
1652 def _ByteListToStr(self, ByteList):\r
1653 String = ''\r
1654 for index in range(len(ByteList)):\r
f7496d71 1655 if ByteList[index] == 0:\r
f51461c8
LG
1656 break\r
1657 String += chr(ByteList[index])\r
1658 return String\r
1659\r
1660 def _ByteListToInt(self, ByteList):\r
1661 Value = 0\r
1662 for index in range(len(ByteList) - 1, -1, -1):\r
1663 Value = (Value << 8) | int(ByteList[index])\r
1664 return Value\r
1665\r
8518bf0b 1666class DefaultStore():\r
ccaa7754 1667 def __init__(self, DefaultStores ):\r
8518bf0b
LG
1668\r
1669 self.DefaultStores = DefaultStores\r
ccaa7754
GL
1670 def DefaultStoreID(self, DefaultStoreName):\r
1671 for key, value in self.DefaultStores.items():\r
8518bf0b
LG
1672 if value == DefaultStoreName:\r
1673 return key\r
1674 return None\r
1675 def GetDefaultDefault(self):\r
1676 if not self.DefaultStores or "0" in self.DefaultStores:\r
ccaa7754 1677 return "0", TAB_DEFAULT_STORES_DEFAULT\r
8518bf0b 1678 else:\r
8252e6bf 1679 minvalue = min(int(value_str) for value_str in self.DefaultStores)\r
8518bf0b 1680 return (str(minvalue), self.DefaultStores[str(minvalue)])\r
ccaa7754 1681 def GetMin(self, DefaultSIdList):\r
8518bf0b 1682 if not DefaultSIdList:\r
4d3b9389 1683 return TAB_DEFAULT_STORES_DEFAULT\r
2b8a6c44
LG
1684 storeidset = {storeid for storeid, storename in self.DefaultStores.values() if storename in DefaultSIdList}\r
1685 if not storeidset:\r
1686 return ""\r
1687 minid = min(storeidset )\r
ccaa7754 1688 for sid, name in self.DefaultStores.values():\r
8518bf0b
LG
1689 if sid == minid:\r
1690 return name\r
f7496d71 1691\r
6c204ed4 1692class SkuClass():\r
f51461c8
LG
1693 DEFAULT = 0\r
1694 SINGLE = 1\r
1695 MULTIPLE =2\r
f7496d71 1696\r
8518bf0b
LG
1697 def __init__(self,SkuIdentifier='', SkuIds=None):\r
1698 if SkuIds is None:\r
1699 SkuIds = {}\r
c05c2c05
LG
1700\r
1701 for SkuName in SkuIds:\r
1702 SkuId = SkuIds[SkuName][0]\r
ccaa7754 1703 skuid_num = int(SkuId, 16) if SkuId.upper().startswith("0X") else int(SkuId)\r
e4ff28c3 1704 if skuid_num > 0xFFFFFFFFFFFFFFFF:\r
c05c2c05 1705 EdkLogger.error("build", PARAMETER_INVALID,\r
e4ff28c3
LG
1706 ExtraData = "SKU-ID [%s] value %s exceeds the max value of UINT64"\r
1707 % (SkuName, SkuId))\r
f7496d71 1708\r
f56c83f8 1709 self.AvailableSkuIds = OrderedDict()\r
f51461c8 1710 self.SkuIdSet = []\r
1ae469b9 1711 self.SkuIdNumberSet = []\r
8518bf0b 1712 self.SkuData = SkuIds\r
6c204ed4
CJ
1713 self._SkuInherit = {}\r
1714 self._SkuIdentifier = SkuIdentifier\r
f51461c8
LG
1715 if SkuIdentifier == '' or SkuIdentifier is None:\r
1716 self.SkuIdSet = ['DEFAULT']\r
1ae469b9 1717 self.SkuIdNumberSet = ['0U']\r
f51461c8 1718 elif SkuIdentifier == 'ALL':\r
f8d11e5a 1719 self.SkuIdSet = list(SkuIds.keys())\r
8518bf0b 1720 self.SkuIdNumberSet = [num[0].strip() + 'U' for num in SkuIds.values()]\r
f51461c8 1721 else:\r
f7496d71 1722 r = SkuIdentifier.split('|')\r
8518bf0b 1723 self.SkuIdSet=[(r[k].strip()).upper() for k in range(len(r))]\r
1ae469b9 1724 k = None\r
f7496d71 1725 try:\r
8518bf0b 1726 self.SkuIdNumberSet = [SkuIds[k][0].strip() + 'U' for k in self.SkuIdSet]\r
1ae469b9
BF
1727 except Exception:\r
1728 EdkLogger.error("build", PARAMETER_INVALID,\r
1729 ExtraData = "SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"\r
6035094d 1730 % (k, " | ".join(SkuIds.keys())))\r
f51461c8
LG
1731 for each in self.SkuIdSet:\r
1732 if each in SkuIds:\r
8518bf0b 1733 self.AvailableSkuIds[each] = SkuIds[each][0]\r
f51461c8
LG
1734 else:\r
1735 EdkLogger.error("build", PARAMETER_INVALID,\r
1736 ExtraData="SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"\r
6035094d 1737 % (each, " | ".join(SkuIds.keys())))\r
6c204ed4 1738 if self.SkuUsageType != SkuClass.SINGLE:\r
8518bf0b 1739 self.AvailableSkuIds.update({'DEFAULT':0, 'COMMON':0})\r
e651d06c
LG
1740 if self.SkuIdSet:\r
1741 GlobalData.gSkuids = (self.SkuIdSet)\r
1742 if 'COMMON' in GlobalData.gSkuids:\r
1743 GlobalData.gSkuids.remove('COMMON')\r
8aaa8f7b
YZ
1744 if self.SkuUsageType == self.SINGLE:\r
1745 if len(GlobalData.gSkuids) != 1:\r
1746 if 'DEFAULT' in GlobalData.gSkuids:\r
1747 GlobalData.gSkuids.remove('DEFAULT')\r
e651d06c
LG
1748 if GlobalData.gSkuids:\r
1749 GlobalData.gSkuids.sort()\r
1750\r
8518bf0b 1751 def GetNextSkuId(self, skuname):\r
6c204ed4
CJ
1752 if not self._SkuInherit:\r
1753 self._SkuInherit = {}\r
8518bf0b 1754 for item in self.SkuData.values():\r
6c204ed4
CJ
1755 self._SkuInherit[item[1]]=item[2] if item[2] else "DEFAULT"\r
1756 return self._SkuInherit.get(skuname, "DEFAULT")\r
c05c2c05 1757\r
ccaa7754 1758 def GetSkuChain(self, sku):\r
09c80b07
B
1759 if sku == "DEFAULT":\r
1760 return ["DEFAULT"]\r
c05c2c05
LG
1761 skulist = [sku]\r
1762 nextsku = sku\r
0d1f5b2b 1763 while True:\r
c05c2c05
LG
1764 nextsku = self.GetNextSkuId(nextsku)\r
1765 skulist.append(nextsku)\r
1766 if nextsku == "DEFAULT":\r
1767 break\r
1768 skulist.reverse()\r
1769 return skulist\r
1770 def SkuOverrideOrder(self):\r
1771 skuorderset = []\r
1772 for skuname in self.SkuIdSet:\r
1773 skuorderset.append(self.GetSkuChain(skuname))\r
f7496d71 1774\r
c05c2c05 1775 skuorder = []\r
8252e6bf 1776 for index in range(max(len(item) for item in skuorderset)):\r
c05c2c05
LG
1777 for subset in skuorderset:\r
1778 if index > len(subset)-1:\r
1779 continue\r
1780 if subset[index] in skuorder:\r
1781 continue\r
1782 skuorder.append(subset[index])\r
1783\r
1784 return skuorder\r
1785\r
6c204ed4
CJ
1786 @property\r
1787 def SkuUsageType(self):\r
1788 if self._SkuIdentifier.upper() == "ALL":\r
c05c2c05
LG
1789 return SkuClass.MULTIPLE\r
1790\r
f51461c8
LG
1791 if len(self.SkuIdSet) == 1:\r
1792 if self.SkuIdSet[0] == 'DEFAULT':\r
1793 return SkuClass.DEFAULT\r
6c204ed4
CJ
1794 return SkuClass.SINGLE\r
1795 if len(self.SkuIdSet) == 2 and 'DEFAULT' in self.SkuIdSet:\r
1796 return SkuClass.SINGLE\r
1797 return SkuClass.MULTIPLE\r
f51461c8 1798\r
6c204ed4 1799 def DumpSkuIdArrary(self):\r
8518bf0b 1800 if self.SkuUsageType == SkuClass.SINGLE:\r
6c204ed4
CJ
1801 return "{0x0}"\r
1802 ArrayStrList = []\r
1803 for skuname in self.AvailableSkuIds:\r
1804 if skuname == "COMMON":\r
1805 continue\r
1806 while skuname != "DEFAULT":\r
1807 ArrayStrList.append(hex(int(self.AvailableSkuIds[skuname])))\r
1808 skuname = self.GetNextSkuId(skuname)\r
1809 ArrayStrList.append("0x0")\r
1810 return "{{{myList}}}".format(myList=",".join(ArrayStrList))\r
1811\r
1812 @property\r
1813 def AvailableSkuIdSet(self):\r
f51461c8 1814 return self.AvailableSkuIds\r
f7496d71 1815\r
6c204ed4
CJ
1816 @property\r
1817 def SystemSkuId(self):\r
1818 if self.SkuUsageType == SkuClass.SINGLE:\r
c05c2c05
LG
1819 if len(self.SkuIdSet) == 1:\r
1820 return self.SkuIdSet[0]\r
1821 else:\r
1822 return self.SkuIdSet[0] if self.SkuIdSet[0] != 'DEFAULT' else self.SkuIdSet[1]\r
f51461c8
LG
1823 else:\r
1824 return 'DEFAULT'\r
a3251d84 1825\r
34952f49
LG
1826## Get the integer value from string like "14U" or integer like 2\r
1827#\r
1828# @param Input The object that may be either a integer value or a string\r
1829#\r
1830# @retval Value The integer value that the input represents\r
1831#\r
1832def GetIntegerValue(Input):\r
d943b0c3 1833 if not isinstance(Input, str):\r
34952f49
LG
1834 return Input\r
1835 String = Input\r
1836 if String.endswith("U"):\r
1837 String = String[:-1]\r
1838 if String.endswith("ULL"):\r
1839 String = String[:-3]\r
1840 if String.endswith("LL"):\r
1841 String = String[:-2]\r
1842\r
1843 if String.startswith("0x") or String.startswith("0X"):\r
1844 return int(String, 16)\r
1845 elif String == '':\r
1846 return 0\r
1847 else:\r
1848 return int(String)\r
db55dac7 1849\r
d0a0c52c
CJ
1850#\r
1851# Pack a GUID (registry format) list into a buffer and return it\r
1852#\r
1853def PackGUID(Guid):\r
1854 return pack(PACK_PATTERN_GUID,\r
1855 int(Guid[0], 16),\r
1856 int(Guid[1], 16),\r
1857 int(Guid[2], 16),\r
1858 int(Guid[3][-4:-2], 16),\r
1859 int(Guid[3][-2:], 16),\r
1860 int(Guid[4][-12:-10], 16),\r
1861 int(Guid[4][-10:-8], 16),\r
1862 int(Guid[4][-8:-6], 16),\r
1863 int(Guid[4][-6:-4], 16),\r
1864 int(Guid[4][-4:-2], 16),\r
1865 int(Guid[4][-2:], 16)\r
1866 )\r
1867\r
1868#\r
1869# Pack a GUID (byte) list into a buffer and return it\r
1870#\r
1871def PackByteFormatGUID(Guid):\r
1872 return pack(PACK_PATTERN_GUID,\r
1873 Guid[0],\r
1874 Guid[1],\r
1875 Guid[2],\r
1876 Guid[3],\r
1877 Guid[4],\r
1878 Guid[5],\r
1879 Guid[6],\r
1880 Guid[7],\r
1881 Guid[8],\r
1882 Guid[9],\r
1883 Guid[10],\r
1884 )\r
1885\r
bf9e6366
B
1886## DeepCopy dict/OrderedDict recusively\r
1887#\r
1888# @param ori_dict a nested dict or ordereddict\r
1889#\r
1890# @retval new dict or orderdict\r
1891#\r
1892def CopyDict(ori_dict):\r
1893 dict_type = ori_dict.__class__\r
9bf86b12
FB
1894 if dict_type not in (dict,OrderedDict):\r
1895 return ori_dict\r
bf9e6366
B
1896 new_dict = dict_type()\r
1897 for key in ori_dict:\r
1898 if isinstance(ori_dict[key],(dict,OrderedDict)):\r
1899 new_dict[key] = CopyDict(ori_dict[key])\r
1900 else:\r
1901 new_dict[key] = ori_dict[key]\r
1902 return new_dict\r
4c6e6f9f
FB
1903\r
1904#\r
1905# Remove the c/c++ comments: // and /* */\r
1906#\r
1907def RemoveCComments(ctext):\r
1908 return re.sub('//.*?\n|/\*.*?\*/', '\n', ctext, flags=re.S)\r