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