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