]> git.proxmox.com Git - mirror_edk2.git/blob - BaseTools/Source/Python/Common/Misc.py
BaseTools: Decouple AutoGen Objects
[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
653 ## A string template class
654 #
655 # This class implements a template for string replacement. A string template
656 # looks like following
657 #
658 # ${BEGIN} other_string ${placeholder_name} other_string ${END}
659 #
660 # The string between ${BEGIN} and ${END} will be repeated as many times as the
661 # length of "placeholder_name", which is a list passed through a dict. The
662 # "placeholder_name" is the key name of the dict. The ${BEGIN} and ${END} can
663 # be not used and, in this case, the "placeholder_name" must not a list and it
664 # will just be replaced once.
665 #
666 class TemplateString(object):
667 _REPEAT_START_FLAG = "BEGIN"
668 _REPEAT_END_FLAG = "END"
669
670 class Section(object):
671 _LIST_TYPES = [type([]), type(set()), type((0,))]
672
673 def __init__(self, TemplateSection, PlaceHolderList):
674 self._Template = TemplateSection
675 self._PlaceHolderList = []
676
677 # Split the section into sub-sections according to the position of placeholders
678 if PlaceHolderList:
679 self._SubSectionList = []
680 SubSectionStart = 0
681 #
682 # The placeholders passed in must be in the format of
683 #
684 # PlaceHolderName, PlaceHolderStartPoint, PlaceHolderEndPoint
685 #
686 for PlaceHolder, Start, End in PlaceHolderList:
687 self._SubSectionList.append(TemplateSection[SubSectionStart:Start])
688 self._SubSectionList.append(TemplateSection[Start:End])
689 self._PlaceHolderList.append(PlaceHolder)
690 SubSectionStart = End
691 if SubSectionStart < len(TemplateSection):
692 self._SubSectionList.append(TemplateSection[SubSectionStart:])
693 else:
694 self._SubSectionList = [TemplateSection]
695
696 def __str__(self):
697 return self._Template + " : " + str(self._PlaceHolderList)
698
699 def Instantiate(self, PlaceHolderValues):
700 RepeatTime = -1
701 RepeatPlaceHolders = {}
702 NonRepeatPlaceHolders = {}
703
704 for PlaceHolder in self._PlaceHolderList:
705 if PlaceHolder not in PlaceHolderValues:
706 continue
707 Value = PlaceHolderValues[PlaceHolder]
708 if type(Value) in self._LIST_TYPES:
709 if RepeatTime < 0:
710 RepeatTime = len(Value)
711 elif RepeatTime != len(Value):
712 EdkLogger.error(
713 "TemplateString",
714 PARAMETER_INVALID,
715 "${%s} has different repeat time from others!" % PlaceHolder,
716 ExtraData=str(self._Template)
717 )
718 RepeatPlaceHolders["${%s}" % PlaceHolder] = Value
719 else:
720 NonRepeatPlaceHolders["${%s}" % PlaceHolder] = Value
721
722 if NonRepeatPlaceHolders:
723 StringList = []
724 for S in self._SubSectionList:
725 if S not in NonRepeatPlaceHolders:
726 StringList.append(S)
727 else:
728 StringList.append(str(NonRepeatPlaceHolders[S]))
729 else:
730 StringList = self._SubSectionList
731
732 if RepeatPlaceHolders:
733 TempStringList = []
734 for Index in range(RepeatTime):
735 for S in StringList:
736 if S not in RepeatPlaceHolders:
737 TempStringList.append(S)
738 else:
739 TempStringList.append(str(RepeatPlaceHolders[S][Index]))
740 StringList = TempStringList
741
742 return "".join(StringList)
743
744 ## Constructor
745 def __init__(self, Template=None):
746 self.String = []
747 self.IsBinary = False
748 self._Template = Template
749 self._TemplateSectionList = self._Parse(Template)
750
751 ## str() operator
752 #
753 # @retval string The string replaced
754 #
755 def __str__(self):
756 return "".join(self.String)
757
758 ## Split the template string into fragments per the ${BEGIN} and ${END} flags
759 #
760 # @retval list A list of TemplateString.Section objects
761 #
762 def _Parse(self, Template):
763 SectionStart = 0
764 SearchFrom = 0
765 MatchEnd = 0
766 PlaceHolderList = []
767 TemplateSectionList = []
768 while Template:
769 MatchObj = gPlaceholderPattern.search(Template, SearchFrom)
770 if not MatchObj:
771 if MatchEnd <= len(Template):
772 TemplateSection = TemplateString.Section(Template[SectionStart:], PlaceHolderList)
773 TemplateSectionList.append(TemplateSection)
774 break
775
776 MatchString = MatchObj.group(1)
777 MatchStart = MatchObj.start()
778 MatchEnd = MatchObj.end()
779
780 if MatchString == self._REPEAT_START_FLAG:
781 if MatchStart > SectionStart:
782 TemplateSection = TemplateString.Section(Template[SectionStart:MatchStart], PlaceHolderList)
783 TemplateSectionList.append(TemplateSection)
784 SectionStart = MatchEnd
785 PlaceHolderList = []
786 elif MatchString == self._REPEAT_END_FLAG:
787 TemplateSection = TemplateString.Section(Template[SectionStart:MatchStart], PlaceHolderList)
788 TemplateSectionList.append(TemplateSection)
789 SectionStart = MatchEnd
790 PlaceHolderList = []
791 else:
792 PlaceHolderList.append((MatchString, MatchStart - SectionStart, MatchEnd - SectionStart))
793 SearchFrom = MatchEnd
794 return TemplateSectionList
795
796 ## Replace the string template with dictionary of placeholders and append it to previous one
797 #
798 # @param AppendString The string template to append
799 # @param Dictionary The placeholder dictionaries
800 #
801 def Append(self, AppendString, Dictionary=None):
802 if Dictionary:
803 SectionList = self._Parse(AppendString)
804 self.String.append( "".join(S.Instantiate(Dictionary) for S in SectionList))
805 else:
806 if isinstance(AppendString,list):
807 self.String.extend(AppendString)
808 else:
809 self.String.append(AppendString)
810
811 ## Replace the string template with dictionary of placeholders
812 #
813 # @param Dictionary The placeholder dictionaries
814 #
815 # @retval str The string replaced with placeholder values
816 #
817 def Replace(self, Dictionary=None):
818 return "".join(S.Instantiate(Dictionary) for S in self._TemplateSectionList)
819
820 ## Progress indicator class
821 #
822 # This class makes use of thread to print progress on console.
823 #
824 class Progressor:
825 # for avoiding deadloop
826 _StopFlag = None
827 _ProgressThread = None
828 _CheckInterval = 0.25
829
830 ## Constructor
831 #
832 # @param OpenMessage The string printed before progress characters
833 # @param CloseMessage The string printed after progress characters
834 # @param ProgressChar The character used to indicate the progress
835 # @param Interval The interval in seconds between two progress characters
836 #
837 def __init__(self, OpenMessage="", CloseMessage="", ProgressChar='.', Interval=1.0):
838 self.PromptMessage = OpenMessage
839 self.CodaMessage = CloseMessage
840 self.ProgressChar = ProgressChar
841 self.Interval = Interval
842 if Progressor._StopFlag is None:
843 Progressor._StopFlag = threading.Event()
844
845 ## Start to print progress character
846 #
847 # @param OpenMessage The string printed before progress characters
848 #
849 def Start(self, OpenMessage=None):
850 if OpenMessage is not None:
851 self.PromptMessage = OpenMessage
852 Progressor._StopFlag.clear()
853 if Progressor._ProgressThread is None:
854 Progressor._ProgressThread = threading.Thread(target=self._ProgressThreadEntry)
855 Progressor._ProgressThread.setDaemon(False)
856 Progressor._ProgressThread.start()
857
858 ## Stop printing progress character
859 #
860 # @param CloseMessage The string printed after progress characters
861 #
862 def Stop(self, CloseMessage=None):
863 OriginalCodaMessage = self.CodaMessage
864 if CloseMessage is not None:
865 self.CodaMessage = CloseMessage
866 self.Abort()
867 self.CodaMessage = OriginalCodaMessage
868
869 ## Thread entry method
870 def _ProgressThreadEntry(self):
871 sys.stdout.write(self.PromptMessage + " ")
872 sys.stdout.flush()
873 TimeUp = 0.0
874 while not Progressor._StopFlag.isSet():
875 if TimeUp <= 0.0:
876 sys.stdout.write(self.ProgressChar)
877 sys.stdout.flush()
878 TimeUp = self.Interval
879 time.sleep(self._CheckInterval)
880 TimeUp -= self._CheckInterval
881 sys.stdout.write(" " + self.CodaMessage + "\n")
882 sys.stdout.flush()
883
884 ## Abort the progress display
885 @staticmethod
886 def Abort():
887 if Progressor._StopFlag is not None:
888 Progressor._StopFlag.set()
889 if Progressor._ProgressThread is not None:
890 Progressor._ProgressThread.join()
891 Progressor._ProgressThread = None
892
893
894 ## Dictionary using prioritized list as key
895 #
896 class tdict:
897 _ListType = type([])
898 _TupleType = type(())
899 _Wildcard = 'COMMON'
900 _ValidWildcardList = ['COMMON', 'DEFAULT', 'ALL', TAB_STAR, 'PLATFORM']
901
902 def __init__(self, _Single_=False, _Level_=2):
903 self._Level_ = _Level_
904 self.data = {}
905 self._Single_ = _Single_
906
907 # =[] operator
908 def __getitem__(self, key):
909 KeyType = type(key)
910 RestKeys = None
911 if KeyType == self._ListType or KeyType == self._TupleType:
912 FirstKey = key[0]
913 if len(key) > 1:
914 RestKeys = key[1:]
915 elif self._Level_ > 1:
916 RestKeys = [self._Wildcard for i in range(0, self._Level_ - 1)]
917 else:
918 FirstKey = key
919 if self._Level_ > 1:
920 RestKeys = [self._Wildcard for i in range(0, self._Level_ - 1)]
921
922 if FirstKey is None or str(FirstKey).upper() in self._ValidWildcardList:
923 FirstKey = self._Wildcard
924
925 if self._Single_:
926 return self._GetSingleValue(FirstKey, RestKeys)
927 else:
928 return self._GetAllValues(FirstKey, RestKeys)
929
930 def _GetSingleValue(self, FirstKey, RestKeys):
931 Value = None
932 #print "%s-%s" % (FirstKey, self._Level_) ,
933 if self._Level_ > 1:
934 if FirstKey == self._Wildcard:
935 if FirstKey in self.data:
936 Value = self.data[FirstKey][RestKeys]
937 if Value is None:
938 for Key in self.data:
939 Value = self.data[Key][RestKeys]
940 if Value is not None: break
941 else:
942 if FirstKey in self.data:
943 Value = self.data[FirstKey][RestKeys]
944 if Value is None and self._Wildcard in self.data:
945 #print "Value=None"
946 Value = self.data[self._Wildcard][RestKeys]
947 else:
948 if FirstKey == self._Wildcard:
949 if FirstKey in self.data:
950 Value = self.data[FirstKey]
951 if Value is None:
952 for Key in self.data:
953 Value = self.data[Key]
954 if Value is not None: break
955 else:
956 if FirstKey in self.data:
957 Value = self.data[FirstKey]
958 elif self._Wildcard in self.data:
959 Value = self.data[self._Wildcard]
960 return Value
961
962 def _GetAllValues(self, FirstKey, RestKeys):
963 Value = []
964 if self._Level_ > 1:
965 if FirstKey == self._Wildcard:
966 for Key in self.data:
967 Value += self.data[Key][RestKeys]
968 else:
969 if FirstKey in self.data:
970 Value += self.data[FirstKey][RestKeys]
971 if self._Wildcard in self.data:
972 Value += self.data[self._Wildcard][RestKeys]
973 else:
974 if FirstKey == self._Wildcard:
975 for Key in self.data:
976 Value.append(self.data[Key])
977 else:
978 if FirstKey in self.data:
979 Value.append(self.data[FirstKey])
980 if self._Wildcard in self.data:
981 Value.append(self.data[self._Wildcard])
982 return Value
983
984 ## []= operator
985 def __setitem__(self, key, value):
986 KeyType = type(key)
987 RestKeys = None
988 if KeyType == self._ListType or KeyType == self._TupleType:
989 FirstKey = key[0]
990 if len(key) > 1:
991 RestKeys = key[1:]
992 else:
993 RestKeys = [self._Wildcard for i in range(0, self._Level_ - 1)]
994 else:
995 FirstKey = key
996 if self._Level_ > 1:
997 RestKeys = [self._Wildcard for i in range(0, self._Level_ - 1)]
998
999 if FirstKey in self._ValidWildcardList:
1000 FirstKey = self._Wildcard
1001
1002 if FirstKey not in self.data and self._Level_ > 0:
1003 self.data[FirstKey] = tdict(self._Single_, self._Level_ - 1)
1004
1005 if self._Level_ > 1:
1006 self.data[FirstKey][RestKeys] = value
1007 else:
1008 self.data[FirstKey] = value
1009
1010 def SetGreedyMode(self):
1011 self._Single_ = False
1012 if self._Level_ > 1:
1013 for Key in self.data:
1014 self.data[Key].SetGreedyMode()
1015
1016 def SetSingleMode(self):
1017 self._Single_ = True
1018 if self._Level_ > 1:
1019 for Key in self.data:
1020 self.data[Key].SetSingleMode()
1021
1022 def GetKeys(self, KeyIndex=0):
1023 assert KeyIndex >= 0
1024 if KeyIndex == 0:
1025 return set(self.data.keys())
1026 else:
1027 keys = set()
1028 for Key in self.data:
1029 keys |= self.data[Key].GetKeys(KeyIndex - 1)
1030 return keys
1031
1032 def AnalyzePcdExpression(Setting):
1033 RanStr = ''.join(sample(string.ascii_letters + string.digits, 8))
1034 Setting = Setting.replace('\\\\', RanStr).strip()
1035 # There might be escaped quote in a string: \", \\\" , \', \\\'
1036 Data = Setting
1037 # There might be '|' in string and in ( ... | ... ), replace it with '-'
1038 NewStr = ''
1039 InSingleQuoteStr = False
1040 InDoubleQuoteStr = False
1041 Pair = 0
1042 for Index, ch in enumerate(Data):
1043 if ch == '"' and not InSingleQuoteStr:
1044 if Data[Index - 1] != '\\':
1045 InDoubleQuoteStr = not InDoubleQuoteStr
1046 elif ch == "'" and not InDoubleQuoteStr:
1047 if Data[Index - 1] != '\\':
1048 InSingleQuoteStr = not InSingleQuoteStr
1049 elif ch == '(' and not (InSingleQuoteStr or InDoubleQuoteStr):
1050 Pair += 1
1051 elif ch == ')' and not (InSingleQuoteStr or InDoubleQuoteStr):
1052 Pair -= 1
1053
1054 if (Pair > 0 or InSingleQuoteStr or InDoubleQuoteStr) and ch == TAB_VALUE_SPLIT:
1055 NewStr += '-'
1056 else:
1057 NewStr += ch
1058 FieldList = []
1059 StartPos = 0
1060 while True:
1061 Pos = NewStr.find(TAB_VALUE_SPLIT, StartPos)
1062 if Pos < 0:
1063 FieldList.append(Setting[StartPos:].strip())
1064 break
1065 FieldList.append(Setting[StartPos:Pos].strip())
1066 StartPos = Pos + 1
1067 for i, ch in enumerate(FieldList):
1068 if RanStr in ch:
1069 FieldList[i] = ch.replace(RanStr,'\\\\')
1070 return FieldList
1071
1072 def ParseFieldValue (Value):
1073 def ParseDevPathValue (Value):
1074 if '\\' in Value:
1075 Value.replace('\\', '/').replace(' ', '')
1076
1077 Cmd = 'DevicePath ' + '"' + Value + '"'
1078 try:
1079 p = subprocess.Popen(Cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
1080 out, err = p.communicate()
1081 except Exception as X:
1082 raise BadExpression("DevicePath: %s" % (str(X)) )
1083 finally:
1084 subprocess._cleanup()
1085 p.stdout.close()
1086 p.stderr.close()
1087 if err:
1088 raise BadExpression("DevicePath: %s" % str(err))
1089 out = out.decode()
1090 Size = len(out.split())
1091 out = ','.join(out.split())
1092 return '{' + out + '}', Size
1093
1094 if "{CODE(" in Value:
1095 return Value, len(Value.split(","))
1096 if isinstance(Value, type(0)):
1097 return Value, (Value.bit_length() + 7) // 8
1098 if not isinstance(Value, type('')):
1099 raise BadExpression('Type %s is %s' %(Value, type(Value)))
1100 Value = Value.strip()
1101 if Value.startswith(TAB_UINT8) and Value.endswith(')'):
1102 Value, Size = ParseFieldValue(Value.split('(', 1)[1][:-1])
1103 if Size > 1:
1104 raise BadExpression('Value (%s) Size larger than %d' %(Value, Size))
1105 return Value, 1
1106 if Value.startswith(TAB_UINT16) and Value.endswith(')'):
1107 Value, Size = ParseFieldValue(Value.split('(', 1)[1][:-1])
1108 if Size > 2:
1109 raise BadExpression('Value (%s) Size larger than %d' %(Value, Size))
1110 return Value, 2
1111 if Value.startswith(TAB_UINT32) and Value.endswith(')'):
1112 Value, Size = ParseFieldValue(Value.split('(', 1)[1][:-1])
1113 if Size > 4:
1114 raise BadExpression('Value (%s) Size larger than %d' %(Value, Size))
1115 return Value, 4
1116 if Value.startswith(TAB_UINT64) and Value.endswith(')'):
1117 Value, Size = ParseFieldValue(Value.split('(', 1)[1][:-1])
1118 if Size > 8:
1119 raise BadExpression('Value (%s) Size larger than %d' % (Value, Size))
1120 return Value, 8
1121 if Value.startswith(TAB_GUID) and Value.endswith(')'):
1122 Value = Value.split('(', 1)[1][:-1].strip()
1123 if Value[0] == '{' and Value[-1] == '}':
1124 TmpValue = GuidStructureStringToGuidString(Value)
1125 if not TmpValue:
1126 raise BadExpression("Invalid GUID value string %s" % Value)
1127 Value = TmpValue
1128 if Value[0] == '"' and Value[-1] == '"':
1129 Value = Value[1:-1]
1130 try:
1131 Value = str(uuid.UUID(Value).bytes_le)
1132 if Value.startswith("b'"):
1133 Value = Value[2:-1]
1134 Value = "'" + Value + "'"
1135 except ValueError as Message:
1136 raise BadExpression(Message)
1137 Value, Size = ParseFieldValue(Value)
1138 return Value, 16
1139 if Value.startswith('L"') and Value.endswith('"'):
1140 # Unicode String
1141 # translate escape character
1142 Value = Value[1:]
1143 try:
1144 Value = eval(Value)
1145 except:
1146 Value = Value[1:-1]
1147 List = list(Value)
1148 List.reverse()
1149 Value = 0
1150 for Char in List:
1151 Value = (Value << 16) | ord(Char)
1152 return Value, (len(List) + 1) * 2
1153 if Value.startswith('"') and Value.endswith('"'):
1154 # ASCII String
1155 # translate escape character
1156 try:
1157 Value = eval(Value)
1158 except:
1159 Value = Value[1:-1]
1160 List = list(Value)
1161 List.reverse()
1162 Value = 0
1163 for Char in List:
1164 Value = (Value << 8) | ord(Char)
1165 return Value, len(List) + 1
1166 if Value.startswith("L'") and Value.endswith("'"):
1167 # Unicode Character Constant
1168 # translate escape character
1169 Value = Value[1:]
1170 try:
1171 Value = eval(Value)
1172 except:
1173 Value = Value[1:-1]
1174 List = list(Value)
1175 if len(List) == 0:
1176 raise BadExpression('Length %s is %s' % (Value, len(List)))
1177 List.reverse()
1178 Value = 0
1179 for Char in List:
1180 Value = (Value << 16) | ord(Char)
1181 return Value, len(List) * 2
1182 if Value.startswith("'") and Value.endswith("'"):
1183 # Character constant
1184 # translate escape character
1185 try:
1186 Value = eval(Value)
1187 except:
1188 Value = Value[1:-1]
1189 List = list(Value)
1190 if len(List) == 0:
1191 raise BadExpression('Length %s is %s' % (Value, len(List)))
1192 List.reverse()
1193 Value = 0
1194 for Char in List:
1195 Value = (Value << 8) | ord(Char)
1196 return Value, len(List)
1197 if Value.startswith('{') and Value.endswith('}'):
1198 # Byte array
1199 Value = Value[1:-1]
1200 List = [Item.strip() for Item in Value.split(',')]
1201 List.reverse()
1202 Value = 0
1203 RetSize = 0
1204 for Item in List:
1205 ItemValue, Size = ParseFieldValue(Item)
1206 RetSize += Size
1207 for I in range(Size):
1208 Value = (Value << 8) | ((ItemValue >> 8 * I) & 0xff)
1209 return Value, RetSize
1210 if Value.startswith('DEVICE_PATH(') and Value.endswith(')'):
1211 Value = Value.replace("DEVICE_PATH(", '').rstrip(')')
1212 Value = Value.strip().strip('"')
1213 return ParseDevPathValue(Value)
1214 if Value.lower().startswith('0x'):
1215 try:
1216 Value = int(Value, 16)
1217 except:
1218 raise BadExpression("invalid hex value: %s" % Value)
1219 if Value == 0:
1220 return 0, 1
1221 return Value, (Value.bit_length() + 7) // 8
1222 if Value[0].isdigit():
1223 Value = int(Value, 10)
1224 if Value == 0:
1225 return 0, 1
1226 return Value, (Value.bit_length() + 7) // 8
1227 if Value.lower() == 'true':
1228 return 1, 1
1229 if Value.lower() == 'false':
1230 return 0, 1
1231 return Value, 1
1232
1233 ## AnalyzeDscPcd
1234 #
1235 # Analyze DSC PCD value, since there is no data type info in DSC
1236 # This function is used to match functions (AnalyzePcdData) used for retrieving PCD value from database
1237 # 1. Feature flag: TokenSpace.PcdCName|PcdValue
1238 # 2. Fix and Patch:TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1239 # 3. Dynamic default:
1240 # TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1241 # TokenSpace.PcdCName|PcdValue
1242 # 4. Dynamic VPD:
1243 # TokenSpace.PcdCName|VpdOffset[|VpdValue]
1244 # TokenSpace.PcdCName|VpdOffset[|MaxSize[|VpdValue]]
1245 # 5. Dynamic HII:
1246 # TokenSpace.PcdCName|HiiString|VariableGuid|VariableOffset[|HiiValue]
1247 # PCD value needs to be located in such kind of string, and the PCD value might be an expression in which
1248 # there might have "|" operator, also in string value.
1249 #
1250 # @param Setting: String contain information described above with "TokenSpace.PcdCName|" stripped
1251 # @param PcdType: PCD type: feature, fixed, dynamic default VPD HII
1252 # @param DataType: The datum type of PCD: VOID*, UNIT, BOOL
1253 # @retval:
1254 # ValueList: A List contain fields described above
1255 # IsValid: True if conforming EBNF, otherwise False
1256 # Index: The index where PcdValue is in ValueList
1257 #
1258 def AnalyzeDscPcd(Setting, PcdType, DataType=''):
1259 FieldList = AnalyzePcdExpression(Setting)
1260
1261 IsValid = True
1262 if PcdType in (MODEL_PCD_FIXED_AT_BUILD, MODEL_PCD_PATCHABLE_IN_MODULE, MODEL_PCD_DYNAMIC_DEFAULT, MODEL_PCD_DYNAMIC_EX_DEFAULT):
1263 Value = FieldList[0]
1264 Size = ''
1265 if len(FieldList) > 1 and FieldList[1]:
1266 DataType = FieldList[1]
1267 if FieldList[1] != TAB_VOID and StructPattern.match(FieldList[1]) is None:
1268 IsValid = False
1269 if len(FieldList) > 2:
1270 Size = FieldList[2]
1271 if IsValid:
1272 if DataType == "":
1273 IsValid = (len(FieldList) <= 1)
1274 else:
1275 IsValid = (len(FieldList) <= 3)
1276
1277 if Size:
1278 try:
1279 int(Size, 16) if Size.upper().startswith("0X") else int(Size)
1280 except:
1281 IsValid = False
1282 Size = -1
1283 return [str(Value), DataType, str(Size)], IsValid, 0
1284 elif PcdType == MODEL_PCD_FEATURE_FLAG:
1285 Value = FieldList[0]
1286 Size = ''
1287 IsValid = (len(FieldList) <= 1)
1288 return [Value, DataType, str(Size)], IsValid, 0
1289 elif PcdType in (MODEL_PCD_DYNAMIC_VPD, MODEL_PCD_DYNAMIC_EX_VPD):
1290 VpdOffset = FieldList[0]
1291 Value = Size = ''
1292 if not DataType == TAB_VOID:
1293 if len(FieldList) > 1:
1294 Value = FieldList[1]
1295 else:
1296 if len(FieldList) > 1:
1297 Size = FieldList[1]
1298 if len(FieldList) > 2:
1299 Value = FieldList[2]
1300 if DataType == "":
1301 IsValid = (len(FieldList) <= 1)
1302 else:
1303 IsValid = (len(FieldList) <= 3)
1304 if Size:
1305 try:
1306 int(Size, 16) if Size.upper().startswith("0X") else int(Size)
1307 except:
1308 IsValid = False
1309 Size = -1
1310 return [VpdOffset, str(Size), Value], IsValid, 2
1311 elif PcdType in (MODEL_PCD_DYNAMIC_HII, MODEL_PCD_DYNAMIC_EX_HII):
1312 IsValid = (3 <= len(FieldList) <= 5)
1313 HiiString = FieldList[0]
1314 Guid = Offset = Value = Attribute = ''
1315 if len(FieldList) > 1:
1316 Guid = FieldList[1]
1317 if len(FieldList) > 2:
1318 Offset = FieldList[2]
1319 if len(FieldList) > 3:
1320 Value = FieldList[3]
1321 if len(FieldList) > 4:
1322 Attribute = FieldList[4]
1323 return [HiiString, Guid, Offset, Value, Attribute], IsValid, 3
1324 return [], False, 0
1325
1326 ## AnalyzePcdData
1327 #
1328 # Analyze the pcd Value, Datum type and TokenNumber.
1329 # Used to avoid split issue while the value string contain "|" character
1330 #
1331 # @param[in] Setting: A String contain value/datum type/token number information;
1332 #
1333 # @retval ValueList: A List contain value, datum type and toke number.
1334 #
1335 def AnalyzePcdData(Setting):
1336 ValueList = ['', '', '']
1337
1338 ValueRe = re.compile(r'^\s*L?\".*\|.*\"')
1339 PtrValue = ValueRe.findall(Setting)
1340
1341 ValueUpdateFlag = False
1342
1343 if len(PtrValue) >= 1:
1344 Setting = re.sub(ValueRe, '', Setting)
1345 ValueUpdateFlag = True
1346
1347 TokenList = Setting.split(TAB_VALUE_SPLIT)
1348 ValueList[0:len(TokenList)] = TokenList
1349
1350 if ValueUpdateFlag:
1351 ValueList[0] = PtrValue[0]
1352
1353 return ValueList
1354
1355 ## check format of PCD value against its the datum type
1356 #
1357 # For PCD value setting
1358 #
1359 def CheckPcdDatum(Type, Value):
1360 if Type == TAB_VOID:
1361 ValueRe = re.compile(r'\s*L?\".*\"\s*$')
1362 if not (((Value.startswith('L"') or Value.startswith('"')) and Value.endswith('"'))
1363 or (Value.startswith('{') and Value.endswith('}')) or (Value.startswith("L'") or Value.startswith("'") and Value.endswith("'"))
1364 ):
1365 return False, "Invalid value [%s] of type [%s]; must be in the form of {...} for array"\
1366 ", \"...\" or \'...\' for string, L\"...\" or L\'...\' for unicode string" % (Value, Type)
1367 elif ValueRe.match(Value):
1368 # Check the chars in UnicodeString or CString is printable
1369 if Value.startswith("L"):
1370 Value = Value[2:-1]
1371 else:
1372 Value = Value[1:-1]
1373 Printset = set(string.printable)
1374 Printset.remove(TAB_PRINTCHAR_VT)
1375 Printset.add(TAB_PRINTCHAR_BS)
1376 Printset.add(TAB_PRINTCHAR_NUL)
1377 if not set(Value).issubset(Printset):
1378 PrintList = sorted(Printset)
1379 return False, "Invalid PCD string value of type [%s]; must be printable chars %s." % (Type, PrintList)
1380 elif Type == 'BOOLEAN':
1381 if Value not in ['TRUE', 'True', 'true', '0x1', '0x01', '1', 'FALSE', 'False', 'false', '0x0', '0x00', '0']:
1382 return False, "Invalid value [%s] of type [%s]; must be one of TRUE, True, true, 0x1, 0x01, 1"\
1383 ", FALSE, False, false, 0x0, 0x00, 0" % (Value, Type)
1384 elif Type in [TAB_UINT8, TAB_UINT16, TAB_UINT32, TAB_UINT64]:
1385 if Value.startswith('0') and not Value.lower().startswith('0x') and len(Value) > 1 and Value.lstrip('0'):
1386 Value = Value.lstrip('0')
1387 try:
1388 if Value and int(Value, 0) < 0:
1389 return False, "PCD can't be set to negative value[%s] for datum type [%s]" % (Value, Type)
1390 Value = int(Value, 0)
1391 if Value > MAX_VAL_TYPE[Type]:
1392 return False, "Too large PCD value[%s] for datum type [%s]" % (Value, Type)
1393 except:
1394 return False, "Invalid value [%s] of type [%s];"\
1395 " must be a hexadecimal, decimal or octal in C language format." % (Value, Type)
1396 else:
1397 return True, "StructurePcd"
1398
1399 return True, ""
1400
1401 def CommonPath(PathList):
1402 P1 = min(PathList).split(os.path.sep)
1403 P2 = max(PathList).split(os.path.sep)
1404 for Index in range(min(len(P1), len(P2))):
1405 if P1[Index] != P2[Index]:
1406 return os.path.sep.join(P1[:Index])
1407 return os.path.sep.join(P1)
1408
1409 class PathClass(object):
1410 def __init__(self, File='', Root='', AlterRoot='', Type='', IsBinary=False,
1411 Arch='COMMON', ToolChainFamily='', Target='', TagName='', ToolCode=''):
1412 self.Arch = Arch
1413 self.File = str(File)
1414 if os.path.isabs(self.File):
1415 self.Root = ''
1416 self.AlterRoot = ''
1417 else:
1418 self.Root = str(Root)
1419 self.AlterRoot = str(AlterRoot)
1420
1421 # Remove any '.' and '..' in path
1422 if self.Root:
1423 self.Root = mws.getWs(self.Root, self.File)
1424 self.Path = os.path.normpath(os.path.join(self.Root, self.File))
1425 self.Root = os.path.normpath(CommonPath([self.Root, self.Path]))
1426 # eliminate the side-effect of 'C:'
1427 if self.Root[-1] == ':':
1428 self.Root += os.path.sep
1429 # file path should not start with path separator
1430 if self.Root[-1] == os.path.sep:
1431 self.File = self.Path[len(self.Root):]
1432 else:
1433 self.File = self.Path[len(self.Root) + 1:]
1434 else:
1435 self.Path = os.path.normpath(self.File)
1436
1437 self.SubDir, self.Name = os.path.split(self.File)
1438 self.BaseName, self.Ext = os.path.splitext(self.Name)
1439
1440 if self.Root:
1441 if self.SubDir:
1442 self.Dir = os.path.join(self.Root, self.SubDir)
1443 else:
1444 self.Dir = self.Root
1445 else:
1446 self.Dir = self.SubDir
1447
1448 if IsBinary:
1449 self.Type = Type
1450 else:
1451 self.Type = self.Ext.lower()
1452
1453 self.IsBinary = IsBinary
1454 self.Target = Target
1455 self.TagName = TagName
1456 self.ToolCode = ToolCode
1457 self.ToolChainFamily = ToolChainFamily
1458 self.OriginalPath = self
1459
1460 ## Convert the object of this class to a string
1461 #
1462 # Convert member Path of the class to a string
1463 #
1464 # @retval string Formatted String
1465 #
1466 def __str__(self):
1467 return self.Path
1468
1469 ## Override __eq__ function
1470 #
1471 # Check whether PathClass are the same
1472 #
1473 # @retval False The two PathClass are different
1474 # @retval True The two PathClass are the same
1475 #
1476 def __eq__(self, Other):
1477 return self.Path == str(Other)
1478
1479 ## Override __cmp__ function
1480 #
1481 # Customize the comparison operation of two PathClass
1482 #
1483 # @retval 0 The two PathClass are different
1484 # @retval -1 The first PathClass is less than the second PathClass
1485 # @retval 1 The first PathClass is Bigger than the second PathClass
1486 def __cmp__(self, Other):
1487 OtherKey = str(Other)
1488
1489 SelfKey = self.Path
1490 if SelfKey == OtherKey:
1491 return 0
1492 elif SelfKey > OtherKey:
1493 return 1
1494 else:
1495 return -1
1496
1497 ## Override __hash__ function
1498 #
1499 # Use Path as key in hash table
1500 #
1501 # @retval string Key for hash table
1502 #
1503 def __hash__(self):
1504 return hash(self.Path)
1505
1506 @cached_property
1507 def Key(self):
1508 return self.Path.upper()
1509
1510 @property
1511 def TimeStamp(self):
1512 return os.stat(self.Path)[8]
1513
1514 def Validate(self, Type='', CaseSensitive=True):
1515 def RealPath2(File, Dir='', OverrideDir=''):
1516 NewFile = None
1517 if OverrideDir:
1518 NewFile = GlobalData.gAllFiles[os.path.normpath(os.path.join(OverrideDir, File))]
1519 if NewFile:
1520 if OverrideDir[-1] == os.path.sep:
1521 return NewFile[len(OverrideDir):], NewFile[0:len(OverrideDir)]
1522 else:
1523 return NewFile[len(OverrideDir) + 1:], NewFile[0:len(OverrideDir)]
1524 if GlobalData.gAllFiles:
1525 NewFile = GlobalData.gAllFiles[os.path.normpath(os.path.join(Dir, File))]
1526 if not NewFile:
1527 NewFile = os.path.normpath(os.path.join(Dir, File))
1528 if not os.path.exists(NewFile):
1529 return None, None
1530 if NewFile:
1531 if Dir:
1532 if Dir[-1] == os.path.sep:
1533 return NewFile[len(Dir):], NewFile[0:len(Dir)]
1534 else:
1535 return NewFile[len(Dir) + 1:], NewFile[0:len(Dir)]
1536 else:
1537 return NewFile, ''
1538
1539 return None, None
1540
1541 if GlobalData.gCaseInsensitive:
1542 CaseSensitive = False
1543 if Type and Type.lower() != self.Type:
1544 return FILE_TYPE_MISMATCH, '%s (expect %s but got %s)' % (self.File, Type, self.Type)
1545
1546 RealFile, RealRoot = RealPath2(self.File, self.Root, self.AlterRoot)
1547 if not RealRoot and not RealFile:
1548 RealFile = self.File
1549 if self.AlterRoot:
1550 RealFile = os.path.join(self.AlterRoot, self.File)
1551 elif self.Root:
1552 RealFile = os.path.join(self.Root, self.File)
1553 if len (mws.getPkgPath()) == 0:
1554 return FILE_NOT_FOUND, os.path.join(self.AlterRoot, RealFile)
1555 else:
1556 return FILE_NOT_FOUND, "%s is not found in packages path:\n\t%s" % (self.File, '\n\t'.join(mws.getPkgPath()))
1557
1558 ErrorCode = 0
1559 ErrorInfo = ''
1560 if RealRoot != self.Root or RealFile != self.File:
1561 if CaseSensitive and (RealFile != self.File or (RealRoot != self.Root and RealRoot != self.AlterRoot)):
1562 ErrorCode = FILE_CASE_MISMATCH
1563 ErrorInfo = self.File + '\n\t' + RealFile + " [in file system]"
1564
1565 self.SubDir, self.Name = os.path.split(RealFile)
1566 self.BaseName, self.Ext = os.path.splitext(self.Name)
1567 if self.SubDir:
1568 self.Dir = os.path.join(RealRoot, self.SubDir)
1569 else:
1570 self.Dir = RealRoot
1571 self.File = RealFile
1572 self.Root = RealRoot
1573 self.Path = os.path.join(RealRoot, RealFile)
1574 return ErrorCode, ErrorInfo
1575
1576 ## Parse PE image to get the required PE information.
1577 #
1578 class PeImageClass():
1579 ## Constructor
1580 #
1581 # @param File FilePath of PeImage
1582 #
1583 def __init__(self, PeFile):
1584 self.FileName = PeFile
1585 self.IsValid = False
1586 self.Size = 0
1587 self.EntryPoint = 0
1588 self.SectionAlignment = 0
1589 self.SectionHeaderList = []
1590 self.ErrorInfo = ''
1591 try:
1592 PeObject = open(PeFile, 'rb')
1593 except:
1594 self.ErrorInfo = self.FileName + ' can not be found\n'
1595 return
1596 # Read DOS header
1597 ByteArray = array.array('B')
1598 ByteArray.fromfile(PeObject, 0x3E)
1599 ByteList = ByteArray.tolist()
1600 # DOS signature should be 'MZ'
1601 if self._ByteListToStr (ByteList[0x0:0x2]) != 'MZ':
1602 self.ErrorInfo = self.FileName + ' has no valid DOS signature MZ'
1603 return
1604
1605 # Read 4 byte PE Signature
1606 PeOffset = self._ByteListToInt(ByteList[0x3C:0x3E])
1607 PeObject.seek(PeOffset)
1608 ByteArray = array.array('B')
1609 ByteArray.fromfile(PeObject, 4)
1610 # PE signature should be 'PE\0\0'
1611 if ByteArray.tostring() != b'PE\0\0':
1612 self.ErrorInfo = self.FileName + ' has no valid PE signature PE00'
1613 return
1614
1615 # Read PE file header
1616 ByteArray = array.array('B')
1617 ByteArray.fromfile(PeObject, 0x14)
1618 ByteList = ByteArray.tolist()
1619 SecNumber = self._ByteListToInt(ByteList[0x2:0x4])
1620 if SecNumber == 0:
1621 self.ErrorInfo = self.FileName + ' has no section header'
1622 return
1623
1624 # Read PE optional header
1625 OptionalHeaderSize = self._ByteListToInt(ByteArray[0x10:0x12])
1626 ByteArray = array.array('B')
1627 ByteArray.fromfile(PeObject, OptionalHeaderSize)
1628 ByteList = ByteArray.tolist()
1629 self.EntryPoint = self._ByteListToInt(ByteList[0x10:0x14])
1630 self.SectionAlignment = self._ByteListToInt(ByteList[0x20:0x24])
1631 self.Size = self._ByteListToInt(ByteList[0x38:0x3C])
1632
1633 # Read each Section Header
1634 for Index in range(SecNumber):
1635 ByteArray = array.array('B')
1636 ByteArray.fromfile(PeObject, 0x28)
1637 ByteList = ByteArray.tolist()
1638 SecName = self._ByteListToStr(ByteList[0:8])
1639 SecVirtualSize = self._ByteListToInt(ByteList[8:12])
1640 SecRawAddress = self._ByteListToInt(ByteList[20:24])
1641 SecVirtualAddress = self._ByteListToInt(ByteList[12:16])
1642 self.SectionHeaderList.append((SecName, SecVirtualAddress, SecRawAddress, SecVirtualSize))
1643 self.IsValid = True
1644 PeObject.close()
1645
1646 def _ByteListToStr(self, ByteList):
1647 String = ''
1648 for index in range(len(ByteList)):
1649 if ByteList[index] == 0:
1650 break
1651 String += chr(ByteList[index])
1652 return String
1653
1654 def _ByteListToInt(self, ByteList):
1655 Value = 0
1656 for index in range(len(ByteList) - 1, -1, -1):
1657 Value = (Value << 8) | int(ByteList[index])
1658 return Value
1659
1660 class DefaultStore():
1661 def __init__(self, DefaultStores ):
1662
1663 self.DefaultStores = DefaultStores
1664 def DefaultStoreID(self, DefaultStoreName):
1665 for key, value in self.DefaultStores.items():
1666 if value == DefaultStoreName:
1667 return key
1668 return None
1669 def GetDefaultDefault(self):
1670 if not self.DefaultStores or "0" in self.DefaultStores:
1671 return "0", TAB_DEFAULT_STORES_DEFAULT
1672 else:
1673 minvalue = min(int(value_str) for value_str in self.DefaultStores)
1674 return (str(minvalue), self.DefaultStores[str(minvalue)])
1675 def GetMin(self, DefaultSIdList):
1676 if not DefaultSIdList:
1677 return TAB_DEFAULT_STORES_DEFAULT
1678 storeidset = {storeid for storeid, storename in self.DefaultStores.values() if storename in DefaultSIdList}
1679 if not storeidset:
1680 return ""
1681 minid = min(storeidset )
1682 for sid, name in self.DefaultStores.values():
1683 if sid == minid:
1684 return name
1685
1686 class SkuClass():
1687 DEFAULT = 0
1688 SINGLE = 1
1689 MULTIPLE =2
1690
1691 def __init__(self,SkuIdentifier='', SkuIds=None):
1692 if SkuIds is None:
1693 SkuIds = {}
1694
1695 for SkuName in SkuIds:
1696 SkuId = SkuIds[SkuName][0]
1697 skuid_num = int(SkuId, 16) if SkuId.upper().startswith("0X") else int(SkuId)
1698 if skuid_num > 0xFFFFFFFFFFFFFFFF:
1699 EdkLogger.error("build", PARAMETER_INVALID,
1700 ExtraData = "SKU-ID [%s] value %s exceeds the max value of UINT64"
1701 % (SkuName, SkuId))
1702
1703 self.AvailableSkuIds = OrderedDict()
1704 self.SkuIdSet = []
1705 self.SkuIdNumberSet = []
1706 self.SkuData = SkuIds
1707 self._SkuInherit = {}
1708 self._SkuIdentifier = SkuIdentifier
1709 if SkuIdentifier == '' or SkuIdentifier is None:
1710 self.SkuIdSet = ['DEFAULT']
1711 self.SkuIdNumberSet = ['0U']
1712 elif SkuIdentifier == 'ALL':
1713 self.SkuIdSet = list(SkuIds.keys())
1714 self.SkuIdNumberSet = [num[0].strip() + 'U' for num in SkuIds.values()]
1715 else:
1716 r = SkuIdentifier.split('|')
1717 self.SkuIdSet=[(r[k].strip()).upper() for k in range(len(r))]
1718 k = None
1719 try:
1720 self.SkuIdNumberSet = [SkuIds[k][0].strip() + 'U' for k in self.SkuIdSet]
1721 except Exception:
1722 EdkLogger.error("build", PARAMETER_INVALID,
1723 ExtraData = "SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1724 % (k, " | ".join(SkuIds.keys())))
1725 for each in self.SkuIdSet:
1726 if each in SkuIds:
1727 self.AvailableSkuIds[each] = SkuIds[each][0]
1728 else:
1729 EdkLogger.error("build", PARAMETER_INVALID,
1730 ExtraData="SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1731 % (each, " | ".join(SkuIds.keys())))
1732 if self.SkuUsageType != SkuClass.SINGLE:
1733 self.AvailableSkuIds.update({'DEFAULT':0, 'COMMON':0})
1734 if self.SkuIdSet:
1735 GlobalData.gSkuids = (self.SkuIdSet)
1736 if 'COMMON' in GlobalData.gSkuids:
1737 GlobalData.gSkuids.remove('COMMON')
1738 if self.SkuUsageType == self.SINGLE:
1739 if len(GlobalData.gSkuids) != 1:
1740 if 'DEFAULT' in GlobalData.gSkuids:
1741 GlobalData.gSkuids.remove('DEFAULT')
1742 if GlobalData.gSkuids:
1743 GlobalData.gSkuids.sort()
1744
1745 def GetNextSkuId(self, skuname):
1746 if not self._SkuInherit:
1747 self._SkuInherit = {}
1748 for item in self.SkuData.values():
1749 self._SkuInherit[item[1]]=item[2] if item[2] else "DEFAULT"
1750 return self._SkuInherit.get(skuname, "DEFAULT")
1751
1752 def GetSkuChain(self, sku):
1753 if sku == "DEFAULT":
1754 return ["DEFAULT"]
1755 skulist = [sku]
1756 nextsku = sku
1757 while True:
1758 nextsku = self.GetNextSkuId(nextsku)
1759 skulist.append(nextsku)
1760 if nextsku == "DEFAULT":
1761 break
1762 skulist.reverse()
1763 return skulist
1764 def SkuOverrideOrder(self):
1765 skuorderset = []
1766 for skuname in self.SkuIdSet:
1767 skuorderset.append(self.GetSkuChain(skuname))
1768
1769 skuorder = []
1770 for index in range(max(len(item) for item in skuorderset)):
1771 for subset in skuorderset:
1772 if index > len(subset)-1:
1773 continue
1774 if subset[index] in skuorder:
1775 continue
1776 skuorder.append(subset[index])
1777
1778 return skuorder
1779
1780 @property
1781 def SkuUsageType(self):
1782 if self._SkuIdentifier.upper() == "ALL":
1783 return SkuClass.MULTIPLE
1784
1785 if len(self.SkuIdSet) == 1:
1786 if self.SkuIdSet[0] == 'DEFAULT':
1787 return SkuClass.DEFAULT
1788 return SkuClass.SINGLE
1789 if len(self.SkuIdSet) == 2 and 'DEFAULT' in self.SkuIdSet:
1790 return SkuClass.SINGLE
1791 return SkuClass.MULTIPLE
1792
1793 def DumpSkuIdArrary(self):
1794 if self.SkuUsageType == SkuClass.SINGLE:
1795 return "{0x0}"
1796 ArrayStrList = []
1797 for skuname in self.AvailableSkuIds:
1798 if skuname == "COMMON":
1799 continue
1800 while skuname != "DEFAULT":
1801 ArrayStrList.append(hex(int(self.AvailableSkuIds[skuname])))
1802 skuname = self.GetNextSkuId(skuname)
1803 ArrayStrList.append("0x0")
1804 return "{{{myList}}}".format(myList=",".join(ArrayStrList))
1805
1806 @property
1807 def AvailableSkuIdSet(self):
1808 return self.AvailableSkuIds
1809
1810 @property
1811 def SystemSkuId(self):
1812 if self.SkuUsageType == SkuClass.SINGLE:
1813 if len(self.SkuIdSet) == 1:
1814 return self.SkuIdSet[0]
1815 else:
1816 return self.SkuIdSet[0] if self.SkuIdSet[0] != 'DEFAULT' else self.SkuIdSet[1]
1817 else:
1818 return 'DEFAULT'
1819
1820 ## Get the integer value from string like "14U" or integer like 2
1821 #
1822 # @param Input The object that may be either a integer value or a string
1823 #
1824 # @retval Value The integer value that the input represents
1825 #
1826 def GetIntegerValue(Input):
1827 if not isinstance(Input, str):
1828 return Input
1829 String = Input
1830 if String.endswith("U"):
1831 String = String[:-1]
1832 if String.endswith("ULL"):
1833 String = String[:-3]
1834 if String.endswith("LL"):
1835 String = String[:-2]
1836
1837 if String.startswith("0x") or String.startswith("0X"):
1838 return int(String, 16)
1839 elif String == '':
1840 return 0
1841 else:
1842 return int(String)
1843
1844 #
1845 # Pack a GUID (registry format) list into a buffer and return it
1846 #
1847 def PackGUID(Guid):
1848 return pack(PACK_PATTERN_GUID,
1849 int(Guid[0], 16),
1850 int(Guid[1], 16),
1851 int(Guid[2], 16),
1852 int(Guid[3][-4:-2], 16),
1853 int(Guid[3][-2:], 16),
1854 int(Guid[4][-12:-10], 16),
1855 int(Guid[4][-10:-8], 16),
1856 int(Guid[4][-8:-6], 16),
1857 int(Guid[4][-6:-4], 16),
1858 int(Guid[4][-4:-2], 16),
1859 int(Guid[4][-2:], 16)
1860 )
1861
1862 #
1863 # Pack a GUID (byte) list into a buffer and return it
1864 #
1865 def PackByteFormatGUID(Guid):
1866 return pack(PACK_PATTERN_GUID,
1867 Guid[0],
1868 Guid[1],
1869 Guid[2],
1870 Guid[3],
1871 Guid[4],
1872 Guid[5],
1873 Guid[6],
1874 Guid[7],
1875 Guid[8],
1876 Guid[9],
1877 Guid[10],
1878 )
1879
1880 ## DeepCopy dict/OrderedDict recusively
1881 #
1882 # @param ori_dict a nested dict or ordereddict
1883 #
1884 # @retval new dict or orderdict
1885 #
1886 def CopyDict(ori_dict):
1887 dict_type = ori_dict.__class__
1888 if dict_type not in (dict,OrderedDict):
1889 return ori_dict
1890 new_dict = dict_type()
1891 for key in ori_dict:
1892 if isinstance(ori_dict[key],(dict,OrderedDict)):
1893 new_dict[key] = CopyDict(ori_dict[key])
1894 else:
1895 new_dict[key] = ori_dict[key]
1896 return new_dict
1897
1898 #
1899 # Remove the c/c++ comments: // and /* */
1900 #
1901 def RemoveCComments(ctext):
1902 return re.sub('//.*?\n|/\*.*?\*/', '\n', ctext, flags=re.S)