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