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 ArrayIndex
= re
.compile("\[\s*[0-9a-fA-FxX]*\s*\]")
41 ## Regular expression used to find out place holders in string template
42 gPlaceholderPattern
= re
.compile("\$\{([^$()\s]+)\}", re
.MULTILINE | re
.UNICODE
)
44 ## regular expressions for map file processing
45 startPatternGeneral
= re
.compile("^Start[' ']+Length[' ']+Name[' ']+Class")
46 addressPatternGeneral
= re
.compile("^Address[' ']+Publics by Value[' ']+Rva\+Base")
47 valuePatternGcc
= re
.compile('^([\w_\.]+) +([\da-fA-Fx]+) +([\da-fA-Fx]+)$')
48 pcdPatternGcc
= re
.compile('^([\da-fA-Fx]+) +([\da-fA-Fx]+)')
49 secReGeneral
= re
.compile('^([\da-fA-F]+):([\da-fA-F]+) +([\da-fA-F]+)[Hh]? +([.\w\$]+) +(\w+)', re
.UNICODE
)
51 StructPattern
= re
.compile(r
'[_a-zA-Z][0-9A-Za-z_]*$')
53 ## Dictionary used to store dependencies of files
54 gDependencyDatabase
= {} # arch : {file path : [dependent files list]}
57 # If a module is built more than once with different PCDs or library classes
58 # a temporary INF file with same content is created, the temporary file is removed
63 def GetVariableOffset(mapfilepath
, efifilepath
, varnames
):
64 """ Parse map file to get variable offset in current EFI file
65 @param mapfilepath Map file absolution path
66 @param efifilepath: EFI binary file full path
67 @param varnames iteratable container whose elements are variable names to be searched
69 @return List whos elements are tuple with variable name and raw offset
73 f
= open(mapfilepath
, 'r')
79 if len(lines
) == 0: return None
80 firstline
= lines
[0].strip()
81 if (firstline
.startswith("Archive member included ") and
82 firstline
.endswith(" file (symbol)")):
83 return _parseForGCC(lines
, efifilepath
, varnames
)
84 if firstline
.startswith("# Path:"):
85 return _parseForXcode(lines
, efifilepath
, varnames
)
86 return _parseGeneral(lines
, efifilepath
, varnames
)
88 def _parseForXcode(lines
, efifilepath
, varnames
):
93 if status
== 0 and line
== "# Symbols:":
96 if status
== 1 and len(line
) != 0:
97 for varname
in varnames
:
99 # cannot pregenerate this RegEx since it uses varname from varnames.
100 m
= re
.match('^([\da-fA-FxX]+)([\s\S]*)([_]*%s)$' % varname
, line
)
102 ret
.append((varname
, m
.group(1)))
105 def _parseForGCC(lines
, efifilepath
, varnames
):
106 """ Parse map file generated by GCC linker """
110 for index
, line
in enumerate(lines
):
112 # status machine transection
113 if status
== 0 and line
== "Memory Configuration":
116 elif status
== 1 and line
== 'Linker script and memory map':
119 elif status
==2 and line
== 'START GROUP':
125 m
= valuePatternGcc
.match(line
)
127 sections
.append(m
.groups(0))
128 for varname
in varnames
:
130 m
= re
.match("^.data.(%s)" % varname
, line
)
132 m
= re
.match(".data.(%s)$" % varname
, line
)
134 Str
= lines
[index
+ 1]
136 Str
= line
[len(".data.%s" % varname
):]
138 m
= pcdPatternGcc
.match(Str
.strip())
140 varoffset
.append((varname
, int(m
.groups(0)[0], 16), int(sections
[-1][1], 16), sections
[-1][0]))
144 # get section information from efi file
145 efisecs
= PeImageClass(efifilepath
).SectionHeaderList
146 if efisecs
is None or len(efisecs
) == 0:
150 for efisec
in efisecs
:
151 for section
in sections
:
152 if section
[0].strip() == efisec
[0].strip() and section
[0].strip() == '.text':
153 redirection
= int(section
[1], 16) - efisec
[1]
156 for var
in varoffset
:
157 for efisec
in efisecs
:
158 if var
[1] >= efisec
[1] and var
[1] < efisec
[1]+efisec
[3]:
159 ret
.append((var
[0], hex(efisec
[2] + var
[1] - efisec
[1] - redirection
)))
162 def _parseGeneral(lines
, efifilepath
, varnames
):
163 status
= 0 #0 - beginning of file; 1 - PE section definition; 2 - symbol table
164 secs
= [] # key = section name
166 symRe
= re
.compile('^([\da-fA-F]+):([\da-fA-F]+) +([\.:\\\\\w\?@\$]+) +([\da-fA-F]+)', re
.UNICODE
)
170 if startPatternGeneral
.match(line
):
173 if addressPatternGeneral
.match(line
):
176 if line
.startswith("entry point at"):
179 if status
== 1 and len(line
) != 0:
180 m
= secReGeneral
.match(line
)
181 assert m
is not None, "Fail to parse the section in map file , line is %s" % line
182 sec_no
, sec_start
, sec_length
, sec_name
, sec_class
= m
.groups(0)
183 secs
.append([int(sec_no
, 16), int(sec_start
, 16), int(sec_length
, 16), sec_name
, sec_class
])
184 if status
== 2 and len(line
) != 0:
185 for varname
in varnames
:
186 m
= symRe
.match(line
)
187 assert m
is not None, "Fail to parse the symbol in map file, line is %s" % line
188 sec_no
, sym_offset
, sym_name
, vir_addr
= m
.groups(0)
189 sec_no
= int(sec_no
, 16)
190 sym_offset
= int(sym_offset
, 16)
191 vir_addr
= int(vir_addr
, 16)
192 # cannot pregenerate this RegEx since it uses varname from varnames.
193 m2
= re
.match('^[_]*(%s)' % varname
, sym_name
)
195 # fond a binary pcd entry in map file
197 if sec
[0] == sec_no
and (sym_offset
>= sec
[1] and sym_offset
< sec
[1] + sec
[2]):
198 varoffset
.append([varname
, sec
[3], sym_offset
, vir_addr
, sec_no
])
200 if not varoffset
: return []
202 # get section information from efi file
203 efisecs
= PeImageClass(efifilepath
).SectionHeaderList
204 if efisecs
is None or len(efisecs
) == 0:
208 for var
in varoffset
:
210 for efisec
in efisecs
:
212 if var
[1].strip() == efisec
[0].strip():
213 ret
.append((var
[0], hex(efisec
[2] + var
[2])))
214 elif var
[4] == index
:
215 ret
.append((var
[0], hex(efisec
[2] + var
[2])))
219 ## Routine to process duplicated INF
221 # This function is called by following two cases:
224 # Pkg/module/module.inf
225 # Pkg/module/module.inf {
227 # FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836
230 # INF Pkg/module/module.inf
231 # INF FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836 Pkg/module/module.inf
233 # This function copies Pkg/module/module.inf to
234 # Conf/.cache/0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf
236 # @param Path Original PathClass object
237 # @param BaseName New file base name
239 # @retval return the new PathClass object
241 def ProcessDuplicatedInf(Path
, BaseName
, Workspace
):
242 Filename
= os
.path
.split(Path
.File
)[1]
244 Filename
= BaseName
+ Path
.BaseName
+ Filename
[Filename
.rfind('.'):]
246 Filename
= BaseName
+ Path
.BaseName
249 # If -N is specified on command line, cache is disabled
250 # The directory has to be created
252 DbDir
= os
.path
.split(GlobalData
.gDatabasePath
)[0]
253 if not os
.path
.exists(DbDir
):
256 # A temporary INF is copied to database path which must have write permission
257 # The temporary will be removed at the end of build
258 # In case of name conflict, the file name is
259 # FILE_GUIDBaseName (0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf)
261 TempFullPath
= os
.path
.join(DbDir
,
263 RtPath
= PathClass(Path
.File
, Workspace
)
265 # Modify the full path to temporary path, keep other unchanged
267 # To build same module more than once, the module path with FILE_GUID overridden has
268 # the file name FILE_GUIDmodule.inf, but the relative path (self.MetaFile.File) is the real path
269 # in DSC which is used as relative path by C files and other files in INF.
270 # A trick was used: all module paths are PathClass instances, after the initialization
271 # of PathClass, the PathClass.Path is overridden by the temporary INF path.
273 # The reason for creating a temporary INF is:
274 # Platform.Modules which is the base to create ModuleAutoGen objects is a dictionary,
275 # the key is the full path of INF, the value is an object to save overridden library instances, PCDs.
276 # A different key for the same module is needed to create different output directory,
277 # retrieve overridden PCDs, library instances.
279 # The BaseName is the FILE_GUID which is also the output directory name.
282 RtPath
.Path
= TempFullPath
283 RtPath
.BaseName
= BaseName
284 RtPath
.OriginalPath
= Path
286 # If file exists, compare contents
288 if os
.path
.exists(TempFullPath
):
289 with
open(str(Path
), 'rb') as f1
, open(TempFullPath
, 'rb') as f2
:
290 if f1
.read() == f2
.read():
292 _TempInfs
.append(TempFullPath
)
293 shutil
.copy2(str(Path
), TempFullPath
)
296 ## Remove temporary created INFs whose paths were saved in _TempInfs
298 def ClearDuplicatedInf():
300 File
= _TempInfs
.pop()
301 if os
.path
.exists(File
):
304 ## Convert GUID string in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx style to C structure style
306 # @param Guid The GUID string
308 # @retval string The GUID string in C structure style
310 def GuidStringToGuidStructureString(Guid
):
311 GuidList
= Guid
.split('-')
313 for Index
in range(0, 3, 1):
314 Result
= Result
+ '0x' + GuidList
[Index
] + ', '
315 Result
= Result
+ '{0x' + GuidList
[3][0:2] + ', 0x' + GuidList
[3][2:4]
316 for Index
in range(0, 12, 2):
317 Result
= Result
+ ', 0x' + GuidList
[4][Index
:Index
+ 2]
321 ## Convert GUID structure in byte array to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
323 # @param GuidValue The GUID value in byte array
325 # @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
327 def GuidStructureByteArrayToGuidString(GuidValue
):
328 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
329 guidValueList
= guidValueString
.split(",")
330 if len(guidValueList
) != 16:
332 #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
334 return "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
335 int(guidValueList
[3], 16),
336 int(guidValueList
[2], 16),
337 int(guidValueList
[1], 16),
338 int(guidValueList
[0], 16),
339 int(guidValueList
[5], 16),
340 int(guidValueList
[4], 16),
341 int(guidValueList
[7], 16),
342 int(guidValueList
[6], 16),
343 int(guidValueList
[8], 16),
344 int(guidValueList
[9], 16),
345 int(guidValueList
[10], 16),
346 int(guidValueList
[11], 16),
347 int(guidValueList
[12], 16),
348 int(guidValueList
[13], 16),
349 int(guidValueList
[14], 16),
350 int(guidValueList
[15], 16)
355 ## Convert GUID string in C structure style to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
357 # @param GuidValue The GUID value in C structure format
359 # @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
361 def GuidStructureStringToGuidString(GuidValue
):
362 if not GlobalData
.gGuidCFormatPattern
.match(GuidValue
):
364 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
365 guidValueList
= guidValueString
.split(",")
366 if len(guidValueList
) != 11:
368 #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
370 return "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
371 int(guidValueList
[0], 16),
372 int(guidValueList
[1], 16),
373 int(guidValueList
[2], 16),
374 int(guidValueList
[3], 16),
375 int(guidValueList
[4], 16),
376 int(guidValueList
[5], 16),
377 int(guidValueList
[6], 16),
378 int(guidValueList
[7], 16),
379 int(guidValueList
[8], 16),
380 int(guidValueList
[9], 16),
381 int(guidValueList
[10], 16)
386 ## Convert GUID string in C structure style to xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx
388 # @param GuidValue The GUID value in C structure format
390 # @retval string The GUID value in xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx format
392 def GuidStructureStringToGuidValueName(GuidValue
):
393 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "")
394 guidValueList
= guidValueString
.split(",")
395 if len(guidValueList
) != 11:
396 EdkLogger
.error(None, FORMAT_INVALID
, "Invalid GUID value string [%s]" % GuidValue
)
397 return "%08x_%04x_%04x_%02x%02x_%02x%02x%02x%02x%02x%02x" % (
398 int(guidValueList
[0], 16),
399 int(guidValueList
[1], 16),
400 int(guidValueList
[2], 16),
401 int(guidValueList
[3], 16),
402 int(guidValueList
[4], 16),
403 int(guidValueList
[5], 16),
404 int(guidValueList
[6], 16),
405 int(guidValueList
[7], 16),
406 int(guidValueList
[8], 16),
407 int(guidValueList
[9], 16),
408 int(guidValueList
[10], 16)
411 ## Create directories
413 # @param Directory The directory name
415 def CreateDirectory(Directory
):
416 if Directory
is None or Directory
.strip() == "":
419 if not os
.access(Directory
, os
.F_OK
):
420 os
.makedirs(Directory
)
425 ## Remove directories, including files and sub-directories in it
427 # @param Directory The directory name
429 def RemoveDirectory(Directory
, Recursively
=False):
430 if Directory
is None or Directory
.strip() == "" or not os
.path
.exists(Directory
):
433 CurrentDirectory
= os
.getcwd()
435 for File
in os
.listdir("."):
436 if os
.path
.isdir(File
):
437 RemoveDirectory(File
, Recursively
)
440 os
.chdir(CurrentDirectory
)
443 ## Store content in file
445 # This method is used to save file only when its content is changed. This is
446 # quite useful for "make" system to decide what will be re-built and what won't.
448 # @param File The path of file
449 # @param Content The new content of the file
450 # @param IsBinaryFile The flag indicating if the file is binary file or not
452 # @retval True If the file content is changed and the file is renewed
453 # @retval False If the file content is the same
455 def SaveFileOnChange(File
, Content
, IsBinaryFile
=True):
457 if os
.path
.exists(File
):
460 with
open(File
, "rb") as f
:
461 if Content
== f
.read():
464 EdkLogger
.error(None, FILE_OPEN_FAILURE
, ExtraData
=File
)
467 with
open(File
, "r") as f
:
468 if Content
== f
.read():
471 EdkLogger
.error(None, FILE_OPEN_FAILURE
, ExtraData
=File
)
473 DirName
= os
.path
.dirname(File
)
474 if not CreateDirectory(DirName
):
475 EdkLogger
.error(None, FILE_CREATE_FAILURE
, "Could not create directory %s" % DirName
)
478 DirName
= os
.getcwd()
479 if not os
.access(DirName
, os
.W_OK
):
480 EdkLogger
.error(None, PERMISSION_FAILURE
, "Do not have write permission on directory %s" % DirName
)
486 if GlobalData
.gIsWindows
and not os
.path
.exists(File
):
487 # write temp file, then rename the temp file to the real file
488 # to make sure the file be immediate saved to disk
489 with tempfile
.NamedTemporaryFile(OpenMode
, dir=os
.path
.dirname(File
), delete
=False) as tf
:
493 os
.rename(tempname
, File
)
495 EdkLogger
.error(None, FILE_CREATE_FAILURE
, ExtraData
='IOError %s' % X
)
498 with
open(File
, OpenMode
) as Fd
:
501 EdkLogger
.error(None, FILE_CREATE_FAILURE
, ExtraData
='IOError %s' % X
)
505 ## Retrieve and cache the real path name in file system
507 # @param Root The root directory of path relative to
509 # @retval str The path string if the path exists
510 # @retval None If path doesn't exist
516 def __init__(self
, Root
):
518 for F
in os
.listdir(Root
):
520 self
._UPPER
_CACHE
_[F
.upper()] = F
523 def __getitem__(self
, Path
):
524 Path
= Path
[len(os
.path
.commonprefix([Path
, self
._Root
])):]
527 if Path
and Path
[0] == os
.path
.sep
:
529 if Path
in self
._CACHE
_:
530 return os
.path
.join(self
._Root
, Path
)
531 UpperPath
= Path
.upper()
532 if UpperPath
in self
._UPPER
_CACHE
_:
533 return os
.path
.join(self
._Root
, self
._UPPER
_CACHE
_[UpperPath
])
537 SepIndex
= Path
.find(os
.path
.sep
)
539 Parent
= UpperPath
[:SepIndex
]
540 if Parent
not in self
._UPPER
_CACHE
_:
542 LastSepIndex
= SepIndex
543 SepIndex
= Path
.find(os
.path
.sep
, LastSepIndex
+ 1)
545 if LastSepIndex
== -1:
550 SepIndex
= LastSepIndex
552 Parent
= Path
[:SepIndex
]
553 ParentKey
= UpperPath
[:SepIndex
]
554 if ParentKey
not in self
._UPPER
_CACHE
_:
558 if Parent
in self
._CACHE
_:
561 ParentDir
= self
._UPPER
_CACHE
_[ParentKey
]
562 for F
in os
.listdir(ParentDir
):
563 Dir
= os
.path
.join(ParentDir
, F
)
564 self
._CACHE
_.add(Dir
)
565 self
._UPPER
_CACHE
_[Dir
.upper()] = Dir
567 SepIndex
= Path
.find(os
.path
.sep
, SepIndex
+ 1)
570 if Path
in self
._CACHE
_:
571 return os
.path
.join(self
._Root
, Path
)
572 elif UpperPath
in self
._UPPER
_CACHE
_:
573 return os
.path
.join(self
._Root
, self
._UPPER
_CACHE
_[UpperPath
])
576 def RealPath(File
, Dir
='', OverrideDir
=''):
577 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
578 NewFile
= GlobalData
.gAllFiles
[NewFile
]
579 if not NewFile
and OverrideDir
:
580 NewFile
= os
.path
.normpath(os
.path
.join(OverrideDir
, File
))
581 NewFile
= GlobalData
.gAllFiles
[NewFile
]
584 ## Get GUID value from given packages
586 # @param CName The CName of the GUID
587 # @param PackageList List of packages looking-up in
588 # @param Inffile The driver file
590 # @retval GuidValue if the CName is found in any given package
591 # @retval None if the CName is not found in all given packages
593 def GuidValue(CName
, PackageList
, Inffile
= None):
594 for P
in PackageList
:
595 GuidKeys
= list(P
.Guids
.keys())
596 if Inffile
and P
._PrivateGuids
:
597 if not Inffile
.startswith(P
.MetaFile
.Dir
):
598 GuidKeys
= [x
for x
in P
.Guids
if x
not in P
._PrivateGuids
]
599 if CName
in GuidKeys
:
600 return P
.Guids
[CName
]
604 ## A string template class
606 # This class implements a template for string replacement. A string template
607 # looks like following
609 # ${BEGIN} other_string ${placeholder_name} other_string ${END}
611 # The string between ${BEGIN} and ${END} will be repeated as many times as the
612 # length of "placeholder_name", which is a list passed through a dict. The
613 # "placeholder_name" is the key name of the dict. The ${BEGIN} and ${END} can
614 # be not used and, in this case, the "placeholder_name" must not a list and it
615 # will just be replaced once.
617 class TemplateString(object):
618 _REPEAT_START_FLAG
= "BEGIN"
619 _REPEAT_END_FLAG
= "END"
621 class Section(object):
622 _LIST_TYPES
= [type([]), type(set()), type((0,))]
624 def __init__(self
, TemplateSection
, PlaceHolderList
):
625 self
._Template
= TemplateSection
626 self
._PlaceHolderList
= []
628 # Split the section into sub-sections according to the position of placeholders
630 self
._SubSectionList
= []
633 # The placeholders passed in must be in the format of
635 # PlaceHolderName, PlaceHolderStartPoint, PlaceHolderEndPoint
637 for PlaceHolder
, Start
, End
in PlaceHolderList
:
638 self
._SubSectionList
.append(TemplateSection
[SubSectionStart
:Start
])
639 self
._SubSectionList
.append(TemplateSection
[Start
:End
])
640 self
._PlaceHolderList
.append(PlaceHolder
)
641 SubSectionStart
= End
642 if SubSectionStart
< len(TemplateSection
):
643 self
._SubSectionList
.append(TemplateSection
[SubSectionStart
:])
645 self
._SubSectionList
= [TemplateSection
]
648 return self
._Template
+ " : " + str(self
._PlaceHolderList
)
650 def Instantiate(self
, PlaceHolderValues
):
652 RepeatPlaceHolders
= {}
653 NonRepeatPlaceHolders
= {}
655 for PlaceHolder
in self
._PlaceHolderList
:
656 if PlaceHolder
not in PlaceHolderValues
:
658 Value
= PlaceHolderValues
[PlaceHolder
]
659 if type(Value
) in self
._LIST
_TYPES
:
661 RepeatTime
= len(Value
)
662 elif RepeatTime
!= len(Value
):
666 "${%s} has different repeat time from others!" % PlaceHolder
,
667 ExtraData
=str(self
._Template
)
669 RepeatPlaceHolders
["${%s}" % PlaceHolder
] = Value
671 NonRepeatPlaceHolders
["${%s}" % PlaceHolder
] = Value
673 if NonRepeatPlaceHolders
:
675 for S
in self
._SubSectionList
:
676 if S
not in NonRepeatPlaceHolders
:
679 StringList
.append(str(NonRepeatPlaceHolders
[S
]))
681 StringList
= self
._SubSectionList
683 if RepeatPlaceHolders
:
685 for Index
in range(RepeatTime
):
687 if S
not in RepeatPlaceHolders
:
688 TempStringList
.append(S
)
690 TempStringList
.append(str(RepeatPlaceHolders
[S
][Index
]))
691 StringList
= TempStringList
693 return "".join(StringList
)
696 def __init__(self
, Template
=None):
698 self
.IsBinary
= False
699 self
._Template
= Template
700 self
._TemplateSectionList
= self
._Parse
(Template
)
704 # @retval string The string replaced
707 return "".join(self
.String
)
709 ## Split the template string into fragments per the ${BEGIN} and ${END} flags
711 # @retval list A list of TemplateString.Section objects
713 def _Parse(self
, Template
):
718 TemplateSectionList
= []
720 MatchObj
= gPlaceholderPattern
.search(Template
, SearchFrom
)
722 if MatchEnd
<= len(Template
):
723 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:], PlaceHolderList
)
724 TemplateSectionList
.append(TemplateSection
)
727 MatchString
= MatchObj
.group(1)
728 MatchStart
= MatchObj
.start()
729 MatchEnd
= MatchObj
.end()
731 if MatchString
== self
._REPEAT
_START
_FLAG
:
732 if MatchStart
> SectionStart
:
733 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:MatchStart
], PlaceHolderList
)
734 TemplateSectionList
.append(TemplateSection
)
735 SectionStart
= MatchEnd
737 elif MatchString
== self
._REPEAT
_END
_FLAG
:
738 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:MatchStart
], PlaceHolderList
)
739 TemplateSectionList
.append(TemplateSection
)
740 SectionStart
= MatchEnd
743 PlaceHolderList
.append((MatchString
, MatchStart
- SectionStart
, MatchEnd
- SectionStart
))
744 SearchFrom
= MatchEnd
745 return TemplateSectionList
747 ## Replace the string template with dictionary of placeholders and append it to previous one
749 # @param AppendString The string template to append
750 # @param Dictionary The placeholder dictionaries
752 def Append(self
, AppendString
, Dictionary
=None):
754 SectionList
= self
._Parse
(AppendString
)
755 self
.String
.append( "".join(S
.Instantiate(Dictionary
) for S
in SectionList
))
757 if isinstance(AppendString
,list):
758 self
.String
.extend(AppendString
)
760 self
.String
.append(AppendString
)
762 ## Replace the string template with dictionary of placeholders
764 # @param Dictionary The placeholder dictionaries
766 # @retval str The string replaced with placeholder values
768 def Replace(self
, Dictionary
=None):
769 return "".join(S
.Instantiate(Dictionary
) for S
in self
._TemplateSectionList
)
771 ## Progress indicator class
773 # This class makes use of thread to print progress on console.
776 # for avoiding deadloop
778 _ProgressThread
= None
779 _CheckInterval
= 0.25
783 # @param OpenMessage The string printed before progress characters
784 # @param CloseMessage The string printed after progress characters
785 # @param ProgressChar The character used to indicate the progress
786 # @param Interval The interval in seconds between two progress characters
788 def __init__(self
, OpenMessage
="", CloseMessage
="", ProgressChar
='.', Interval
=1.0):
789 self
.PromptMessage
= OpenMessage
790 self
.CodaMessage
= CloseMessage
791 self
.ProgressChar
= ProgressChar
792 self
.Interval
= Interval
793 if Progressor
._StopFlag
is None:
794 Progressor
._StopFlag
= threading
.Event()
796 ## Start to print progress character
798 # @param OpenMessage The string printed before progress characters
800 def Start(self
, OpenMessage
=None):
801 if OpenMessage
is not None:
802 self
.PromptMessage
= OpenMessage
803 Progressor
._StopFlag
.clear()
804 if Progressor
._ProgressThread
is None:
805 Progressor
._ProgressThread
= threading
.Thread(target
=self
._ProgressThreadEntry
)
806 Progressor
._ProgressThread
.setDaemon(False)
807 Progressor
._ProgressThread
.start()
809 ## Stop printing progress character
811 # @param CloseMessage The string printed after progress characters
813 def Stop(self
, CloseMessage
=None):
814 OriginalCodaMessage
= self
.CodaMessage
815 if CloseMessage
is not None:
816 self
.CodaMessage
= CloseMessage
818 self
.CodaMessage
= OriginalCodaMessage
820 ## Thread entry method
821 def _ProgressThreadEntry(self
):
822 sys
.stdout
.write(self
.PromptMessage
+ " ")
825 while not Progressor
._StopFlag
.isSet():
827 sys
.stdout
.write(self
.ProgressChar
)
829 TimeUp
= self
.Interval
830 time
.sleep(self
._CheckInterval
)
831 TimeUp
-= self
._CheckInterval
832 sys
.stdout
.write(" " + self
.CodaMessage
+ "\n")
835 ## Abort the progress display
838 if Progressor
._StopFlag
is not None:
839 Progressor
._StopFlag
.set()
840 if Progressor
._ProgressThread
is not None:
841 Progressor
._ProgressThread
.join()
842 Progressor
._ProgressThread
= None
845 ## Dictionary using prioritized list as key
849 _TupleType
= type(())
851 _ValidWildcardList
= ['COMMON', 'DEFAULT', 'ALL', TAB_STAR
, 'PLATFORM']
853 def __init__(self
, _Single_
=False, _Level_
=2):
854 self
._Level
_ = _Level_
856 self
._Single
_ = _Single_
859 def __getitem__(self
, key
):
862 if KeyType
== self
._ListType
or KeyType
== self
._TupleType
:
866 elif self
._Level
_ > 1:
867 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
871 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
873 if FirstKey
is None or str(FirstKey
).upper() in self
._ValidWildcardList
:
874 FirstKey
= self
._Wildcard
877 return self
._GetSingleValue
(FirstKey
, RestKeys
)
879 return self
._GetAllValues
(FirstKey
, RestKeys
)
881 def _GetSingleValue(self
, FirstKey
, RestKeys
):
883 #print "%s-%s" % (FirstKey, self._Level_) ,
885 if FirstKey
== self
._Wildcard
:
886 if FirstKey
in self
.data
:
887 Value
= self
.data
[FirstKey
][RestKeys
]
889 for Key
in self
.data
:
890 Value
= self
.data
[Key
][RestKeys
]
891 if Value
is not None: break
893 if FirstKey
in self
.data
:
894 Value
= self
.data
[FirstKey
][RestKeys
]
895 if Value
is None and self
._Wildcard
in self
.data
:
897 Value
= self
.data
[self
._Wildcard
][RestKeys
]
899 if FirstKey
== self
._Wildcard
:
900 if FirstKey
in self
.data
:
901 Value
= self
.data
[FirstKey
]
903 for Key
in self
.data
:
904 Value
= self
.data
[Key
]
905 if Value
is not None: break
907 if FirstKey
in self
.data
:
908 Value
= self
.data
[FirstKey
]
909 elif self
._Wildcard
in self
.data
:
910 Value
= self
.data
[self
._Wildcard
]
913 def _GetAllValues(self
, FirstKey
, RestKeys
):
916 if FirstKey
== self
._Wildcard
:
917 for Key
in self
.data
:
918 Value
+= self
.data
[Key
][RestKeys
]
920 if FirstKey
in self
.data
:
921 Value
+= self
.data
[FirstKey
][RestKeys
]
922 if self
._Wildcard
in self
.data
:
923 Value
+= self
.data
[self
._Wildcard
][RestKeys
]
925 if FirstKey
== self
._Wildcard
:
926 for Key
in self
.data
:
927 Value
.append(self
.data
[Key
])
929 if FirstKey
in self
.data
:
930 Value
.append(self
.data
[FirstKey
])
931 if self
._Wildcard
in self
.data
:
932 Value
.append(self
.data
[self
._Wildcard
])
936 def __setitem__(self
, key
, value
):
939 if KeyType
== self
._ListType
or KeyType
== self
._TupleType
:
944 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
948 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
950 if FirstKey
in self
._ValidWildcardList
:
951 FirstKey
= self
._Wildcard
953 if FirstKey
not in self
.data
and self
._Level
_ > 0:
954 self
.data
[FirstKey
] = tdict(self
._Single
_, self
._Level
_ - 1)
957 self
.data
[FirstKey
][RestKeys
] = value
959 self
.data
[FirstKey
] = value
961 def SetGreedyMode(self
):
962 self
._Single
_ = False
964 for Key
in self
.data
:
965 self
.data
[Key
].SetGreedyMode()
967 def SetSingleMode(self
):
970 for Key
in self
.data
:
971 self
.data
[Key
].SetSingleMode()
973 def GetKeys(self
, KeyIndex
=0):
976 return set(self
.data
.keys())
979 for Key
in self
.data
:
980 keys |
= self
.data
[Key
].GetKeys(KeyIndex
- 1)
983 def AnalyzePcdExpression(Setting
):
984 RanStr
= ''.join(sample(string
.ascii_letters
+ string
.digits
, 8))
985 Setting
= Setting
.replace('\\\\', RanStr
).strip()
986 # There might be escaped quote in a string: \", \\\" , \', \\\'
988 # There might be '|' in string and in ( ... | ... ), replace it with '-'
990 InSingleQuoteStr
= False
991 InDoubleQuoteStr
= False
993 for Index
, ch
in enumerate(Data
):
994 if ch
== '"' and not InSingleQuoteStr
:
995 if Data
[Index
- 1] != '\\':
996 InDoubleQuoteStr
= not InDoubleQuoteStr
997 elif ch
== "'" and not InDoubleQuoteStr
:
998 if Data
[Index
- 1] != '\\':
999 InSingleQuoteStr
= not InSingleQuoteStr
1000 elif ch
== '(' and not (InSingleQuoteStr
or InDoubleQuoteStr
):
1002 elif ch
== ')' and not (InSingleQuoteStr
or InDoubleQuoteStr
):
1005 if (Pair
> 0 or InSingleQuoteStr
or InDoubleQuoteStr
) and ch
== TAB_VALUE_SPLIT
:
1012 Pos
= NewStr
.find(TAB_VALUE_SPLIT
, StartPos
)
1014 FieldList
.append(Setting
[StartPos
:].strip())
1016 FieldList
.append(Setting
[StartPos
:Pos
].strip())
1018 for i
, ch
in enumerate(FieldList
):
1020 FieldList
[i
] = ch
.replace(RanStr
,'\\\\')
1023 def ParseFieldValue (Value
):
1024 def ParseDevPathValue (Value
):
1026 Value
.replace('\\', '/').replace(' ', '')
1028 Cmd
= 'DevicePath ' + '"' + Value
+ '"'
1030 p
= subprocess
.Popen(Cmd
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, shell
=True)
1031 out
, err
= p
.communicate()
1032 except Exception as X
:
1033 raise BadExpression("DevicePath: %s" % (str(X
)) )
1035 subprocess
._cleanup
()
1039 raise BadExpression("DevicePath: %s" % str(err
))
1040 out
= out
.decode(encoding
='utf-8', errors
='ignore')
1041 Size
= len(out
.split())
1042 out
= ','.join(out
.split())
1043 return '{' + out
+ '}', Size
1045 if "{CODE(" in Value
:
1046 return Value
, len(Value
.split(","))
1047 if isinstance(Value
, type(0)):
1048 return Value
, (Value
.bit_length() + 7) // 8
1049 if not isinstance(Value
, type('')):
1050 raise BadExpression('Type %s is %s' %(Value
, type(Value
)))
1051 Value
= Value
.strip()
1052 if Value
.startswith(TAB_UINT8
) and Value
.endswith(')'):
1053 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1055 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1057 if Value
.startswith(TAB_UINT16
) and Value
.endswith(')'):
1058 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1060 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1062 if Value
.startswith(TAB_UINT32
) and Value
.endswith(')'):
1063 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1065 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1067 if Value
.startswith(TAB_UINT64
) and Value
.endswith(')'):
1068 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1070 raise BadExpression('Value (%s) Size larger than %d' % (Value
, Size
))
1072 if Value
.startswith(TAB_GUID
) and Value
.endswith(')'):
1073 Value
= Value
.split('(', 1)[1][:-1].strip()
1074 if Value
[0] == '{' and Value
[-1] == '}':
1075 TmpValue
= GuidStructureStringToGuidString(Value
)
1077 raise BadExpression("Invalid GUID value string %s" % Value
)
1079 if Value
[0] == '"' and Value
[-1] == '"':
1082 Value
= str(uuid
.UUID(Value
).bytes_le
)
1083 if Value
.startswith("b'"):
1085 Value
= "'" + Value
+ "'"
1086 except ValueError as Message
:
1087 raise BadExpression(Message
)
1088 Value
, Size
= ParseFieldValue(Value
)
1090 if Value
.startswith('L"') and Value
.endswith('"'):
1092 # translate escape character
1102 Value
= (Value
<< 16) |
ord(Char
)
1103 return Value
, (len(List
) + 1) * 2
1104 if Value
.startswith('"') and Value
.endswith('"'):
1106 # translate escape character
1115 Value
= (Value
<< 8) |
ord(Char
)
1116 return Value
, len(List
) + 1
1117 if Value
.startswith("L'") and Value
.endswith("'"):
1118 # Unicode Character Constant
1119 # translate escape character
1127 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1131 Value
= (Value
<< 16) |
ord(Char
)
1132 return Value
, len(List
) * 2
1133 if Value
.startswith("'") and Value
.endswith("'"):
1134 # Character constant
1135 # translate escape character
1142 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1146 Value
= (Value
<< 8) |
ord(Char
)
1147 return Value
, len(List
)
1148 if Value
.startswith('{') and Value
.endswith('}'):
1151 List
= [Item
.strip() for Item
in Value
.split(',')]
1156 ItemValue
, Size
= ParseFieldValue(Item
)
1158 for I
in range(Size
):
1159 Value
= (Value
<< 8) |
((ItemValue
>> 8 * I
) & 0xff)
1160 return Value
, RetSize
1161 if Value
.startswith('DEVICE_PATH(') and Value
.endswith(')'):
1162 Value
= Value
.replace("DEVICE_PATH(", '').rstrip(')')
1163 Value
= Value
.strip().strip('"')
1164 return ParseDevPathValue(Value
)
1165 if Value
.lower().startswith('0x'):
1167 Value
= int(Value
, 16)
1169 raise BadExpression("invalid hex value: %s" % Value
)
1172 return Value
, (Value
.bit_length() + 7) // 8
1173 if Value
[0].isdigit():
1174 Value
= int(Value
, 10)
1177 return Value
, (Value
.bit_length() + 7) // 8
1178 if Value
.lower() == 'true':
1180 if Value
.lower() == 'false':
1186 # Analyze DSC PCD value, since there is no data type info in DSC
1187 # This function is used to match functions (AnalyzePcdData) used for retrieving PCD value from database
1188 # 1. Feature flag: TokenSpace.PcdCName|PcdValue
1189 # 2. Fix and Patch:TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1190 # 3. Dynamic default:
1191 # TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1192 # TokenSpace.PcdCName|PcdValue
1194 # TokenSpace.PcdCName|VpdOffset[|VpdValue]
1195 # TokenSpace.PcdCName|VpdOffset[|MaxSize[|VpdValue]]
1197 # TokenSpace.PcdCName|HiiString|VariableGuid|VariableOffset[|HiiValue]
1198 # PCD value needs to be located in such kind of string, and the PCD value might be an expression in which
1199 # there might have "|" operator, also in string value.
1201 # @param Setting: String contain information described above with "TokenSpace.PcdCName|" stripped
1202 # @param PcdType: PCD type: feature, fixed, dynamic default VPD HII
1203 # @param DataType: The datum type of PCD: VOID*, UNIT, BOOL
1205 # ValueList: A List contain fields described above
1206 # IsValid: True if conforming EBNF, otherwise False
1207 # Index: The index where PcdValue is in ValueList
1209 def AnalyzeDscPcd(Setting
, PcdType
, DataType
=''):
1210 FieldList
= AnalyzePcdExpression(Setting
)
1213 if PcdType
in (MODEL_PCD_FIXED_AT_BUILD
, MODEL_PCD_PATCHABLE_IN_MODULE
, MODEL_PCD_DYNAMIC_DEFAULT
, MODEL_PCD_DYNAMIC_EX_DEFAULT
):
1214 Value
= FieldList
[0]
1216 if len(FieldList
) > 1 and FieldList
[1]:
1217 DataType
= FieldList
[1]
1218 if FieldList
[1] != TAB_VOID
and StructPattern
.match(FieldList
[1]) is None:
1220 if len(FieldList
) > 2:
1224 IsValid
= (len(FieldList
) <= 1)
1226 IsValid
= (len(FieldList
) <= 3)
1230 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1234 return [str(Value
), DataType
, str(Size
)], IsValid
, 0
1235 elif PcdType
== MODEL_PCD_FEATURE_FLAG
:
1236 Value
= FieldList
[0]
1238 IsValid
= (len(FieldList
) <= 1)
1239 return [Value
, DataType
, str(Size
)], IsValid
, 0
1240 elif PcdType
in (MODEL_PCD_DYNAMIC_VPD
, MODEL_PCD_DYNAMIC_EX_VPD
):
1241 VpdOffset
= FieldList
[0]
1243 if not DataType
== TAB_VOID
:
1244 if len(FieldList
) > 1:
1245 Value
= FieldList
[1]
1247 if len(FieldList
) > 1:
1249 if len(FieldList
) > 2:
1250 Value
= FieldList
[2]
1252 IsValid
= (len(FieldList
) <= 1)
1254 IsValid
= (len(FieldList
) <= 3)
1257 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1261 return [VpdOffset
, str(Size
), Value
], IsValid
, 2
1262 elif PcdType
in (MODEL_PCD_DYNAMIC_HII
, MODEL_PCD_DYNAMIC_EX_HII
):
1263 IsValid
= (3 <= len(FieldList
) <= 5)
1264 HiiString
= FieldList
[0]
1265 Guid
= Offset
= Value
= Attribute
= ''
1266 if len(FieldList
) > 1:
1268 if len(FieldList
) > 2:
1269 Offset
= FieldList
[2]
1270 if len(FieldList
) > 3:
1271 Value
= FieldList
[3]
1272 if len(FieldList
) > 4:
1273 Attribute
= FieldList
[4]
1274 return [HiiString
, Guid
, Offset
, Value
, Attribute
], IsValid
, 3
1279 # Analyze the pcd Value, Datum type and TokenNumber.
1280 # Used to avoid split issue while the value string contain "|" character
1282 # @param[in] Setting: A String contain value/datum type/token number information;
1284 # @retval ValueList: A List contain value, datum type and toke number.
1286 def AnalyzePcdData(Setting
):
1287 ValueList
= ['', '', '']
1289 ValueRe
= re
.compile(r
'^\s*L?\".*\|.*\"')
1290 PtrValue
= ValueRe
.findall(Setting
)
1292 ValueUpdateFlag
= False
1294 if len(PtrValue
) >= 1:
1295 Setting
= re
.sub(ValueRe
, '', Setting
)
1296 ValueUpdateFlag
= True
1298 TokenList
= Setting
.split(TAB_VALUE_SPLIT
)
1299 ValueList
[0:len(TokenList
)] = TokenList
1302 ValueList
[0] = PtrValue
[0]
1306 ## check format of PCD value against its the datum type
1308 # For PCD value setting
1310 def CheckPcdDatum(Type
, Value
):
1311 if Type
== TAB_VOID
:
1312 ValueRe
= re
.compile(r
'\s*L?\".*\"\s*$')
1313 if not (((Value
.startswith('L"') or Value
.startswith('"')) and Value
.endswith('"'))
1314 or (Value
.startswith('{') and Value
.endswith('}')) or (Value
.startswith("L'") or Value
.startswith("'") and Value
.endswith("'"))
1316 return False, "Invalid value [%s] of type [%s]; must be in the form of {...} for array"\
1317 ", \"...\" or \'...\' for string, L\"...\" or L\'...\' for unicode string" % (Value
, Type
)
1318 elif ValueRe
.match(Value
):
1319 # Check the chars in UnicodeString or CString is printable
1320 if Value
.startswith("L"):
1324 Printset
= set(string
.printable
)
1325 Printset
.remove(TAB_PRINTCHAR_VT
)
1326 Printset
.add(TAB_PRINTCHAR_BS
)
1327 Printset
.add(TAB_PRINTCHAR_NUL
)
1328 if not set(Value
).issubset(Printset
):
1329 PrintList
= sorted(Printset
)
1330 return False, "Invalid PCD string value of type [%s]; must be printable chars %s." % (Type
, PrintList
)
1331 elif Type
== 'BOOLEAN':
1332 if Value
not in ['TRUE', 'True', 'true', '0x1', '0x01', '1', 'FALSE', 'False', 'false', '0x0', '0x00', '0']:
1333 return False, "Invalid value [%s] of type [%s]; must be one of TRUE, True, true, 0x1, 0x01, 1"\
1334 ", FALSE, False, false, 0x0, 0x00, 0" % (Value
, Type
)
1335 elif Type
in [TAB_UINT8
, TAB_UINT16
, TAB_UINT32
, TAB_UINT64
]:
1336 if Value
.startswith('0') and not Value
.lower().startswith('0x') and len(Value
) > 1 and Value
.lstrip('0'):
1337 Value
= Value
.lstrip('0')
1339 if Value
and int(Value
, 0) < 0:
1340 return False, "PCD can't be set to negative value[%s] for datum type [%s]" % (Value
, Type
)
1341 Value
= int(Value
, 0)
1342 if Value
> MAX_VAL_TYPE
[Type
]:
1343 return False, "Too large PCD value[%s] for datum type [%s]" % (Value
, Type
)
1345 return False, "Invalid value [%s] of type [%s];"\
1346 " must be a hexadecimal, decimal or octal in C language format." % (Value
, Type
)
1348 return True, "StructurePcd"
1352 def CommonPath(PathList
):
1353 P1
= min(PathList
).split(os
.path
.sep
)
1354 P2
= max(PathList
).split(os
.path
.sep
)
1355 for Index
in range(min(len(P1
), len(P2
))):
1356 if P1
[Index
] != P2
[Index
]:
1357 return os
.path
.sep
.join(P1
[:Index
])
1358 return os
.path
.sep
.join(P1
)
1360 class PathClass(object):
1361 def __init__(self
, File
='', Root
='', AlterRoot
='', Type
='', IsBinary
=False,
1362 Arch
='COMMON', ToolChainFamily
='', Target
='', TagName
='', ToolCode
=''):
1364 self
.File
= str(File
)
1365 if os
.path
.isabs(self
.File
):
1369 self
.Root
= str(Root
)
1370 self
.AlterRoot
= str(AlterRoot
)
1372 # Remove any '.' and '..' in path
1374 self
.Root
= mws
.getWs(self
.Root
, self
.File
)
1375 self
.Path
= os
.path
.normpath(os
.path
.join(self
.Root
, self
.File
))
1376 self
.Root
= os
.path
.normpath(CommonPath([self
.Root
, self
.Path
]))
1377 # eliminate the side-effect of 'C:'
1378 if self
.Root
[-1] == ':':
1379 self
.Root
+= os
.path
.sep
1380 # file path should not start with path separator
1381 if self
.Root
[-1] == os
.path
.sep
:
1382 self
.File
= self
.Path
[len(self
.Root
):]
1384 self
.File
= self
.Path
[len(self
.Root
) + 1:]
1386 self
.Path
= os
.path
.normpath(self
.File
)
1388 self
.SubDir
, self
.Name
= os
.path
.split(self
.File
)
1389 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1393 self
.Dir
= os
.path
.join(self
.Root
, self
.SubDir
)
1395 self
.Dir
= self
.Root
1397 self
.Dir
= self
.SubDir
1402 self
.Type
= self
.Ext
.lower()
1404 self
.IsBinary
= IsBinary
1405 self
.Target
= Target
1406 self
.TagName
= TagName
1407 self
.ToolCode
= ToolCode
1408 self
.ToolChainFamily
= ToolChainFamily
1409 self
.OriginalPath
= self
1411 ## Convert the object of this class to a string
1413 # Convert member Path of the class to a string
1415 # @retval string Formatted String
1420 ## Override __eq__ function
1422 # Check whether PathClass are the same
1424 # @retval False The two PathClass are different
1425 # @retval True The two PathClass are the same
1427 def __eq__(self
, Other
):
1428 return self
.Path
== str(Other
)
1430 ## Override __cmp__ function
1432 # Customize the comparison operation of two PathClass
1434 # @retval 0 The two PathClass are different
1435 # @retval -1 The first PathClass is less than the second PathClass
1436 # @retval 1 The first PathClass is Bigger than the second PathClass
1437 def __cmp__(self
, Other
):
1438 OtherKey
= str(Other
)
1441 if SelfKey
== OtherKey
:
1443 elif SelfKey
> OtherKey
:
1448 ## Override __hash__ function
1450 # Use Path as key in hash table
1452 # @retval string Key for hash table
1455 return hash(self
.Path
)
1459 return self
.Path
.upper()
1462 def TimeStamp(self
):
1463 return os
.stat(self
.Path
)[8]
1465 def Validate(self
, Type
='', CaseSensitive
=True):
1466 def RealPath2(File
, Dir
='', OverrideDir
=''):
1469 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(OverrideDir
, File
))]
1471 if OverrideDir
[-1] == os
.path
.sep
:
1472 return NewFile
[len(OverrideDir
):], NewFile
[0:len(OverrideDir
)]
1474 return NewFile
[len(OverrideDir
) + 1:], NewFile
[0:len(OverrideDir
)]
1475 if GlobalData
.gAllFiles
:
1476 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(Dir
, File
))]
1478 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
1479 if not os
.path
.exists(NewFile
):
1483 if Dir
[-1] == os
.path
.sep
:
1484 return NewFile
[len(Dir
):], NewFile
[0:len(Dir
)]
1486 return NewFile
[len(Dir
) + 1:], NewFile
[0:len(Dir
)]
1492 if GlobalData
.gCaseInsensitive
:
1493 CaseSensitive
= False
1494 if Type
and Type
.lower() != self
.Type
:
1495 return FILE_TYPE_MISMATCH
, '%s (expect %s but got %s)' % (self
.File
, Type
, self
.Type
)
1497 RealFile
, RealRoot
= RealPath2(self
.File
, self
.Root
, self
.AlterRoot
)
1498 if not RealRoot
and not RealFile
:
1499 RealFile
= self
.File
1501 RealFile
= os
.path
.join(self
.AlterRoot
, self
.File
)
1503 RealFile
= os
.path
.join(self
.Root
, self
.File
)
1504 if len (mws
.getPkgPath()) == 0:
1505 return FILE_NOT_FOUND
, os
.path
.join(self
.AlterRoot
, RealFile
)
1507 return FILE_NOT_FOUND
, "%s is not found in packages path:\n\t%s" % (self
.File
, '\n\t'.join(mws
.getPkgPath()))
1511 if RealRoot
!= self
.Root
or RealFile
!= self
.File
:
1512 if CaseSensitive
and (RealFile
!= self
.File
or (RealRoot
!= self
.Root
and RealRoot
!= self
.AlterRoot
)):
1513 ErrorCode
= FILE_CASE_MISMATCH
1514 ErrorInfo
= self
.File
+ '\n\t' + RealFile
+ " [in file system]"
1516 self
.SubDir
, self
.Name
= os
.path
.split(RealFile
)
1517 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1519 self
.Dir
= os
.path
.join(RealRoot
, self
.SubDir
)
1522 self
.File
= RealFile
1523 self
.Root
= RealRoot
1524 self
.Path
= os
.path
.join(RealRoot
, RealFile
)
1525 return ErrorCode
, ErrorInfo
1527 ## Parse PE image to get the required PE information.
1529 class PeImageClass():
1532 # @param File FilePath of PeImage
1534 def __init__(self
, PeFile
):
1535 self
.FileName
= PeFile
1536 self
.IsValid
= False
1539 self
.SectionAlignment
= 0
1540 self
.SectionHeaderList
= []
1543 PeObject
= open(PeFile
, 'rb')
1545 self
.ErrorInfo
= self
.FileName
+ ' can not be found\n'
1548 ByteArray
= array
.array('B')
1549 ByteArray
.fromfile(PeObject
, 0x3E)
1550 ByteList
= ByteArray
.tolist()
1551 # DOS signature should be 'MZ'
1552 if self
._ByteListToStr
(ByteList
[0x0:0x2]) != 'MZ':
1553 self
.ErrorInfo
= self
.FileName
+ ' has no valid DOS signature MZ'
1556 # Read 4 byte PE Signature
1557 PeOffset
= self
._ByteListToInt
(ByteList
[0x3C:0x3E])
1558 PeObject
.seek(PeOffset
)
1559 ByteArray
= array
.array('B')
1560 ByteArray
.fromfile(PeObject
, 4)
1561 # PE signature should be 'PE\0\0'
1562 if ByteArray
.tostring() != b
'PE\0\0':
1563 self
.ErrorInfo
= self
.FileName
+ ' has no valid PE signature PE00'
1566 # Read PE file header
1567 ByteArray
= array
.array('B')
1568 ByteArray
.fromfile(PeObject
, 0x14)
1569 ByteList
= ByteArray
.tolist()
1570 SecNumber
= self
._ByteListToInt
(ByteList
[0x2:0x4])
1572 self
.ErrorInfo
= self
.FileName
+ ' has no section header'
1575 # Read PE optional header
1576 OptionalHeaderSize
= self
._ByteListToInt
(ByteArray
[0x10:0x12])
1577 ByteArray
= array
.array('B')
1578 ByteArray
.fromfile(PeObject
, OptionalHeaderSize
)
1579 ByteList
= ByteArray
.tolist()
1580 self
.EntryPoint
= self
._ByteListToInt
(ByteList
[0x10:0x14])
1581 self
.SectionAlignment
= self
._ByteListToInt
(ByteList
[0x20:0x24])
1582 self
.Size
= self
._ByteListToInt
(ByteList
[0x38:0x3C])
1584 # Read each Section Header
1585 for Index
in range(SecNumber
):
1586 ByteArray
= array
.array('B')
1587 ByteArray
.fromfile(PeObject
, 0x28)
1588 ByteList
= ByteArray
.tolist()
1589 SecName
= self
._ByteListToStr
(ByteList
[0:8])
1590 SecVirtualSize
= self
._ByteListToInt
(ByteList
[8:12])
1591 SecRawAddress
= self
._ByteListToInt
(ByteList
[20:24])
1592 SecVirtualAddress
= self
._ByteListToInt
(ByteList
[12:16])
1593 self
.SectionHeaderList
.append((SecName
, SecVirtualAddress
, SecRawAddress
, SecVirtualSize
))
1597 def _ByteListToStr(self
, ByteList
):
1599 for index
in range(len(ByteList
)):
1600 if ByteList
[index
] == 0:
1602 String
+= chr(ByteList
[index
])
1605 def _ByteListToInt(self
, ByteList
):
1607 for index
in range(len(ByteList
) - 1, -1, -1):
1608 Value
= (Value
<< 8) |
int(ByteList
[index
])
1611 class DefaultStore():
1612 def __init__(self
, DefaultStores
):
1614 self
.DefaultStores
= DefaultStores
1615 def DefaultStoreID(self
, DefaultStoreName
):
1616 for key
, value
in self
.DefaultStores
.items():
1617 if value
== DefaultStoreName
:
1620 def GetDefaultDefault(self
):
1621 if not self
.DefaultStores
or "0" in self
.DefaultStores
:
1622 return "0", TAB_DEFAULT_STORES_DEFAULT
1624 minvalue
= min(int(value_str
) for value_str
in self
.DefaultStores
)
1625 return (str(minvalue
), self
.DefaultStores
[str(minvalue
)])
1626 def GetMin(self
, DefaultSIdList
):
1627 if not DefaultSIdList
:
1628 return TAB_DEFAULT_STORES_DEFAULT
1629 storeidset
= {storeid
for storeid
, storename
in self
.DefaultStores
.values() if storename
in DefaultSIdList
}
1632 minid
= min(storeidset
)
1633 for sid
, name
in self
.DefaultStores
.values():
1642 def __init__(self
,SkuIdentifier
='', SkuIds
=None):
1646 for SkuName
in SkuIds
:
1647 SkuId
= SkuIds
[SkuName
][0]
1648 skuid_num
= int(SkuId
, 16) if SkuId
.upper().startswith("0X") else int(SkuId
)
1649 if skuid_num
> 0xFFFFFFFFFFFFFFFF:
1650 EdkLogger
.error("build", PARAMETER_INVALID
,
1651 ExtraData
= "SKU-ID [%s] value %s exceeds the max value of UINT64"
1654 self
.AvailableSkuIds
= OrderedDict()
1656 self
.SkuIdNumberSet
= []
1657 self
.SkuData
= SkuIds
1658 self
._SkuInherit
= {}
1659 self
._SkuIdentifier
= SkuIdentifier
1660 if SkuIdentifier
== '' or SkuIdentifier
is None:
1661 self
.SkuIdSet
= ['DEFAULT']
1662 self
.SkuIdNumberSet
= ['0U']
1663 elif SkuIdentifier
== 'ALL':
1664 self
.SkuIdSet
= list(SkuIds
.keys())
1665 self
.SkuIdNumberSet
= [num
[0].strip() + 'U' for num
in SkuIds
.values()]
1667 r
= SkuIdentifier
.split('|')
1668 self
.SkuIdSet
=[(r
[k
].strip()).upper() for k
in range(len(r
))]
1671 self
.SkuIdNumberSet
= [SkuIds
[k
][0].strip() + 'U' for k
in self
.SkuIdSet
]
1673 EdkLogger
.error("build", PARAMETER_INVALID
,
1674 ExtraData
= "SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1675 % (k
, " | ".join(SkuIds
.keys())))
1676 for each
in self
.SkuIdSet
:
1678 self
.AvailableSkuIds
[each
] = SkuIds
[each
][0]
1680 EdkLogger
.error("build", PARAMETER_INVALID
,
1681 ExtraData
="SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1682 % (each
, " | ".join(SkuIds
.keys())))
1683 if self
.SkuUsageType
!= SkuClass
.SINGLE
:
1684 self
.AvailableSkuIds
.update({'DEFAULT':0, 'COMMON':0})
1686 GlobalData
.gSkuids
= (self
.SkuIdSet
)
1687 if 'COMMON' in GlobalData
.gSkuids
:
1688 GlobalData
.gSkuids
.remove('COMMON')
1689 if self
.SkuUsageType
== self
.SINGLE
:
1690 if len(GlobalData
.gSkuids
) != 1:
1691 if 'DEFAULT' in GlobalData
.gSkuids
:
1692 GlobalData
.gSkuids
.remove('DEFAULT')
1693 if GlobalData
.gSkuids
:
1694 GlobalData
.gSkuids
.sort()
1696 def GetNextSkuId(self
, skuname
):
1697 if not self
._SkuInherit
:
1698 self
._SkuInherit
= {}
1699 for item
in self
.SkuData
.values():
1700 self
._SkuInherit
[item
[1]]=item
[2] if item
[2] else "DEFAULT"
1701 return self
._SkuInherit
.get(skuname
, "DEFAULT")
1703 def GetSkuChain(self
, sku
):
1704 if sku
== "DEFAULT":
1709 nextsku
= self
.GetNextSkuId(nextsku
)
1710 skulist
.append(nextsku
)
1711 if nextsku
== "DEFAULT":
1715 def SkuOverrideOrder(self
):
1717 for skuname
in self
.SkuIdSet
:
1718 skuorderset
.append(self
.GetSkuChain(skuname
))
1721 for index
in range(max(len(item
) for item
in skuorderset
)):
1722 for subset
in skuorderset
:
1723 if index
> len(subset
)-1:
1725 if subset
[index
] in skuorder
:
1727 skuorder
.append(subset
[index
])
1732 def SkuUsageType(self
):
1733 if self
._SkuIdentifier
.upper() == "ALL":
1734 return SkuClass
.MULTIPLE
1736 if len(self
.SkuIdSet
) == 1:
1737 if self
.SkuIdSet
[0] == 'DEFAULT':
1738 return SkuClass
.DEFAULT
1739 return SkuClass
.SINGLE
1740 if len(self
.SkuIdSet
) == 2 and 'DEFAULT' in self
.SkuIdSet
:
1741 return SkuClass
.SINGLE
1742 return SkuClass
.MULTIPLE
1744 def DumpSkuIdArrary(self
):
1745 if self
.SkuUsageType
== SkuClass
.SINGLE
:
1748 for skuname
in self
.AvailableSkuIds
:
1749 if skuname
== "COMMON":
1751 while skuname
!= "DEFAULT":
1752 ArrayStrList
.append(hex(int(self
.AvailableSkuIds
[skuname
])))
1753 skuname
= self
.GetNextSkuId(skuname
)
1754 ArrayStrList
.append("0x0")
1755 return "{{{myList}}}".format(myList
=",".join(ArrayStrList
))
1758 def AvailableSkuIdSet(self
):
1759 return self
.AvailableSkuIds
1762 def SystemSkuId(self
):
1763 if self
.SkuUsageType
== SkuClass
.SINGLE
:
1764 if len(self
.SkuIdSet
) == 1:
1765 return self
.SkuIdSet
[0]
1767 return self
.SkuIdSet
[0] if self
.SkuIdSet
[0] != 'DEFAULT' else self
.SkuIdSet
[1]
1771 ## Get the integer value from string like "14U" or integer like 2
1773 # @param Input The object that may be either a integer value or a string
1775 # @retval Value The integer value that the input represents
1777 def GetIntegerValue(Input
):
1778 if not isinstance(Input
, str):
1781 if String
.endswith("U"):
1782 String
= String
[:-1]
1783 if String
.endswith("ULL"):
1784 String
= String
[:-3]
1785 if String
.endswith("LL"):
1786 String
= String
[:-2]
1788 if String
.startswith("0x") or String
.startswith("0X"):
1789 return int(String
, 16)
1796 # Pack a GUID (registry format) list into a buffer and return it
1799 return pack(PACK_PATTERN_GUID
,
1803 int(Guid
[3][-4:-2], 16),
1804 int(Guid
[3][-2:], 16),
1805 int(Guid
[4][-12:-10], 16),
1806 int(Guid
[4][-10:-8], 16),
1807 int(Guid
[4][-8:-6], 16),
1808 int(Guid
[4][-6:-4], 16),
1809 int(Guid
[4][-4:-2], 16),
1810 int(Guid
[4][-2:], 16)
1814 # Pack a GUID (byte) list into a buffer and return it
1816 def PackByteFormatGUID(Guid
):
1817 return pack(PACK_PATTERN_GUID
,
1831 ## DeepCopy dict/OrderedDict recusively
1833 # @param ori_dict a nested dict or ordereddict
1835 # @retval new dict or orderdict
1837 def CopyDict(ori_dict
):
1838 dict_type
= ori_dict
.__class
__
1839 if dict_type
not in (dict,OrderedDict
):
1841 new_dict
= dict_type()
1842 for key
in ori_dict
:
1843 if isinstance(ori_dict
[key
],(dict,OrderedDict
)):
1844 new_dict
[key
] = CopyDict(ori_dict
[key
])
1846 new_dict
[key
] = ori_dict
[key
]
1850 # Remove the c/c++ comments: // and /* */
1852 def RemoveCComments(ctext
):
1853 return re
.sub('//.*?\n|/\*.*?\*/', '\n', ctext
, flags
=re
.S
)