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