]> git.proxmox.com Git - mirror_edk2.git/blob - BaseTools/Source/Python/Common/Misc.py
BaseTools:Coding problems caused by special characters
[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 out = out.decode(encoding='utf-8', errors='ignore')
1036 Size = len(out.split())
1037 out = ','.join(out.split())
1038 return '{' + out + '}', Size
1039
1040 if "{CODE(" in Value:
1041 return Value, len(Value.split(","))
1042 if isinstance(Value, type(0)):
1043 return Value, (Value.bit_length() + 7) // 8
1044 if not isinstance(Value, type('')):
1045 raise BadExpression('Type %s is %s' %(Value, type(Value)))
1046 Value = Value.strip()
1047 if Value.startswith(TAB_UINT8) and Value.endswith(')'):
1048 Value, Size = ParseFieldValue(Value.split('(', 1)[1][:-1])
1049 if Size > 1:
1050 raise BadExpression('Value (%s) Size larger than %d' %(Value, Size))
1051 return Value, 1
1052 if Value.startswith(TAB_UINT16) and Value.endswith(')'):
1053 Value, Size = ParseFieldValue(Value.split('(', 1)[1][:-1])
1054 if Size > 2:
1055 raise BadExpression('Value (%s) Size larger than %d' %(Value, Size))
1056 return Value, 2
1057 if Value.startswith(TAB_UINT32) and Value.endswith(')'):
1058 Value, Size = ParseFieldValue(Value.split('(', 1)[1][:-1])
1059 if Size > 4:
1060 raise BadExpression('Value (%s) Size larger than %d' %(Value, Size))
1061 return Value, 4
1062 if Value.startswith(TAB_UINT64) and Value.endswith(')'):
1063 Value, Size = ParseFieldValue(Value.split('(', 1)[1][:-1])
1064 if Size > 8:
1065 raise BadExpression('Value (%s) Size larger than %d' % (Value, Size))
1066 return Value, 8
1067 if Value.startswith(TAB_GUID) and Value.endswith(')'):
1068 Value = Value.split('(', 1)[1][:-1].strip()
1069 if Value[0] == '{' and Value[-1] == '}':
1070 TmpValue = GuidStructureStringToGuidString(Value)
1071 if not TmpValue:
1072 raise BadExpression("Invalid GUID value string %s" % Value)
1073 Value = TmpValue
1074 if Value[0] == '"' and Value[-1] == '"':
1075 Value = Value[1:-1]
1076 try:
1077 Value = str(uuid.UUID(Value).bytes_le)
1078 if Value.startswith("b'"):
1079 Value = Value[2:-1]
1080 Value = "'" + Value + "'"
1081 except ValueError as Message:
1082 raise BadExpression(Message)
1083 Value, Size = ParseFieldValue(Value)
1084 return Value, 16
1085 if Value.startswith('L"') and Value.endswith('"'):
1086 # Unicode String
1087 # translate escape character
1088 Value = Value[1:]
1089 try:
1090 Value = eval(Value)
1091 except:
1092 Value = Value[1:-1]
1093 List = list(Value)
1094 List.reverse()
1095 Value = 0
1096 for Char in List:
1097 Value = (Value << 16) | ord(Char)
1098 return Value, (len(List) + 1) * 2
1099 if Value.startswith('"') and Value.endswith('"'):
1100 # ASCII String
1101 # translate escape character
1102 try:
1103 Value = eval(Value)
1104 except:
1105 Value = Value[1:-1]
1106 List = list(Value)
1107 List.reverse()
1108 Value = 0
1109 for Char in List:
1110 Value = (Value << 8) | ord(Char)
1111 return Value, len(List) + 1
1112 if Value.startswith("L'") and Value.endswith("'"):
1113 # Unicode Character Constant
1114 # translate escape character
1115 Value = Value[1:]
1116 try:
1117 Value = eval(Value)
1118 except:
1119 Value = Value[1:-1]
1120 List = list(Value)
1121 if len(List) == 0:
1122 raise BadExpression('Length %s is %s' % (Value, len(List)))
1123 List.reverse()
1124 Value = 0
1125 for Char in List:
1126 Value = (Value << 16) | ord(Char)
1127 return Value, len(List) * 2
1128 if Value.startswith("'") and Value.endswith("'"):
1129 # Character constant
1130 # translate escape character
1131 try:
1132 Value = eval(Value)
1133 except:
1134 Value = Value[1:-1]
1135 List = list(Value)
1136 if len(List) == 0:
1137 raise BadExpression('Length %s is %s' % (Value, len(List)))
1138 List.reverse()
1139 Value = 0
1140 for Char in List:
1141 Value = (Value << 8) | ord(Char)
1142 return Value, len(List)
1143 if Value.startswith('{') and Value.endswith('}'):
1144 # Byte array
1145 Value = Value[1:-1]
1146 List = [Item.strip() for Item in Value.split(',')]
1147 List.reverse()
1148 Value = 0
1149 RetSize = 0
1150 for Item in List:
1151 ItemValue, Size = ParseFieldValue(Item)
1152 RetSize += Size
1153 for I in range(Size):
1154 Value = (Value << 8) | ((ItemValue >> 8 * I) & 0xff)
1155 return Value, RetSize
1156 if Value.startswith('DEVICE_PATH(') and Value.endswith(')'):
1157 Value = Value.replace("DEVICE_PATH(", '').rstrip(')')
1158 Value = Value.strip().strip('"')
1159 return ParseDevPathValue(Value)
1160 if Value.lower().startswith('0x'):
1161 try:
1162 Value = int(Value, 16)
1163 except:
1164 raise BadExpression("invalid hex value: %s" % Value)
1165 if Value == 0:
1166 return 0, 1
1167 return Value, (Value.bit_length() + 7) // 8
1168 if Value[0].isdigit():
1169 Value = int(Value, 10)
1170 if Value == 0:
1171 return 0, 1
1172 return Value, (Value.bit_length() + 7) // 8
1173 if Value.lower() == 'true':
1174 return 1, 1
1175 if Value.lower() == 'false':
1176 return 0, 1
1177 return Value, 1
1178
1179 ## AnalyzeDscPcd
1180 #
1181 # Analyze DSC PCD value, since there is no data type info in DSC
1182 # This function is used to match functions (AnalyzePcdData) used for retrieving PCD value from database
1183 # 1. Feature flag: TokenSpace.PcdCName|PcdValue
1184 # 2. Fix and Patch:TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1185 # 3. Dynamic default:
1186 # TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1187 # TokenSpace.PcdCName|PcdValue
1188 # 4. Dynamic VPD:
1189 # TokenSpace.PcdCName|VpdOffset[|VpdValue]
1190 # TokenSpace.PcdCName|VpdOffset[|MaxSize[|VpdValue]]
1191 # 5. Dynamic HII:
1192 # TokenSpace.PcdCName|HiiString|VariableGuid|VariableOffset[|HiiValue]
1193 # PCD value needs to be located in such kind of string, and the PCD value might be an expression in which
1194 # there might have "|" operator, also in string value.
1195 #
1196 # @param Setting: String contain information described above with "TokenSpace.PcdCName|" stripped
1197 # @param PcdType: PCD type: feature, fixed, dynamic default VPD HII
1198 # @param DataType: The datum type of PCD: VOID*, UNIT, BOOL
1199 # @retval:
1200 # ValueList: A List contain fields described above
1201 # IsValid: True if conforming EBNF, otherwise False
1202 # Index: The index where PcdValue is in ValueList
1203 #
1204 def AnalyzeDscPcd(Setting, PcdType, DataType=''):
1205 FieldList = AnalyzePcdExpression(Setting)
1206
1207 IsValid = True
1208 if PcdType in (MODEL_PCD_FIXED_AT_BUILD, MODEL_PCD_PATCHABLE_IN_MODULE, MODEL_PCD_DYNAMIC_DEFAULT, MODEL_PCD_DYNAMIC_EX_DEFAULT):
1209 Value = FieldList[0]
1210 Size = ''
1211 if len(FieldList) > 1 and FieldList[1]:
1212 DataType = FieldList[1]
1213 if FieldList[1] != TAB_VOID and StructPattern.match(FieldList[1]) is None:
1214 IsValid = False
1215 if len(FieldList) > 2:
1216 Size = FieldList[2]
1217 if IsValid:
1218 if DataType == "":
1219 IsValid = (len(FieldList) <= 1)
1220 else:
1221 IsValid = (len(FieldList) <= 3)
1222
1223 if Size:
1224 try:
1225 int(Size, 16) if Size.upper().startswith("0X") else int(Size)
1226 except:
1227 IsValid = False
1228 Size = -1
1229 return [str(Value), DataType, str(Size)], IsValid, 0
1230 elif PcdType == MODEL_PCD_FEATURE_FLAG:
1231 Value = FieldList[0]
1232 Size = ''
1233 IsValid = (len(FieldList) <= 1)
1234 return [Value, DataType, str(Size)], IsValid, 0
1235 elif PcdType in (MODEL_PCD_DYNAMIC_VPD, MODEL_PCD_DYNAMIC_EX_VPD):
1236 VpdOffset = FieldList[0]
1237 Value = Size = ''
1238 if not DataType == TAB_VOID:
1239 if len(FieldList) > 1:
1240 Value = FieldList[1]
1241 else:
1242 if len(FieldList) > 1:
1243 Size = FieldList[1]
1244 if len(FieldList) > 2:
1245 Value = FieldList[2]
1246 if DataType == "":
1247 IsValid = (len(FieldList) <= 1)
1248 else:
1249 IsValid = (len(FieldList) <= 3)
1250 if Size:
1251 try:
1252 int(Size, 16) if Size.upper().startswith("0X") else int(Size)
1253 except:
1254 IsValid = False
1255 Size = -1
1256 return [VpdOffset, str(Size), Value], IsValid, 2
1257 elif PcdType in (MODEL_PCD_DYNAMIC_HII, MODEL_PCD_DYNAMIC_EX_HII):
1258 IsValid = (3 <= len(FieldList) <= 5)
1259 HiiString = FieldList[0]
1260 Guid = Offset = Value = Attribute = ''
1261 if len(FieldList) > 1:
1262 Guid = FieldList[1]
1263 if len(FieldList) > 2:
1264 Offset = FieldList[2]
1265 if len(FieldList) > 3:
1266 Value = FieldList[3]
1267 if len(FieldList) > 4:
1268 Attribute = FieldList[4]
1269 return [HiiString, Guid, Offset, Value, Attribute], IsValid, 3
1270 return [], False, 0
1271
1272 ## AnalyzePcdData
1273 #
1274 # Analyze the pcd Value, Datum type and TokenNumber.
1275 # Used to avoid split issue while the value string contain "|" character
1276 #
1277 # @param[in] Setting: A String contain value/datum type/token number information;
1278 #
1279 # @retval ValueList: A List contain value, datum type and toke number.
1280 #
1281 def AnalyzePcdData(Setting):
1282 ValueList = ['', '', '']
1283
1284 ValueRe = re.compile(r'^\s*L?\".*\|.*\"')
1285 PtrValue = ValueRe.findall(Setting)
1286
1287 ValueUpdateFlag = False
1288
1289 if len(PtrValue) >= 1:
1290 Setting = re.sub(ValueRe, '', Setting)
1291 ValueUpdateFlag = True
1292
1293 TokenList = Setting.split(TAB_VALUE_SPLIT)
1294 ValueList[0:len(TokenList)] = TokenList
1295
1296 if ValueUpdateFlag:
1297 ValueList[0] = PtrValue[0]
1298
1299 return ValueList
1300
1301 ## check format of PCD value against its the datum type
1302 #
1303 # For PCD value setting
1304 #
1305 def CheckPcdDatum(Type, Value):
1306 if Type == TAB_VOID:
1307 ValueRe = re.compile(r'\s*L?\".*\"\s*$')
1308 if not (((Value.startswith('L"') or Value.startswith('"')) and Value.endswith('"'))
1309 or (Value.startswith('{') and Value.endswith('}')) or (Value.startswith("L'") or Value.startswith("'") and Value.endswith("'"))
1310 ):
1311 return False, "Invalid value [%s] of type [%s]; must be in the form of {...} for array"\
1312 ", \"...\" or \'...\' for string, L\"...\" or L\'...\' for unicode string" % (Value, Type)
1313 elif ValueRe.match(Value):
1314 # Check the chars in UnicodeString or CString is printable
1315 if Value.startswith("L"):
1316 Value = Value[2:-1]
1317 else:
1318 Value = Value[1:-1]
1319 Printset = set(string.printable)
1320 Printset.remove(TAB_PRINTCHAR_VT)
1321 Printset.add(TAB_PRINTCHAR_BS)
1322 Printset.add(TAB_PRINTCHAR_NUL)
1323 if not set(Value).issubset(Printset):
1324 PrintList = sorted(Printset)
1325 return False, "Invalid PCD string value of type [%s]; must be printable chars %s." % (Type, PrintList)
1326 elif Type == 'BOOLEAN':
1327 if Value not in ['TRUE', 'True', 'true', '0x1', '0x01', '1', 'FALSE', 'False', 'false', '0x0', '0x00', '0']:
1328 return False, "Invalid value [%s] of type [%s]; must be one of TRUE, True, true, 0x1, 0x01, 1"\
1329 ", FALSE, False, false, 0x0, 0x00, 0" % (Value, Type)
1330 elif Type in [TAB_UINT8, TAB_UINT16, TAB_UINT32, TAB_UINT64]:
1331 if Value.startswith('0') and not Value.lower().startswith('0x') and len(Value) > 1 and Value.lstrip('0'):
1332 Value = Value.lstrip('0')
1333 try:
1334 if Value and int(Value, 0) < 0:
1335 return False, "PCD can't be set to negative value[%s] for datum type [%s]" % (Value, Type)
1336 Value = int(Value, 0)
1337 if Value > MAX_VAL_TYPE[Type]:
1338 return False, "Too large PCD value[%s] for datum type [%s]" % (Value, Type)
1339 except:
1340 return False, "Invalid value [%s] of type [%s];"\
1341 " must be a hexadecimal, decimal or octal in C language format." % (Value, Type)
1342 else:
1343 return True, "StructurePcd"
1344
1345 return True, ""
1346
1347 def CommonPath(PathList):
1348 P1 = min(PathList).split(os.path.sep)
1349 P2 = max(PathList).split(os.path.sep)
1350 for Index in range(min(len(P1), len(P2))):
1351 if P1[Index] != P2[Index]:
1352 return os.path.sep.join(P1[:Index])
1353 return os.path.sep.join(P1)
1354
1355 class PathClass(object):
1356 def __init__(self, File='', Root='', AlterRoot='', Type='', IsBinary=False,
1357 Arch='COMMON', ToolChainFamily='', Target='', TagName='', ToolCode=''):
1358 self.Arch = Arch
1359 self.File = str(File)
1360 if os.path.isabs(self.File):
1361 self.Root = ''
1362 self.AlterRoot = ''
1363 else:
1364 self.Root = str(Root)
1365 self.AlterRoot = str(AlterRoot)
1366
1367 # Remove any '.' and '..' in path
1368 if self.Root:
1369 self.Root = mws.getWs(self.Root, self.File)
1370 self.Path = os.path.normpath(os.path.join(self.Root, self.File))
1371 self.Root = os.path.normpath(CommonPath([self.Root, self.Path]))
1372 # eliminate the side-effect of 'C:'
1373 if self.Root[-1] == ':':
1374 self.Root += os.path.sep
1375 # file path should not start with path separator
1376 if self.Root[-1] == os.path.sep:
1377 self.File = self.Path[len(self.Root):]
1378 else:
1379 self.File = self.Path[len(self.Root) + 1:]
1380 else:
1381 self.Path = os.path.normpath(self.File)
1382
1383 self.SubDir, self.Name = os.path.split(self.File)
1384 self.BaseName, self.Ext = os.path.splitext(self.Name)
1385
1386 if self.Root:
1387 if self.SubDir:
1388 self.Dir = os.path.join(self.Root, self.SubDir)
1389 else:
1390 self.Dir = self.Root
1391 else:
1392 self.Dir = self.SubDir
1393
1394 if IsBinary:
1395 self.Type = Type
1396 else:
1397 self.Type = self.Ext.lower()
1398
1399 self.IsBinary = IsBinary
1400 self.Target = Target
1401 self.TagName = TagName
1402 self.ToolCode = ToolCode
1403 self.ToolChainFamily = ToolChainFamily
1404
1405 ## Convert the object of this class to a string
1406 #
1407 # Convert member Path of the class to a string
1408 #
1409 # @retval string Formatted String
1410 #
1411 def __str__(self):
1412 return self.Path
1413
1414 ## Override __eq__ function
1415 #
1416 # Check whether PathClass are the same
1417 #
1418 # @retval False The two PathClass are different
1419 # @retval True The two PathClass are the same
1420 #
1421 def __eq__(self, Other):
1422 return self.Path == str(Other)
1423
1424 ## Override __cmp__ function
1425 #
1426 # Customize the comparison operation of two PathClass
1427 #
1428 # @retval 0 The two PathClass are different
1429 # @retval -1 The first PathClass is less than the second PathClass
1430 # @retval 1 The first PathClass is Bigger than the second PathClass
1431 def __cmp__(self, Other):
1432 OtherKey = str(Other)
1433
1434 SelfKey = self.Path
1435 if SelfKey == OtherKey:
1436 return 0
1437 elif SelfKey > OtherKey:
1438 return 1
1439 else:
1440 return -1
1441
1442 ## Override __hash__ function
1443 #
1444 # Use Path as key in hash table
1445 #
1446 # @retval string Key for hash table
1447 #
1448 def __hash__(self):
1449 return hash(self.Path)
1450
1451 @cached_property
1452 def Key(self):
1453 return self.Path.upper()
1454
1455 @property
1456 def TimeStamp(self):
1457 return os.stat(self.Path)[8]
1458
1459 def Validate(self, Type='', CaseSensitive=True):
1460 def RealPath2(File, Dir='', OverrideDir=''):
1461 NewFile = None
1462 if OverrideDir:
1463 NewFile = GlobalData.gAllFiles[os.path.normpath(os.path.join(OverrideDir, File))]
1464 if NewFile:
1465 if OverrideDir[-1] == os.path.sep:
1466 return NewFile[len(OverrideDir):], NewFile[0:len(OverrideDir)]
1467 else:
1468 return NewFile[len(OverrideDir) + 1:], NewFile[0:len(OverrideDir)]
1469 if GlobalData.gAllFiles:
1470 NewFile = GlobalData.gAllFiles[os.path.normpath(os.path.join(Dir, File))]
1471 if not NewFile:
1472 NewFile = os.path.normpath(os.path.join(Dir, File))
1473 if not os.path.exists(NewFile):
1474 return None, None
1475 if NewFile:
1476 if Dir:
1477 if Dir[-1] == os.path.sep:
1478 return NewFile[len(Dir):], NewFile[0:len(Dir)]
1479 else:
1480 return NewFile[len(Dir) + 1:], NewFile[0:len(Dir)]
1481 else:
1482 return NewFile, ''
1483
1484 return None, None
1485
1486 if GlobalData.gCaseInsensitive:
1487 CaseSensitive = False
1488 if Type and Type.lower() != self.Type:
1489 return FILE_TYPE_MISMATCH, '%s (expect %s but got %s)' % (self.File, Type, self.Type)
1490
1491 RealFile, RealRoot = RealPath2(self.File, self.Root, self.AlterRoot)
1492 if not RealRoot and not RealFile:
1493 RealFile = self.File
1494 if self.AlterRoot:
1495 RealFile = os.path.join(self.AlterRoot, self.File)
1496 elif self.Root:
1497 RealFile = os.path.join(self.Root, self.File)
1498 if len (mws.getPkgPath()) == 0:
1499 return FILE_NOT_FOUND, os.path.join(self.AlterRoot, RealFile)
1500 else:
1501 return FILE_NOT_FOUND, "%s is not found in packages path:\n\t%s" % (self.File, '\n\t'.join(mws.getPkgPath()))
1502
1503 ErrorCode = 0
1504 ErrorInfo = ''
1505 if RealRoot != self.Root or RealFile != self.File:
1506 if CaseSensitive and (RealFile != self.File or (RealRoot != self.Root and RealRoot != self.AlterRoot)):
1507 ErrorCode = FILE_CASE_MISMATCH
1508 ErrorInfo = self.File + '\n\t' + RealFile + " [in file system]"
1509
1510 self.SubDir, self.Name = os.path.split(RealFile)
1511 self.BaseName, self.Ext = os.path.splitext(self.Name)
1512 if self.SubDir:
1513 self.Dir = os.path.join(RealRoot, self.SubDir)
1514 else:
1515 self.Dir = RealRoot
1516 self.File = RealFile
1517 self.Root = RealRoot
1518 self.Path = os.path.join(RealRoot, RealFile)
1519 return ErrorCode, ErrorInfo
1520
1521 ## Parse PE image to get the required PE information.
1522 #
1523 class PeImageClass():
1524 ## Constructor
1525 #
1526 # @param File FilePath of PeImage
1527 #
1528 def __init__(self, PeFile):
1529 self.FileName = PeFile
1530 self.IsValid = False
1531 self.Size = 0
1532 self.EntryPoint = 0
1533 self.SectionAlignment = 0
1534 self.SectionHeaderList = []
1535 self.ErrorInfo = ''
1536 try:
1537 PeObject = open(PeFile, 'rb')
1538 except:
1539 self.ErrorInfo = self.FileName + ' can not be found\n'
1540 return
1541 # Read DOS header
1542 ByteArray = array.array('B')
1543 ByteArray.fromfile(PeObject, 0x3E)
1544 ByteList = ByteArray.tolist()
1545 # DOS signature should be 'MZ'
1546 if self._ByteListToStr (ByteList[0x0:0x2]) != 'MZ':
1547 self.ErrorInfo = self.FileName + ' has no valid DOS signature MZ'
1548 return
1549
1550 # Read 4 byte PE Signature
1551 PeOffset = self._ByteListToInt(ByteList[0x3C:0x3E])
1552 PeObject.seek(PeOffset)
1553 ByteArray = array.array('B')
1554 ByteArray.fromfile(PeObject, 4)
1555 # PE signature should be 'PE\0\0'
1556 if ByteArray.tostring() != b'PE\0\0':
1557 self.ErrorInfo = self.FileName + ' has no valid PE signature PE00'
1558 return
1559
1560 # Read PE file header
1561 ByteArray = array.array('B')
1562 ByteArray.fromfile(PeObject, 0x14)
1563 ByteList = ByteArray.tolist()
1564 SecNumber = self._ByteListToInt(ByteList[0x2:0x4])
1565 if SecNumber == 0:
1566 self.ErrorInfo = self.FileName + ' has no section header'
1567 return
1568
1569 # Read PE optional header
1570 OptionalHeaderSize = self._ByteListToInt(ByteArray[0x10:0x12])
1571 ByteArray = array.array('B')
1572 ByteArray.fromfile(PeObject, OptionalHeaderSize)
1573 ByteList = ByteArray.tolist()
1574 self.EntryPoint = self._ByteListToInt(ByteList[0x10:0x14])
1575 self.SectionAlignment = self._ByteListToInt(ByteList[0x20:0x24])
1576 self.Size = self._ByteListToInt(ByteList[0x38:0x3C])
1577
1578 # Read each Section Header
1579 for Index in range(SecNumber):
1580 ByteArray = array.array('B')
1581 ByteArray.fromfile(PeObject, 0x28)
1582 ByteList = ByteArray.tolist()
1583 SecName = self._ByteListToStr(ByteList[0:8])
1584 SecVirtualSize = self._ByteListToInt(ByteList[8:12])
1585 SecRawAddress = self._ByteListToInt(ByteList[20:24])
1586 SecVirtualAddress = self._ByteListToInt(ByteList[12:16])
1587 self.SectionHeaderList.append((SecName, SecVirtualAddress, SecRawAddress, SecVirtualSize))
1588 self.IsValid = True
1589 PeObject.close()
1590
1591 def _ByteListToStr(self, ByteList):
1592 String = ''
1593 for index in range(len(ByteList)):
1594 if ByteList[index] == 0:
1595 break
1596 String += chr(ByteList[index])
1597 return String
1598
1599 def _ByteListToInt(self, ByteList):
1600 Value = 0
1601 for index in range(len(ByteList) - 1, -1, -1):
1602 Value = (Value << 8) | int(ByteList[index])
1603 return Value
1604
1605 class DefaultStore():
1606 def __init__(self, DefaultStores ):
1607
1608 self.DefaultStores = DefaultStores
1609 def DefaultStoreID(self, DefaultStoreName):
1610 for key, value in self.DefaultStores.items():
1611 if value == DefaultStoreName:
1612 return key
1613 return None
1614 def GetDefaultDefault(self):
1615 if not self.DefaultStores or "0" in self.DefaultStores:
1616 return "0", TAB_DEFAULT_STORES_DEFAULT
1617 else:
1618 minvalue = min(int(value_str) for value_str in self.DefaultStores)
1619 return (str(minvalue), self.DefaultStores[str(minvalue)])
1620 def GetMin(self, DefaultSIdList):
1621 if not DefaultSIdList:
1622 return TAB_DEFAULT_STORES_DEFAULT
1623 storeidset = {storeid for storeid, storename in self.DefaultStores.values() if storename in DefaultSIdList}
1624 if not storeidset:
1625 return ""
1626 minid = min(storeidset )
1627 for sid, name in self.DefaultStores.values():
1628 if sid == minid:
1629 return name
1630
1631 class SkuClass():
1632 DEFAULT = 0
1633 SINGLE = 1
1634 MULTIPLE =2
1635
1636 def __init__(self,SkuIdentifier='', SkuIds=None):
1637 if SkuIds is None:
1638 SkuIds = {}
1639
1640 for SkuName in SkuIds:
1641 SkuId = SkuIds[SkuName][0]
1642 skuid_num = int(SkuId, 16) if SkuId.upper().startswith("0X") else int(SkuId)
1643 if skuid_num > 0xFFFFFFFFFFFFFFFF:
1644 EdkLogger.error("build", PARAMETER_INVALID,
1645 ExtraData = "SKU-ID [%s] value %s exceeds the max value of UINT64"
1646 % (SkuName, SkuId))
1647
1648 self.AvailableSkuIds = OrderedDict()
1649 self.SkuIdSet = []
1650 self.SkuIdNumberSet = []
1651 self.SkuData = SkuIds
1652 self._SkuInherit = {}
1653 self._SkuIdentifier = SkuIdentifier
1654 if SkuIdentifier == '' or SkuIdentifier is None:
1655 self.SkuIdSet = ['DEFAULT']
1656 self.SkuIdNumberSet = ['0U']
1657 elif SkuIdentifier == 'ALL':
1658 self.SkuIdSet = list(SkuIds.keys())
1659 self.SkuIdNumberSet = [num[0].strip() + 'U' for num in SkuIds.values()]
1660 else:
1661 r = SkuIdentifier.split('|')
1662 self.SkuIdSet=[(r[k].strip()).upper() for k in range(len(r))]
1663 k = None
1664 try:
1665 self.SkuIdNumberSet = [SkuIds[k][0].strip() + 'U' for k in self.SkuIdSet]
1666 except Exception:
1667 EdkLogger.error("build", PARAMETER_INVALID,
1668 ExtraData = "SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1669 % (k, " | ".join(SkuIds.keys())))
1670 for each in self.SkuIdSet:
1671 if each in SkuIds:
1672 self.AvailableSkuIds[each] = SkuIds[each][0]
1673 else:
1674 EdkLogger.error("build", PARAMETER_INVALID,
1675 ExtraData="SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1676 % (each, " | ".join(SkuIds.keys())))
1677 if self.SkuUsageType != SkuClass.SINGLE:
1678 self.AvailableSkuIds.update({'DEFAULT':0, 'COMMON':0})
1679 if self.SkuIdSet:
1680 GlobalData.gSkuids = (self.SkuIdSet)
1681 if 'COMMON' in GlobalData.gSkuids:
1682 GlobalData.gSkuids.remove('COMMON')
1683 if self.SkuUsageType == self.SINGLE:
1684 if len(GlobalData.gSkuids) != 1:
1685 if 'DEFAULT' in GlobalData.gSkuids:
1686 GlobalData.gSkuids.remove('DEFAULT')
1687 if GlobalData.gSkuids:
1688 GlobalData.gSkuids.sort()
1689
1690 def GetNextSkuId(self, skuname):
1691 if not self._SkuInherit:
1692 self._SkuInherit = {}
1693 for item in self.SkuData.values():
1694 self._SkuInherit[item[1]]=item[2] if item[2] else "DEFAULT"
1695 return self._SkuInherit.get(skuname, "DEFAULT")
1696
1697 def GetSkuChain(self, sku):
1698 if sku == "DEFAULT":
1699 return ["DEFAULT"]
1700 skulist = [sku]
1701 nextsku = sku
1702 while True:
1703 nextsku = self.GetNextSkuId(nextsku)
1704 skulist.append(nextsku)
1705 if nextsku == "DEFAULT":
1706 break
1707 skulist.reverse()
1708 return skulist
1709 def SkuOverrideOrder(self):
1710 skuorderset = []
1711 for skuname in self.SkuIdSet:
1712 skuorderset.append(self.GetSkuChain(skuname))
1713
1714 skuorder = []
1715 for index in range(max(len(item) for item in skuorderset)):
1716 for subset in skuorderset:
1717 if index > len(subset)-1:
1718 continue
1719 if subset[index] in skuorder:
1720 continue
1721 skuorder.append(subset[index])
1722
1723 return skuorder
1724
1725 @property
1726 def SkuUsageType(self):
1727 if self._SkuIdentifier.upper() == "ALL":
1728 return SkuClass.MULTIPLE
1729
1730 if len(self.SkuIdSet) == 1:
1731 if self.SkuIdSet[0] == 'DEFAULT':
1732 return SkuClass.DEFAULT
1733 return SkuClass.SINGLE
1734 if len(self.SkuIdSet) == 2 and 'DEFAULT' in self.SkuIdSet:
1735 return SkuClass.SINGLE
1736 return SkuClass.MULTIPLE
1737
1738 def DumpSkuIdArrary(self):
1739 if self.SkuUsageType == SkuClass.SINGLE:
1740 return "{0x0}"
1741 ArrayStrList = []
1742 for skuname in self.AvailableSkuIds:
1743 if skuname == "COMMON":
1744 continue
1745 while skuname != "DEFAULT":
1746 ArrayStrList.append(hex(int(self.AvailableSkuIds[skuname])))
1747 skuname = self.GetNextSkuId(skuname)
1748 ArrayStrList.append("0x0")
1749 return "{{{myList}}}".format(myList=",".join(ArrayStrList))
1750
1751 @property
1752 def AvailableSkuIdSet(self):
1753 return self.AvailableSkuIds
1754
1755 @property
1756 def SystemSkuId(self):
1757 if self.SkuUsageType == SkuClass.SINGLE:
1758 if len(self.SkuIdSet) == 1:
1759 return self.SkuIdSet[0]
1760 else:
1761 return self.SkuIdSet[0] if self.SkuIdSet[0] != 'DEFAULT' else self.SkuIdSet[1]
1762 else:
1763 return 'DEFAULT'
1764
1765 ## Get the integer value from string like "14U" or integer like 2
1766 #
1767 # @param Input The object that may be either a integer value or a string
1768 #
1769 # @retval Value The integer value that the input represents
1770 #
1771 def GetIntegerValue(Input):
1772 if not isinstance(Input, str):
1773 return Input
1774 String = Input
1775 if String.endswith("U"):
1776 String = String[:-1]
1777 if String.endswith("ULL"):
1778 String = String[:-3]
1779 if String.endswith("LL"):
1780 String = String[:-2]
1781
1782 if String.startswith("0x") or String.startswith("0X"):
1783 return int(String, 16)
1784 elif String == '':
1785 return 0
1786 else:
1787 return int(String)
1788
1789 #
1790 # Pack a GUID (registry format) list into a buffer and return it
1791 #
1792 def PackGUID(Guid):
1793 return pack(PACK_PATTERN_GUID,
1794 int(Guid[0], 16),
1795 int(Guid[1], 16),
1796 int(Guid[2], 16),
1797 int(Guid[3][-4:-2], 16),
1798 int(Guid[3][-2:], 16),
1799 int(Guid[4][-12:-10], 16),
1800 int(Guid[4][-10:-8], 16),
1801 int(Guid[4][-8:-6], 16),
1802 int(Guid[4][-6:-4], 16),
1803 int(Guid[4][-4:-2], 16),
1804 int(Guid[4][-2:], 16)
1805 )
1806
1807 #
1808 # Pack a GUID (byte) list into a buffer and return it
1809 #
1810 def PackByteFormatGUID(Guid):
1811 return pack(PACK_PATTERN_GUID,
1812 Guid[0],
1813 Guid[1],
1814 Guid[2],
1815 Guid[3],
1816 Guid[4],
1817 Guid[5],
1818 Guid[6],
1819 Guid[7],
1820 Guid[8],
1821 Guid[9],
1822 Guid[10],
1823 )
1824
1825 ## DeepCopy dict/OrderedDict recusively
1826 #
1827 # @param ori_dict a nested dict or ordereddict
1828 #
1829 # @retval new dict or orderdict
1830 #
1831 def CopyDict(ori_dict):
1832 dict_type = ori_dict.__class__
1833 if dict_type not in (dict,OrderedDict):
1834 return ori_dict
1835 new_dict = dict_type()
1836 for key in ori_dict:
1837 if isinstance(ori_dict[key],(dict,OrderedDict)):
1838 new_dict[key] = CopyDict(ori_dict[key])
1839 else:
1840 new_dict[key] = ori_dict[key]
1841 return new_dict
1842
1843 #
1844 # Remove the c/c++ comments: // and /* */
1845 #
1846 def RemoveCComments(ctext):
1847 return re.sub('//.*?\n|/\*.*?\*/', '\n', ctext, flags=re.S)