2 # Common routines used by all tools
4 # Copyright (c) 2007 - 2019, Intel Corporation. All rights reserved.<BR>
5 # SPDX-License-Identifier: BSD-2-Clause-Patent
11 from __future__
import absolute_import
22 from random
import sample
23 from struct
import pack
27 from collections
import OrderedDict
29 import Common
.LongFilePathOs
as os
30 from Common
import EdkLogger
as EdkLogger
31 from Common
import GlobalData
as GlobalData
32 from Common
.DataType
import *
33 from Common
.BuildToolError
import *
34 from CommonDataClass
.DataClass
import *
35 from Common
.Parsing
import GetSplitValueList
36 from Common
.LongFilePathSupport
import OpenLongFilePath
as open
37 from Common
.LongFilePathSupport
import CopyLongFilePath
as CopyLong
38 from Common
.LongFilePathSupport
import LongFilePath
as LongFilePath
39 from Common
.MultipleWorkspace
import MultipleWorkspace
as mws
40 from CommonDataClass
.Exceptions
import BadExpression
41 from Common
.caching
import cached_property
43 ArrayIndex
= re
.compile("\[\s*[0-9a-fA-FxX]*\s*\]")
44 ## Regular expression used to find out place holders in string template
45 gPlaceholderPattern
= re
.compile("\$\{([^$()\s]+)\}", re
.MULTILINE | re
.UNICODE
)
47 ## regular expressions for map file processing
48 startPatternGeneral
= re
.compile("^Start[' ']+Length[' ']+Name[' ']+Class")
49 addressPatternGeneral
= re
.compile("^Address[' ']+Publics by Value[' ']+Rva\+Base")
50 valuePatternGcc
= re
.compile('^([\w_\.]+) +([\da-fA-Fx]+) +([\da-fA-Fx]+)$')
51 pcdPatternGcc
= re
.compile('^([\da-fA-Fx]+) +([\da-fA-Fx]+)')
52 secReGeneral
= re
.compile('^([\da-fA-F]+):([\da-fA-F]+) +([\da-fA-F]+)[Hh]? +([.\w\$]+) +(\w+)', re
.UNICODE
)
54 StructPattern
= re
.compile(r
'[_a-zA-Z][0-9A-Za-z_]*$')
56 ## Dictionary used to store dependencies of files
57 gDependencyDatabase
= {} # arch : {file path : [dependent files list]}
60 # If a module is built more than once with different PCDs or library classes
61 # a temporary INF file with same content is created, the temporary file is removed
66 def GetVariableOffset(mapfilepath
, efifilepath
, varnames
):
67 """ Parse map file to get variable offset in current EFI file
68 @param mapfilepath Map file absolution path
69 @param efifilepath: EFI binary file full path
70 @param varnames iteratable container whose elements are variable names to be searched
72 @return List whos elements are tuple with variable name and raw offset
76 f
= open(mapfilepath
, 'r')
82 if len(lines
) == 0: return None
83 firstline
= lines
[0].strip()
84 if re
.match('^\s*Address\s*Size\s*Align\s*Out\s*In\s*Symbol\s*$', firstline
):
85 return _parseForXcodeAndClang9(lines
, efifilepath
, varnames
)
86 if (firstline
.startswith("Archive member included ") and
87 firstline
.endswith(" file (symbol)")):
88 return _parseForGCC(lines
, efifilepath
, varnames
)
89 if firstline
.startswith("# Path:"):
90 return _parseForXcodeAndClang9(lines
, efifilepath
, varnames
)
91 return _parseGeneral(lines
, efifilepath
, varnames
)
93 def _parseForXcodeAndClang9(lines
, efifilepath
, varnames
):
98 if status
== 0 and (re
.match('^\s*Address\s*Size\s*Align\s*Out\s*In\s*Symbol\s*$', line
) \
99 or line
== "# Symbols:"):
102 if status
== 1 and len(line
) != 0:
103 for varname
in varnames
:
105 # cannot pregenerate this RegEx since it uses varname from varnames.
106 m
= re
.match('^([\da-fA-FxX]+)([\s\S]*)([_]*%s)$' % varname
, line
)
108 ret
.append((varname
, m
.group(1)))
111 def _parseForGCC(lines
, efifilepath
, varnames
):
112 """ Parse map file generated by GCC linker """
116 for index
, line
in enumerate(lines
):
118 # status machine transection
119 if status
== 0 and line
== "Memory Configuration":
122 elif status
== 1 and line
== 'Linker script and memory map':
125 elif status
==2 and line
== 'START GROUP':
131 m
= valuePatternGcc
.match(line
)
133 sections
.append(m
.groups(0))
134 for varname
in varnames
:
136 m
= re
.match("^.data.(%s)" % varname
, line
)
138 m
= re
.match(".data.(%s)$" % varname
, line
)
140 Str
= lines
[index
+ 1]
142 Str
= line
[len(".data.%s" % varname
):]
144 m
= pcdPatternGcc
.match(Str
.strip())
146 varoffset
.append((varname
, int(m
.groups(0)[0], 16), int(sections
[-1][1], 16), sections
[-1][0]))
150 # get section information from efi file
151 efisecs
= PeImageClass(efifilepath
).SectionHeaderList
152 if efisecs
is None or len(efisecs
) == 0:
156 for efisec
in efisecs
:
157 for section
in sections
:
158 if section
[0].strip() == efisec
[0].strip() and section
[0].strip() == '.text':
159 redirection
= int(section
[1], 16) - efisec
[1]
162 for var
in varoffset
:
163 for efisec
in efisecs
:
164 if var
[1] >= efisec
[1] and var
[1] < efisec
[1]+efisec
[3]:
165 ret
.append((var
[0], hex(efisec
[2] + var
[1] - efisec
[1] - redirection
)))
168 def _parseGeneral(lines
, efifilepath
, varnames
):
169 status
= 0 #0 - beginning of file; 1 - PE section definition; 2 - symbol table
170 secs
= [] # key = section name
172 symRe
= re
.compile('^([\da-fA-F]+):([\da-fA-F]+) +([\.:\\\\\w\?@\$-]+) +([\da-fA-F]+)', re
.UNICODE
)
176 if startPatternGeneral
.match(line
):
179 if addressPatternGeneral
.match(line
):
182 if line
.startswith("entry point at"):
185 if status
== 1 and len(line
) != 0:
186 m
= secReGeneral
.match(line
)
187 assert m
is not None, "Fail to parse the section in map file , line is %s" % line
188 sec_no
, sec_start
, sec_length
, sec_name
, sec_class
= m
.groups(0)
189 secs
.append([int(sec_no
, 16), int(sec_start
, 16), int(sec_length
, 16), sec_name
, sec_class
])
190 if status
== 2 and len(line
) != 0:
191 for varname
in varnames
:
192 m
= symRe
.match(line
)
193 assert m
is not None, "Fail to parse the symbol in map file, line is %s" % line
194 sec_no
, sym_offset
, sym_name
, vir_addr
= m
.groups(0)
195 sec_no
= int(sec_no
, 16)
196 sym_offset
= int(sym_offset
, 16)
197 vir_addr
= int(vir_addr
, 16)
198 # cannot pregenerate this RegEx since it uses varname from varnames.
199 m2
= re
.match('^[_]*(%s)' % varname
, sym_name
)
201 # fond a binary pcd entry in map file
203 if sec
[0] == sec_no
and (sym_offset
>= sec
[1] and sym_offset
< sec
[1] + sec
[2]):
204 varoffset
.append([varname
, sec
[3], sym_offset
, vir_addr
, sec_no
])
206 if not varoffset
: return []
208 # get section information from efi file
209 efisecs
= PeImageClass(efifilepath
).SectionHeaderList
210 if efisecs
is None or len(efisecs
) == 0:
214 for var
in varoffset
:
216 for efisec
in efisecs
:
218 if var
[1].strip() == efisec
[0].strip():
219 ret
.append((var
[0], hex(efisec
[2] + var
[2])))
220 elif var
[4] == index
:
221 ret
.append((var
[0], hex(efisec
[2] + var
[2])))
225 ## Routine to process duplicated INF
227 # This function is called by following two cases:
230 # Pkg/module/module.inf
231 # Pkg/module/module.inf {
233 # FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836
236 # INF Pkg/module/module.inf
237 # INF FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836 Pkg/module/module.inf
239 # This function copies Pkg/module/module.inf to
240 # Conf/.cache/0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf
242 # @param Path Original PathClass object
243 # @param BaseName New file base name
245 # @retval return the new PathClass object
247 def ProcessDuplicatedInf(Path
, BaseName
, Workspace
):
248 Filename
= os
.path
.split(Path
.File
)[1]
250 Filename
= BaseName
+ Path
.BaseName
+ Filename
[Filename
.rfind('.'):]
252 Filename
= BaseName
+ Path
.BaseName
254 DbDir
= os
.path
.split(GlobalData
.gDatabasePath
)[0]
257 # A temporary INF is copied to database path which must have write permission
258 # The temporary will be removed at the end of build
259 # In case of name conflict, the file name is
260 # FILE_GUIDBaseName (0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf)
262 TempFullPath
= os
.path
.join(DbDir
,
264 RtPath
= PathClass(Path
.File
, Workspace
)
266 # Modify the full path to temporary path, keep other unchanged
268 # To build same module more than once, the module path with FILE_GUID overridden has
269 # the file name FILE_GUIDmodule.inf, but the relative path (self.MetaFile.File) is the real path
270 # in DSC which is used as relative path by C files and other files in INF.
271 # A trick was used: all module paths are PathClass instances, after the initialization
272 # of PathClass, the PathClass.Path is overridden by the temporary INF path.
274 # The reason for creating a temporary INF is:
275 # Platform.Modules which is the base to create ModuleAutoGen objects is a dictionary,
276 # the key is the full path of INF, the value is an object to save overridden library instances, PCDs.
277 # A different key for the same module is needed to create different output directory,
278 # retrieve overridden PCDs, library instances.
280 # The BaseName is the FILE_GUID which is also the output directory name.
283 RtPath
.Path
= TempFullPath
284 RtPath
.BaseName
= BaseName
285 RtPath
.OriginalPath
= Path
287 # If file exists, compare contents
289 if os
.path
.exists(TempFullPath
):
290 with
open(str(Path
), 'rb') as f1
, open(TempFullPath
, 'rb') as f2
:
291 if f1
.read() == f2
.read():
293 _TempInfs
.append(TempFullPath
)
294 shutil
.copy2(str(Path
), TempFullPath
)
297 ## Remove temporary created INFs whose paths were saved in _TempInfs
299 def ClearDuplicatedInf():
301 File
= _TempInfs
.pop()
302 if os
.path
.exists(File
):
305 ## Convert GUID string in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx style to C structure style
307 # @param Guid The GUID string
309 # @retval string The GUID string in C structure style
311 def GuidStringToGuidStructureString(Guid
):
312 GuidList
= Guid
.split('-')
314 for Index
in range(0, 3, 1):
315 Result
= Result
+ '0x' + GuidList
[Index
] + ', '
316 Result
= Result
+ '{0x' + GuidList
[3][0:2] + ', 0x' + GuidList
[3][2:4]
317 for Index
in range(0, 12, 2):
318 Result
= Result
+ ', 0x' + GuidList
[4][Index
:Index
+ 2]
322 ## Convert GUID structure in byte array to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
324 # @param GuidValue The GUID value in byte array
326 # @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
328 def GuidStructureByteArrayToGuidString(GuidValue
):
329 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
330 guidValueList
= guidValueString
.split(",")
331 if len(guidValueList
) != 16:
333 #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
335 return "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
336 int(guidValueList
[3], 16),
337 int(guidValueList
[2], 16),
338 int(guidValueList
[1], 16),
339 int(guidValueList
[0], 16),
340 int(guidValueList
[5], 16),
341 int(guidValueList
[4], 16),
342 int(guidValueList
[7], 16),
343 int(guidValueList
[6], 16),
344 int(guidValueList
[8], 16),
345 int(guidValueList
[9], 16),
346 int(guidValueList
[10], 16),
347 int(guidValueList
[11], 16),
348 int(guidValueList
[12], 16),
349 int(guidValueList
[13], 16),
350 int(guidValueList
[14], 16),
351 int(guidValueList
[15], 16)
356 ## Convert GUID string in C structure style to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
358 # @param GuidValue The GUID value in C structure format
360 # @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
362 def GuidStructureStringToGuidString(GuidValue
):
363 if not GlobalData
.gGuidCFormatPattern
.match(GuidValue
):
365 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
366 guidValueList
= guidValueString
.split(",")
367 if len(guidValueList
) != 11:
369 #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
371 return "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
372 int(guidValueList
[0], 16),
373 int(guidValueList
[1], 16),
374 int(guidValueList
[2], 16),
375 int(guidValueList
[3], 16),
376 int(guidValueList
[4], 16),
377 int(guidValueList
[5], 16),
378 int(guidValueList
[6], 16),
379 int(guidValueList
[7], 16),
380 int(guidValueList
[8], 16),
381 int(guidValueList
[9], 16),
382 int(guidValueList
[10], 16)
387 ## Convert GUID string in C structure style to xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx
389 # @param GuidValue The GUID value in C structure format
391 # @retval string The GUID value in xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx format
393 def GuidStructureStringToGuidValueName(GuidValue
):
394 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "")
395 guidValueList
= guidValueString
.split(",")
396 if len(guidValueList
) != 11:
397 EdkLogger
.error(None, FORMAT_INVALID
, "Invalid GUID value string [%s]" % GuidValue
)
398 return "%08x_%04x_%04x_%02x%02x_%02x%02x%02x%02x%02x%02x" % (
399 int(guidValueList
[0], 16),
400 int(guidValueList
[1], 16),
401 int(guidValueList
[2], 16),
402 int(guidValueList
[3], 16),
403 int(guidValueList
[4], 16),
404 int(guidValueList
[5], 16),
405 int(guidValueList
[6], 16),
406 int(guidValueList
[7], 16),
407 int(guidValueList
[8], 16),
408 int(guidValueList
[9], 16),
409 int(guidValueList
[10], 16)
412 ## Create directories
414 # @param Directory The directory name
416 def CreateDirectory(Directory
):
417 if Directory
is None or Directory
.strip() == "":
420 if not os
.access(Directory
, os
.F_OK
):
421 os
.makedirs(Directory
)
426 ## Remove directories, including files and sub-directories in it
428 # @param Directory The directory name
430 def RemoveDirectory(Directory
, Recursively
=False):
431 if Directory
is None or Directory
.strip() == "" or not os
.path
.exists(Directory
):
434 CurrentDirectory
= os
.getcwd()
436 for File
in os
.listdir("."):
437 if os
.path
.isdir(File
):
438 RemoveDirectory(File
, Recursively
)
441 os
.chdir(CurrentDirectory
)
444 ## Store content in file
446 # This method is used to save file only when its content is changed. This is
447 # quite useful for "make" system to decide what will be re-built and what won't.
449 # @param File The path of file
450 # @param Content The new content of the file
451 # @param IsBinaryFile The flag indicating if the file is binary file or not
453 # @retval True If the file content is changed and the file is renewed
454 # @retval False If the file content is the same
456 def SaveFileOnChange(File
, Content
, IsBinaryFile
=True, FileLock
=None):
458 # Convert to long file path format
459 File
= LongFilePath(File
)
461 if os
.path
.exists(File
):
464 with
open(File
, "rb") as f
:
465 if Content
== f
.read():
468 EdkLogger
.error(None, FILE_OPEN_FAILURE
, ExtraData
=File
)
471 with
open(File
, "r") as f
:
472 if Content
== f
.read():
475 EdkLogger
.error(None, FILE_OPEN_FAILURE
, ExtraData
=File
)
477 DirName
= os
.path
.dirname(File
)
478 if not CreateDirectory(DirName
):
479 EdkLogger
.error(None, FILE_CREATE_FAILURE
, "Could not create directory %s" % DirName
)
482 DirName
= os
.getcwd()
483 if not os
.access(DirName
, os
.W_OK
):
484 EdkLogger
.error(None, PERMISSION_FAILURE
, "Do not have write permission on directory %s" % DirName
)
490 # use default file_lock if no input new lock
492 FileLock
= GlobalData
.file_lock
497 if GlobalData
.gIsWindows
and not os
.path
.exists(File
):
499 with
open(File
, OpenMode
) as tf
:
502 if GlobalData
.gBinCacheSource
:
503 EdkLogger
.quiet("[cache error]:fails to save file with error: %s" % (X
))
505 EdkLogger
.error(None, FILE_CREATE_FAILURE
, ExtraData
='IOError %s' % X
)
511 with
open(File
, OpenMode
) as Fd
:
514 if GlobalData
.gBinCacheSource
:
515 EdkLogger
.quiet("[cache error]:fails to save file with error: %s" % (X
))
517 EdkLogger
.error(None, FILE_CREATE_FAILURE
, ExtraData
='IOError %s' % X
)
524 ## Copy source file only if it is different from the destination file
526 # This method is used to copy file only if the source file and destination
527 # file content are different. This is quite useful to avoid duplicated
530 # @param SrcFile The path of source file
531 # @param Dst The path of destination file or folder
533 # @retval True The two files content are different and the file is copied
534 # @retval False No copy really happen
536 def CopyFileOnChange(SrcFile
, Dst
, FileLock
=None):
538 # Convert to long file path format
539 SrcFile
= LongFilePath(SrcFile
)
540 Dst
= LongFilePath(Dst
)
542 if os
.path
.isdir(SrcFile
):
543 EdkLogger
.error(None, FILE_COPY_FAILURE
, ExtraData
='CopyFileOnChange SrcFile is a dir, not a file: %s' % SrcFile
)
546 if os
.path
.isdir(Dst
):
547 DstFile
= os
.path
.join(Dst
, os
.path
.basename(SrcFile
))
551 if os
.path
.exists(DstFile
) and filecmp
.cmp(SrcFile
, DstFile
, shallow
=False):
554 DirName
= os
.path
.dirname(DstFile
)
555 if not CreateDirectory(DirName
):
556 EdkLogger
.error(None, FILE_CREATE_FAILURE
, "Could not create directory %s" % DirName
)
559 DirName
= os
.getcwd()
560 if not os
.access(DirName
, os
.W_OK
):
561 EdkLogger
.error(None, PERMISSION_FAILURE
, "Do not have write permission on directory %s" % DirName
)
563 # use default file_lock if no input new lock
565 FileLock
= GlobalData
.file_lock
570 CopyLong(SrcFile
, DstFile
)
572 if GlobalData
.gBinCacheSource
:
573 EdkLogger
.quiet("[cache error]:fails to copy file with error: %s" % (X
))
575 EdkLogger
.error(None, FILE_COPY_FAILURE
, ExtraData
='IOError %s' % X
)
582 ## Retrieve and cache the real path name in file system
584 # @param Root The root directory of path relative to
586 # @retval str The path string if the path exists
587 # @retval None If path doesn't exist
593 def __init__(self
, Root
):
595 for F
in os
.listdir(Root
):
597 self
._UPPER
_CACHE
_[F
.upper()] = F
600 def __getitem__(self
, Path
):
601 Path
= Path
[len(os
.path
.commonprefix([Path
, self
._Root
])):]
604 if Path
and Path
[0] == os
.path
.sep
:
606 if Path
in self
._CACHE
_:
607 return os
.path
.join(self
._Root
, Path
)
608 UpperPath
= Path
.upper()
609 if UpperPath
in self
._UPPER
_CACHE
_:
610 return os
.path
.join(self
._Root
, self
._UPPER
_CACHE
_[UpperPath
])
614 SepIndex
= Path
.find(os
.path
.sep
)
616 Parent
= UpperPath
[:SepIndex
]
617 if Parent
not in self
._UPPER
_CACHE
_:
619 LastSepIndex
= SepIndex
620 SepIndex
= Path
.find(os
.path
.sep
, LastSepIndex
+ 1)
622 if LastSepIndex
== -1:
627 SepIndex
= LastSepIndex
629 Parent
= Path
[:SepIndex
]
630 ParentKey
= UpperPath
[:SepIndex
]
631 if ParentKey
not in self
._UPPER
_CACHE
_:
635 if Parent
in self
._CACHE
_:
638 ParentDir
= self
._UPPER
_CACHE
_[ParentKey
]
639 for F
in os
.listdir(ParentDir
):
640 Dir
= os
.path
.join(ParentDir
, F
)
641 self
._CACHE
_.add(Dir
)
642 self
._UPPER
_CACHE
_[Dir
.upper()] = Dir
644 SepIndex
= Path
.find(os
.path
.sep
, SepIndex
+ 1)
647 if Path
in self
._CACHE
_:
648 return os
.path
.join(self
._Root
, Path
)
649 elif UpperPath
in self
._UPPER
_CACHE
_:
650 return os
.path
.join(self
._Root
, self
._UPPER
_CACHE
_[UpperPath
])
653 def RealPath(File
, Dir
='', OverrideDir
=''):
654 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
655 NewFile
= GlobalData
.gAllFiles
[NewFile
]
656 if not NewFile
and OverrideDir
:
657 NewFile
= os
.path
.normpath(os
.path
.join(OverrideDir
, File
))
658 NewFile
= GlobalData
.gAllFiles
[NewFile
]
661 ## Get GUID value from given packages
663 # @param CName The CName of the GUID
664 # @param PackageList List of packages looking-up in
665 # @param Inffile The driver file
667 # @retval GuidValue if the CName is found in any given package
668 # @retval None if the CName is not found in all given packages
670 def GuidValue(CName
, PackageList
, Inffile
= None):
671 for P
in PackageList
:
672 GuidKeys
= list(P
.Guids
.keys())
673 if Inffile
and P
._PrivateGuids
:
674 if not Inffile
.startswith(P
.MetaFile
.Dir
):
675 GuidKeys
= [x
for x
in P
.Guids
if x
not in P
._PrivateGuids
]
676 if CName
in GuidKeys
:
677 return P
.Guids
[CName
]
680 ## A string template class
682 # This class implements a template for string replacement. A string template
683 # looks like following
685 # ${BEGIN} other_string ${placeholder_name} other_string ${END}
687 # The string between ${BEGIN} and ${END} will be repeated as many times as the
688 # length of "placeholder_name", which is a list passed through a dict. The
689 # "placeholder_name" is the key name of the dict. The ${BEGIN} and ${END} can
690 # be not used and, in this case, the "placeholder_name" must not a list and it
691 # will just be replaced once.
693 class TemplateString(object):
694 _REPEAT_START_FLAG
= "BEGIN"
695 _REPEAT_END_FLAG
= "END"
697 class Section(object):
698 _LIST_TYPES
= [type([]), type(set()), type((0,))]
700 def __init__(self
, TemplateSection
, PlaceHolderList
):
701 self
._Template
= TemplateSection
702 self
._PlaceHolderList
= []
704 # Split the section into sub-sections according to the position of placeholders
706 self
._SubSectionList
= []
709 # The placeholders passed in must be in the format of
711 # PlaceHolderName, PlaceHolderStartPoint, PlaceHolderEndPoint
713 for PlaceHolder
, Start
, End
in PlaceHolderList
:
714 self
._SubSectionList
.append(TemplateSection
[SubSectionStart
:Start
])
715 self
._SubSectionList
.append(TemplateSection
[Start
:End
])
716 self
._PlaceHolderList
.append(PlaceHolder
)
717 SubSectionStart
= End
718 if SubSectionStart
< len(TemplateSection
):
719 self
._SubSectionList
.append(TemplateSection
[SubSectionStart
:])
721 self
._SubSectionList
= [TemplateSection
]
724 return self
._Template
+ " : " + str(self
._PlaceHolderList
)
726 def Instantiate(self
, PlaceHolderValues
):
728 RepeatPlaceHolders
= {}
729 NonRepeatPlaceHolders
= {}
731 for PlaceHolder
in self
._PlaceHolderList
:
732 if PlaceHolder
not in PlaceHolderValues
:
734 Value
= PlaceHolderValues
[PlaceHolder
]
735 if type(Value
) in self
._LIST
_TYPES
:
737 RepeatTime
= len(Value
)
738 elif RepeatTime
!= len(Value
):
742 "${%s} has different repeat time from others!" % PlaceHolder
,
743 ExtraData
=str(self
._Template
)
745 RepeatPlaceHolders
["${%s}" % PlaceHolder
] = Value
747 NonRepeatPlaceHolders
["${%s}" % PlaceHolder
] = Value
749 if NonRepeatPlaceHolders
:
751 for S
in self
._SubSectionList
:
752 if S
not in NonRepeatPlaceHolders
:
755 StringList
.append(str(NonRepeatPlaceHolders
[S
]))
757 StringList
= self
._SubSectionList
759 if RepeatPlaceHolders
:
761 for Index
in range(RepeatTime
):
763 if S
not in RepeatPlaceHolders
:
764 TempStringList
.append(S
)
766 TempStringList
.append(str(RepeatPlaceHolders
[S
][Index
]))
767 StringList
= TempStringList
769 return "".join(StringList
)
772 def __init__(self
, Template
=None):
774 self
.IsBinary
= False
775 self
._Template
= Template
776 self
._TemplateSectionList
= self
._Parse
(Template
)
780 # @retval string The string replaced
783 return "".join(self
.String
)
785 ## Split the template string into fragments per the ${BEGIN} and ${END} flags
787 # @retval list A list of TemplateString.Section objects
789 def _Parse(self
, Template
):
794 TemplateSectionList
= []
796 MatchObj
= gPlaceholderPattern
.search(Template
, SearchFrom
)
798 if MatchEnd
<= len(Template
):
799 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:], PlaceHolderList
)
800 TemplateSectionList
.append(TemplateSection
)
803 MatchString
= MatchObj
.group(1)
804 MatchStart
= MatchObj
.start()
805 MatchEnd
= MatchObj
.end()
807 if MatchString
== self
._REPEAT
_START
_FLAG
:
808 if MatchStart
> SectionStart
:
809 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:MatchStart
], PlaceHolderList
)
810 TemplateSectionList
.append(TemplateSection
)
811 SectionStart
= MatchEnd
813 elif MatchString
== self
._REPEAT
_END
_FLAG
:
814 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:MatchStart
], PlaceHolderList
)
815 TemplateSectionList
.append(TemplateSection
)
816 SectionStart
= MatchEnd
819 PlaceHolderList
.append((MatchString
, MatchStart
- SectionStart
, MatchEnd
- SectionStart
))
820 SearchFrom
= MatchEnd
821 return TemplateSectionList
823 ## Replace the string template with dictionary of placeholders and append it to previous one
825 # @param AppendString The string template to append
826 # @param Dictionary The placeholder dictionaries
828 def Append(self
, AppendString
, Dictionary
=None):
830 SectionList
= self
._Parse
(AppendString
)
831 self
.String
.append( "".join(S
.Instantiate(Dictionary
) for S
in SectionList
))
833 if isinstance(AppendString
,list):
834 self
.String
.extend(AppendString
)
836 self
.String
.append(AppendString
)
838 ## Replace the string template with dictionary of placeholders
840 # @param Dictionary The placeholder dictionaries
842 # @retval str The string replaced with placeholder values
844 def Replace(self
, Dictionary
=None):
845 return "".join(S
.Instantiate(Dictionary
) for S
in self
._TemplateSectionList
)
847 ## Progress indicator class
849 # This class makes use of thread to print progress on console.
852 # for avoiding deadloop
854 _ProgressThread
= None
855 _CheckInterval
= 0.25
859 # @param OpenMessage The string printed before progress characters
860 # @param CloseMessage The string printed after progress characters
861 # @param ProgressChar The character used to indicate the progress
862 # @param Interval The interval in seconds between two progress characters
864 def __init__(self
, OpenMessage
="", CloseMessage
="", ProgressChar
='.', Interval
=1.0):
865 self
.PromptMessage
= OpenMessage
866 self
.CodaMessage
= CloseMessage
867 self
.ProgressChar
= ProgressChar
868 self
.Interval
= Interval
869 if Progressor
._StopFlag
is None:
870 Progressor
._StopFlag
= threading
.Event()
872 ## Start to print progress character
874 # @param OpenMessage The string printed before progress characters
876 def Start(self
, OpenMessage
=None):
877 if OpenMessage
is not None:
878 self
.PromptMessage
= OpenMessage
879 Progressor
._StopFlag
.clear()
880 if Progressor
._ProgressThread
is None:
881 Progressor
._ProgressThread
= threading
.Thread(target
=self
._ProgressThreadEntry
)
882 Progressor
._ProgressThread
.setDaemon(False)
883 Progressor
._ProgressThread
.start()
885 ## Stop printing progress character
887 # @param CloseMessage The string printed after progress characters
889 def Stop(self
, CloseMessage
=None):
890 OriginalCodaMessage
= self
.CodaMessage
891 if CloseMessage
is not None:
892 self
.CodaMessage
= CloseMessage
894 self
.CodaMessage
= OriginalCodaMessage
896 ## Thread entry method
897 def _ProgressThreadEntry(self
):
898 sys
.stdout
.write(self
.PromptMessage
+ " ")
901 while not Progressor
._StopFlag
.isSet():
903 sys
.stdout
.write(self
.ProgressChar
)
905 TimeUp
= self
.Interval
906 time
.sleep(self
._CheckInterval
)
907 TimeUp
-= self
._CheckInterval
908 sys
.stdout
.write(" " + self
.CodaMessage
+ "\n")
911 ## Abort the progress display
914 if Progressor
._StopFlag
is not None:
915 Progressor
._StopFlag
.set()
916 if Progressor
._ProgressThread
is not None:
917 Progressor
._ProgressThread
.join()
918 Progressor
._ProgressThread
= None
921 ## Dictionary using prioritized list as key
925 _TupleType
= type(())
927 _ValidWildcardList
= ['COMMON', 'DEFAULT', 'ALL', TAB_STAR
, 'PLATFORM']
929 def __init__(self
, _Single_
=False, _Level_
=2):
930 self
._Level
_ = _Level_
932 self
._Single
_ = _Single_
935 def __getitem__(self
, key
):
938 if KeyType
== self
._ListType
or KeyType
== self
._TupleType
:
942 elif self
._Level
_ > 1:
943 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
947 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
949 if FirstKey
is None or str(FirstKey
).upper() in self
._ValidWildcardList
:
950 FirstKey
= self
._Wildcard
953 return self
._GetSingleValue
(FirstKey
, RestKeys
)
955 return self
._GetAllValues
(FirstKey
, RestKeys
)
957 def _GetSingleValue(self
, FirstKey
, RestKeys
):
959 #print "%s-%s" % (FirstKey, self._Level_) ,
961 if FirstKey
== self
._Wildcard
:
962 if FirstKey
in self
.data
:
963 Value
= self
.data
[FirstKey
][RestKeys
]
965 for Key
in self
.data
:
966 Value
= self
.data
[Key
][RestKeys
]
967 if Value
is not None: break
969 if FirstKey
in self
.data
:
970 Value
= self
.data
[FirstKey
][RestKeys
]
971 if Value
is None and self
._Wildcard
in self
.data
:
973 Value
= self
.data
[self
._Wildcard
][RestKeys
]
975 if FirstKey
== self
._Wildcard
:
976 if FirstKey
in self
.data
:
977 Value
= self
.data
[FirstKey
]
979 for Key
in self
.data
:
980 Value
= self
.data
[Key
]
981 if Value
is not None: break
983 if FirstKey
in self
.data
:
984 Value
= self
.data
[FirstKey
]
985 elif self
._Wildcard
in self
.data
:
986 Value
= self
.data
[self
._Wildcard
]
989 def _GetAllValues(self
, FirstKey
, RestKeys
):
992 if FirstKey
== self
._Wildcard
:
993 for Key
in self
.data
:
994 Value
+= self
.data
[Key
][RestKeys
]
996 if FirstKey
in self
.data
:
997 Value
+= self
.data
[FirstKey
][RestKeys
]
998 if self
._Wildcard
in self
.data
:
999 Value
+= self
.data
[self
._Wildcard
][RestKeys
]
1001 if FirstKey
== self
._Wildcard
:
1002 for Key
in self
.data
:
1003 Value
.append(self
.data
[Key
])
1005 if FirstKey
in self
.data
:
1006 Value
.append(self
.data
[FirstKey
])
1007 if self
._Wildcard
in self
.data
:
1008 Value
.append(self
.data
[self
._Wildcard
])
1012 def __setitem__(self
, key
, value
):
1015 if KeyType
== self
._ListType
or KeyType
== self
._TupleType
:
1020 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
1023 if self
._Level
_ > 1:
1024 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
1026 if FirstKey
in self
._ValidWildcardList
:
1027 FirstKey
= self
._Wildcard
1029 if FirstKey
not in self
.data
and self
._Level
_ > 0:
1030 self
.data
[FirstKey
] = tdict(self
._Single
_, self
._Level
_ - 1)
1032 if self
._Level
_ > 1:
1033 self
.data
[FirstKey
][RestKeys
] = value
1035 self
.data
[FirstKey
] = value
1037 def SetGreedyMode(self
):
1038 self
._Single
_ = False
1039 if self
._Level
_ > 1:
1040 for Key
in self
.data
:
1041 self
.data
[Key
].SetGreedyMode()
1043 def SetSingleMode(self
):
1044 self
._Single
_ = True
1045 if self
._Level
_ > 1:
1046 for Key
in self
.data
:
1047 self
.data
[Key
].SetSingleMode()
1049 def GetKeys(self
, KeyIndex
=0):
1050 assert KeyIndex
>= 0
1052 return set(self
.data
.keys())
1055 for Key
in self
.data
:
1056 keys |
= self
.data
[Key
].GetKeys(KeyIndex
- 1)
1059 def AnalyzePcdExpression(Setting
):
1060 RanStr
= ''.join(sample(string
.ascii_letters
+ string
.digits
, 8))
1061 Setting
= Setting
.replace('\\\\', RanStr
).strip()
1062 # There might be escaped quote in a string: \", \\\" , \', \\\'
1064 # There might be '|' in string and in ( ... | ... ), replace it with '-'
1066 InSingleQuoteStr
= False
1067 InDoubleQuoteStr
= False
1069 for Index
, ch
in enumerate(Data
):
1070 if ch
== '"' and not InSingleQuoteStr
:
1071 if Data
[Index
- 1] != '\\':
1072 InDoubleQuoteStr
= not InDoubleQuoteStr
1073 elif ch
== "'" and not InDoubleQuoteStr
:
1074 if Data
[Index
- 1] != '\\':
1075 InSingleQuoteStr
= not InSingleQuoteStr
1076 elif ch
== '(' and not (InSingleQuoteStr
or InDoubleQuoteStr
):
1078 elif ch
== ')' and not (InSingleQuoteStr
or InDoubleQuoteStr
):
1081 if (Pair
> 0 or InSingleQuoteStr
or InDoubleQuoteStr
) and ch
== TAB_VALUE_SPLIT
:
1088 Pos
= NewStr
.find(TAB_VALUE_SPLIT
, StartPos
)
1090 FieldList
.append(Setting
[StartPos
:].strip())
1092 FieldList
.append(Setting
[StartPos
:Pos
].strip())
1094 for i
, ch
in enumerate(FieldList
):
1096 FieldList
[i
] = ch
.replace(RanStr
,'\\\\')
1099 def ParseFieldValue (Value
):
1100 def ParseDevPathValue (Value
):
1102 Value
.replace('\\', '/').replace(' ', '')
1104 Cmd
= 'DevicePath ' + '"' + Value
+ '"'
1106 p
= subprocess
.Popen(Cmd
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, shell
=True)
1107 out
, err
= p
.communicate()
1108 except Exception as X
:
1109 raise BadExpression("DevicePath: %s" % (str(X
)) )
1111 subprocess
._cleanup
()
1115 raise BadExpression("DevicePath: %s" % str(err
))
1117 Size
= len(out
.split())
1118 out
= ','.join(out
.split())
1119 return '{' + out
+ '}', Size
1121 if "{CODE(" in Value
:
1122 return Value
, len(Value
.split(","))
1123 if isinstance(Value
, type(0)):
1124 return Value
, (Value
.bit_length() + 7) // 8
1125 if not isinstance(Value
, type('')):
1126 raise BadExpression('Type %s is %s' %(Value
, type(Value
)))
1127 Value
= Value
.strip()
1128 if Value
.startswith(TAB_UINT8
) and Value
.endswith(')'):
1129 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1131 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1133 if Value
.startswith(TAB_UINT16
) and Value
.endswith(')'):
1134 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1136 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1138 if Value
.startswith(TAB_UINT32
) and Value
.endswith(')'):
1139 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1141 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1143 if Value
.startswith(TAB_UINT64
) and Value
.endswith(')'):
1144 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1146 raise BadExpression('Value (%s) Size larger than %d' % (Value
, Size
))
1148 if Value
.startswith(TAB_GUID
) and Value
.endswith(')'):
1149 Value
= Value
.split('(', 1)[1][:-1].strip()
1150 if Value
[0] == '{' and Value
[-1] == '}':
1151 TmpValue
= GuidStructureStringToGuidString(Value
)
1153 raise BadExpression("Invalid GUID value string %s" % Value
)
1155 if Value
[0] == '"' and Value
[-1] == '"':
1158 Value
= str(uuid
.UUID(Value
).bytes_le
)
1159 if Value
.startswith("b'"):
1161 Value
= "'" + Value
+ "'"
1162 except ValueError as Message
:
1163 raise BadExpression(Message
)
1164 Value
, Size
= ParseFieldValue(Value
)
1166 if Value
.startswith('L"') and Value
.endswith('"'):
1168 # translate escape character
1178 Value
= (Value
<< 16) |
ord(Char
)
1179 return Value
, (len(List
) + 1) * 2
1180 if Value
.startswith('"') and Value
.endswith('"'):
1182 # translate escape character
1191 Value
= (Value
<< 8) |
ord(Char
)
1192 return Value
, len(List
) + 1
1193 if Value
.startswith("L'") and Value
.endswith("'"):
1194 # Unicode Character Constant
1195 # translate escape character
1203 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1207 Value
= (Value
<< 16) |
ord(Char
)
1208 return Value
, len(List
) * 2
1209 if Value
.startswith("'") and Value
.endswith("'"):
1210 # Character constant
1211 # translate escape character
1218 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1222 Value
= (Value
<< 8) |
ord(Char
)
1223 return Value
, len(List
)
1224 if Value
.startswith('{') and Value
.endswith('}'):
1227 List
= [Item
.strip() for Item
in Value
.split(',')]
1232 ItemValue
, Size
= ParseFieldValue(Item
)
1234 for I
in range(Size
):
1235 Value
= (Value
<< 8) |
((ItemValue
>> 8 * I
) & 0xff)
1236 return Value
, RetSize
1237 if Value
.startswith('DEVICE_PATH(') and Value
.endswith(')'):
1238 Value
= Value
.replace("DEVICE_PATH(", '').rstrip(')')
1239 Value
= Value
.strip().strip('"')
1240 return ParseDevPathValue(Value
)
1241 if Value
.lower().startswith('0x'):
1243 Value
= int(Value
, 16)
1245 raise BadExpression("invalid hex value: %s" % Value
)
1248 return Value
, (Value
.bit_length() + 7) // 8
1249 if Value
[0].isdigit():
1250 Value
= int(Value
, 10)
1253 return Value
, (Value
.bit_length() + 7) // 8
1254 if Value
.lower() == 'true':
1256 if Value
.lower() == 'false':
1262 # Analyze DSC PCD value, since there is no data type info in DSC
1263 # This function is used to match functions (AnalyzePcdData) used for retrieving PCD value from database
1264 # 1. Feature flag: TokenSpace.PcdCName|PcdValue
1265 # 2. Fix and Patch:TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1266 # 3. Dynamic default:
1267 # TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1268 # TokenSpace.PcdCName|PcdValue
1270 # TokenSpace.PcdCName|VpdOffset[|VpdValue]
1271 # TokenSpace.PcdCName|VpdOffset[|MaxSize[|VpdValue]]
1273 # TokenSpace.PcdCName|HiiString|VariableGuid|VariableOffset[|HiiValue]
1274 # PCD value needs to be located in such kind of string, and the PCD value might be an expression in which
1275 # there might have "|" operator, also in string value.
1277 # @param Setting: String contain information described above with "TokenSpace.PcdCName|" stripped
1278 # @param PcdType: PCD type: feature, fixed, dynamic default VPD HII
1279 # @param DataType: The datum type of PCD: VOID*, UNIT, BOOL
1281 # ValueList: A List contain fields described above
1282 # IsValid: True if conforming EBNF, otherwise False
1283 # Index: The index where PcdValue is in ValueList
1285 def AnalyzeDscPcd(Setting
, PcdType
, DataType
=''):
1286 FieldList
= AnalyzePcdExpression(Setting
)
1289 if PcdType
in (MODEL_PCD_FIXED_AT_BUILD
, MODEL_PCD_PATCHABLE_IN_MODULE
, MODEL_PCD_DYNAMIC_DEFAULT
, MODEL_PCD_DYNAMIC_EX_DEFAULT
):
1290 Value
= FieldList
[0]
1292 if len(FieldList
) > 1 and FieldList
[1]:
1293 DataType
= FieldList
[1]
1294 if FieldList
[1] != TAB_VOID
and StructPattern
.match(FieldList
[1]) is None:
1296 if len(FieldList
) > 2:
1300 IsValid
= (len(FieldList
) <= 1)
1302 IsValid
= (len(FieldList
) <= 3)
1306 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1310 return [str(Value
), DataType
, str(Size
)], IsValid
, 0
1311 elif PcdType
== MODEL_PCD_FEATURE_FLAG
:
1312 Value
= FieldList
[0]
1314 IsValid
= (len(FieldList
) <= 1)
1315 return [Value
, DataType
, str(Size
)], IsValid
, 0
1316 elif PcdType
in (MODEL_PCD_DYNAMIC_VPD
, MODEL_PCD_DYNAMIC_EX_VPD
):
1317 VpdOffset
= FieldList
[0]
1319 if not DataType
== TAB_VOID
:
1320 if len(FieldList
) > 1:
1321 Value
= FieldList
[1]
1323 if len(FieldList
) > 1:
1325 if len(FieldList
) > 2:
1326 Value
= FieldList
[2]
1328 IsValid
= (len(FieldList
) <= 1)
1330 IsValid
= (len(FieldList
) <= 3)
1333 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1337 return [VpdOffset
, str(Size
), Value
], IsValid
, 2
1338 elif PcdType
in (MODEL_PCD_DYNAMIC_HII
, MODEL_PCD_DYNAMIC_EX_HII
):
1339 IsValid
= (3 <= len(FieldList
) <= 5)
1340 HiiString
= FieldList
[0]
1341 Guid
= Offset
= Value
= Attribute
= ''
1342 if len(FieldList
) > 1:
1344 if len(FieldList
) > 2:
1345 Offset
= FieldList
[2]
1346 if len(FieldList
) > 3:
1347 Value
= FieldList
[3]
1348 if len(FieldList
) > 4:
1349 Attribute
= FieldList
[4]
1350 return [HiiString
, Guid
, Offset
, Value
, Attribute
], IsValid
, 3
1355 # Analyze the pcd Value, Datum type and TokenNumber.
1356 # Used to avoid split issue while the value string contain "|" character
1358 # @param[in] Setting: A String contain value/datum type/token number information;
1360 # @retval ValueList: A List contain value, datum type and toke number.
1362 def AnalyzePcdData(Setting
):
1363 ValueList
= ['', '', '']
1365 ValueRe
= re
.compile(r
'^\s*L?\".*\|.*\"')
1366 PtrValue
= ValueRe
.findall(Setting
)
1368 ValueUpdateFlag
= False
1370 if len(PtrValue
) >= 1:
1371 Setting
= re
.sub(ValueRe
, '', Setting
)
1372 ValueUpdateFlag
= True
1374 TokenList
= Setting
.split(TAB_VALUE_SPLIT
)
1375 ValueList
[0:len(TokenList
)] = TokenList
1378 ValueList
[0] = PtrValue
[0]
1382 ## check format of PCD value against its the datum type
1384 # For PCD value setting
1386 def CheckPcdDatum(Type
, Value
):
1387 if Type
== TAB_VOID
:
1388 ValueRe
= re
.compile(r
'\s*L?\".*\"\s*$')
1389 if not (((Value
.startswith('L"') or Value
.startswith('"')) and Value
.endswith('"'))
1390 or (Value
.startswith('{') and Value
.endswith('}')) or (Value
.startswith("L'") or Value
.startswith("'") and Value
.endswith("'"))
1392 return False, "Invalid value [%s] of type [%s]; must be in the form of {...} for array"\
1393 ", \"...\" or \'...\' for string, L\"...\" or L\'...\' for unicode string" % (Value
, Type
)
1394 elif ValueRe
.match(Value
):
1395 # Check the chars in UnicodeString or CString is printable
1396 if Value
.startswith("L"):
1400 Printset
= set(string
.printable
)
1401 Printset
.remove(TAB_PRINTCHAR_VT
)
1402 Printset
.add(TAB_PRINTCHAR_BS
)
1403 Printset
.add(TAB_PRINTCHAR_NUL
)
1404 if not set(Value
).issubset(Printset
):
1405 PrintList
= sorted(Printset
)
1406 return False, "Invalid PCD string value of type [%s]; must be printable chars %s." % (Type
, PrintList
)
1407 elif Type
== 'BOOLEAN':
1408 if Value
not in ['TRUE', 'True', 'true', '0x1', '0x01', '1', 'FALSE', 'False', 'false', '0x0', '0x00', '0']:
1409 return False, "Invalid value [%s] of type [%s]; must be one of TRUE, True, true, 0x1, 0x01, 1"\
1410 ", FALSE, False, false, 0x0, 0x00, 0" % (Value
, Type
)
1411 elif Type
in [TAB_UINT8
, TAB_UINT16
, TAB_UINT32
, TAB_UINT64
]:
1412 if Value
.startswith('0') and not Value
.lower().startswith('0x') and len(Value
) > 1 and Value
.lstrip('0'):
1413 Value
= Value
.lstrip('0')
1415 if Value
and int(Value
, 0) < 0:
1416 return False, "PCD can't be set to negative value[%s] for datum type [%s]" % (Value
, Type
)
1417 Value
= int(Value
, 0)
1418 if Value
> MAX_VAL_TYPE
[Type
]:
1419 return False, "Too large PCD value[%s] for datum type [%s]" % (Value
, Type
)
1421 return False, "Invalid value [%s] of type [%s];"\
1422 " must be a hexadecimal, decimal or octal in C language format." % (Value
, Type
)
1424 return True, "StructurePcd"
1428 def CommonPath(PathList
):
1429 P1
= min(PathList
).split(os
.path
.sep
)
1430 P2
= max(PathList
).split(os
.path
.sep
)
1431 for Index
in range(min(len(P1
), len(P2
))):
1432 if P1
[Index
] != P2
[Index
]:
1433 return os
.path
.sep
.join(P1
[:Index
])
1434 return os
.path
.sep
.join(P1
)
1436 class PathClass(object):
1437 def __init__(self
, File
='', Root
='', AlterRoot
='', Type
='', IsBinary
=False,
1438 Arch
='COMMON', ToolChainFamily
='', Target
='', TagName
='', ToolCode
=''):
1440 self
.File
= str(File
)
1441 if os
.path
.isabs(self
.File
):
1445 self
.Root
= str(Root
)
1446 self
.AlterRoot
= str(AlterRoot
)
1448 # Remove any '.' and '..' in path
1450 self
.Root
= mws
.getWs(self
.Root
, self
.File
)
1451 self
.Path
= os
.path
.normpath(os
.path
.join(self
.Root
, self
.File
))
1452 self
.Root
= os
.path
.normpath(CommonPath([self
.Root
, self
.Path
]))
1453 # eliminate the side-effect of 'C:'
1454 if self
.Root
[-1] == ':':
1455 self
.Root
+= os
.path
.sep
1456 # file path should not start with path separator
1457 if self
.Root
[-1] == os
.path
.sep
:
1458 self
.File
= self
.Path
[len(self
.Root
):]
1460 self
.File
= self
.Path
[len(self
.Root
) + 1:]
1462 self
.Path
= os
.path
.normpath(self
.File
)
1464 self
.SubDir
, self
.Name
= os
.path
.split(self
.File
)
1465 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1469 self
.Dir
= os
.path
.join(self
.Root
, self
.SubDir
)
1471 self
.Dir
= self
.Root
1473 self
.Dir
= self
.SubDir
1478 self
.Type
= self
.Ext
.lower()
1480 self
.IsBinary
= IsBinary
1481 self
.Target
= Target
1482 self
.TagName
= TagName
1483 self
.ToolCode
= ToolCode
1484 self
.ToolChainFamily
= ToolChainFamily
1485 self
.OriginalPath
= self
1487 ## Convert the object of this class to a string
1489 # Convert member Path of the class to a string
1491 # @retval string Formatted String
1496 ## Override __eq__ function
1498 # Check whether PathClass are the same
1500 # @retval False The two PathClass are different
1501 # @retval True The two PathClass are the same
1503 def __eq__(self
, Other
):
1504 return self
.Path
== str(Other
)
1506 ## Override __cmp__ function
1508 # Customize the comparison operation of two PathClass
1510 # @retval 0 The two PathClass are different
1511 # @retval -1 The first PathClass is less than the second PathClass
1512 # @retval 1 The first PathClass is Bigger than the second PathClass
1513 def __cmp__(self
, Other
):
1514 OtherKey
= str(Other
)
1517 if SelfKey
== OtherKey
:
1519 elif SelfKey
> OtherKey
:
1524 ## Override __hash__ function
1526 # Use Path as key in hash table
1528 # @retval string Key for hash table
1531 return hash(self
.Path
)
1535 return self
.Path
.upper()
1538 def TimeStamp(self
):
1539 return os
.stat(self
.Path
)[8]
1541 def Validate(self
, Type
='', CaseSensitive
=True):
1542 def RealPath2(File
, Dir
='', OverrideDir
=''):
1545 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(OverrideDir
, File
))]
1547 if OverrideDir
[-1] == os
.path
.sep
:
1548 return NewFile
[len(OverrideDir
):], NewFile
[0:len(OverrideDir
)]
1550 return NewFile
[len(OverrideDir
) + 1:], NewFile
[0:len(OverrideDir
)]
1551 if GlobalData
.gAllFiles
:
1552 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(Dir
, File
))]
1554 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
1555 if not os
.path
.exists(NewFile
):
1559 if Dir
[-1] == os
.path
.sep
:
1560 return NewFile
[len(Dir
):], NewFile
[0:len(Dir
)]
1562 return NewFile
[len(Dir
) + 1:], NewFile
[0:len(Dir
)]
1568 if GlobalData
.gCaseInsensitive
:
1569 CaseSensitive
= False
1570 if Type
and Type
.lower() != self
.Type
:
1571 return FILE_TYPE_MISMATCH
, '%s (expect %s but got %s)' % (self
.File
, Type
, self
.Type
)
1573 RealFile
, RealRoot
= RealPath2(self
.File
, self
.Root
, self
.AlterRoot
)
1574 if not RealRoot
and not RealFile
:
1575 RealFile
= self
.File
1577 RealFile
= os
.path
.join(self
.AlterRoot
, self
.File
)
1579 RealFile
= os
.path
.join(self
.Root
, self
.File
)
1580 if len (mws
.getPkgPath()) == 0:
1581 return FILE_NOT_FOUND
, os
.path
.join(self
.AlterRoot
, RealFile
)
1583 return FILE_NOT_FOUND
, "%s is not found in packages path:\n\t%s" % (self
.File
, '\n\t'.join(mws
.getPkgPath()))
1587 if RealRoot
!= self
.Root
or RealFile
!= self
.File
:
1588 if CaseSensitive
and (RealFile
!= self
.File
or (RealRoot
!= self
.Root
and RealRoot
!= self
.AlterRoot
)):
1589 ErrorCode
= FILE_CASE_MISMATCH
1590 ErrorInfo
= self
.File
+ '\n\t' + RealFile
+ " [in file system]"
1592 self
.SubDir
, self
.Name
= os
.path
.split(RealFile
)
1593 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1595 self
.Dir
= os
.path
.join(RealRoot
, self
.SubDir
)
1598 self
.File
= RealFile
1599 self
.Root
= RealRoot
1600 self
.Path
= os
.path
.join(RealRoot
, RealFile
)
1601 return ErrorCode
, ErrorInfo
1603 ## Parse PE image to get the required PE information.
1605 class PeImageClass():
1608 # @param File FilePath of PeImage
1610 def __init__(self
, PeFile
):
1611 self
.FileName
= PeFile
1612 self
.IsValid
= False
1615 self
.SectionAlignment
= 0
1616 self
.SectionHeaderList
= []
1619 PeObject
= open(PeFile
, 'rb')
1621 self
.ErrorInfo
= self
.FileName
+ ' can not be found\n'
1624 ByteArray
= array
.array('B')
1625 ByteArray
.fromfile(PeObject
, 0x3E)
1626 ByteList
= ByteArray
.tolist()
1627 # DOS signature should be 'MZ'
1628 if self
._ByteListToStr
(ByteList
[0x0:0x2]) != 'MZ':
1629 self
.ErrorInfo
= self
.FileName
+ ' has no valid DOS signature MZ'
1632 # Read 4 byte PE Signature
1633 PeOffset
= self
._ByteListToInt
(ByteList
[0x3C:0x3E])
1634 PeObject
.seek(PeOffset
)
1635 ByteArray
= array
.array('B')
1636 ByteArray
.fromfile(PeObject
, 4)
1637 # PE signature should be 'PE\0\0'
1638 if ByteArray
.tostring() != b
'PE\0\0':
1639 self
.ErrorInfo
= self
.FileName
+ ' has no valid PE signature PE00'
1642 # Read PE file header
1643 ByteArray
= array
.array('B')
1644 ByteArray
.fromfile(PeObject
, 0x14)
1645 ByteList
= ByteArray
.tolist()
1646 SecNumber
= self
._ByteListToInt
(ByteList
[0x2:0x4])
1648 self
.ErrorInfo
= self
.FileName
+ ' has no section header'
1651 # Read PE optional header
1652 OptionalHeaderSize
= self
._ByteListToInt
(ByteArray
[0x10:0x12])
1653 ByteArray
= array
.array('B')
1654 ByteArray
.fromfile(PeObject
, OptionalHeaderSize
)
1655 ByteList
= ByteArray
.tolist()
1656 self
.EntryPoint
= self
._ByteListToInt
(ByteList
[0x10:0x14])
1657 self
.SectionAlignment
= self
._ByteListToInt
(ByteList
[0x20:0x24])
1658 self
.Size
= self
._ByteListToInt
(ByteList
[0x38:0x3C])
1660 # Read each Section Header
1661 for Index
in range(SecNumber
):
1662 ByteArray
= array
.array('B')
1663 ByteArray
.fromfile(PeObject
, 0x28)
1664 ByteList
= ByteArray
.tolist()
1665 SecName
= self
._ByteListToStr
(ByteList
[0:8])
1666 SecVirtualSize
= self
._ByteListToInt
(ByteList
[8:12])
1667 SecRawAddress
= self
._ByteListToInt
(ByteList
[20:24])
1668 SecVirtualAddress
= self
._ByteListToInt
(ByteList
[12:16])
1669 self
.SectionHeaderList
.append((SecName
, SecVirtualAddress
, SecRawAddress
, SecVirtualSize
))
1673 def _ByteListToStr(self
, ByteList
):
1675 for index
in range(len(ByteList
)):
1676 if ByteList
[index
] == 0:
1678 String
+= chr(ByteList
[index
])
1681 def _ByteListToInt(self
, ByteList
):
1683 for index
in range(len(ByteList
) - 1, -1, -1):
1684 Value
= (Value
<< 8) |
int(ByteList
[index
])
1687 class DefaultStore():
1688 def __init__(self
, DefaultStores
):
1690 self
.DefaultStores
= DefaultStores
1691 def DefaultStoreID(self
, DefaultStoreName
):
1692 for key
, value
in self
.DefaultStores
.items():
1693 if value
== DefaultStoreName
:
1696 def GetDefaultDefault(self
):
1697 if not self
.DefaultStores
or "0" in self
.DefaultStores
:
1698 return "0", TAB_DEFAULT_STORES_DEFAULT
1700 minvalue
= min(int(value_str
) for value_str
in self
.DefaultStores
)
1701 return (str(minvalue
), self
.DefaultStores
[str(minvalue
)])
1702 def GetMin(self
, DefaultSIdList
):
1703 if not DefaultSIdList
:
1704 return TAB_DEFAULT_STORES_DEFAULT
1705 storeidset
= {storeid
for storeid
, storename
in self
.DefaultStores
.values() if storename
in DefaultSIdList
}
1708 minid
= min(storeidset
)
1709 for sid
, name
in self
.DefaultStores
.values():
1718 def __init__(self
,SkuIdentifier
='', SkuIds
=None):
1722 for SkuName
in SkuIds
:
1723 SkuId
= SkuIds
[SkuName
][0]
1724 skuid_num
= int(SkuId
, 16) if SkuId
.upper().startswith("0X") else int(SkuId
)
1725 if skuid_num
> 0xFFFFFFFFFFFFFFFF:
1726 EdkLogger
.error("build", PARAMETER_INVALID
,
1727 ExtraData
= "SKU-ID [%s] value %s exceeds the max value of UINT64"
1730 self
.AvailableSkuIds
= OrderedDict()
1732 self
.SkuIdNumberSet
= []
1733 self
.SkuData
= SkuIds
1734 self
._SkuInherit
= {}
1735 self
._SkuIdentifier
= SkuIdentifier
1736 if SkuIdentifier
== '' or SkuIdentifier
is None:
1737 self
.SkuIdSet
= ['DEFAULT']
1738 self
.SkuIdNumberSet
= ['0U']
1739 elif SkuIdentifier
== 'ALL':
1740 self
.SkuIdSet
= list(SkuIds
.keys())
1741 self
.SkuIdNumberSet
= [num
[0].strip() + 'U' for num
in SkuIds
.values()]
1743 r
= SkuIdentifier
.split('|')
1744 self
.SkuIdSet
=[(r
[k
].strip()).upper() for k
in range(len(r
))]
1747 self
.SkuIdNumberSet
= [SkuIds
[k
][0].strip() + 'U' for k
in self
.SkuIdSet
]
1749 EdkLogger
.error("build", PARAMETER_INVALID
,
1750 ExtraData
= "SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1751 % (k
, " | ".join(SkuIds
.keys())))
1752 for each
in self
.SkuIdSet
:
1754 self
.AvailableSkuIds
[each
] = SkuIds
[each
][0]
1756 EdkLogger
.error("build", PARAMETER_INVALID
,
1757 ExtraData
="SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1758 % (each
, " | ".join(SkuIds
.keys())))
1759 if self
.SkuUsageType
!= SkuClass
.SINGLE
:
1760 self
.AvailableSkuIds
.update({'DEFAULT':0, 'COMMON':0})
1762 GlobalData
.gSkuids
= (self
.SkuIdSet
)
1763 if 'COMMON' in GlobalData
.gSkuids
:
1764 GlobalData
.gSkuids
.remove('COMMON')
1765 if self
.SkuUsageType
== self
.SINGLE
:
1766 if len(GlobalData
.gSkuids
) != 1:
1767 if 'DEFAULT' in GlobalData
.gSkuids
:
1768 GlobalData
.gSkuids
.remove('DEFAULT')
1769 if GlobalData
.gSkuids
:
1770 GlobalData
.gSkuids
.sort()
1772 def GetNextSkuId(self
, skuname
):
1773 if not self
._SkuInherit
:
1774 self
._SkuInherit
= {}
1775 for item
in self
.SkuData
.values():
1776 self
._SkuInherit
[item
[1]]=item
[2] if item
[2] else "DEFAULT"
1777 return self
._SkuInherit
.get(skuname
, "DEFAULT")
1779 def GetSkuChain(self
, sku
):
1780 if sku
== "DEFAULT":
1785 nextsku
= self
.GetNextSkuId(nextsku
)
1786 skulist
.append(nextsku
)
1787 if nextsku
== "DEFAULT":
1791 def SkuOverrideOrder(self
):
1793 for skuname
in self
.SkuIdSet
:
1794 skuorderset
.append(self
.GetSkuChain(skuname
))
1797 for index
in range(max(len(item
) for item
in skuorderset
)):
1798 for subset
in skuorderset
:
1799 if index
> len(subset
)-1:
1801 if subset
[index
] in skuorder
:
1803 skuorder
.append(subset
[index
])
1808 def SkuUsageType(self
):
1809 if self
._SkuIdentifier
.upper() == "ALL":
1810 return SkuClass
.MULTIPLE
1812 if len(self
.SkuIdSet
) == 1:
1813 if self
.SkuIdSet
[0] == 'DEFAULT':
1814 return SkuClass
.DEFAULT
1815 return SkuClass
.SINGLE
1816 if len(self
.SkuIdSet
) == 2 and 'DEFAULT' in self
.SkuIdSet
:
1817 return SkuClass
.SINGLE
1818 return SkuClass
.MULTIPLE
1820 def DumpSkuIdArrary(self
):
1821 if self
.SkuUsageType
== SkuClass
.SINGLE
:
1824 for skuname
in self
.AvailableSkuIds
:
1825 if skuname
== "COMMON":
1827 while skuname
!= "DEFAULT":
1828 ArrayStrList
.append(hex(int(self
.AvailableSkuIds
[skuname
])))
1829 skuname
= self
.GetNextSkuId(skuname
)
1830 ArrayStrList
.append("0x0")
1831 return "{{{myList}}}".format(myList
=",".join(ArrayStrList
))
1834 def AvailableSkuIdSet(self
):
1835 return self
.AvailableSkuIds
1838 def SystemSkuId(self
):
1839 if self
.SkuUsageType
== SkuClass
.SINGLE
:
1840 if len(self
.SkuIdSet
) == 1:
1841 return self
.SkuIdSet
[0]
1843 return self
.SkuIdSet
[0] if self
.SkuIdSet
[0] != 'DEFAULT' else self
.SkuIdSet
[1]
1847 ## Get the integer value from string like "14U" or integer like 2
1849 # @param Input The object that may be either a integer value or a string
1851 # @retval Value The integer value that the input represents
1853 def GetIntegerValue(Input
):
1854 if not isinstance(Input
, str):
1857 if String
.endswith("U"):
1858 String
= String
[:-1]
1859 if String
.endswith("ULL"):
1860 String
= String
[:-3]
1861 if String
.endswith("LL"):
1862 String
= String
[:-2]
1864 if String
.startswith("0x") or String
.startswith("0X"):
1865 return int(String
, 16)
1872 # Pack a GUID (registry format) list into a buffer and return it
1875 return pack(PACK_PATTERN_GUID
,
1879 int(Guid
[3][-4:-2], 16),
1880 int(Guid
[3][-2:], 16),
1881 int(Guid
[4][-12:-10], 16),
1882 int(Guid
[4][-10:-8], 16),
1883 int(Guid
[4][-8:-6], 16),
1884 int(Guid
[4][-6:-4], 16),
1885 int(Guid
[4][-4:-2], 16),
1886 int(Guid
[4][-2:], 16)
1890 # Pack a GUID (byte) list into a buffer and return it
1892 def PackByteFormatGUID(Guid
):
1893 return pack(PACK_PATTERN_GUID
,
1907 ## DeepCopy dict/OrderedDict recusively
1909 # @param ori_dict a nested dict or ordereddict
1911 # @retval new dict or orderdict
1913 def CopyDict(ori_dict
):
1914 dict_type
= ori_dict
.__class
__
1915 if dict_type
not in (dict,OrderedDict
):
1917 new_dict
= dict_type()
1918 for key
in ori_dict
:
1919 if isinstance(ori_dict
[key
],(dict,OrderedDict
)):
1920 new_dict
[key
] = CopyDict(ori_dict
[key
])
1922 new_dict
[key
] = ori_dict
[key
]
1926 # Remove the c/c++ comments: // and /* */
1928 def RemoveCComments(ctext
):
1929 return re
.sub('//.*?\n|/\*.*?\*/', '\n', ctext
, flags
=re
.S
)