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
25 from collections
import OrderedDict
27 import Common
.LongFilePathOs
as os
28 from Common
import EdkLogger
as EdkLogger
29 from Common
import GlobalData
as GlobalData
30 from Common
.DataType
import *
31 from Common
.BuildToolError
import *
32 from CommonDataClass
.DataClass
import *
33 from Common
.Parsing
import GetSplitValueList
34 from Common
.LongFilePathSupport
import OpenLongFilePath
as open
35 from Common
.MultipleWorkspace
import MultipleWorkspace
as mws
36 from CommonDataClass
.Exceptions
import BadExpression
37 from Common
.caching
import cached_property
39 ## Regular expression used to find out place holders in string template
40 gPlaceholderPattern
= re
.compile("\$\{([^$()\s]+)\}", re
.MULTILINE | re
.UNICODE
)
42 ## regular expressions for map file processing
43 startPatternGeneral
= re
.compile("^Start[' ']+Length[' ']+Name[' ']+Class")
44 addressPatternGeneral
= re
.compile("^Address[' ']+Publics by Value[' ']+Rva\+Base")
45 valuePatternGcc
= re
.compile('^([\w_\.]+) +([\da-fA-Fx]+) +([\da-fA-Fx]+)$')
46 pcdPatternGcc
= re
.compile('^([\da-fA-Fx]+) +([\da-fA-Fx]+)')
47 secReGeneral
= re
.compile('^([\da-fA-F]+):([\da-fA-F]+) +([\da-fA-F]+)[Hh]? +([.\w\$]+) +(\w+)', re
.UNICODE
)
49 StructPattern
= re
.compile(r
'[_a-zA-Z][0-9A-Za-z_]*$')
51 ## Dictionary used to store dependencies of files
52 gDependencyDatabase
= {} # arch : {file path : [dependent files list]}
55 # If a module is built more than once with different PCDs or library classes
56 # a temporary INF file with same content is created, the temporary file is removed
61 def GetVariableOffset(mapfilepath
, efifilepath
, varnames
):
62 """ Parse map file to get variable offset in current EFI file
63 @param mapfilepath Map file absolution path
64 @param efifilepath: EFI binary file full path
65 @param varnames iteratable container whose elements are variable names to be searched
67 @return List whos elements are tuple with variable name and raw offset
71 f
= open(mapfilepath
, 'r')
77 if len(lines
) == 0: return None
78 firstline
= lines
[0].strip()
79 if (firstline
.startswith("Archive member included ") and
80 firstline
.endswith(" file (symbol)")):
81 return _parseForGCC(lines
, efifilepath
, varnames
)
82 if firstline
.startswith("# Path:"):
83 return _parseForXcode(lines
, efifilepath
, varnames
)
84 return _parseGeneral(lines
, efifilepath
, varnames
)
86 def _parseForXcode(lines
, efifilepath
, varnames
):
91 if status
== 0 and line
== "# Symbols:":
94 if status
== 1 and len(line
) != 0:
95 for varname
in varnames
:
97 # cannot pregenerate this RegEx since it uses varname from varnames.
98 m
= re
.match('^([\da-fA-FxX]+)([\s\S]*)([_]*%s)$' % varname
, line
)
100 ret
.append((varname
, m
.group(1)))
103 def _parseForGCC(lines
, efifilepath
, varnames
):
104 """ Parse map file generated by GCC linker """
108 for index
, line
in enumerate(lines
):
110 # status machine transection
111 if status
== 0 and line
== "Memory Configuration":
114 elif status
== 1 and line
== 'Linker script and memory map':
117 elif status
==2 and line
== 'START GROUP':
123 m
= valuePatternGcc
.match(line
)
125 sections
.append(m
.groups(0))
126 for varname
in varnames
:
128 m
= re
.match("^.data.(%s)" % varname
, line
)
130 m
= re
.match(".data.(%s)$" % varname
, line
)
132 Str
= lines
[index
+ 1]
134 Str
= line
[len(".data.%s" % varname
):]
136 m
= pcdPatternGcc
.match(Str
.strip())
138 varoffset
.append((varname
, int(m
.groups(0)[0], 16), int(sections
[-1][1], 16), sections
[-1][0]))
142 # get section information from efi file
143 efisecs
= PeImageClass(efifilepath
).SectionHeaderList
144 if efisecs
is None or len(efisecs
) == 0:
148 for efisec
in efisecs
:
149 for section
in sections
:
150 if section
[0].strip() == efisec
[0].strip() and section
[0].strip() == '.text':
151 redirection
= int(section
[1], 16) - efisec
[1]
154 for var
in varoffset
:
155 for efisec
in efisecs
:
156 if var
[1] >= efisec
[1] and var
[1] < efisec
[1]+efisec
[3]:
157 ret
.append((var
[0], hex(efisec
[2] + var
[1] - efisec
[1] - redirection
)))
160 def _parseGeneral(lines
, efifilepath
, varnames
):
161 status
= 0 #0 - beginning of file; 1 - PE section definition; 2 - symbol table
162 secs
= [] # key = section name
164 symRe
= re
.compile('^([\da-fA-F]+):([\da-fA-F]+) +([\.:\\\\\w\?@\$]+) +([\da-fA-F]+)', re
.UNICODE
)
168 if startPatternGeneral
.match(line
):
171 if addressPatternGeneral
.match(line
):
174 if line
.startswith("entry point at"):
177 if status
== 1 and len(line
) != 0:
178 m
= secReGeneral
.match(line
)
179 assert m
is not None, "Fail to parse the section in map file , line is %s" % line
180 sec_no
, sec_start
, sec_length
, sec_name
, sec_class
= m
.groups(0)
181 secs
.append([int(sec_no
, 16), int(sec_start
, 16), int(sec_length
, 16), sec_name
, sec_class
])
182 if status
== 2 and len(line
) != 0:
183 for varname
in varnames
:
184 m
= symRe
.match(line
)
185 assert m
is not None, "Fail to parse the symbol in map file, line is %s" % line
186 sec_no
, sym_offset
, sym_name
, vir_addr
= m
.groups(0)
187 sec_no
= int(sec_no
, 16)
188 sym_offset
= int(sym_offset
, 16)
189 vir_addr
= int(vir_addr
, 16)
190 # cannot pregenerate this RegEx since it uses varname from varnames.
191 m2
= re
.match('^[_]*(%s)' % varname
, sym_name
)
193 # fond a binary pcd entry in map file
195 if sec
[0] == sec_no
and (sym_offset
>= sec
[1] and sym_offset
< sec
[1] + sec
[2]):
196 varoffset
.append([varname
, sec
[3], sym_offset
, vir_addr
, sec_no
])
198 if not varoffset
: return []
200 # get section information from efi file
201 efisecs
= PeImageClass(efifilepath
).SectionHeaderList
202 if efisecs
is None or len(efisecs
) == 0:
206 for var
in varoffset
:
208 for efisec
in efisecs
:
210 if var
[1].strip() == efisec
[0].strip():
211 ret
.append((var
[0], hex(efisec
[2] + var
[2])))
212 elif var
[4] == index
:
213 ret
.append((var
[0], hex(efisec
[2] + var
[2])))
217 ## Routine to process duplicated INF
219 # This function is called by following two cases:
222 # Pkg/module/module.inf
223 # Pkg/module/module.inf {
225 # FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836
228 # INF Pkg/module/module.inf
229 # INF FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836 Pkg/module/module.inf
231 # This function copies Pkg/module/module.inf to
232 # Conf/.cache/0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf
234 # @param Path Original PathClass object
235 # @param BaseName New file base name
237 # @retval return the new PathClass object
239 def ProcessDuplicatedInf(Path
, BaseName
, Workspace
):
240 Filename
= os
.path
.split(Path
.File
)[1]
242 Filename
= BaseName
+ Path
.BaseName
+ Filename
[Filename
.rfind('.'):]
244 Filename
= BaseName
+ Path
.BaseName
247 # If -N is specified on command line, cache is disabled
248 # The directory has to be created
250 DbDir
= os
.path
.split(GlobalData
.gDatabasePath
)[0]
251 if not os
.path
.exists(DbDir
):
254 # A temporary INF is copied to database path which must have write permission
255 # The temporary will be removed at the end of build
256 # In case of name conflict, the file name is
257 # FILE_GUIDBaseName (0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf)
259 TempFullPath
= os
.path
.join(DbDir
,
261 RtPath
= PathClass(Path
.File
, Workspace
)
263 # Modify the full path to temporary path, keep other unchanged
265 # To build same module more than once, the module path with FILE_GUID overridden has
266 # the file name FILE_GUIDmodule.inf, but the relative path (self.MetaFile.File) is the real path
267 # in DSC which is used as relative path by C files and other files in INF.
268 # A trick was used: all module paths are PathClass instances, after the initialization
269 # of PathClass, the PathClass.Path is overridden by the temporary INF path.
271 # The reason for creating a temporary INF is:
272 # Platform.Modules which is the base to create ModuleAutoGen objects is a dictionary,
273 # the key is the full path of INF, the value is an object to save overridden library instances, PCDs.
274 # A different key for the same module is needed to create different output directory,
275 # retrieve overridden PCDs, library instances.
277 # The BaseName is the FILE_GUID which is also the output directory name.
280 RtPath
.Path
= TempFullPath
281 RtPath
.BaseName
= BaseName
283 # If file exists, compare contents
285 if os
.path
.exists(TempFullPath
):
286 with
open(str(Path
), 'rb') as f1
, open(TempFullPath
, 'rb') as f2
:
287 if f1
.read() == f2
.read():
289 _TempInfs
.append(TempFullPath
)
290 shutil
.copy2(str(Path
), TempFullPath
)
293 ## Remove temporary created INFs whose paths were saved in _TempInfs
295 def ClearDuplicatedInf():
297 File
= _TempInfs
.pop()
298 if os
.path
.exists(File
):
301 ## Convert GUID string in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx style to C structure style
303 # @param Guid The GUID string
305 # @retval string The GUID string in C structure style
307 def GuidStringToGuidStructureString(Guid
):
308 GuidList
= Guid
.split('-')
310 for Index
in range(0, 3, 1):
311 Result
= Result
+ '0x' + GuidList
[Index
] + ', '
312 Result
= Result
+ '{0x' + GuidList
[3][0:2] + ', 0x' + GuidList
[3][2:4]
313 for Index
in range(0, 12, 2):
314 Result
= Result
+ ', 0x' + GuidList
[4][Index
:Index
+ 2]
318 ## Convert GUID structure in byte array to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
320 # @param GuidValue The GUID value in byte array
322 # @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
324 def GuidStructureByteArrayToGuidString(GuidValue
):
325 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
326 guidValueList
= guidValueString
.split(",")
327 if len(guidValueList
) != 16:
329 #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
331 return "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
332 int(guidValueList
[3], 16),
333 int(guidValueList
[2], 16),
334 int(guidValueList
[1], 16),
335 int(guidValueList
[0], 16),
336 int(guidValueList
[5], 16),
337 int(guidValueList
[4], 16),
338 int(guidValueList
[7], 16),
339 int(guidValueList
[6], 16),
340 int(guidValueList
[8], 16),
341 int(guidValueList
[9], 16),
342 int(guidValueList
[10], 16),
343 int(guidValueList
[11], 16),
344 int(guidValueList
[12], 16),
345 int(guidValueList
[13], 16),
346 int(guidValueList
[14], 16),
347 int(guidValueList
[15], 16)
352 ## Convert GUID string in C structure style to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
354 # @param GuidValue The GUID value in C structure format
356 # @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
358 def GuidStructureStringToGuidString(GuidValue
):
359 if not GlobalData
.gGuidCFormatPattern
.match(GuidValue
):
361 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
362 guidValueList
= guidValueString
.split(",")
363 if len(guidValueList
) != 11:
365 #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
367 return "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
368 int(guidValueList
[0], 16),
369 int(guidValueList
[1], 16),
370 int(guidValueList
[2], 16),
371 int(guidValueList
[3], 16),
372 int(guidValueList
[4], 16),
373 int(guidValueList
[5], 16),
374 int(guidValueList
[6], 16),
375 int(guidValueList
[7], 16),
376 int(guidValueList
[8], 16),
377 int(guidValueList
[9], 16),
378 int(guidValueList
[10], 16)
383 ## Convert GUID string in C structure style to xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx
385 # @param GuidValue The GUID value in C structure format
387 # @retval string The GUID value in xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx format
389 def GuidStructureStringToGuidValueName(GuidValue
):
390 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "")
391 guidValueList
= guidValueString
.split(",")
392 if len(guidValueList
) != 11:
393 EdkLogger
.error(None, FORMAT_INVALID
, "Invalid GUID value string [%s]" % GuidValue
)
394 return "%08x_%04x_%04x_%02x%02x_%02x%02x%02x%02x%02x%02x" % (
395 int(guidValueList
[0], 16),
396 int(guidValueList
[1], 16),
397 int(guidValueList
[2], 16),
398 int(guidValueList
[3], 16),
399 int(guidValueList
[4], 16),
400 int(guidValueList
[5], 16),
401 int(guidValueList
[6], 16),
402 int(guidValueList
[7], 16),
403 int(guidValueList
[8], 16),
404 int(guidValueList
[9], 16),
405 int(guidValueList
[10], 16)
408 ## Create directories
410 # @param Directory The directory name
412 def CreateDirectory(Directory
):
413 if Directory
is None or Directory
.strip() == "":
416 if not os
.access(Directory
, os
.F_OK
):
417 os
.makedirs(Directory
)
422 ## Remove directories, including files and sub-directories in it
424 # @param Directory The directory name
426 def RemoveDirectory(Directory
, Recursively
=False):
427 if Directory
is None or Directory
.strip() == "" or not os
.path
.exists(Directory
):
430 CurrentDirectory
= os
.getcwd()
432 for File
in os
.listdir("."):
433 if os
.path
.isdir(File
):
434 RemoveDirectory(File
, Recursively
)
437 os
.chdir(CurrentDirectory
)
440 ## Store content in file
442 # This method is used to save file only when its content is changed. This is
443 # quite useful for "make" system to decide what will be re-built and what won't.
445 # @param File The path of file
446 # @param Content The new content of the file
447 # @param IsBinaryFile The flag indicating if the file is binary file or not
449 # @retval True If the file content is changed and the file is renewed
450 # @retval False If the file content is the same
452 def SaveFileOnChange(File
, Content
, IsBinaryFile
=True):
454 if os
.path
.exists(File
):
457 with
open(File
, "rb") as f
:
458 if Content
== f
.read():
461 EdkLogger
.error(None, FILE_OPEN_FAILURE
, ExtraData
=File
)
464 with
open(File
, "r") as f
:
465 if Content
== f
.read():
468 EdkLogger
.error(None, FILE_OPEN_FAILURE
, ExtraData
=File
)
470 DirName
= os
.path
.dirname(File
)
471 if not CreateDirectory(DirName
):
472 EdkLogger
.error(None, FILE_CREATE_FAILURE
, "Could not create directory %s" % DirName
)
475 DirName
= os
.getcwd()
476 if not os
.access(DirName
, os
.W_OK
):
477 EdkLogger
.error(None, PERMISSION_FAILURE
, "Do not have write permission on directory %s" % DirName
)
481 with
open(File
, "wb") as Fd
:
484 EdkLogger
.error(None, FILE_CREATE_FAILURE
, ExtraData
='IOError %s' % X
)
487 with
open(File
, 'w') as Fd
:
490 EdkLogger
.error(None, FILE_CREATE_FAILURE
, ExtraData
='IOError %s' % X
)
494 ## Retrieve and cache the real path name in file system
496 # @param Root The root directory of path relative to
498 # @retval str The path string if the path exists
499 # @retval None If path doesn't exist
505 def __init__(self
, Root
):
507 for F
in os
.listdir(Root
):
509 self
._UPPER
_CACHE
_[F
.upper()] = F
512 def __getitem__(self
, Path
):
513 Path
= Path
[len(os
.path
.commonprefix([Path
, self
._Root
])):]
516 if Path
and Path
[0] == os
.path
.sep
:
518 if Path
in self
._CACHE
_:
519 return os
.path
.join(self
._Root
, Path
)
520 UpperPath
= Path
.upper()
521 if UpperPath
in self
._UPPER
_CACHE
_:
522 return os
.path
.join(self
._Root
, self
._UPPER
_CACHE
_[UpperPath
])
526 SepIndex
= Path
.find(os
.path
.sep
)
528 Parent
= UpperPath
[:SepIndex
]
529 if Parent
not in self
._UPPER
_CACHE
_:
531 LastSepIndex
= SepIndex
532 SepIndex
= Path
.find(os
.path
.sep
, LastSepIndex
+ 1)
534 if LastSepIndex
== -1:
539 SepIndex
= LastSepIndex
541 Parent
= Path
[:SepIndex
]
542 ParentKey
= UpperPath
[:SepIndex
]
543 if ParentKey
not in self
._UPPER
_CACHE
_:
547 if Parent
in self
._CACHE
_:
550 ParentDir
= self
._UPPER
_CACHE
_[ParentKey
]
551 for F
in os
.listdir(ParentDir
):
552 Dir
= os
.path
.join(ParentDir
, F
)
553 self
._CACHE
_.add(Dir
)
554 self
._UPPER
_CACHE
_[Dir
.upper()] = Dir
556 SepIndex
= Path
.find(os
.path
.sep
, SepIndex
+ 1)
559 if Path
in self
._CACHE
_:
560 return os
.path
.join(self
._Root
, Path
)
561 elif UpperPath
in self
._UPPER
_CACHE
_:
562 return os
.path
.join(self
._Root
, self
._UPPER
_CACHE
_[UpperPath
])
565 def RealPath(File
, Dir
='', OverrideDir
=''):
566 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
567 NewFile
= GlobalData
.gAllFiles
[NewFile
]
568 if not NewFile
and OverrideDir
:
569 NewFile
= os
.path
.normpath(os
.path
.join(OverrideDir
, File
))
570 NewFile
= GlobalData
.gAllFiles
[NewFile
]
573 ## Get GUID value from given packages
575 # @param CName The CName of the GUID
576 # @param PackageList List of packages looking-up in
577 # @param Inffile The driver file
579 # @retval GuidValue if the CName is found in any given package
580 # @retval None if the CName is not found in all given packages
582 def GuidValue(CName
, PackageList
, Inffile
= None):
583 for P
in PackageList
:
584 GuidKeys
= list(P
.Guids
.keys())
585 if Inffile
and P
._PrivateGuids
:
586 if not Inffile
.startswith(P
.MetaFile
.Dir
):
587 GuidKeys
= [x
for x
in P
.Guids
if x
not in P
._PrivateGuids
]
588 if CName
in GuidKeys
:
589 return P
.Guids
[CName
]
593 ## A string template class
595 # This class implements a template for string replacement. A string template
596 # looks like following
598 # ${BEGIN} other_string ${placeholder_name} other_string ${END}
600 # The string between ${BEGIN} and ${END} will be repeated as many times as the
601 # length of "placeholder_name", which is a list passed through a dict. The
602 # "placeholder_name" is the key name of the dict. The ${BEGIN} and ${END} can
603 # be not used and, in this case, the "placeholder_name" must not a list and it
604 # will just be replaced once.
606 class TemplateString(object):
607 _REPEAT_START_FLAG
= "BEGIN"
608 _REPEAT_END_FLAG
= "END"
610 class Section(object):
611 _LIST_TYPES
= [type([]), type(set()), type((0,))]
613 def __init__(self
, TemplateSection
, PlaceHolderList
):
614 self
._Template
= TemplateSection
615 self
._PlaceHolderList
= []
617 # Split the section into sub-sections according to the position of placeholders
619 self
._SubSectionList
= []
622 # The placeholders passed in must be in the format of
624 # PlaceHolderName, PlaceHolderStartPoint, PlaceHolderEndPoint
626 for PlaceHolder
, Start
, End
in PlaceHolderList
:
627 self
._SubSectionList
.append(TemplateSection
[SubSectionStart
:Start
])
628 self
._SubSectionList
.append(TemplateSection
[Start
:End
])
629 self
._PlaceHolderList
.append(PlaceHolder
)
630 SubSectionStart
= End
631 if SubSectionStart
< len(TemplateSection
):
632 self
._SubSectionList
.append(TemplateSection
[SubSectionStart
:])
634 self
._SubSectionList
= [TemplateSection
]
637 return self
._Template
+ " : " + str(self
._PlaceHolderList
)
639 def Instantiate(self
, PlaceHolderValues
):
641 RepeatPlaceHolders
= {}
642 NonRepeatPlaceHolders
= {}
644 for PlaceHolder
in self
._PlaceHolderList
:
645 if PlaceHolder
not in PlaceHolderValues
:
647 Value
= PlaceHolderValues
[PlaceHolder
]
648 if type(Value
) in self
._LIST
_TYPES
:
650 RepeatTime
= len(Value
)
651 elif RepeatTime
!= len(Value
):
655 "${%s} has different repeat time from others!" % PlaceHolder
,
656 ExtraData
=str(self
._Template
)
658 RepeatPlaceHolders
["${%s}" % PlaceHolder
] = Value
660 NonRepeatPlaceHolders
["${%s}" % PlaceHolder
] = Value
662 if NonRepeatPlaceHolders
:
664 for S
in self
._SubSectionList
:
665 if S
not in NonRepeatPlaceHolders
:
668 StringList
.append(str(NonRepeatPlaceHolders
[S
]))
670 StringList
= self
._SubSectionList
672 if RepeatPlaceHolders
:
674 for Index
in range(RepeatTime
):
676 if S
not in RepeatPlaceHolders
:
677 TempStringList
.append(S
)
679 TempStringList
.append(str(RepeatPlaceHolders
[S
][Index
]))
680 StringList
= TempStringList
682 return "".join(StringList
)
685 def __init__(self
, Template
=None):
687 self
.IsBinary
= False
688 self
._Template
= Template
689 self
._TemplateSectionList
= self
._Parse
(Template
)
693 # @retval string The string replaced
696 return "".join(self
.String
)
698 ## Split the template string into fragments per the ${BEGIN} and ${END} flags
700 # @retval list A list of TemplateString.Section objects
702 def _Parse(self
, Template
):
707 TemplateSectionList
= []
709 MatchObj
= gPlaceholderPattern
.search(Template
, SearchFrom
)
711 if MatchEnd
<= len(Template
):
712 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:], PlaceHolderList
)
713 TemplateSectionList
.append(TemplateSection
)
716 MatchString
= MatchObj
.group(1)
717 MatchStart
= MatchObj
.start()
718 MatchEnd
= MatchObj
.end()
720 if MatchString
== self
._REPEAT
_START
_FLAG
:
721 if MatchStart
> SectionStart
:
722 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:MatchStart
], PlaceHolderList
)
723 TemplateSectionList
.append(TemplateSection
)
724 SectionStart
= MatchEnd
726 elif MatchString
== self
._REPEAT
_END
_FLAG
:
727 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:MatchStart
], PlaceHolderList
)
728 TemplateSectionList
.append(TemplateSection
)
729 SectionStart
= MatchEnd
732 PlaceHolderList
.append((MatchString
, MatchStart
- SectionStart
, MatchEnd
- SectionStart
))
733 SearchFrom
= MatchEnd
734 return TemplateSectionList
736 ## Replace the string template with dictionary of placeholders and append it to previous one
738 # @param AppendString The string template to append
739 # @param Dictionary The placeholder dictionaries
741 def Append(self
, AppendString
, Dictionary
=None):
743 SectionList
= self
._Parse
(AppendString
)
744 self
.String
.append( "".join(S
.Instantiate(Dictionary
) for S
in SectionList
))
746 if isinstance(AppendString
,list):
747 self
.String
.extend(AppendString
)
749 self
.String
.append(AppendString
)
751 ## Replace the string template with dictionary of placeholders
753 # @param Dictionary The placeholder dictionaries
755 # @retval str The string replaced with placeholder values
757 def Replace(self
, Dictionary
=None):
758 return "".join(S
.Instantiate(Dictionary
) for S
in self
._TemplateSectionList
)
760 ## Progress indicator class
762 # This class makes use of thread to print progress on console.
765 # for avoiding deadloop
767 _ProgressThread
= None
768 _CheckInterval
= 0.25
772 # @param OpenMessage The string printed before progress characters
773 # @param CloseMessage The string printed after progress characters
774 # @param ProgressChar The character used to indicate the progress
775 # @param Interval The interval in seconds between two progress characters
777 def __init__(self
, OpenMessage
="", CloseMessage
="", ProgressChar
='.', Interval
=1.0):
778 self
.PromptMessage
= OpenMessage
779 self
.CodaMessage
= CloseMessage
780 self
.ProgressChar
= ProgressChar
781 self
.Interval
= Interval
782 if Progressor
._StopFlag
is None:
783 Progressor
._StopFlag
= threading
.Event()
785 ## Start to print progress character
787 # @param OpenMessage The string printed before progress characters
789 def Start(self
, OpenMessage
=None):
790 if OpenMessage
is not None:
791 self
.PromptMessage
= OpenMessage
792 Progressor
._StopFlag
.clear()
793 if Progressor
._ProgressThread
is None:
794 Progressor
._ProgressThread
= threading
.Thread(target
=self
._ProgressThreadEntry
)
795 Progressor
._ProgressThread
.setDaemon(False)
796 Progressor
._ProgressThread
.start()
798 ## Stop printing progress character
800 # @param CloseMessage The string printed after progress characters
802 def Stop(self
, CloseMessage
=None):
803 OriginalCodaMessage
= self
.CodaMessage
804 if CloseMessage
is not None:
805 self
.CodaMessage
= CloseMessage
807 self
.CodaMessage
= OriginalCodaMessage
809 ## Thread entry method
810 def _ProgressThreadEntry(self
):
811 sys
.stdout
.write(self
.PromptMessage
+ " ")
814 while not Progressor
._StopFlag
.isSet():
816 sys
.stdout
.write(self
.ProgressChar
)
818 TimeUp
= self
.Interval
819 time
.sleep(self
._CheckInterval
)
820 TimeUp
-= self
._CheckInterval
821 sys
.stdout
.write(" " + self
.CodaMessage
+ "\n")
824 ## Abort the progress display
827 if Progressor
._StopFlag
is not None:
828 Progressor
._StopFlag
.set()
829 if Progressor
._ProgressThread
is not None:
830 Progressor
._ProgressThread
.join()
831 Progressor
._ProgressThread
= None
834 ## Dictionary using prioritized list as key
838 _TupleType
= type(())
840 _ValidWildcardList
= ['COMMON', 'DEFAULT', 'ALL', TAB_STAR
, 'PLATFORM']
842 def __init__(self
, _Single_
=False, _Level_
=2):
843 self
._Level
_ = _Level_
845 self
._Single
_ = _Single_
848 def __getitem__(self
, key
):
851 if KeyType
== self
._ListType
or KeyType
== self
._TupleType
:
855 elif self
._Level
_ > 1:
856 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
860 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
862 if FirstKey
is None or str(FirstKey
).upper() in self
._ValidWildcardList
:
863 FirstKey
= self
._Wildcard
866 return self
._GetSingleValue
(FirstKey
, RestKeys
)
868 return self
._GetAllValues
(FirstKey
, RestKeys
)
870 def _GetSingleValue(self
, FirstKey
, RestKeys
):
872 #print "%s-%s" % (FirstKey, self._Level_) ,
874 if FirstKey
== self
._Wildcard
:
875 if FirstKey
in self
.data
:
876 Value
= self
.data
[FirstKey
][RestKeys
]
878 for Key
in self
.data
:
879 Value
= self
.data
[Key
][RestKeys
]
880 if Value
is not None: break
882 if FirstKey
in self
.data
:
883 Value
= self
.data
[FirstKey
][RestKeys
]
884 if Value
is None and self
._Wildcard
in self
.data
:
886 Value
= self
.data
[self
._Wildcard
][RestKeys
]
888 if FirstKey
== self
._Wildcard
:
889 if FirstKey
in self
.data
:
890 Value
= self
.data
[FirstKey
]
892 for Key
in self
.data
:
893 Value
= self
.data
[Key
]
894 if Value
is not None: break
896 if FirstKey
in self
.data
:
897 Value
= self
.data
[FirstKey
]
898 elif self
._Wildcard
in self
.data
:
899 Value
= self
.data
[self
._Wildcard
]
902 def _GetAllValues(self
, FirstKey
, RestKeys
):
905 if FirstKey
== self
._Wildcard
:
906 for Key
in self
.data
:
907 Value
+= self
.data
[Key
][RestKeys
]
909 if FirstKey
in self
.data
:
910 Value
+= self
.data
[FirstKey
][RestKeys
]
911 if self
._Wildcard
in self
.data
:
912 Value
+= self
.data
[self
._Wildcard
][RestKeys
]
914 if FirstKey
== self
._Wildcard
:
915 for Key
in self
.data
:
916 Value
.append(self
.data
[Key
])
918 if FirstKey
in self
.data
:
919 Value
.append(self
.data
[FirstKey
])
920 if self
._Wildcard
in self
.data
:
921 Value
.append(self
.data
[self
._Wildcard
])
925 def __setitem__(self
, key
, value
):
928 if KeyType
== self
._ListType
or KeyType
== self
._TupleType
:
933 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
937 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
939 if FirstKey
in self
._ValidWildcardList
:
940 FirstKey
= self
._Wildcard
942 if FirstKey
not in self
.data
and self
._Level
_ > 0:
943 self
.data
[FirstKey
] = tdict(self
._Single
_, self
._Level
_ - 1)
946 self
.data
[FirstKey
][RestKeys
] = value
948 self
.data
[FirstKey
] = value
950 def SetGreedyMode(self
):
951 self
._Single
_ = False
953 for Key
in self
.data
:
954 self
.data
[Key
].SetGreedyMode()
956 def SetSingleMode(self
):
959 for Key
in self
.data
:
960 self
.data
[Key
].SetSingleMode()
962 def GetKeys(self
, KeyIndex
=0):
965 return set(self
.data
.keys())
968 for Key
in self
.data
:
969 keys |
= self
.data
[Key
].GetKeys(KeyIndex
- 1)
972 def AnalyzePcdExpression(Setting
):
973 RanStr
= ''.join(sample(string
.ascii_letters
+ string
.digits
, 8))
974 Setting
= Setting
.replace('\\\\', RanStr
).strip()
975 # There might be escaped quote in a string: \", \\\" , \', \\\'
977 # There might be '|' in string and in ( ... | ... ), replace it with '-'
979 InSingleQuoteStr
= False
980 InDoubleQuoteStr
= False
982 for Index
, ch
in enumerate(Data
):
983 if ch
== '"' and not InSingleQuoteStr
:
984 if Data
[Index
- 1] != '\\':
985 InDoubleQuoteStr
= not InDoubleQuoteStr
986 elif ch
== "'" and not InDoubleQuoteStr
:
987 if Data
[Index
- 1] != '\\':
988 InSingleQuoteStr
= not InSingleQuoteStr
989 elif ch
== '(' and not (InSingleQuoteStr
or InDoubleQuoteStr
):
991 elif ch
== ')' and not (InSingleQuoteStr
or InDoubleQuoteStr
):
994 if (Pair
> 0 or InSingleQuoteStr
or InDoubleQuoteStr
) and ch
== TAB_VALUE_SPLIT
:
1001 Pos
= NewStr
.find(TAB_VALUE_SPLIT
, StartPos
)
1003 FieldList
.append(Setting
[StartPos
:].strip())
1005 FieldList
.append(Setting
[StartPos
:Pos
].strip())
1007 for i
, ch
in enumerate(FieldList
):
1009 FieldList
[i
] = ch
.replace(RanStr
,'\\\\')
1012 def ParseFieldValue (Value
):
1013 def ParseDevPathValue (Value
):
1015 Value
.replace('\\', '/').replace(' ', '')
1017 Cmd
= 'DevicePath ' + '"' + Value
+ '"'
1019 p
= subprocess
.Popen(Cmd
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, shell
=True)
1020 out
, err
= p
.communicate()
1021 except Exception as X
:
1022 raise BadExpression("DevicePath: %s" % (str(X
)) )
1024 subprocess
._cleanup
()
1028 raise BadExpression("DevicePath: %s" % str(err
))
1029 out
= out
.decode(encoding
='utf-8', errors
='ignore')
1030 Size
= len(out
.split())
1031 out
= ','.join(out
.split())
1032 return '{' + out
+ '}', Size
1034 if "{CODE(" in Value
:
1035 return Value
, len(Value
.split(","))
1036 if isinstance(Value
, type(0)):
1037 return Value
, (Value
.bit_length() + 7) // 8
1038 if not isinstance(Value
, type('')):
1039 raise BadExpression('Type %s is %s' %(Value
, type(Value
)))
1040 Value
= Value
.strip()
1041 if Value
.startswith(TAB_UINT8
) and Value
.endswith(')'):
1042 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1044 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1046 if Value
.startswith(TAB_UINT16
) and Value
.endswith(')'):
1047 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1049 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1051 if Value
.startswith(TAB_UINT32
) and Value
.endswith(')'):
1052 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1054 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1056 if Value
.startswith(TAB_UINT64
) and Value
.endswith(')'):
1057 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1059 raise BadExpression('Value (%s) Size larger than %d' % (Value
, Size
))
1061 if Value
.startswith(TAB_GUID
) and Value
.endswith(')'):
1062 Value
= Value
.split('(', 1)[1][:-1].strip()
1063 if Value
[0] == '{' and Value
[-1] == '}':
1064 TmpValue
= GuidStructureStringToGuidString(Value
)
1066 raise BadExpression("Invalid GUID value string %s" % Value
)
1068 if Value
[0] == '"' and Value
[-1] == '"':
1071 Value
= str(uuid
.UUID(Value
).bytes_le
)
1072 if Value
.startswith("b'"):
1074 Value
= "'" + Value
+ "'"
1075 except ValueError as Message
:
1076 raise BadExpression(Message
)
1077 Value
, Size
= ParseFieldValue(Value
)
1079 if Value
.startswith('L"') and Value
.endswith('"'):
1081 # translate escape character
1091 Value
= (Value
<< 16) |
ord(Char
)
1092 return Value
, (len(List
) + 1) * 2
1093 if Value
.startswith('"') and Value
.endswith('"'):
1095 # translate escape character
1104 Value
= (Value
<< 8) |
ord(Char
)
1105 return Value
, len(List
) + 1
1106 if Value
.startswith("L'") and Value
.endswith("'"):
1107 # Unicode Character Constant
1108 # translate escape character
1116 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1120 Value
= (Value
<< 16) |
ord(Char
)
1121 return Value
, len(List
) * 2
1122 if Value
.startswith("'") and Value
.endswith("'"):
1123 # Character constant
1124 # translate escape character
1131 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1135 Value
= (Value
<< 8) |
ord(Char
)
1136 return Value
, len(List
)
1137 if Value
.startswith('{') and Value
.endswith('}'):
1140 List
= [Item
.strip() for Item
in Value
.split(',')]
1145 ItemValue
, Size
= ParseFieldValue(Item
)
1147 for I
in range(Size
):
1148 Value
= (Value
<< 8) |
((ItemValue
>> 8 * I
) & 0xff)
1149 return Value
, RetSize
1150 if Value
.startswith('DEVICE_PATH(') and Value
.endswith(')'):
1151 Value
= Value
.replace("DEVICE_PATH(", '').rstrip(')')
1152 Value
= Value
.strip().strip('"')
1153 return ParseDevPathValue(Value
)
1154 if Value
.lower().startswith('0x'):
1156 Value
= int(Value
, 16)
1158 raise BadExpression("invalid hex value: %s" % Value
)
1161 return Value
, (Value
.bit_length() + 7) // 8
1162 if Value
[0].isdigit():
1163 Value
= int(Value
, 10)
1166 return Value
, (Value
.bit_length() + 7) // 8
1167 if Value
.lower() == 'true':
1169 if Value
.lower() == 'false':
1175 # Analyze DSC PCD value, since there is no data type info in DSC
1176 # This function is used to match functions (AnalyzePcdData) used for retrieving PCD value from database
1177 # 1. Feature flag: TokenSpace.PcdCName|PcdValue
1178 # 2. Fix and Patch:TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1179 # 3. Dynamic default:
1180 # TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1181 # TokenSpace.PcdCName|PcdValue
1183 # TokenSpace.PcdCName|VpdOffset[|VpdValue]
1184 # TokenSpace.PcdCName|VpdOffset[|MaxSize[|VpdValue]]
1186 # TokenSpace.PcdCName|HiiString|VariableGuid|VariableOffset[|HiiValue]
1187 # PCD value needs to be located in such kind of string, and the PCD value might be an expression in which
1188 # there might have "|" operator, also in string value.
1190 # @param Setting: String contain information described above with "TokenSpace.PcdCName|" stripped
1191 # @param PcdType: PCD type: feature, fixed, dynamic default VPD HII
1192 # @param DataType: The datum type of PCD: VOID*, UNIT, BOOL
1194 # ValueList: A List contain fields described above
1195 # IsValid: True if conforming EBNF, otherwise False
1196 # Index: The index where PcdValue is in ValueList
1198 def AnalyzeDscPcd(Setting
, PcdType
, DataType
=''):
1199 FieldList
= AnalyzePcdExpression(Setting
)
1202 if PcdType
in (MODEL_PCD_FIXED_AT_BUILD
, MODEL_PCD_PATCHABLE_IN_MODULE
, MODEL_PCD_DYNAMIC_DEFAULT
, MODEL_PCD_DYNAMIC_EX_DEFAULT
):
1203 Value
= FieldList
[0]
1205 if len(FieldList
) > 1 and FieldList
[1]:
1206 DataType
= FieldList
[1]
1207 if FieldList
[1] != TAB_VOID
and StructPattern
.match(FieldList
[1]) is None:
1209 if len(FieldList
) > 2:
1213 IsValid
= (len(FieldList
) <= 1)
1215 IsValid
= (len(FieldList
) <= 3)
1219 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1223 return [str(Value
), DataType
, str(Size
)], IsValid
, 0
1224 elif PcdType
== MODEL_PCD_FEATURE_FLAG
:
1225 Value
= FieldList
[0]
1227 IsValid
= (len(FieldList
) <= 1)
1228 return [Value
, DataType
, str(Size
)], IsValid
, 0
1229 elif PcdType
in (MODEL_PCD_DYNAMIC_VPD
, MODEL_PCD_DYNAMIC_EX_VPD
):
1230 VpdOffset
= FieldList
[0]
1232 if not DataType
== TAB_VOID
:
1233 if len(FieldList
) > 1:
1234 Value
= FieldList
[1]
1236 if len(FieldList
) > 1:
1238 if len(FieldList
) > 2:
1239 Value
= FieldList
[2]
1241 IsValid
= (len(FieldList
) <= 1)
1243 IsValid
= (len(FieldList
) <= 3)
1246 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1250 return [VpdOffset
, str(Size
), Value
], IsValid
, 2
1251 elif PcdType
in (MODEL_PCD_DYNAMIC_HII
, MODEL_PCD_DYNAMIC_EX_HII
):
1252 IsValid
= (3 <= len(FieldList
) <= 5)
1253 HiiString
= FieldList
[0]
1254 Guid
= Offset
= Value
= Attribute
= ''
1255 if len(FieldList
) > 1:
1257 if len(FieldList
) > 2:
1258 Offset
= FieldList
[2]
1259 if len(FieldList
) > 3:
1260 Value
= FieldList
[3]
1261 if len(FieldList
) > 4:
1262 Attribute
= FieldList
[4]
1263 return [HiiString
, Guid
, Offset
, Value
, Attribute
], IsValid
, 3
1268 # Analyze the pcd Value, Datum type and TokenNumber.
1269 # Used to avoid split issue while the value string contain "|" character
1271 # @param[in] Setting: A String contain value/datum type/token number information;
1273 # @retval ValueList: A List contain value, datum type and toke number.
1275 def AnalyzePcdData(Setting
):
1276 ValueList
= ['', '', '']
1278 ValueRe
= re
.compile(r
'^\s*L?\".*\|.*\"')
1279 PtrValue
= ValueRe
.findall(Setting
)
1281 ValueUpdateFlag
= False
1283 if len(PtrValue
) >= 1:
1284 Setting
= re
.sub(ValueRe
, '', Setting
)
1285 ValueUpdateFlag
= True
1287 TokenList
= Setting
.split(TAB_VALUE_SPLIT
)
1288 ValueList
[0:len(TokenList
)] = TokenList
1291 ValueList
[0] = PtrValue
[0]
1295 ## check format of PCD value against its the datum type
1297 # For PCD value setting
1299 def CheckPcdDatum(Type
, Value
):
1300 if Type
== TAB_VOID
:
1301 ValueRe
= re
.compile(r
'\s*L?\".*\"\s*$')
1302 if not (((Value
.startswith('L"') or Value
.startswith('"')) and Value
.endswith('"'))
1303 or (Value
.startswith('{') and Value
.endswith('}')) or (Value
.startswith("L'") or Value
.startswith("'") and Value
.endswith("'"))
1305 return False, "Invalid value [%s] of type [%s]; must be in the form of {...} for array"\
1306 ", \"...\" or \'...\' for string, L\"...\" or L\'...\' for unicode string" % (Value
, Type
)
1307 elif ValueRe
.match(Value
):
1308 # Check the chars in UnicodeString or CString is printable
1309 if Value
.startswith("L"):
1313 Printset
= set(string
.printable
)
1314 Printset
.remove(TAB_PRINTCHAR_VT
)
1315 Printset
.add(TAB_PRINTCHAR_BS
)
1316 Printset
.add(TAB_PRINTCHAR_NUL
)
1317 if not set(Value
).issubset(Printset
):
1318 PrintList
= sorted(Printset
)
1319 return False, "Invalid PCD string value of type [%s]; must be printable chars %s." % (Type
, PrintList
)
1320 elif Type
== 'BOOLEAN':
1321 if Value
not in ['TRUE', 'True', 'true', '0x1', '0x01', '1', 'FALSE', 'False', 'false', '0x0', '0x00', '0']:
1322 return False, "Invalid value [%s] of type [%s]; must be one of TRUE, True, true, 0x1, 0x01, 1"\
1323 ", FALSE, False, false, 0x0, 0x00, 0" % (Value
, Type
)
1324 elif Type
in [TAB_UINT8
, TAB_UINT16
, TAB_UINT32
, TAB_UINT64
]:
1325 if Value
.startswith('0') and not Value
.lower().startswith('0x') and len(Value
) > 1 and Value
.lstrip('0'):
1326 Value
= Value
.lstrip('0')
1328 if Value
and int(Value
, 0) < 0:
1329 return False, "PCD can't be set to negative value[%s] for datum type [%s]" % (Value
, Type
)
1330 Value
= int(Value
, 0)
1331 if Value
> MAX_VAL_TYPE
[Type
]:
1332 return False, "Too large PCD value[%s] for datum type [%s]" % (Value
, Type
)
1334 return False, "Invalid value [%s] of type [%s];"\
1335 " must be a hexadecimal, decimal or octal in C language format." % (Value
, Type
)
1337 return True, "StructurePcd"
1341 def CommonPath(PathList
):
1342 P1
= min(PathList
).split(os
.path
.sep
)
1343 P2
= max(PathList
).split(os
.path
.sep
)
1344 for Index
in range(min(len(P1
), len(P2
))):
1345 if P1
[Index
] != P2
[Index
]:
1346 return os
.path
.sep
.join(P1
[:Index
])
1347 return os
.path
.sep
.join(P1
)
1349 class PathClass(object):
1350 def __init__(self
, File
='', Root
='', AlterRoot
='', Type
='', IsBinary
=False,
1351 Arch
='COMMON', ToolChainFamily
='', Target
='', TagName
='', ToolCode
=''):
1353 self
.File
= str(File
)
1354 if os
.path
.isabs(self
.File
):
1358 self
.Root
= str(Root
)
1359 self
.AlterRoot
= str(AlterRoot
)
1361 # Remove any '.' and '..' in path
1363 self
.Root
= mws
.getWs(self
.Root
, self
.File
)
1364 self
.Path
= os
.path
.normpath(os
.path
.join(self
.Root
, self
.File
))
1365 self
.Root
= os
.path
.normpath(CommonPath([self
.Root
, self
.Path
]))
1366 # eliminate the side-effect of 'C:'
1367 if self
.Root
[-1] == ':':
1368 self
.Root
+= os
.path
.sep
1369 # file path should not start with path separator
1370 if self
.Root
[-1] == os
.path
.sep
:
1371 self
.File
= self
.Path
[len(self
.Root
):]
1373 self
.File
= self
.Path
[len(self
.Root
) + 1:]
1375 self
.Path
= os
.path
.normpath(self
.File
)
1377 self
.SubDir
, self
.Name
= os
.path
.split(self
.File
)
1378 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1382 self
.Dir
= os
.path
.join(self
.Root
, self
.SubDir
)
1384 self
.Dir
= self
.Root
1386 self
.Dir
= self
.SubDir
1391 self
.Type
= self
.Ext
.lower()
1393 self
.IsBinary
= IsBinary
1394 self
.Target
= Target
1395 self
.TagName
= TagName
1396 self
.ToolCode
= ToolCode
1397 self
.ToolChainFamily
= ToolChainFamily
1399 ## Convert the object of this class to a string
1401 # Convert member Path of the class to a string
1403 # @retval string Formatted String
1408 ## Override __eq__ function
1410 # Check whether PathClass are the same
1412 # @retval False The two PathClass are different
1413 # @retval True The two PathClass are the same
1415 def __eq__(self
, Other
):
1416 return self
.Path
== str(Other
)
1418 ## Override __cmp__ function
1420 # Customize the comparison operation of two PathClass
1422 # @retval 0 The two PathClass are different
1423 # @retval -1 The first PathClass is less than the second PathClass
1424 # @retval 1 The first PathClass is Bigger than the second PathClass
1425 def __cmp__(self
, Other
):
1426 OtherKey
= str(Other
)
1429 if SelfKey
== OtherKey
:
1431 elif SelfKey
> OtherKey
:
1436 ## Override __hash__ function
1438 # Use Path as key in hash table
1440 # @retval string Key for hash table
1443 return hash(self
.Path
)
1447 return self
.Path
.upper()
1450 def TimeStamp(self
):
1451 return os
.stat(self
.Path
)[8]
1453 def Validate(self
, Type
='', CaseSensitive
=True):
1454 def RealPath2(File
, Dir
='', OverrideDir
=''):
1457 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(OverrideDir
, File
))]
1459 if OverrideDir
[-1] == os
.path
.sep
:
1460 return NewFile
[len(OverrideDir
):], NewFile
[0:len(OverrideDir
)]
1462 return NewFile
[len(OverrideDir
) + 1:], NewFile
[0:len(OverrideDir
)]
1463 if GlobalData
.gAllFiles
:
1464 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(Dir
, File
))]
1466 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
1467 if not os
.path
.exists(NewFile
):
1471 if Dir
[-1] == os
.path
.sep
:
1472 return NewFile
[len(Dir
):], NewFile
[0:len(Dir
)]
1474 return NewFile
[len(Dir
) + 1:], NewFile
[0:len(Dir
)]
1480 if GlobalData
.gCaseInsensitive
:
1481 CaseSensitive
= False
1482 if Type
and Type
.lower() != self
.Type
:
1483 return FILE_TYPE_MISMATCH
, '%s (expect %s but got %s)' % (self
.File
, Type
, self
.Type
)
1485 RealFile
, RealRoot
= RealPath2(self
.File
, self
.Root
, self
.AlterRoot
)
1486 if not RealRoot
and not RealFile
:
1487 RealFile
= self
.File
1489 RealFile
= os
.path
.join(self
.AlterRoot
, self
.File
)
1491 RealFile
= os
.path
.join(self
.Root
, self
.File
)
1492 if len (mws
.getPkgPath()) == 0:
1493 return FILE_NOT_FOUND
, os
.path
.join(self
.AlterRoot
, RealFile
)
1495 return FILE_NOT_FOUND
, "%s is not found in packages path:\n\t%s" % (self
.File
, '\n\t'.join(mws
.getPkgPath()))
1499 if RealRoot
!= self
.Root
or RealFile
!= self
.File
:
1500 if CaseSensitive
and (RealFile
!= self
.File
or (RealRoot
!= self
.Root
and RealRoot
!= self
.AlterRoot
)):
1501 ErrorCode
= FILE_CASE_MISMATCH
1502 ErrorInfo
= self
.File
+ '\n\t' + RealFile
+ " [in file system]"
1504 self
.SubDir
, self
.Name
= os
.path
.split(RealFile
)
1505 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1507 self
.Dir
= os
.path
.join(RealRoot
, self
.SubDir
)
1510 self
.File
= RealFile
1511 self
.Root
= RealRoot
1512 self
.Path
= os
.path
.join(RealRoot
, RealFile
)
1513 return ErrorCode
, ErrorInfo
1515 ## Parse PE image to get the required PE information.
1517 class PeImageClass():
1520 # @param File FilePath of PeImage
1522 def __init__(self
, PeFile
):
1523 self
.FileName
= PeFile
1524 self
.IsValid
= False
1527 self
.SectionAlignment
= 0
1528 self
.SectionHeaderList
= []
1531 PeObject
= open(PeFile
, 'rb')
1533 self
.ErrorInfo
= self
.FileName
+ ' can not be found\n'
1536 ByteArray
= array
.array('B')
1537 ByteArray
.fromfile(PeObject
, 0x3E)
1538 ByteList
= ByteArray
.tolist()
1539 # DOS signature should be 'MZ'
1540 if self
._ByteListToStr
(ByteList
[0x0:0x2]) != 'MZ':
1541 self
.ErrorInfo
= self
.FileName
+ ' has no valid DOS signature MZ'
1544 # Read 4 byte PE Signature
1545 PeOffset
= self
._ByteListToInt
(ByteList
[0x3C:0x3E])
1546 PeObject
.seek(PeOffset
)
1547 ByteArray
= array
.array('B')
1548 ByteArray
.fromfile(PeObject
, 4)
1549 # PE signature should be 'PE\0\0'
1550 if ByteArray
.tostring() != b
'PE\0\0':
1551 self
.ErrorInfo
= self
.FileName
+ ' has no valid PE signature PE00'
1554 # Read PE file header
1555 ByteArray
= array
.array('B')
1556 ByteArray
.fromfile(PeObject
, 0x14)
1557 ByteList
= ByteArray
.tolist()
1558 SecNumber
= self
._ByteListToInt
(ByteList
[0x2:0x4])
1560 self
.ErrorInfo
= self
.FileName
+ ' has no section header'
1563 # Read PE optional header
1564 OptionalHeaderSize
= self
._ByteListToInt
(ByteArray
[0x10:0x12])
1565 ByteArray
= array
.array('B')
1566 ByteArray
.fromfile(PeObject
, OptionalHeaderSize
)
1567 ByteList
= ByteArray
.tolist()
1568 self
.EntryPoint
= self
._ByteListToInt
(ByteList
[0x10:0x14])
1569 self
.SectionAlignment
= self
._ByteListToInt
(ByteList
[0x20:0x24])
1570 self
.Size
= self
._ByteListToInt
(ByteList
[0x38:0x3C])
1572 # Read each Section Header
1573 for Index
in range(SecNumber
):
1574 ByteArray
= array
.array('B')
1575 ByteArray
.fromfile(PeObject
, 0x28)
1576 ByteList
= ByteArray
.tolist()
1577 SecName
= self
._ByteListToStr
(ByteList
[0:8])
1578 SecVirtualSize
= self
._ByteListToInt
(ByteList
[8:12])
1579 SecRawAddress
= self
._ByteListToInt
(ByteList
[20:24])
1580 SecVirtualAddress
= self
._ByteListToInt
(ByteList
[12:16])
1581 self
.SectionHeaderList
.append((SecName
, SecVirtualAddress
, SecRawAddress
, SecVirtualSize
))
1585 def _ByteListToStr(self
, ByteList
):
1587 for index
in range(len(ByteList
)):
1588 if ByteList
[index
] == 0:
1590 String
+= chr(ByteList
[index
])
1593 def _ByteListToInt(self
, ByteList
):
1595 for index
in range(len(ByteList
) - 1, -1, -1):
1596 Value
= (Value
<< 8) |
int(ByteList
[index
])
1599 class DefaultStore():
1600 def __init__(self
, DefaultStores
):
1602 self
.DefaultStores
= DefaultStores
1603 def DefaultStoreID(self
, DefaultStoreName
):
1604 for key
, value
in self
.DefaultStores
.items():
1605 if value
== DefaultStoreName
:
1608 def GetDefaultDefault(self
):
1609 if not self
.DefaultStores
or "0" in self
.DefaultStores
:
1610 return "0", TAB_DEFAULT_STORES_DEFAULT
1612 minvalue
= min(int(value_str
) for value_str
in self
.DefaultStores
)
1613 return (str(minvalue
), self
.DefaultStores
[str(minvalue
)])
1614 def GetMin(self
, DefaultSIdList
):
1615 if not DefaultSIdList
:
1616 return TAB_DEFAULT_STORES_DEFAULT
1617 storeidset
= {storeid
for storeid
, storename
in self
.DefaultStores
.values() if storename
in DefaultSIdList
}
1620 minid
= min(storeidset
)
1621 for sid
, name
in self
.DefaultStores
.values():
1630 def __init__(self
,SkuIdentifier
='', SkuIds
=None):
1634 for SkuName
in SkuIds
:
1635 SkuId
= SkuIds
[SkuName
][0]
1636 skuid_num
= int(SkuId
, 16) if SkuId
.upper().startswith("0X") else int(SkuId
)
1637 if skuid_num
> 0xFFFFFFFFFFFFFFFF:
1638 EdkLogger
.error("build", PARAMETER_INVALID
,
1639 ExtraData
= "SKU-ID [%s] value %s exceeds the max value of UINT64"
1642 self
.AvailableSkuIds
= OrderedDict()
1644 self
.SkuIdNumberSet
= []
1645 self
.SkuData
= SkuIds
1646 self
._SkuInherit
= {}
1647 self
._SkuIdentifier
= SkuIdentifier
1648 if SkuIdentifier
== '' or SkuIdentifier
is None:
1649 self
.SkuIdSet
= ['DEFAULT']
1650 self
.SkuIdNumberSet
= ['0U']
1651 elif SkuIdentifier
== 'ALL':
1652 self
.SkuIdSet
= list(SkuIds
.keys())
1653 self
.SkuIdNumberSet
= [num
[0].strip() + 'U' for num
in SkuIds
.values()]
1655 r
= SkuIdentifier
.split('|')
1656 self
.SkuIdSet
=[(r
[k
].strip()).upper() for k
in range(len(r
))]
1659 self
.SkuIdNumberSet
= [SkuIds
[k
][0].strip() + 'U' for k
in self
.SkuIdSet
]
1661 EdkLogger
.error("build", PARAMETER_INVALID
,
1662 ExtraData
= "SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1663 % (k
, " | ".join(SkuIds
.keys())))
1664 for each
in self
.SkuIdSet
:
1666 self
.AvailableSkuIds
[each
] = SkuIds
[each
][0]
1668 EdkLogger
.error("build", PARAMETER_INVALID
,
1669 ExtraData
="SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1670 % (each
, " | ".join(SkuIds
.keys())))
1671 if self
.SkuUsageType
!= SkuClass
.SINGLE
:
1672 self
.AvailableSkuIds
.update({'DEFAULT':0, 'COMMON':0})
1674 GlobalData
.gSkuids
= (self
.SkuIdSet
)
1675 if 'COMMON' in GlobalData
.gSkuids
:
1676 GlobalData
.gSkuids
.remove('COMMON')
1677 if self
.SkuUsageType
== self
.SINGLE
:
1678 if len(GlobalData
.gSkuids
) != 1:
1679 if 'DEFAULT' in GlobalData
.gSkuids
:
1680 GlobalData
.gSkuids
.remove('DEFAULT')
1681 if GlobalData
.gSkuids
:
1682 GlobalData
.gSkuids
.sort()
1684 def GetNextSkuId(self
, skuname
):
1685 if not self
._SkuInherit
:
1686 self
._SkuInherit
= {}
1687 for item
in self
.SkuData
.values():
1688 self
._SkuInherit
[item
[1]]=item
[2] if item
[2] else "DEFAULT"
1689 return self
._SkuInherit
.get(skuname
, "DEFAULT")
1691 def GetSkuChain(self
, sku
):
1692 if sku
== "DEFAULT":
1697 nextsku
= self
.GetNextSkuId(nextsku
)
1698 skulist
.append(nextsku
)
1699 if nextsku
== "DEFAULT":
1703 def SkuOverrideOrder(self
):
1705 for skuname
in self
.SkuIdSet
:
1706 skuorderset
.append(self
.GetSkuChain(skuname
))
1709 for index
in range(max(len(item
) for item
in skuorderset
)):
1710 for subset
in skuorderset
:
1711 if index
> len(subset
)-1:
1713 if subset
[index
] in skuorder
:
1715 skuorder
.append(subset
[index
])
1720 def SkuUsageType(self
):
1721 if self
._SkuIdentifier
.upper() == "ALL":
1722 return SkuClass
.MULTIPLE
1724 if len(self
.SkuIdSet
) == 1:
1725 if self
.SkuIdSet
[0] == 'DEFAULT':
1726 return SkuClass
.DEFAULT
1727 return SkuClass
.SINGLE
1728 if len(self
.SkuIdSet
) == 2 and 'DEFAULT' in self
.SkuIdSet
:
1729 return SkuClass
.SINGLE
1730 return SkuClass
.MULTIPLE
1732 def DumpSkuIdArrary(self
):
1733 if self
.SkuUsageType
== SkuClass
.SINGLE
:
1736 for skuname
in self
.AvailableSkuIds
:
1737 if skuname
== "COMMON":
1739 while skuname
!= "DEFAULT":
1740 ArrayStrList
.append(hex(int(self
.AvailableSkuIds
[skuname
])))
1741 skuname
= self
.GetNextSkuId(skuname
)
1742 ArrayStrList
.append("0x0")
1743 return "{{{myList}}}".format(myList
=",".join(ArrayStrList
))
1746 def AvailableSkuIdSet(self
):
1747 return self
.AvailableSkuIds
1750 def SystemSkuId(self
):
1751 if self
.SkuUsageType
== SkuClass
.SINGLE
:
1752 if len(self
.SkuIdSet
) == 1:
1753 return self
.SkuIdSet
[0]
1755 return self
.SkuIdSet
[0] if self
.SkuIdSet
[0] != 'DEFAULT' else self
.SkuIdSet
[1]
1759 ## Get the integer value from string like "14U" or integer like 2
1761 # @param Input The object that may be either a integer value or a string
1763 # @retval Value The integer value that the input represents
1765 def GetIntegerValue(Input
):
1766 if not isinstance(Input
, str):
1769 if String
.endswith("U"):
1770 String
= String
[:-1]
1771 if String
.endswith("ULL"):
1772 String
= String
[:-3]
1773 if String
.endswith("LL"):
1774 String
= String
[:-2]
1776 if String
.startswith("0x") or String
.startswith("0X"):
1777 return int(String
, 16)
1784 # Pack a GUID (registry format) list into a buffer and return it
1787 return pack(PACK_PATTERN_GUID
,
1791 int(Guid
[3][-4:-2], 16),
1792 int(Guid
[3][-2:], 16),
1793 int(Guid
[4][-12:-10], 16),
1794 int(Guid
[4][-10:-8], 16),
1795 int(Guid
[4][-8:-6], 16),
1796 int(Guid
[4][-6:-4], 16),
1797 int(Guid
[4][-4:-2], 16),
1798 int(Guid
[4][-2:], 16)
1802 # Pack a GUID (byte) list into a buffer and return it
1804 def PackByteFormatGUID(Guid
):
1805 return pack(PACK_PATTERN_GUID
,
1819 ## DeepCopy dict/OrderedDict recusively
1821 # @param ori_dict a nested dict or ordereddict
1823 # @retval new dict or orderdict
1825 def CopyDict(ori_dict
):
1826 dict_type
= ori_dict
.__class
__
1827 if dict_type
not in (dict,OrderedDict
):
1829 new_dict
= dict_type()
1830 for key
in ori_dict
:
1831 if isinstance(ori_dict
[key
],(dict,OrderedDict
)):
1832 new_dict
[key
] = CopyDict(ori_dict
[key
])
1834 new_dict
[key
] = ori_dict
[key
]
1838 # Remove the c/c++ comments: // and /* */
1840 def RemoveCComments(ctext
):
1841 return re
.sub('//.*?\n|/\*.*?\*/', '\n', ctext
, flags
=re
.S
)