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