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