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