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