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
21 from random
import sample
22 from struct
import pack
26 from collections
import OrderedDict
28 import Common
.LongFilePathOs
as os
29 from Common
import EdkLogger
as EdkLogger
30 from Common
import GlobalData
as GlobalData
31 from Common
.DataType
import *
32 from Common
.BuildToolError
import *
33 from CommonDataClass
.DataClass
import *
34 from Common
.Parsing
import GetSplitValueList
35 from Common
.LongFilePathSupport
import OpenLongFilePath
as open
36 from Common
.MultipleWorkspace
import MultipleWorkspace
as mws
37 from CommonDataClass
.Exceptions
import BadExpression
38 from Common
.caching
import cached_property
40 ## Regular expression used to find out place holders in string template
41 gPlaceholderPattern
= re
.compile("\$\{([^$()\s]+)\}", re
.MULTILINE | re
.UNICODE
)
43 ## regular expressions for map file processing
44 startPatternGeneral
= re
.compile("^Start[' ']+Length[' ']+Name[' ']+Class")
45 addressPatternGeneral
= re
.compile("^Address[' ']+Publics by Value[' ']+Rva\+Base")
46 valuePatternGcc
= re
.compile('^([\w_\.]+) +([\da-fA-Fx]+) +([\da-fA-Fx]+)$')
47 pcdPatternGcc
= re
.compile('^([\da-fA-Fx]+) +([\da-fA-Fx]+)')
48 secReGeneral
= re
.compile('^([\da-fA-F]+):([\da-fA-F]+) +([\da-fA-F]+)[Hh]? +([.\w\$]+) +(\w+)', re
.UNICODE
)
50 StructPattern
= re
.compile(r
'[_a-zA-Z][0-9A-Za-z_]*$')
52 ## Dictionary used to store dependencies of files
53 gDependencyDatabase
= {} # arch : {file path : [dependent files list]}
56 # If a module is built more than once with different PCDs or library classes
57 # a temporary INF file with same content is created, the temporary file is removed
62 def GetVariableOffset(mapfilepath
, efifilepath
, varnames
):
63 """ Parse map file to get variable offset in current EFI file
64 @param mapfilepath Map file absolution path
65 @param efifilepath: EFI binary file full path
66 @param varnames iteratable container whose elements are variable names to be searched
68 @return List whos elements are tuple with variable name and raw offset
72 f
= open(mapfilepath
, 'r')
78 if len(lines
) == 0: return None
79 firstline
= lines
[0].strip()
80 if (firstline
.startswith("Archive member included ") and
81 firstline
.endswith(" file (symbol)")):
82 return _parseForGCC(lines
, efifilepath
, varnames
)
83 if firstline
.startswith("# Path:"):
84 return _parseForXcode(lines
, efifilepath
, varnames
)
85 return _parseGeneral(lines
, efifilepath
, varnames
)
87 def _parseForXcode(lines
, efifilepath
, varnames
):
92 if status
== 0 and line
== "# Symbols:":
95 if status
== 1 and len(line
) != 0:
96 for varname
in varnames
:
98 # cannot pregenerate this RegEx since it uses varname from varnames.
99 m
= re
.match('^([\da-fA-FxX]+)([\s\S]*)([_]*%s)$' % varname
, line
)
101 ret
.append((varname
, m
.group(1)))
104 def _parseForGCC(lines
, efifilepath
, varnames
):
105 """ Parse map file generated by GCC linker """
109 for index
, line
in enumerate(lines
):
111 # status machine transection
112 if status
== 0 and line
== "Memory Configuration":
115 elif status
== 1 and line
== 'Linker script and memory map':
118 elif status
==2 and line
== 'START GROUP':
124 m
= valuePatternGcc
.match(line
)
126 sections
.append(m
.groups(0))
127 for varname
in varnames
:
129 m
= re
.match("^.data.(%s)" % varname
, line
)
131 m
= re
.match(".data.(%s)$" % varname
, line
)
133 Str
= lines
[index
+ 1]
135 Str
= line
[len(".data.%s" % varname
):]
137 m
= pcdPatternGcc
.match(Str
.strip())
139 varoffset
.append((varname
, int(m
.groups(0)[0], 16), int(sections
[-1][1], 16), sections
[-1][0]))
143 # get section information from efi file
144 efisecs
= PeImageClass(efifilepath
).SectionHeaderList
145 if efisecs
is None or len(efisecs
) == 0:
149 for efisec
in efisecs
:
150 for section
in sections
:
151 if section
[0].strip() == efisec
[0].strip() and section
[0].strip() == '.text':
152 redirection
= int(section
[1], 16) - efisec
[1]
155 for var
in varoffset
:
156 for efisec
in efisecs
:
157 if var
[1] >= efisec
[1] and var
[1] < efisec
[1]+efisec
[3]:
158 ret
.append((var
[0], hex(efisec
[2] + var
[1] - efisec
[1] - redirection
)))
161 def _parseGeneral(lines
, efifilepath
, varnames
):
162 status
= 0 #0 - beginning of file; 1 - PE section definition; 2 - symbol table
163 secs
= [] # key = section name
165 symRe
= re
.compile('^([\da-fA-F]+):([\da-fA-F]+) +([\.:\\\\\w\?@\$]+) +([\da-fA-F]+)', re
.UNICODE
)
169 if startPatternGeneral
.match(line
):
172 if addressPatternGeneral
.match(line
):
175 if line
.startswith("entry point at"):
178 if status
== 1 and len(line
) != 0:
179 m
= secReGeneral
.match(line
)
180 assert m
is not None, "Fail to parse the section in map file , line is %s" % line
181 sec_no
, sec_start
, sec_length
, sec_name
, sec_class
= m
.groups(0)
182 secs
.append([int(sec_no
, 16), int(sec_start
, 16), int(sec_length
, 16), sec_name
, sec_class
])
183 if status
== 2 and len(line
) != 0:
184 for varname
in varnames
:
185 m
= symRe
.match(line
)
186 assert m
is not None, "Fail to parse the symbol in map file, line is %s" % line
187 sec_no
, sym_offset
, sym_name
, vir_addr
= m
.groups(0)
188 sec_no
= int(sec_no
, 16)
189 sym_offset
= int(sym_offset
, 16)
190 vir_addr
= int(vir_addr
, 16)
191 # cannot pregenerate this RegEx since it uses varname from varnames.
192 m2
= re
.match('^[_]*(%s)' % varname
, sym_name
)
194 # fond a binary pcd entry in map file
196 if sec
[0] == sec_no
and (sym_offset
>= sec
[1] and sym_offset
< sec
[1] + sec
[2]):
197 varoffset
.append([varname
, sec
[3], sym_offset
, vir_addr
, sec_no
])
199 if not varoffset
: return []
201 # get section information from efi file
202 efisecs
= PeImageClass(efifilepath
).SectionHeaderList
203 if efisecs
is None or len(efisecs
) == 0:
207 for var
in varoffset
:
209 for efisec
in efisecs
:
211 if var
[1].strip() == efisec
[0].strip():
212 ret
.append((var
[0], hex(efisec
[2] + var
[2])))
213 elif var
[4] == index
:
214 ret
.append((var
[0], hex(efisec
[2] + var
[2])))
218 ## Routine to process duplicated INF
220 # This function is called by following two cases:
223 # Pkg/module/module.inf
224 # Pkg/module/module.inf {
226 # FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836
229 # INF Pkg/module/module.inf
230 # INF FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836 Pkg/module/module.inf
232 # This function copies Pkg/module/module.inf to
233 # Conf/.cache/0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf
235 # @param Path Original PathClass object
236 # @param BaseName New file base name
238 # @retval return the new PathClass object
240 def ProcessDuplicatedInf(Path
, BaseName
, Workspace
):
241 Filename
= os
.path
.split(Path
.File
)[1]
243 Filename
= BaseName
+ Path
.BaseName
+ Filename
[Filename
.rfind('.'):]
245 Filename
= BaseName
+ Path
.BaseName
248 # If -N is specified on command line, cache is disabled
249 # The directory has to be created
251 DbDir
= os
.path
.split(GlobalData
.gDatabasePath
)[0]
252 if not os
.path
.exists(DbDir
):
255 # A temporary INF is copied to database path which must have write permission
256 # The temporary will be removed at the end of build
257 # In case of name conflict, the file name is
258 # FILE_GUIDBaseName (0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf)
260 TempFullPath
= os
.path
.join(DbDir
,
262 RtPath
= PathClass(Path
.File
, Workspace
)
264 # Modify the full path to temporary path, keep other unchanged
266 # To build same module more than once, the module path with FILE_GUID overridden has
267 # the file name FILE_GUIDmodule.inf, but the relative path (self.MetaFile.File) is the real path
268 # in DSC which is used as relative path by C files and other files in INF.
269 # A trick was used: all module paths are PathClass instances, after the initialization
270 # of PathClass, the PathClass.Path is overridden by the temporary INF path.
272 # The reason for creating a temporary INF is:
273 # Platform.Modules which is the base to create ModuleAutoGen objects is a dictionary,
274 # the key is the full path of INF, the value is an object to save overridden library instances, PCDs.
275 # A different key for the same module is needed to create different output directory,
276 # retrieve overridden PCDs, library instances.
278 # The BaseName is the FILE_GUID which is also the output directory name.
281 RtPath
.Path
= TempFullPath
282 RtPath
.BaseName
= BaseName
284 # If file exists, compare contents
286 if os
.path
.exists(TempFullPath
):
287 with
open(str(Path
), 'rb') as f1
, open(TempFullPath
, 'rb') as f2
:
288 if f1
.read() == f2
.read():
290 _TempInfs
.append(TempFullPath
)
291 shutil
.copy2(str(Path
), TempFullPath
)
294 ## Remove temporary created INFs whose paths were saved in _TempInfs
296 def ClearDuplicatedInf():
298 File
= _TempInfs
.pop()
299 if os
.path
.exists(File
):
302 ## Convert GUID string in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx style to C structure style
304 # @param Guid The GUID string
306 # @retval string The GUID string in C structure style
308 def GuidStringToGuidStructureString(Guid
):
309 GuidList
= Guid
.split('-')
311 for Index
in range(0, 3, 1):
312 Result
= Result
+ '0x' + GuidList
[Index
] + ', '
313 Result
= Result
+ '{0x' + GuidList
[3][0:2] + ', 0x' + GuidList
[3][2:4]
314 for Index
in range(0, 12, 2):
315 Result
= Result
+ ', 0x' + GuidList
[4][Index
:Index
+ 2]
319 ## Convert GUID structure in byte array to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
321 # @param GuidValue The GUID value in byte array
323 # @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
325 def GuidStructureByteArrayToGuidString(GuidValue
):
326 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
327 guidValueList
= guidValueString
.split(",")
328 if len(guidValueList
) != 16:
330 #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
332 return "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
333 int(guidValueList
[3], 16),
334 int(guidValueList
[2], 16),
335 int(guidValueList
[1], 16),
336 int(guidValueList
[0], 16),
337 int(guidValueList
[5], 16),
338 int(guidValueList
[4], 16),
339 int(guidValueList
[7], 16),
340 int(guidValueList
[6], 16),
341 int(guidValueList
[8], 16),
342 int(guidValueList
[9], 16),
343 int(guidValueList
[10], 16),
344 int(guidValueList
[11], 16),
345 int(guidValueList
[12], 16),
346 int(guidValueList
[13], 16),
347 int(guidValueList
[14], 16),
348 int(guidValueList
[15], 16)
353 ## Convert GUID string in C structure style to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
355 # @param GuidValue The GUID value in C structure format
357 # @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
359 def GuidStructureStringToGuidString(GuidValue
):
360 if not GlobalData
.gGuidCFormatPattern
.match(GuidValue
):
362 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
363 guidValueList
= guidValueString
.split(",")
364 if len(guidValueList
) != 11:
366 #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
368 return "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
369 int(guidValueList
[0], 16),
370 int(guidValueList
[1], 16),
371 int(guidValueList
[2], 16),
372 int(guidValueList
[3], 16),
373 int(guidValueList
[4], 16),
374 int(guidValueList
[5], 16),
375 int(guidValueList
[6], 16),
376 int(guidValueList
[7], 16),
377 int(guidValueList
[8], 16),
378 int(guidValueList
[9], 16),
379 int(guidValueList
[10], 16)
384 ## Convert GUID string in C structure style to xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx
386 # @param GuidValue The GUID value in C structure format
388 # @retval string The GUID value in xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx format
390 def GuidStructureStringToGuidValueName(GuidValue
):
391 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "")
392 guidValueList
= guidValueString
.split(",")
393 if len(guidValueList
) != 11:
394 EdkLogger
.error(None, FORMAT_INVALID
, "Invalid GUID value string [%s]" % GuidValue
)
395 return "%08x_%04x_%04x_%02x%02x_%02x%02x%02x%02x%02x%02x" % (
396 int(guidValueList
[0], 16),
397 int(guidValueList
[1], 16),
398 int(guidValueList
[2], 16),
399 int(guidValueList
[3], 16),
400 int(guidValueList
[4], 16),
401 int(guidValueList
[5], 16),
402 int(guidValueList
[6], 16),
403 int(guidValueList
[7], 16),
404 int(guidValueList
[8], 16),
405 int(guidValueList
[9], 16),
406 int(guidValueList
[10], 16)
409 ## Create directories
411 # @param Directory The directory name
413 def CreateDirectory(Directory
):
414 if Directory
is None or Directory
.strip() == "":
417 if not os
.access(Directory
, os
.F_OK
):
418 os
.makedirs(Directory
)
423 ## Remove directories, including files and sub-directories in it
425 # @param Directory The directory name
427 def RemoveDirectory(Directory
, Recursively
=False):
428 if Directory
is None or Directory
.strip() == "" or not os
.path
.exists(Directory
):
431 CurrentDirectory
= os
.getcwd()
433 for File
in os
.listdir("."):
434 if os
.path
.isdir(File
):
435 RemoveDirectory(File
, Recursively
)
438 os
.chdir(CurrentDirectory
)
441 ## Store content in file
443 # This method is used to save file only when its content is changed. This is
444 # quite useful for "make" system to decide what will be re-built and what won't.
446 # @param File The path of file
447 # @param Content The new content of the file
448 # @param IsBinaryFile The flag indicating if the file is binary file or not
450 # @retval True If the file content is changed and the file is renewed
451 # @retval False If the file content is the same
453 def SaveFileOnChange(File
, Content
, IsBinaryFile
=True):
455 if os
.path
.exists(File
):
458 with
open(File
, "rb") as f
:
459 if Content
== f
.read():
462 EdkLogger
.error(None, FILE_OPEN_FAILURE
, ExtraData
=File
)
465 with
open(File
, "r") as f
:
466 if Content
== f
.read():
469 EdkLogger
.error(None, FILE_OPEN_FAILURE
, ExtraData
=File
)
471 DirName
= os
.path
.dirname(File
)
472 if not CreateDirectory(DirName
):
473 EdkLogger
.error(None, FILE_CREATE_FAILURE
, "Could not create directory %s" % DirName
)
476 DirName
= os
.getcwd()
477 if not os
.access(DirName
, os
.W_OK
):
478 EdkLogger
.error(None, PERMISSION_FAILURE
, "Do not have write permission on directory %s" % DirName
)
484 if GlobalData
.gIsWindows
and not os
.path
.exists(File
):
485 # write temp file, then rename the temp file to the real file
486 # to make sure the file be immediate saved to disk
487 with tempfile
.NamedTemporaryFile(OpenMode
, dir=os
.path
.dirname(File
), delete
=False) as tf
:
491 os
.rename(tempname
, File
)
493 EdkLogger
.error(None, FILE_CREATE_FAILURE
, ExtraData
='IOError %s' % X
)
496 with
open(File
, OpenMode
) as Fd
:
499 EdkLogger
.error(None, FILE_CREATE_FAILURE
, ExtraData
='IOError %s' % X
)
503 ## Retrieve and cache the real path name in file system
505 # @param Root The root directory of path relative to
507 # @retval str The path string if the path exists
508 # @retval None If path doesn't exist
514 def __init__(self
, Root
):
516 for F
in os
.listdir(Root
):
518 self
._UPPER
_CACHE
_[F
.upper()] = F
521 def __getitem__(self
, Path
):
522 Path
= Path
[len(os
.path
.commonprefix([Path
, self
._Root
])):]
525 if Path
and Path
[0] == os
.path
.sep
:
527 if Path
in self
._CACHE
_:
528 return os
.path
.join(self
._Root
, Path
)
529 UpperPath
= Path
.upper()
530 if UpperPath
in self
._UPPER
_CACHE
_:
531 return os
.path
.join(self
._Root
, self
._UPPER
_CACHE
_[UpperPath
])
535 SepIndex
= Path
.find(os
.path
.sep
)
537 Parent
= UpperPath
[:SepIndex
]
538 if Parent
not in self
._UPPER
_CACHE
_:
540 LastSepIndex
= SepIndex
541 SepIndex
= Path
.find(os
.path
.sep
, LastSepIndex
+ 1)
543 if LastSepIndex
== -1:
548 SepIndex
= LastSepIndex
550 Parent
= Path
[:SepIndex
]
551 ParentKey
= UpperPath
[:SepIndex
]
552 if ParentKey
not in self
._UPPER
_CACHE
_:
556 if Parent
in self
._CACHE
_:
559 ParentDir
= self
._UPPER
_CACHE
_[ParentKey
]
560 for F
in os
.listdir(ParentDir
):
561 Dir
= os
.path
.join(ParentDir
, F
)
562 self
._CACHE
_.add(Dir
)
563 self
._UPPER
_CACHE
_[Dir
.upper()] = Dir
565 SepIndex
= Path
.find(os
.path
.sep
, SepIndex
+ 1)
568 if Path
in self
._CACHE
_:
569 return os
.path
.join(self
._Root
, Path
)
570 elif UpperPath
in self
._UPPER
_CACHE
_:
571 return os
.path
.join(self
._Root
, self
._UPPER
_CACHE
_[UpperPath
])
574 def RealPath(File
, Dir
='', OverrideDir
=''):
575 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
576 NewFile
= GlobalData
.gAllFiles
[NewFile
]
577 if not NewFile
and OverrideDir
:
578 NewFile
= os
.path
.normpath(os
.path
.join(OverrideDir
, File
))
579 NewFile
= GlobalData
.gAllFiles
[NewFile
]
582 ## Get GUID value from given packages
584 # @param CName The CName of the GUID
585 # @param PackageList List of packages looking-up in
586 # @param Inffile The driver file
588 # @retval GuidValue if the CName is found in any given package
589 # @retval None if the CName is not found in all given packages
591 def GuidValue(CName
, PackageList
, Inffile
= None):
592 for P
in PackageList
:
593 GuidKeys
= list(P
.Guids
.keys())
594 if Inffile
and P
._PrivateGuids
:
595 if not Inffile
.startswith(P
.MetaFile
.Dir
):
596 GuidKeys
= [x
for x
in P
.Guids
if x
not in P
._PrivateGuids
]
597 if CName
in GuidKeys
:
598 return P
.Guids
[CName
]
602 ## A string template class
604 # This class implements a template for string replacement. A string template
605 # looks like following
607 # ${BEGIN} other_string ${placeholder_name} other_string ${END}
609 # The string between ${BEGIN} and ${END} will be repeated as many times as the
610 # length of "placeholder_name", which is a list passed through a dict. The
611 # "placeholder_name" is the key name of the dict. The ${BEGIN} and ${END} can
612 # be not used and, in this case, the "placeholder_name" must not a list and it
613 # will just be replaced once.
615 class TemplateString(object):
616 _REPEAT_START_FLAG
= "BEGIN"
617 _REPEAT_END_FLAG
= "END"
619 class Section(object):
620 _LIST_TYPES
= [type([]), type(set()), type((0,))]
622 def __init__(self
, TemplateSection
, PlaceHolderList
):
623 self
._Template
= TemplateSection
624 self
._PlaceHolderList
= []
626 # Split the section into sub-sections according to the position of placeholders
628 self
._SubSectionList
= []
631 # The placeholders passed in must be in the format of
633 # PlaceHolderName, PlaceHolderStartPoint, PlaceHolderEndPoint
635 for PlaceHolder
, Start
, End
in PlaceHolderList
:
636 self
._SubSectionList
.append(TemplateSection
[SubSectionStart
:Start
])
637 self
._SubSectionList
.append(TemplateSection
[Start
:End
])
638 self
._PlaceHolderList
.append(PlaceHolder
)
639 SubSectionStart
= End
640 if SubSectionStart
< len(TemplateSection
):
641 self
._SubSectionList
.append(TemplateSection
[SubSectionStart
:])
643 self
._SubSectionList
= [TemplateSection
]
646 return self
._Template
+ " : " + str(self
._PlaceHolderList
)
648 def Instantiate(self
, PlaceHolderValues
):
650 RepeatPlaceHolders
= {}
651 NonRepeatPlaceHolders
= {}
653 for PlaceHolder
in self
._PlaceHolderList
:
654 if PlaceHolder
not in PlaceHolderValues
:
656 Value
= PlaceHolderValues
[PlaceHolder
]
657 if type(Value
) in self
._LIST
_TYPES
:
659 RepeatTime
= len(Value
)
660 elif RepeatTime
!= len(Value
):
664 "${%s} has different repeat time from others!" % PlaceHolder
,
665 ExtraData
=str(self
._Template
)
667 RepeatPlaceHolders
["${%s}" % PlaceHolder
] = Value
669 NonRepeatPlaceHolders
["${%s}" % PlaceHolder
] = Value
671 if NonRepeatPlaceHolders
:
673 for S
in self
._SubSectionList
:
674 if S
not in NonRepeatPlaceHolders
:
677 StringList
.append(str(NonRepeatPlaceHolders
[S
]))
679 StringList
= self
._SubSectionList
681 if RepeatPlaceHolders
:
683 for Index
in range(RepeatTime
):
685 if S
not in RepeatPlaceHolders
:
686 TempStringList
.append(S
)
688 TempStringList
.append(str(RepeatPlaceHolders
[S
][Index
]))
689 StringList
= TempStringList
691 return "".join(StringList
)
694 def __init__(self
, Template
=None):
696 self
.IsBinary
= False
697 self
._Template
= Template
698 self
._TemplateSectionList
= self
._Parse
(Template
)
702 # @retval string The string replaced
705 return "".join(self
.String
)
707 ## Split the template string into fragments per the ${BEGIN} and ${END} flags
709 # @retval list A list of TemplateString.Section objects
711 def _Parse(self
, Template
):
716 TemplateSectionList
= []
718 MatchObj
= gPlaceholderPattern
.search(Template
, SearchFrom
)
720 if MatchEnd
<= len(Template
):
721 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:], PlaceHolderList
)
722 TemplateSectionList
.append(TemplateSection
)
725 MatchString
= MatchObj
.group(1)
726 MatchStart
= MatchObj
.start()
727 MatchEnd
= MatchObj
.end()
729 if MatchString
== self
._REPEAT
_START
_FLAG
:
730 if MatchStart
> SectionStart
:
731 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:MatchStart
], PlaceHolderList
)
732 TemplateSectionList
.append(TemplateSection
)
733 SectionStart
= MatchEnd
735 elif MatchString
== self
._REPEAT
_END
_FLAG
:
736 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:MatchStart
], PlaceHolderList
)
737 TemplateSectionList
.append(TemplateSection
)
738 SectionStart
= MatchEnd
741 PlaceHolderList
.append((MatchString
, MatchStart
- SectionStart
, MatchEnd
- SectionStart
))
742 SearchFrom
= MatchEnd
743 return TemplateSectionList
745 ## Replace the string template with dictionary of placeholders and append it to previous one
747 # @param AppendString The string template to append
748 # @param Dictionary The placeholder dictionaries
750 def Append(self
, AppendString
, Dictionary
=None):
752 SectionList
= self
._Parse
(AppendString
)
753 self
.String
.append( "".join(S
.Instantiate(Dictionary
) for S
in SectionList
))
755 if isinstance(AppendString
,list):
756 self
.String
.extend(AppendString
)
758 self
.String
.append(AppendString
)
760 ## Replace the string template with dictionary of placeholders
762 # @param Dictionary The placeholder dictionaries
764 # @retval str The string replaced with placeholder values
766 def Replace(self
, Dictionary
=None):
767 return "".join(S
.Instantiate(Dictionary
) for S
in self
._TemplateSectionList
)
769 ## Progress indicator class
771 # This class makes use of thread to print progress on console.
774 # for avoiding deadloop
776 _ProgressThread
= None
777 _CheckInterval
= 0.25
781 # @param OpenMessage The string printed before progress characters
782 # @param CloseMessage The string printed after progress characters
783 # @param ProgressChar The character used to indicate the progress
784 # @param Interval The interval in seconds between two progress characters
786 def __init__(self
, OpenMessage
="", CloseMessage
="", ProgressChar
='.', Interval
=1.0):
787 self
.PromptMessage
= OpenMessage
788 self
.CodaMessage
= CloseMessage
789 self
.ProgressChar
= ProgressChar
790 self
.Interval
= Interval
791 if Progressor
._StopFlag
is None:
792 Progressor
._StopFlag
= threading
.Event()
794 ## Start to print progress character
796 # @param OpenMessage The string printed before progress characters
798 def Start(self
, OpenMessage
=None):
799 if OpenMessage
is not None:
800 self
.PromptMessage
= OpenMessage
801 Progressor
._StopFlag
.clear()
802 if Progressor
._ProgressThread
is None:
803 Progressor
._ProgressThread
= threading
.Thread(target
=self
._ProgressThreadEntry
)
804 Progressor
._ProgressThread
.setDaemon(False)
805 Progressor
._ProgressThread
.start()
807 ## Stop printing progress character
809 # @param CloseMessage The string printed after progress characters
811 def Stop(self
, CloseMessage
=None):
812 OriginalCodaMessage
= self
.CodaMessage
813 if CloseMessage
is not None:
814 self
.CodaMessage
= CloseMessage
816 self
.CodaMessage
= OriginalCodaMessage
818 ## Thread entry method
819 def _ProgressThreadEntry(self
):
820 sys
.stdout
.write(self
.PromptMessage
+ " ")
823 while not Progressor
._StopFlag
.isSet():
825 sys
.stdout
.write(self
.ProgressChar
)
827 TimeUp
= self
.Interval
828 time
.sleep(self
._CheckInterval
)
829 TimeUp
-= self
._CheckInterval
830 sys
.stdout
.write(" " + self
.CodaMessage
+ "\n")
833 ## Abort the progress display
836 if Progressor
._StopFlag
is not None:
837 Progressor
._StopFlag
.set()
838 if Progressor
._ProgressThread
is not None:
839 Progressor
._ProgressThread
.join()
840 Progressor
._ProgressThread
= None
843 ## Dictionary using prioritized list as key
847 _TupleType
= type(())
849 _ValidWildcardList
= ['COMMON', 'DEFAULT', 'ALL', TAB_STAR
, 'PLATFORM']
851 def __init__(self
, _Single_
=False, _Level_
=2):
852 self
._Level
_ = _Level_
854 self
._Single
_ = _Single_
857 def __getitem__(self
, key
):
860 if KeyType
== self
._ListType
or KeyType
== self
._TupleType
:
864 elif self
._Level
_ > 1:
865 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
869 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
871 if FirstKey
is None or str(FirstKey
).upper() in self
._ValidWildcardList
:
872 FirstKey
= self
._Wildcard
875 return self
._GetSingleValue
(FirstKey
, RestKeys
)
877 return self
._GetAllValues
(FirstKey
, RestKeys
)
879 def _GetSingleValue(self
, FirstKey
, RestKeys
):
881 #print "%s-%s" % (FirstKey, self._Level_) ,
883 if FirstKey
== self
._Wildcard
:
884 if FirstKey
in self
.data
:
885 Value
= self
.data
[FirstKey
][RestKeys
]
887 for Key
in self
.data
:
888 Value
= self
.data
[Key
][RestKeys
]
889 if Value
is not None: break
891 if FirstKey
in self
.data
:
892 Value
= self
.data
[FirstKey
][RestKeys
]
893 if Value
is None and self
._Wildcard
in self
.data
:
895 Value
= self
.data
[self
._Wildcard
][RestKeys
]
897 if FirstKey
== self
._Wildcard
:
898 if FirstKey
in self
.data
:
899 Value
= self
.data
[FirstKey
]
901 for Key
in self
.data
:
902 Value
= self
.data
[Key
]
903 if Value
is not None: break
905 if FirstKey
in self
.data
:
906 Value
= self
.data
[FirstKey
]
907 elif self
._Wildcard
in self
.data
:
908 Value
= self
.data
[self
._Wildcard
]
911 def _GetAllValues(self
, FirstKey
, RestKeys
):
914 if FirstKey
== self
._Wildcard
:
915 for Key
in self
.data
:
916 Value
+= self
.data
[Key
][RestKeys
]
918 if FirstKey
in self
.data
:
919 Value
+= self
.data
[FirstKey
][RestKeys
]
920 if self
._Wildcard
in self
.data
:
921 Value
+= self
.data
[self
._Wildcard
][RestKeys
]
923 if FirstKey
== self
._Wildcard
:
924 for Key
in self
.data
:
925 Value
.append(self
.data
[Key
])
927 if FirstKey
in self
.data
:
928 Value
.append(self
.data
[FirstKey
])
929 if self
._Wildcard
in self
.data
:
930 Value
.append(self
.data
[self
._Wildcard
])
934 def __setitem__(self
, key
, value
):
937 if KeyType
== self
._ListType
or KeyType
== self
._TupleType
:
942 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
946 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
948 if FirstKey
in self
._ValidWildcardList
:
949 FirstKey
= self
._Wildcard
951 if FirstKey
not in self
.data
and self
._Level
_ > 0:
952 self
.data
[FirstKey
] = tdict(self
._Single
_, self
._Level
_ - 1)
955 self
.data
[FirstKey
][RestKeys
] = value
957 self
.data
[FirstKey
] = value
959 def SetGreedyMode(self
):
960 self
._Single
_ = False
962 for Key
in self
.data
:
963 self
.data
[Key
].SetGreedyMode()
965 def SetSingleMode(self
):
968 for Key
in self
.data
:
969 self
.data
[Key
].SetSingleMode()
971 def GetKeys(self
, KeyIndex
=0):
974 return set(self
.data
.keys())
977 for Key
in self
.data
:
978 keys |
= self
.data
[Key
].GetKeys(KeyIndex
- 1)
981 def AnalyzePcdExpression(Setting
):
982 RanStr
= ''.join(sample(string
.ascii_letters
+ string
.digits
, 8))
983 Setting
= Setting
.replace('\\\\', RanStr
).strip()
984 # There might be escaped quote in a string: \", \\\" , \', \\\'
986 # There might be '|' in string and in ( ... | ... ), replace it with '-'
988 InSingleQuoteStr
= False
989 InDoubleQuoteStr
= False
991 for Index
, ch
in enumerate(Data
):
992 if ch
== '"' and not InSingleQuoteStr
:
993 if Data
[Index
- 1] != '\\':
994 InDoubleQuoteStr
= not InDoubleQuoteStr
995 elif ch
== "'" and not InDoubleQuoteStr
:
996 if Data
[Index
- 1] != '\\':
997 InSingleQuoteStr
= not InSingleQuoteStr
998 elif ch
== '(' and not (InSingleQuoteStr
or InDoubleQuoteStr
):
1000 elif ch
== ')' and not (InSingleQuoteStr
or InDoubleQuoteStr
):
1003 if (Pair
> 0 or InSingleQuoteStr
or InDoubleQuoteStr
) and ch
== TAB_VALUE_SPLIT
:
1010 Pos
= NewStr
.find(TAB_VALUE_SPLIT
, StartPos
)
1012 FieldList
.append(Setting
[StartPos
:].strip())
1014 FieldList
.append(Setting
[StartPos
:Pos
].strip())
1016 for i
, ch
in enumerate(FieldList
):
1018 FieldList
[i
] = ch
.replace(RanStr
,'\\\\')
1021 def ParseFieldValue (Value
):
1022 def ParseDevPathValue (Value
):
1024 Value
.replace('\\', '/').replace(' ', '')
1026 Cmd
= 'DevicePath ' + '"' + Value
+ '"'
1028 p
= subprocess
.Popen(Cmd
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, shell
=True)
1029 out
, err
= p
.communicate()
1030 except Exception as X
:
1031 raise BadExpression("DevicePath: %s" % (str(X
)) )
1033 subprocess
._cleanup
()
1037 raise BadExpression("DevicePath: %s" % str(err
))
1038 out
= out
.decode(encoding
='utf-8', errors
='ignore')
1039 Size
= len(out
.split())
1040 out
= ','.join(out
.split())
1041 return '{' + out
+ '}', Size
1043 if "{CODE(" in Value
:
1044 return Value
, len(Value
.split(","))
1045 if isinstance(Value
, type(0)):
1046 return Value
, (Value
.bit_length() + 7) // 8
1047 if not isinstance(Value
, type('')):
1048 raise BadExpression('Type %s is %s' %(Value
, type(Value
)))
1049 Value
= Value
.strip()
1050 if Value
.startswith(TAB_UINT8
) and Value
.endswith(')'):
1051 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1053 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1055 if Value
.startswith(TAB_UINT16
) and Value
.endswith(')'):
1056 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1058 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1060 if Value
.startswith(TAB_UINT32
) and Value
.endswith(')'):
1061 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1063 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1065 if Value
.startswith(TAB_UINT64
) and Value
.endswith(')'):
1066 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1068 raise BadExpression('Value (%s) Size larger than %d' % (Value
, Size
))
1070 if Value
.startswith(TAB_GUID
) and Value
.endswith(')'):
1071 Value
= Value
.split('(', 1)[1][:-1].strip()
1072 if Value
[0] == '{' and Value
[-1] == '}':
1073 TmpValue
= GuidStructureStringToGuidString(Value
)
1075 raise BadExpression("Invalid GUID value string %s" % Value
)
1077 if Value
[0] == '"' and Value
[-1] == '"':
1080 Value
= str(uuid
.UUID(Value
).bytes_le
)
1081 if Value
.startswith("b'"):
1083 Value
= "'" + Value
+ "'"
1084 except ValueError as Message
:
1085 raise BadExpression(Message
)
1086 Value
, Size
= ParseFieldValue(Value
)
1088 if Value
.startswith('L"') and Value
.endswith('"'):
1090 # translate escape character
1100 Value
= (Value
<< 16) |
ord(Char
)
1101 return Value
, (len(List
) + 1) * 2
1102 if Value
.startswith('"') and Value
.endswith('"'):
1104 # translate escape character
1113 Value
= (Value
<< 8) |
ord(Char
)
1114 return Value
, len(List
) + 1
1115 if Value
.startswith("L'") and Value
.endswith("'"):
1116 # Unicode Character Constant
1117 # translate escape character
1125 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1129 Value
= (Value
<< 16) |
ord(Char
)
1130 return Value
, len(List
) * 2
1131 if Value
.startswith("'") and Value
.endswith("'"):
1132 # Character constant
1133 # translate escape character
1140 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1144 Value
= (Value
<< 8) |
ord(Char
)
1145 return Value
, len(List
)
1146 if Value
.startswith('{') and Value
.endswith('}'):
1149 List
= [Item
.strip() for Item
in Value
.split(',')]
1154 ItemValue
, Size
= ParseFieldValue(Item
)
1156 for I
in range(Size
):
1157 Value
= (Value
<< 8) |
((ItemValue
>> 8 * I
) & 0xff)
1158 return Value
, RetSize
1159 if Value
.startswith('DEVICE_PATH(') and Value
.endswith(')'):
1160 Value
= Value
.replace("DEVICE_PATH(", '').rstrip(')')
1161 Value
= Value
.strip().strip('"')
1162 return ParseDevPathValue(Value
)
1163 if Value
.lower().startswith('0x'):
1165 Value
= int(Value
, 16)
1167 raise BadExpression("invalid hex value: %s" % Value
)
1170 return Value
, (Value
.bit_length() + 7) // 8
1171 if Value
[0].isdigit():
1172 Value
= int(Value
, 10)
1175 return Value
, (Value
.bit_length() + 7) // 8
1176 if Value
.lower() == 'true':
1178 if Value
.lower() == 'false':
1184 # Analyze DSC PCD value, since there is no data type info in DSC
1185 # This function is used to match functions (AnalyzePcdData) used for retrieving PCD value from database
1186 # 1. Feature flag: TokenSpace.PcdCName|PcdValue
1187 # 2. Fix and Patch:TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1188 # 3. Dynamic default:
1189 # TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1190 # TokenSpace.PcdCName|PcdValue
1192 # TokenSpace.PcdCName|VpdOffset[|VpdValue]
1193 # TokenSpace.PcdCName|VpdOffset[|MaxSize[|VpdValue]]
1195 # TokenSpace.PcdCName|HiiString|VariableGuid|VariableOffset[|HiiValue]
1196 # PCD value needs to be located in such kind of string, and the PCD value might be an expression in which
1197 # there might have "|" operator, also in string value.
1199 # @param Setting: String contain information described above with "TokenSpace.PcdCName|" stripped
1200 # @param PcdType: PCD type: feature, fixed, dynamic default VPD HII
1201 # @param DataType: The datum type of PCD: VOID*, UNIT, BOOL
1203 # ValueList: A List contain fields described above
1204 # IsValid: True if conforming EBNF, otherwise False
1205 # Index: The index where PcdValue is in ValueList
1207 def AnalyzeDscPcd(Setting
, PcdType
, DataType
=''):
1208 FieldList
= AnalyzePcdExpression(Setting
)
1211 if PcdType
in (MODEL_PCD_FIXED_AT_BUILD
, MODEL_PCD_PATCHABLE_IN_MODULE
, MODEL_PCD_DYNAMIC_DEFAULT
, MODEL_PCD_DYNAMIC_EX_DEFAULT
):
1212 Value
= FieldList
[0]
1214 if len(FieldList
) > 1 and FieldList
[1]:
1215 DataType
= FieldList
[1]
1216 if FieldList
[1] != TAB_VOID
and StructPattern
.match(FieldList
[1]) is None:
1218 if len(FieldList
) > 2:
1222 IsValid
= (len(FieldList
) <= 1)
1224 IsValid
= (len(FieldList
) <= 3)
1228 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1232 return [str(Value
), DataType
, str(Size
)], IsValid
, 0
1233 elif PcdType
== MODEL_PCD_FEATURE_FLAG
:
1234 Value
= FieldList
[0]
1236 IsValid
= (len(FieldList
) <= 1)
1237 return [Value
, DataType
, str(Size
)], IsValid
, 0
1238 elif PcdType
in (MODEL_PCD_DYNAMIC_VPD
, MODEL_PCD_DYNAMIC_EX_VPD
):
1239 VpdOffset
= FieldList
[0]
1241 if not DataType
== TAB_VOID
:
1242 if len(FieldList
) > 1:
1243 Value
= FieldList
[1]
1245 if len(FieldList
) > 1:
1247 if len(FieldList
) > 2:
1248 Value
= FieldList
[2]
1250 IsValid
= (len(FieldList
) <= 1)
1252 IsValid
= (len(FieldList
) <= 3)
1255 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1259 return [VpdOffset
, str(Size
), Value
], IsValid
, 2
1260 elif PcdType
in (MODEL_PCD_DYNAMIC_HII
, MODEL_PCD_DYNAMIC_EX_HII
):
1261 IsValid
= (3 <= len(FieldList
) <= 5)
1262 HiiString
= FieldList
[0]
1263 Guid
= Offset
= Value
= Attribute
= ''
1264 if len(FieldList
) > 1:
1266 if len(FieldList
) > 2:
1267 Offset
= FieldList
[2]
1268 if len(FieldList
) > 3:
1269 Value
= FieldList
[3]
1270 if len(FieldList
) > 4:
1271 Attribute
= FieldList
[4]
1272 return [HiiString
, Guid
, Offset
, Value
, Attribute
], IsValid
, 3
1277 # Analyze the pcd Value, Datum type and TokenNumber.
1278 # Used to avoid split issue while the value string contain "|" character
1280 # @param[in] Setting: A String contain value/datum type/token number information;
1282 # @retval ValueList: A List contain value, datum type and toke number.
1284 def AnalyzePcdData(Setting
):
1285 ValueList
= ['', '', '']
1287 ValueRe
= re
.compile(r
'^\s*L?\".*\|.*\"')
1288 PtrValue
= ValueRe
.findall(Setting
)
1290 ValueUpdateFlag
= False
1292 if len(PtrValue
) >= 1:
1293 Setting
= re
.sub(ValueRe
, '', Setting
)
1294 ValueUpdateFlag
= True
1296 TokenList
= Setting
.split(TAB_VALUE_SPLIT
)
1297 ValueList
[0:len(TokenList
)] = TokenList
1300 ValueList
[0] = PtrValue
[0]
1304 ## check format of PCD value against its the datum type
1306 # For PCD value setting
1308 def CheckPcdDatum(Type
, Value
):
1309 if Type
== TAB_VOID
:
1310 ValueRe
= re
.compile(r
'\s*L?\".*\"\s*$')
1311 if not (((Value
.startswith('L"') or Value
.startswith('"')) and Value
.endswith('"'))
1312 or (Value
.startswith('{') and Value
.endswith('}')) or (Value
.startswith("L'") or Value
.startswith("'") and Value
.endswith("'"))
1314 return False, "Invalid value [%s] of type [%s]; must be in the form of {...} for array"\
1315 ", \"...\" or \'...\' for string, L\"...\" or L\'...\' for unicode string" % (Value
, Type
)
1316 elif ValueRe
.match(Value
):
1317 # Check the chars in UnicodeString or CString is printable
1318 if Value
.startswith("L"):
1322 Printset
= set(string
.printable
)
1323 Printset
.remove(TAB_PRINTCHAR_VT
)
1324 Printset
.add(TAB_PRINTCHAR_BS
)
1325 Printset
.add(TAB_PRINTCHAR_NUL
)
1326 if not set(Value
).issubset(Printset
):
1327 PrintList
= sorted(Printset
)
1328 return False, "Invalid PCD string value of type [%s]; must be printable chars %s." % (Type
, PrintList
)
1329 elif Type
== 'BOOLEAN':
1330 if Value
not in ['TRUE', 'True', 'true', '0x1', '0x01', '1', 'FALSE', 'False', 'false', '0x0', '0x00', '0']:
1331 return False, "Invalid value [%s] of type [%s]; must be one of TRUE, True, true, 0x1, 0x01, 1"\
1332 ", FALSE, False, false, 0x0, 0x00, 0" % (Value
, Type
)
1333 elif Type
in [TAB_UINT8
, TAB_UINT16
, TAB_UINT32
, TAB_UINT64
]:
1334 if Value
.startswith('0') and not Value
.lower().startswith('0x') and len(Value
) > 1 and Value
.lstrip('0'):
1335 Value
= Value
.lstrip('0')
1337 if Value
and int(Value
, 0) < 0:
1338 return False, "PCD can't be set to negative value[%s] for datum type [%s]" % (Value
, Type
)
1339 Value
= int(Value
, 0)
1340 if Value
> MAX_VAL_TYPE
[Type
]:
1341 return False, "Too large PCD value[%s] for datum type [%s]" % (Value
, Type
)
1343 return False, "Invalid value [%s] of type [%s];"\
1344 " must be a hexadecimal, decimal or octal in C language format." % (Value
, Type
)
1346 return True, "StructurePcd"
1350 def CommonPath(PathList
):
1351 P1
= min(PathList
).split(os
.path
.sep
)
1352 P2
= max(PathList
).split(os
.path
.sep
)
1353 for Index
in range(min(len(P1
), len(P2
))):
1354 if P1
[Index
] != P2
[Index
]:
1355 return os
.path
.sep
.join(P1
[:Index
])
1356 return os
.path
.sep
.join(P1
)
1358 class PathClass(object):
1359 def __init__(self
, File
='', Root
='', AlterRoot
='', Type
='', IsBinary
=False,
1360 Arch
='COMMON', ToolChainFamily
='', Target
='', TagName
='', ToolCode
=''):
1362 self
.File
= str(File
)
1363 if os
.path
.isabs(self
.File
):
1367 self
.Root
= str(Root
)
1368 self
.AlterRoot
= str(AlterRoot
)
1370 # Remove any '.' and '..' in path
1372 self
.Root
= mws
.getWs(self
.Root
, self
.File
)
1373 self
.Path
= os
.path
.normpath(os
.path
.join(self
.Root
, self
.File
))
1374 self
.Root
= os
.path
.normpath(CommonPath([self
.Root
, self
.Path
]))
1375 # eliminate the side-effect of 'C:'
1376 if self
.Root
[-1] == ':':
1377 self
.Root
+= os
.path
.sep
1378 # file path should not start with path separator
1379 if self
.Root
[-1] == os
.path
.sep
:
1380 self
.File
= self
.Path
[len(self
.Root
):]
1382 self
.File
= self
.Path
[len(self
.Root
) + 1:]
1384 self
.Path
= os
.path
.normpath(self
.File
)
1386 self
.SubDir
, self
.Name
= os
.path
.split(self
.File
)
1387 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1391 self
.Dir
= os
.path
.join(self
.Root
, self
.SubDir
)
1393 self
.Dir
= self
.Root
1395 self
.Dir
= self
.SubDir
1400 self
.Type
= self
.Ext
.lower()
1402 self
.IsBinary
= IsBinary
1403 self
.Target
= Target
1404 self
.TagName
= TagName
1405 self
.ToolCode
= ToolCode
1406 self
.ToolChainFamily
= ToolChainFamily
1408 ## Convert the object of this class to a string
1410 # Convert member Path of the class to a string
1412 # @retval string Formatted String
1417 ## Override __eq__ function
1419 # Check whether PathClass are the same
1421 # @retval False The two PathClass are different
1422 # @retval True The two PathClass are the same
1424 def __eq__(self
, Other
):
1425 return self
.Path
== str(Other
)
1427 ## Override __cmp__ function
1429 # Customize the comparison operation of two PathClass
1431 # @retval 0 The two PathClass are different
1432 # @retval -1 The first PathClass is less than the second PathClass
1433 # @retval 1 The first PathClass is Bigger than the second PathClass
1434 def __cmp__(self
, Other
):
1435 OtherKey
= str(Other
)
1438 if SelfKey
== OtherKey
:
1440 elif SelfKey
> OtherKey
:
1445 ## Override __hash__ function
1447 # Use Path as key in hash table
1449 # @retval string Key for hash table
1452 return hash(self
.Path
)
1456 return self
.Path
.upper()
1459 def TimeStamp(self
):
1460 return os
.stat(self
.Path
)[8]
1462 def Validate(self
, Type
='', CaseSensitive
=True):
1463 def RealPath2(File
, Dir
='', OverrideDir
=''):
1466 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(OverrideDir
, File
))]
1468 if OverrideDir
[-1] == os
.path
.sep
:
1469 return NewFile
[len(OverrideDir
):], NewFile
[0:len(OverrideDir
)]
1471 return NewFile
[len(OverrideDir
) + 1:], NewFile
[0:len(OverrideDir
)]
1472 if GlobalData
.gAllFiles
:
1473 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(Dir
, File
))]
1475 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
1476 if not os
.path
.exists(NewFile
):
1480 if Dir
[-1] == os
.path
.sep
:
1481 return NewFile
[len(Dir
):], NewFile
[0:len(Dir
)]
1483 return NewFile
[len(Dir
) + 1:], NewFile
[0:len(Dir
)]
1489 if GlobalData
.gCaseInsensitive
:
1490 CaseSensitive
= False
1491 if Type
and Type
.lower() != self
.Type
:
1492 return FILE_TYPE_MISMATCH
, '%s (expect %s but got %s)' % (self
.File
, Type
, self
.Type
)
1494 RealFile
, RealRoot
= RealPath2(self
.File
, self
.Root
, self
.AlterRoot
)
1495 if not RealRoot
and not RealFile
:
1496 RealFile
= self
.File
1498 RealFile
= os
.path
.join(self
.AlterRoot
, self
.File
)
1500 RealFile
= os
.path
.join(self
.Root
, self
.File
)
1501 if len (mws
.getPkgPath()) == 0:
1502 return FILE_NOT_FOUND
, os
.path
.join(self
.AlterRoot
, RealFile
)
1504 return FILE_NOT_FOUND
, "%s is not found in packages path:\n\t%s" % (self
.File
, '\n\t'.join(mws
.getPkgPath()))
1508 if RealRoot
!= self
.Root
or RealFile
!= self
.File
:
1509 if CaseSensitive
and (RealFile
!= self
.File
or (RealRoot
!= self
.Root
and RealRoot
!= self
.AlterRoot
)):
1510 ErrorCode
= FILE_CASE_MISMATCH
1511 ErrorInfo
= self
.File
+ '\n\t' + RealFile
+ " [in file system]"
1513 self
.SubDir
, self
.Name
= os
.path
.split(RealFile
)
1514 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1516 self
.Dir
= os
.path
.join(RealRoot
, self
.SubDir
)
1519 self
.File
= RealFile
1520 self
.Root
= RealRoot
1521 self
.Path
= os
.path
.join(RealRoot
, RealFile
)
1522 return ErrorCode
, ErrorInfo
1524 ## Parse PE image to get the required PE information.
1526 class PeImageClass():
1529 # @param File FilePath of PeImage
1531 def __init__(self
, PeFile
):
1532 self
.FileName
= PeFile
1533 self
.IsValid
= False
1536 self
.SectionAlignment
= 0
1537 self
.SectionHeaderList
= []
1540 PeObject
= open(PeFile
, 'rb')
1542 self
.ErrorInfo
= self
.FileName
+ ' can not be found\n'
1545 ByteArray
= array
.array('B')
1546 ByteArray
.fromfile(PeObject
, 0x3E)
1547 ByteList
= ByteArray
.tolist()
1548 # DOS signature should be 'MZ'
1549 if self
._ByteListToStr
(ByteList
[0x0:0x2]) != 'MZ':
1550 self
.ErrorInfo
= self
.FileName
+ ' has no valid DOS signature MZ'
1553 # Read 4 byte PE Signature
1554 PeOffset
= self
._ByteListToInt
(ByteList
[0x3C:0x3E])
1555 PeObject
.seek(PeOffset
)
1556 ByteArray
= array
.array('B')
1557 ByteArray
.fromfile(PeObject
, 4)
1558 # PE signature should be 'PE\0\0'
1559 if ByteArray
.tostring() != b
'PE\0\0':
1560 self
.ErrorInfo
= self
.FileName
+ ' has no valid PE signature PE00'
1563 # Read PE file header
1564 ByteArray
= array
.array('B')
1565 ByteArray
.fromfile(PeObject
, 0x14)
1566 ByteList
= ByteArray
.tolist()
1567 SecNumber
= self
._ByteListToInt
(ByteList
[0x2:0x4])
1569 self
.ErrorInfo
= self
.FileName
+ ' has no section header'
1572 # Read PE optional header
1573 OptionalHeaderSize
= self
._ByteListToInt
(ByteArray
[0x10:0x12])
1574 ByteArray
= array
.array('B')
1575 ByteArray
.fromfile(PeObject
, OptionalHeaderSize
)
1576 ByteList
= ByteArray
.tolist()
1577 self
.EntryPoint
= self
._ByteListToInt
(ByteList
[0x10:0x14])
1578 self
.SectionAlignment
= self
._ByteListToInt
(ByteList
[0x20:0x24])
1579 self
.Size
= self
._ByteListToInt
(ByteList
[0x38:0x3C])
1581 # Read each Section Header
1582 for Index
in range(SecNumber
):
1583 ByteArray
= array
.array('B')
1584 ByteArray
.fromfile(PeObject
, 0x28)
1585 ByteList
= ByteArray
.tolist()
1586 SecName
= self
._ByteListToStr
(ByteList
[0:8])
1587 SecVirtualSize
= self
._ByteListToInt
(ByteList
[8:12])
1588 SecRawAddress
= self
._ByteListToInt
(ByteList
[20:24])
1589 SecVirtualAddress
= self
._ByteListToInt
(ByteList
[12:16])
1590 self
.SectionHeaderList
.append((SecName
, SecVirtualAddress
, SecRawAddress
, SecVirtualSize
))
1594 def _ByteListToStr(self
, ByteList
):
1596 for index
in range(len(ByteList
)):
1597 if ByteList
[index
] == 0:
1599 String
+= chr(ByteList
[index
])
1602 def _ByteListToInt(self
, ByteList
):
1604 for index
in range(len(ByteList
) - 1, -1, -1):
1605 Value
= (Value
<< 8) |
int(ByteList
[index
])
1608 class DefaultStore():
1609 def __init__(self
, DefaultStores
):
1611 self
.DefaultStores
= DefaultStores
1612 def DefaultStoreID(self
, DefaultStoreName
):
1613 for key
, value
in self
.DefaultStores
.items():
1614 if value
== DefaultStoreName
:
1617 def GetDefaultDefault(self
):
1618 if not self
.DefaultStores
or "0" in self
.DefaultStores
:
1619 return "0", TAB_DEFAULT_STORES_DEFAULT
1621 minvalue
= min(int(value_str
) for value_str
in self
.DefaultStores
)
1622 return (str(minvalue
), self
.DefaultStores
[str(minvalue
)])
1623 def GetMin(self
, DefaultSIdList
):
1624 if not DefaultSIdList
:
1625 return TAB_DEFAULT_STORES_DEFAULT
1626 storeidset
= {storeid
for storeid
, storename
in self
.DefaultStores
.values() if storename
in DefaultSIdList
}
1629 minid
= min(storeidset
)
1630 for sid
, name
in self
.DefaultStores
.values():
1639 def __init__(self
,SkuIdentifier
='', SkuIds
=None):
1643 for SkuName
in SkuIds
:
1644 SkuId
= SkuIds
[SkuName
][0]
1645 skuid_num
= int(SkuId
, 16) if SkuId
.upper().startswith("0X") else int(SkuId
)
1646 if skuid_num
> 0xFFFFFFFFFFFFFFFF:
1647 EdkLogger
.error("build", PARAMETER_INVALID
,
1648 ExtraData
= "SKU-ID [%s] value %s exceeds the max value of UINT64"
1651 self
.AvailableSkuIds
= OrderedDict()
1653 self
.SkuIdNumberSet
= []
1654 self
.SkuData
= SkuIds
1655 self
._SkuInherit
= {}
1656 self
._SkuIdentifier
= SkuIdentifier
1657 if SkuIdentifier
== '' or SkuIdentifier
is None:
1658 self
.SkuIdSet
= ['DEFAULT']
1659 self
.SkuIdNumberSet
= ['0U']
1660 elif SkuIdentifier
== 'ALL':
1661 self
.SkuIdSet
= list(SkuIds
.keys())
1662 self
.SkuIdNumberSet
= [num
[0].strip() + 'U' for num
in SkuIds
.values()]
1664 r
= SkuIdentifier
.split('|')
1665 self
.SkuIdSet
=[(r
[k
].strip()).upper() for k
in range(len(r
))]
1668 self
.SkuIdNumberSet
= [SkuIds
[k
][0].strip() + 'U' for k
in self
.SkuIdSet
]
1670 EdkLogger
.error("build", PARAMETER_INVALID
,
1671 ExtraData
= "SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1672 % (k
, " | ".join(SkuIds
.keys())))
1673 for each
in self
.SkuIdSet
:
1675 self
.AvailableSkuIds
[each
] = SkuIds
[each
][0]
1677 EdkLogger
.error("build", PARAMETER_INVALID
,
1678 ExtraData
="SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1679 % (each
, " | ".join(SkuIds
.keys())))
1680 if self
.SkuUsageType
!= SkuClass
.SINGLE
:
1681 self
.AvailableSkuIds
.update({'DEFAULT':0, 'COMMON':0})
1683 GlobalData
.gSkuids
= (self
.SkuIdSet
)
1684 if 'COMMON' in GlobalData
.gSkuids
:
1685 GlobalData
.gSkuids
.remove('COMMON')
1686 if self
.SkuUsageType
== self
.SINGLE
:
1687 if len(GlobalData
.gSkuids
) != 1:
1688 if 'DEFAULT' in GlobalData
.gSkuids
:
1689 GlobalData
.gSkuids
.remove('DEFAULT')
1690 if GlobalData
.gSkuids
:
1691 GlobalData
.gSkuids
.sort()
1693 def GetNextSkuId(self
, skuname
):
1694 if not self
._SkuInherit
:
1695 self
._SkuInherit
= {}
1696 for item
in self
.SkuData
.values():
1697 self
._SkuInherit
[item
[1]]=item
[2] if item
[2] else "DEFAULT"
1698 return self
._SkuInherit
.get(skuname
, "DEFAULT")
1700 def GetSkuChain(self
, sku
):
1701 if sku
== "DEFAULT":
1706 nextsku
= self
.GetNextSkuId(nextsku
)
1707 skulist
.append(nextsku
)
1708 if nextsku
== "DEFAULT":
1712 def SkuOverrideOrder(self
):
1714 for skuname
in self
.SkuIdSet
:
1715 skuorderset
.append(self
.GetSkuChain(skuname
))
1718 for index
in range(max(len(item
) for item
in skuorderset
)):
1719 for subset
in skuorderset
:
1720 if index
> len(subset
)-1:
1722 if subset
[index
] in skuorder
:
1724 skuorder
.append(subset
[index
])
1729 def SkuUsageType(self
):
1730 if self
._SkuIdentifier
.upper() == "ALL":
1731 return SkuClass
.MULTIPLE
1733 if len(self
.SkuIdSet
) == 1:
1734 if self
.SkuIdSet
[0] == 'DEFAULT':
1735 return SkuClass
.DEFAULT
1736 return SkuClass
.SINGLE
1737 if len(self
.SkuIdSet
) == 2 and 'DEFAULT' in self
.SkuIdSet
:
1738 return SkuClass
.SINGLE
1739 return SkuClass
.MULTIPLE
1741 def DumpSkuIdArrary(self
):
1742 if self
.SkuUsageType
== SkuClass
.SINGLE
:
1745 for skuname
in self
.AvailableSkuIds
:
1746 if skuname
== "COMMON":
1748 while skuname
!= "DEFAULT":
1749 ArrayStrList
.append(hex(int(self
.AvailableSkuIds
[skuname
])))
1750 skuname
= self
.GetNextSkuId(skuname
)
1751 ArrayStrList
.append("0x0")
1752 return "{{{myList}}}".format(myList
=",".join(ArrayStrList
))
1755 def AvailableSkuIdSet(self
):
1756 return self
.AvailableSkuIds
1759 def SystemSkuId(self
):
1760 if self
.SkuUsageType
== SkuClass
.SINGLE
:
1761 if len(self
.SkuIdSet
) == 1:
1762 return self
.SkuIdSet
[0]
1764 return self
.SkuIdSet
[0] if self
.SkuIdSet
[0] != 'DEFAULT' else self
.SkuIdSet
[1]
1768 ## Get the integer value from string like "14U" or integer like 2
1770 # @param Input The object that may be either a integer value or a string
1772 # @retval Value The integer value that the input represents
1774 def GetIntegerValue(Input
):
1775 if not isinstance(Input
, str):
1778 if String
.endswith("U"):
1779 String
= String
[:-1]
1780 if String
.endswith("ULL"):
1781 String
= String
[:-3]
1782 if String
.endswith("LL"):
1783 String
= String
[:-2]
1785 if String
.startswith("0x") or String
.startswith("0X"):
1786 return int(String
, 16)
1793 # Pack a GUID (registry format) list into a buffer and return it
1796 return pack(PACK_PATTERN_GUID
,
1800 int(Guid
[3][-4:-2], 16),
1801 int(Guid
[3][-2:], 16),
1802 int(Guid
[4][-12:-10], 16),
1803 int(Guid
[4][-10:-8], 16),
1804 int(Guid
[4][-8:-6], 16),
1805 int(Guid
[4][-6:-4], 16),
1806 int(Guid
[4][-4:-2], 16),
1807 int(Guid
[4][-2:], 16)
1811 # Pack a GUID (byte) list into a buffer and return it
1813 def PackByteFormatGUID(Guid
):
1814 return pack(PACK_PATTERN_GUID
,
1828 ## DeepCopy dict/OrderedDict recusively
1830 # @param ori_dict a nested dict or ordereddict
1832 # @retval new dict or orderdict
1834 def CopyDict(ori_dict
):
1835 dict_type
= ori_dict
.__class
__
1836 if dict_type
not in (dict,OrderedDict
):
1838 new_dict
= dict_type()
1839 for key
in ori_dict
:
1840 if isinstance(ori_dict
[key
],(dict,OrderedDict
)):
1841 new_dict
[key
] = CopyDict(ori_dict
[key
])
1843 new_dict
[key
] = ori_dict
[key
]
1847 # Remove the c/c++ comments: // and /* */
1849 def RemoveCComments(ctext
):
1850 return re
.sub('//.*?\n|/\*.*?\*/', '\n', ctext
, flags
=re
.S
)