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