2 # Common routines used by all tools
4 # Copyright (c) 2007 - 2019, Intel Corporation. All rights reserved.<BR>
5 # This program and the accompanying materials
6 # are licensed and made available under the terms and conditions of the BSD License
7 # which accompanies this distribution. The full text of the license may be found at
8 # http://opensource.org/licenses/bsd-license.php
10 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
17 from __future__
import absolute_import
27 from random
import sample
28 from struct
import pack
31 from collections
import OrderedDict
33 import Common
.LongFilePathOs
as os
34 from Common
import EdkLogger
as EdkLogger
35 from Common
import GlobalData
as GlobalData
36 from Common
.DataType
import *
37 from Common
.BuildToolError
import *
38 from CommonDataClass
.DataClass
import *
39 from Common
.Parsing
import GetSplitValueList
40 from Common
.LongFilePathSupport
import OpenLongFilePath
as open
41 from Common
.MultipleWorkspace
import MultipleWorkspace
as mws
42 from CommonDataClass
.Exceptions
import BadExpression
43 from Common
.caching
import cached_property
45 ## Regular expression used to find out place holders in string template
46 gPlaceholderPattern
= re
.compile("\$\{([^$()\s]+)\}", re
.MULTILINE | re
.UNICODE
)
48 ## regular expressions for map file processing
49 startPatternGeneral
= re
.compile("^Start[' ']+Length[' ']+Name[' ']+Class")
50 addressPatternGeneral
= re
.compile("^Address[' ']+Publics by Value[' ']+Rva\+Base")
51 valuePatternGcc
= re
.compile('^([\w_\.]+) +([\da-fA-Fx]+) +([\da-fA-Fx]+)$')
52 pcdPatternGcc
= re
.compile('^([\da-fA-Fx]+) +([\da-fA-Fx]+)')
53 secReGeneral
= re
.compile('^([\da-fA-F]+):([\da-fA-F]+) +([\da-fA-F]+)[Hh]? +([.\w\$]+) +(\w+)', re
.UNICODE
)
55 StructPattern
= re
.compile(r
'[_a-zA-Z][0-9A-Za-z_]*$')
57 ## Dictionary used to store dependencies of files
58 gDependencyDatabase
= {} # arch : {file path : [dependent files list]}
61 # If a module is built more than once with different PCDs or library classes
62 # a temporary INF file with same content is created, the temporary file is removed
67 def GetVariableOffset(mapfilepath
, efifilepath
, varnames
):
68 """ Parse map file to get variable offset in current EFI file
69 @param mapfilepath Map file absolution path
70 @param efifilepath: EFI binary file full path
71 @param varnames iteratable container whose elements are variable names to be searched
73 @return List whos elements are tuple with variable name and raw offset
77 f
= open(mapfilepath
, 'r')
83 if len(lines
) == 0: return None
84 firstline
= lines
[0].strip()
85 if (firstline
.startswith("Archive member included ") and
86 firstline
.endswith(" file (symbol)")):
87 return _parseForGCC(lines
, efifilepath
, varnames
)
88 if firstline
.startswith("# Path:"):
89 return _parseForXcode(lines
, efifilepath
, varnames
)
90 return _parseGeneral(lines
, efifilepath
, varnames
)
92 def _parseForXcode(lines
, efifilepath
, varnames
):
97 if status
== 0 and line
== "# Symbols:":
100 if status
== 1 and len(line
) != 0:
101 for varname
in varnames
:
103 # cannot pregenerate this RegEx since it uses varname from varnames.
104 m
= re
.match('^([\da-fA-FxX]+)([\s\S]*)([_]*%s)$' % varname
, line
)
106 ret
.append((varname
, m
.group(1)))
109 def _parseForGCC(lines
, efifilepath
, varnames
):
110 """ Parse map file generated by GCC linker """
114 for index
, line
in enumerate(lines
):
116 # status machine transection
117 if status
== 0 and line
== "Memory Configuration":
120 elif status
== 1 and line
== 'Linker script and memory map':
123 elif status
==2 and line
== 'START GROUP':
129 m
= valuePatternGcc
.match(line
)
131 sections
.append(m
.groups(0))
132 for varname
in varnames
:
134 m
= re
.match("^.data.(%s)" % varname
, line
)
136 m
= re
.match(".data.(%s)$" % varname
, line
)
138 Str
= lines
[index
+ 1]
140 Str
= line
[len(".data.%s" % varname
):]
142 m
= pcdPatternGcc
.match(Str
.strip())
144 varoffset
.append((varname
, int(m
.groups(0)[0], 16), int(sections
[-1][1], 16), sections
[-1][0]))
148 # get section information from efi file
149 efisecs
= PeImageClass(efifilepath
).SectionHeaderList
150 if efisecs
is None or len(efisecs
) == 0:
154 for efisec
in efisecs
:
155 for section
in sections
:
156 if section
[0].strip() == efisec
[0].strip() and section
[0].strip() == '.text':
157 redirection
= int(section
[1], 16) - efisec
[1]
160 for var
in varoffset
:
161 for efisec
in efisecs
:
162 if var
[1] >= efisec
[1] and var
[1] < efisec
[1]+efisec
[3]:
163 ret
.append((var
[0], hex(efisec
[2] + var
[1] - efisec
[1] - redirection
)))
166 def _parseGeneral(lines
, efifilepath
, varnames
):
167 status
= 0 #0 - beginning of file; 1 - PE section definition; 2 - symbol table
168 secs
= [] # key = section name
170 symRe
= re
.compile('^([\da-fA-F]+):([\da-fA-F]+) +([\.:\\\\\w\?@\$]+) +([\da-fA-F]+)', re
.UNICODE
)
174 if startPatternGeneral
.match(line
):
177 if addressPatternGeneral
.match(line
):
180 if line
.startswith("entry point at"):
183 if status
== 1 and len(line
) != 0:
184 m
= secReGeneral
.match(line
)
185 assert m
is not None, "Fail to parse the section in map file , line is %s" % line
186 sec_no
, sec_start
, sec_length
, sec_name
, sec_class
= m
.groups(0)
187 secs
.append([int(sec_no
, 16), int(sec_start
, 16), int(sec_length
, 16), sec_name
, sec_class
])
188 if status
== 2 and len(line
) != 0:
189 for varname
in varnames
:
190 m
= symRe
.match(line
)
191 assert m
is not None, "Fail to parse the symbol in map file, line is %s" % line
192 sec_no
, sym_offset
, sym_name
, vir_addr
= m
.groups(0)
193 sec_no
= int(sec_no
, 16)
194 sym_offset
= int(sym_offset
, 16)
195 vir_addr
= int(vir_addr
, 16)
196 # cannot pregenerate this RegEx since it uses varname from varnames.
197 m2
= re
.match('^[_]*(%s)' % varname
, sym_name
)
199 # fond a binary pcd entry in map file
201 if sec
[0] == sec_no
and (sym_offset
>= sec
[1] and sym_offset
< sec
[1] + sec
[2]):
202 varoffset
.append([varname
, sec
[3], sym_offset
, vir_addr
, sec_no
])
204 if not varoffset
: return []
206 # get section information from efi file
207 efisecs
= PeImageClass(efifilepath
).SectionHeaderList
208 if efisecs
is None or len(efisecs
) == 0:
212 for var
in varoffset
:
214 for efisec
in efisecs
:
216 if var
[1].strip() == efisec
[0].strip():
217 ret
.append((var
[0], hex(efisec
[2] + var
[2])))
218 elif var
[4] == index
:
219 ret
.append((var
[0], hex(efisec
[2] + var
[2])))
223 ## Routine to process duplicated INF
225 # This function is called by following two cases:
228 # Pkg/module/module.inf
229 # Pkg/module/module.inf {
231 # FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836
234 # INF Pkg/module/module.inf
235 # INF FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836 Pkg/module/module.inf
237 # This function copies Pkg/module/module.inf to
238 # Conf/.cache/0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf
240 # @param Path Original PathClass object
241 # @param BaseName New file base name
243 # @retval return the new PathClass object
245 def ProcessDuplicatedInf(Path
, BaseName
, Workspace
):
246 Filename
= os
.path
.split(Path
.File
)[1]
248 Filename
= BaseName
+ Path
.BaseName
+ Filename
[Filename
.rfind('.'):]
250 Filename
= BaseName
+ Path
.BaseName
253 # If -N is specified on command line, cache is disabled
254 # The directory has to be created
256 DbDir
= os
.path
.split(GlobalData
.gDatabasePath
)[0]
257 if not os
.path
.exists(DbDir
):
260 # A temporary INF is copied to database path which must have write permission
261 # The temporary will be removed at the end of build
262 # In case of name conflict, the file name is
263 # FILE_GUIDBaseName (0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf)
265 TempFullPath
= os
.path
.join(DbDir
,
267 RtPath
= PathClass(Path
.File
, Workspace
)
269 # Modify the full path to temporary path, keep other unchanged
271 # To build same module more than once, the module path with FILE_GUID overridden has
272 # the file name FILE_GUIDmodule.inf, but the relative path (self.MetaFile.File) is the real path
273 # in DSC which is used as relative path by C files and other files in INF.
274 # A trick was used: all module paths are PathClass instances, after the initialization
275 # of PathClass, the PathClass.Path is overridden by the temporary INF path.
277 # The reason for creating a temporary INF is:
278 # Platform.Modules which is the base to create ModuleAutoGen objects is a dictionary,
279 # the key is the full path of INF, the value is an object to save overridden library instances, PCDs.
280 # A different key for the same module is needed to create different output directory,
281 # retrieve overridden PCDs, library instances.
283 # The BaseName is the FILE_GUID which is also the output directory name.
286 RtPath
.Path
= TempFullPath
287 RtPath
.BaseName
= BaseName
289 # If file exists, compare contents
291 if os
.path
.exists(TempFullPath
):
292 with
open(str(Path
), 'rb') as f1
, open(TempFullPath
, 'rb') as f2
:
293 if f1
.read() == f2
.read():
295 _TempInfs
.append(TempFullPath
)
296 shutil
.copy2(str(Path
), TempFullPath
)
299 ## Remove temporary created INFs whose paths were saved in _TempInfs
301 def ClearDuplicatedInf():
303 File
= _TempInfs
.pop()
304 if os
.path
.exists(File
):
307 ## Convert GUID string in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx style to C structure style
309 # @param Guid The GUID string
311 # @retval string The GUID string in C structure style
313 def GuidStringToGuidStructureString(Guid
):
314 GuidList
= Guid
.split('-')
316 for Index
in range(0, 3, 1):
317 Result
= Result
+ '0x' + GuidList
[Index
] + ', '
318 Result
= Result
+ '{0x' + GuidList
[3][0:2] + ', 0x' + GuidList
[3][2:4]
319 for Index
in range(0, 12, 2):
320 Result
= Result
+ ', 0x' + GuidList
[4][Index
:Index
+ 2]
324 ## Convert GUID structure in byte array to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
326 # @param GuidValue The GUID value in byte array
328 # @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
330 def GuidStructureByteArrayToGuidString(GuidValue
):
331 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
332 guidValueList
= guidValueString
.split(",")
333 if len(guidValueList
) != 16:
335 #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
337 return "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
338 int(guidValueList
[3], 16),
339 int(guidValueList
[2], 16),
340 int(guidValueList
[1], 16),
341 int(guidValueList
[0], 16),
342 int(guidValueList
[5], 16),
343 int(guidValueList
[4], 16),
344 int(guidValueList
[7], 16),
345 int(guidValueList
[6], 16),
346 int(guidValueList
[8], 16),
347 int(guidValueList
[9], 16),
348 int(guidValueList
[10], 16),
349 int(guidValueList
[11], 16),
350 int(guidValueList
[12], 16),
351 int(guidValueList
[13], 16),
352 int(guidValueList
[14], 16),
353 int(guidValueList
[15], 16)
358 ## Convert GUID string in C structure style to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
360 # @param GuidValue The GUID value in C structure format
362 # @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
364 def GuidStructureStringToGuidString(GuidValue
):
365 if not GlobalData
.gGuidCFormatPattern
.match(GuidValue
):
367 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
368 guidValueList
= guidValueString
.split(",")
369 if len(guidValueList
) != 11:
371 #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
373 return "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
374 int(guidValueList
[0], 16),
375 int(guidValueList
[1], 16),
376 int(guidValueList
[2], 16),
377 int(guidValueList
[3], 16),
378 int(guidValueList
[4], 16),
379 int(guidValueList
[5], 16),
380 int(guidValueList
[6], 16),
381 int(guidValueList
[7], 16),
382 int(guidValueList
[8], 16),
383 int(guidValueList
[9], 16),
384 int(guidValueList
[10], 16)
389 ## Convert GUID string in C structure style to xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx
391 # @param GuidValue The GUID value in C structure format
393 # @retval string The GUID value in xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx format
395 def GuidStructureStringToGuidValueName(GuidValue
):
396 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "")
397 guidValueList
= guidValueString
.split(",")
398 if len(guidValueList
) != 11:
399 EdkLogger
.error(None, FORMAT_INVALID
, "Invalid GUID value string [%s]" % GuidValue
)
400 return "%08x_%04x_%04x_%02x%02x_%02x%02x%02x%02x%02x%02x" % (
401 int(guidValueList
[0], 16),
402 int(guidValueList
[1], 16),
403 int(guidValueList
[2], 16),
404 int(guidValueList
[3], 16),
405 int(guidValueList
[4], 16),
406 int(guidValueList
[5], 16),
407 int(guidValueList
[6], 16),
408 int(guidValueList
[7], 16),
409 int(guidValueList
[8], 16),
410 int(guidValueList
[9], 16),
411 int(guidValueList
[10], 16)
414 ## Create directories
416 # @param Directory The directory name
418 def CreateDirectory(Directory
):
419 if Directory
is None or Directory
.strip() == "":
422 if not os
.access(Directory
, os
.F_OK
):
423 os
.makedirs(Directory
)
428 ## Remove directories, including files and sub-directories in it
430 # @param Directory The directory name
432 def RemoveDirectory(Directory
, Recursively
=False):
433 if Directory
is None or Directory
.strip() == "" or not os
.path
.exists(Directory
):
436 CurrentDirectory
= os
.getcwd()
438 for File
in os
.listdir("."):
439 if os
.path
.isdir(File
):
440 RemoveDirectory(File
, Recursively
)
443 os
.chdir(CurrentDirectory
)
446 ## Store content in file
448 # This method is used to save file only when its content is changed. This is
449 # quite useful for "make" system to decide what will be re-built and what won't.
451 # @param File The path of file
452 # @param Content The new content of the file
453 # @param IsBinaryFile The flag indicating if the file is binary file or not
455 # @retval True If the file content is changed and the file is renewed
456 # @retval False If the file content is the same
458 def SaveFileOnChange(File
, Content
, IsBinaryFile
=True):
460 if os
.path
.exists(File
):
463 with
open(File
, "rb") as f
:
464 if Content
== f
.read():
467 EdkLogger
.error(None, FILE_OPEN_FAILURE
, ExtraData
=File
)
470 with
open(File
, "r") as f
:
471 if Content
== f
.read():
474 EdkLogger
.error(None, FILE_OPEN_FAILURE
, ExtraData
=File
)
476 DirName
= os
.path
.dirname(File
)
477 if not CreateDirectory(DirName
):
478 EdkLogger
.error(None, FILE_CREATE_FAILURE
, "Could not create directory %s" % DirName
)
481 DirName
= os
.getcwd()
482 if not os
.access(DirName
, os
.W_OK
):
483 EdkLogger
.error(None, PERMISSION_FAILURE
, "Do not have write permission on directory %s" % DirName
)
487 with
open(File
, "wb") as Fd
:
490 EdkLogger
.error(None, FILE_CREATE_FAILURE
, ExtraData
='IOError %s' % X
)
493 with
open(File
, 'w') as Fd
:
496 EdkLogger
.error(None, FILE_CREATE_FAILURE
, ExtraData
='IOError %s' % X
)
500 ## Retrieve and cache the real path name in file system
502 # @param Root The root directory of path relative to
504 # @retval str The path string if the path exists
505 # @retval None If path doesn't exist
511 def __init__(self
, Root
):
513 for F
in os
.listdir(Root
):
515 self
._UPPER
_CACHE
_[F
.upper()] = F
518 def __getitem__(self
, Path
):
519 Path
= Path
[len(os
.path
.commonprefix([Path
, self
._Root
])):]
522 if Path
and Path
[0] == os
.path
.sep
:
524 if Path
in self
._CACHE
_:
525 return os
.path
.join(self
._Root
, Path
)
526 UpperPath
= Path
.upper()
527 if UpperPath
in self
._UPPER
_CACHE
_:
528 return os
.path
.join(self
._Root
, self
._UPPER
_CACHE
_[UpperPath
])
532 SepIndex
= Path
.find(os
.path
.sep
)
534 Parent
= UpperPath
[:SepIndex
]
535 if Parent
not in self
._UPPER
_CACHE
_:
537 LastSepIndex
= SepIndex
538 SepIndex
= Path
.find(os
.path
.sep
, LastSepIndex
+ 1)
540 if LastSepIndex
== -1:
545 SepIndex
= LastSepIndex
547 Parent
= Path
[:SepIndex
]
548 ParentKey
= UpperPath
[:SepIndex
]
549 if ParentKey
not in self
._UPPER
_CACHE
_:
553 if Parent
in self
._CACHE
_:
556 ParentDir
= self
._UPPER
_CACHE
_[ParentKey
]
557 for F
in os
.listdir(ParentDir
):
558 Dir
= os
.path
.join(ParentDir
, F
)
559 self
._CACHE
_.add(Dir
)
560 self
._UPPER
_CACHE
_[Dir
.upper()] = Dir
562 SepIndex
= Path
.find(os
.path
.sep
, SepIndex
+ 1)
565 if Path
in self
._CACHE
_:
566 return os
.path
.join(self
._Root
, Path
)
567 elif UpperPath
in self
._UPPER
_CACHE
_:
568 return os
.path
.join(self
._Root
, self
._UPPER
_CACHE
_[UpperPath
])
571 def RealPath(File
, Dir
='', OverrideDir
=''):
572 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
573 NewFile
= GlobalData
.gAllFiles
[NewFile
]
574 if not NewFile
and OverrideDir
:
575 NewFile
= os
.path
.normpath(os
.path
.join(OverrideDir
, File
))
576 NewFile
= GlobalData
.gAllFiles
[NewFile
]
579 ## Get GUID value from given packages
581 # @param CName The CName of the GUID
582 # @param PackageList List of packages looking-up in
583 # @param Inffile The driver file
585 # @retval GuidValue if the CName is found in any given package
586 # @retval None if the CName is not found in all given packages
588 def GuidValue(CName
, PackageList
, Inffile
= None):
589 for P
in PackageList
:
590 GuidKeys
= list(P
.Guids
.keys())
591 if Inffile
and P
._PrivateGuids
:
592 if not Inffile
.startswith(P
.MetaFile
.Dir
):
593 GuidKeys
= [x
for x
in P
.Guids
if x
not in P
._PrivateGuids
]
594 if CName
in GuidKeys
:
595 return P
.Guids
[CName
]
599 ## A string template class
601 # This class implements a template for string replacement. A string template
602 # looks like following
604 # ${BEGIN} other_string ${placeholder_name} other_string ${END}
606 # The string between ${BEGIN} and ${END} will be repeated as many times as the
607 # length of "placeholder_name", which is a list passed through a dict. The
608 # "placeholder_name" is the key name of the dict. The ${BEGIN} and ${END} can
609 # be not used and, in this case, the "placeholder_name" must not a list and it
610 # will just be replaced once.
612 class TemplateString(object):
613 _REPEAT_START_FLAG
= "BEGIN"
614 _REPEAT_END_FLAG
= "END"
616 class Section(object):
617 _LIST_TYPES
= [type([]), type(set()), type((0,))]
619 def __init__(self
, TemplateSection
, PlaceHolderList
):
620 self
._Template
= TemplateSection
621 self
._PlaceHolderList
= []
623 # Split the section into sub-sections according to the position of placeholders
625 self
._SubSectionList
= []
628 # The placeholders passed in must be in the format of
630 # PlaceHolderName, PlaceHolderStartPoint, PlaceHolderEndPoint
632 for PlaceHolder
, Start
, End
in PlaceHolderList
:
633 self
._SubSectionList
.append(TemplateSection
[SubSectionStart
:Start
])
634 self
._SubSectionList
.append(TemplateSection
[Start
:End
])
635 self
._PlaceHolderList
.append(PlaceHolder
)
636 SubSectionStart
= End
637 if SubSectionStart
< len(TemplateSection
):
638 self
._SubSectionList
.append(TemplateSection
[SubSectionStart
:])
640 self
._SubSectionList
= [TemplateSection
]
643 return self
._Template
+ " : " + str(self
._PlaceHolderList
)
645 def Instantiate(self
, PlaceHolderValues
):
647 RepeatPlaceHolders
= {}
648 NonRepeatPlaceHolders
= {}
650 for PlaceHolder
in self
._PlaceHolderList
:
651 if PlaceHolder
not in PlaceHolderValues
:
653 Value
= PlaceHolderValues
[PlaceHolder
]
654 if type(Value
) in self
._LIST
_TYPES
:
656 RepeatTime
= len(Value
)
657 elif RepeatTime
!= len(Value
):
661 "${%s} has different repeat time from others!" % PlaceHolder
,
662 ExtraData
=str(self
._Template
)
664 RepeatPlaceHolders
["${%s}" % PlaceHolder
] = Value
666 NonRepeatPlaceHolders
["${%s}" % PlaceHolder
] = Value
668 if NonRepeatPlaceHolders
:
670 for S
in self
._SubSectionList
:
671 if S
not in NonRepeatPlaceHolders
:
674 StringList
.append(str(NonRepeatPlaceHolders
[S
]))
676 StringList
= self
._SubSectionList
678 if RepeatPlaceHolders
:
680 for Index
in range(RepeatTime
):
682 if S
not in RepeatPlaceHolders
:
683 TempStringList
.append(S
)
685 TempStringList
.append(str(RepeatPlaceHolders
[S
][Index
]))
686 StringList
= TempStringList
688 return "".join(StringList
)
691 def __init__(self
, Template
=None):
693 self
.IsBinary
= False
694 self
._Template
= Template
695 self
._TemplateSectionList
= self
._Parse
(Template
)
699 # @retval string The string replaced
702 return "".join(self
.String
)
704 ## Split the template string into fragments per the ${BEGIN} and ${END} flags
706 # @retval list A list of TemplateString.Section objects
708 def _Parse(self
, Template
):
713 TemplateSectionList
= []
715 MatchObj
= gPlaceholderPattern
.search(Template
, SearchFrom
)
717 if MatchEnd
<= len(Template
):
718 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:], PlaceHolderList
)
719 TemplateSectionList
.append(TemplateSection
)
722 MatchString
= MatchObj
.group(1)
723 MatchStart
= MatchObj
.start()
724 MatchEnd
= MatchObj
.end()
726 if MatchString
== self
._REPEAT
_START
_FLAG
:
727 if MatchStart
> SectionStart
:
728 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:MatchStart
], PlaceHolderList
)
729 TemplateSectionList
.append(TemplateSection
)
730 SectionStart
= MatchEnd
732 elif MatchString
== self
._REPEAT
_END
_FLAG
:
733 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:MatchStart
], PlaceHolderList
)
734 TemplateSectionList
.append(TemplateSection
)
735 SectionStart
= MatchEnd
738 PlaceHolderList
.append((MatchString
, MatchStart
- SectionStart
, MatchEnd
- SectionStart
))
739 SearchFrom
= MatchEnd
740 return TemplateSectionList
742 ## Replace the string template with dictionary of placeholders and append it to previous one
744 # @param AppendString The string template to append
745 # @param Dictionary The placeholder dictionaries
747 def Append(self
, AppendString
, Dictionary
=None):
749 SectionList
= self
._Parse
(AppendString
)
750 self
.String
.append( "".join(S
.Instantiate(Dictionary
) for S
in SectionList
))
752 if isinstance(AppendString
,list):
753 self
.String
.extend(AppendString
)
755 self
.String
.append(AppendString
)
757 ## Replace the string template with dictionary of placeholders
759 # @param Dictionary The placeholder dictionaries
761 # @retval str The string replaced with placeholder values
763 def Replace(self
, Dictionary
=None):
764 return "".join(S
.Instantiate(Dictionary
) for S
in self
._TemplateSectionList
)
766 ## Progress indicator class
768 # This class makes use of thread to print progress on console.
771 # for avoiding deadloop
773 _ProgressThread
= None
774 _CheckInterval
= 0.25
778 # @param OpenMessage The string printed before progress characters
779 # @param CloseMessage The string printed after progress characters
780 # @param ProgressChar The character used to indicate the progress
781 # @param Interval The interval in seconds between two progress characters
783 def __init__(self
, OpenMessage
="", CloseMessage
="", ProgressChar
='.', Interval
=1.0):
784 self
.PromptMessage
= OpenMessage
785 self
.CodaMessage
= CloseMessage
786 self
.ProgressChar
= ProgressChar
787 self
.Interval
= Interval
788 if Progressor
._StopFlag
is None:
789 Progressor
._StopFlag
= threading
.Event()
791 ## Start to print progress character
793 # @param OpenMessage The string printed before progress characters
795 def Start(self
, OpenMessage
=None):
796 if OpenMessage
is not None:
797 self
.PromptMessage
= OpenMessage
798 Progressor
._StopFlag
.clear()
799 if Progressor
._ProgressThread
is None:
800 Progressor
._ProgressThread
= threading
.Thread(target
=self
._ProgressThreadEntry
)
801 Progressor
._ProgressThread
.setDaemon(False)
802 Progressor
._ProgressThread
.start()
804 ## Stop printing progress character
806 # @param CloseMessage The string printed after progress characters
808 def Stop(self
, CloseMessage
=None):
809 OriginalCodaMessage
= self
.CodaMessage
810 if CloseMessage
is not None:
811 self
.CodaMessage
= CloseMessage
813 self
.CodaMessage
= OriginalCodaMessage
815 ## Thread entry method
816 def _ProgressThreadEntry(self
):
817 sys
.stdout
.write(self
.PromptMessage
+ " ")
820 while not Progressor
._StopFlag
.isSet():
822 sys
.stdout
.write(self
.ProgressChar
)
824 TimeUp
= self
.Interval
825 time
.sleep(self
._CheckInterval
)
826 TimeUp
-= self
._CheckInterval
827 sys
.stdout
.write(" " + self
.CodaMessage
+ "\n")
830 ## Abort the progress display
833 if Progressor
._StopFlag
is not None:
834 Progressor
._StopFlag
.set()
835 if Progressor
._ProgressThread
is not None:
836 Progressor
._ProgressThread
.join()
837 Progressor
._ProgressThread
= None
840 ## Dictionary using prioritized list as key
844 _TupleType
= type(())
846 _ValidWildcardList
= ['COMMON', 'DEFAULT', 'ALL', TAB_STAR
, 'PLATFORM']
848 def __init__(self
, _Single_
=False, _Level_
=2):
849 self
._Level
_ = _Level_
851 self
._Single
_ = _Single_
854 def __getitem__(self
, key
):
857 if KeyType
== self
._ListType
or KeyType
== self
._TupleType
:
861 elif self
._Level
_ > 1:
862 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
866 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
868 if FirstKey
is None or str(FirstKey
).upper() in self
._ValidWildcardList
:
869 FirstKey
= self
._Wildcard
872 return self
._GetSingleValue
(FirstKey
, RestKeys
)
874 return self
._GetAllValues
(FirstKey
, RestKeys
)
876 def _GetSingleValue(self
, FirstKey
, RestKeys
):
878 #print "%s-%s" % (FirstKey, self._Level_) ,
880 if FirstKey
== self
._Wildcard
:
881 if FirstKey
in self
.data
:
882 Value
= self
.data
[FirstKey
][RestKeys
]
884 for Key
in self
.data
:
885 Value
= self
.data
[Key
][RestKeys
]
886 if Value
is not None: break
888 if FirstKey
in self
.data
:
889 Value
= self
.data
[FirstKey
][RestKeys
]
890 if Value
is None and self
._Wildcard
in self
.data
:
892 Value
= self
.data
[self
._Wildcard
][RestKeys
]
894 if FirstKey
== self
._Wildcard
:
895 if FirstKey
in self
.data
:
896 Value
= self
.data
[FirstKey
]
898 for Key
in self
.data
:
899 Value
= self
.data
[Key
]
900 if Value
is not None: break
902 if FirstKey
in self
.data
:
903 Value
= self
.data
[FirstKey
]
904 elif self
._Wildcard
in self
.data
:
905 Value
= self
.data
[self
._Wildcard
]
908 def _GetAllValues(self
, FirstKey
, RestKeys
):
911 if FirstKey
== self
._Wildcard
:
912 for Key
in self
.data
:
913 Value
+= self
.data
[Key
][RestKeys
]
915 if FirstKey
in self
.data
:
916 Value
+= self
.data
[FirstKey
][RestKeys
]
917 if self
._Wildcard
in self
.data
:
918 Value
+= self
.data
[self
._Wildcard
][RestKeys
]
920 if FirstKey
== self
._Wildcard
:
921 for Key
in self
.data
:
922 Value
.append(self
.data
[Key
])
924 if FirstKey
in self
.data
:
925 Value
.append(self
.data
[FirstKey
])
926 if self
._Wildcard
in self
.data
:
927 Value
.append(self
.data
[self
._Wildcard
])
931 def __setitem__(self
, key
, value
):
934 if KeyType
== self
._ListType
or KeyType
== self
._TupleType
:
939 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
943 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
945 if FirstKey
in self
._ValidWildcardList
:
946 FirstKey
= self
._Wildcard
948 if FirstKey
not in self
.data
and self
._Level
_ > 0:
949 self
.data
[FirstKey
] = tdict(self
._Single
_, self
._Level
_ - 1)
952 self
.data
[FirstKey
][RestKeys
] = value
954 self
.data
[FirstKey
] = value
956 def SetGreedyMode(self
):
957 self
._Single
_ = False
959 for Key
in self
.data
:
960 self
.data
[Key
].SetGreedyMode()
962 def SetSingleMode(self
):
965 for Key
in self
.data
:
966 self
.data
[Key
].SetSingleMode()
968 def GetKeys(self
, KeyIndex
=0):
971 return set(self
.data
.keys())
974 for Key
in self
.data
:
975 keys |
= self
.data
[Key
].GetKeys(KeyIndex
- 1)
978 def AnalyzePcdExpression(Setting
):
979 RanStr
= ''.join(sample(string
.ascii_letters
+ string
.digits
, 8))
980 Setting
= Setting
.replace('\\\\', RanStr
).strip()
981 # There might be escaped quote in a string: \", \\\" , \', \\\'
983 # There might be '|' in string and in ( ... | ... ), replace it with '-'
985 InSingleQuoteStr
= False
986 InDoubleQuoteStr
= False
988 for Index
, ch
in enumerate(Data
):
989 if ch
== '"' and not InSingleQuoteStr
:
990 if Data
[Index
- 1] != '\\':
991 InDoubleQuoteStr
= not InDoubleQuoteStr
992 elif ch
== "'" and not InDoubleQuoteStr
:
993 if Data
[Index
- 1] != '\\':
994 InSingleQuoteStr
= not InSingleQuoteStr
995 elif ch
== '(' and not (InSingleQuoteStr
or InDoubleQuoteStr
):
997 elif ch
== ')' and not (InSingleQuoteStr
or InDoubleQuoteStr
):
1000 if (Pair
> 0 or InSingleQuoteStr
or InDoubleQuoteStr
) and ch
== TAB_VALUE_SPLIT
:
1007 Pos
= NewStr
.find(TAB_VALUE_SPLIT
, StartPos
)
1009 FieldList
.append(Setting
[StartPos
:].strip())
1011 FieldList
.append(Setting
[StartPos
:Pos
].strip())
1013 for i
, ch
in enumerate(FieldList
):
1015 FieldList
[i
] = ch
.replace(RanStr
,'\\\\')
1018 def ParseFieldValue (Value
):
1019 def ParseDevPathValue (Value
):
1021 Value
.replace('\\', '/').replace(' ', '')
1023 Cmd
= 'DevicePath ' + '"' + Value
+ '"'
1025 p
= subprocess
.Popen(Cmd
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, shell
=True)
1026 out
, err
= p
.communicate()
1027 except Exception as X
:
1028 raise BadExpression("DevicePath: %s" % (str(X
)) )
1030 subprocess
._cleanup
()
1034 raise BadExpression("DevicePath: %s" % str(err
))
1035 out
= out
.decode(encoding
='utf-8', errors
='ignore')
1036 Size
= len(out
.split())
1037 out
= ','.join(out
.split())
1038 return '{' + out
+ '}', Size
1040 if "{CODE(" in Value
:
1041 return Value
, len(Value
.split(","))
1042 if isinstance(Value
, type(0)):
1043 return Value
, (Value
.bit_length() + 7) // 8
1044 if not isinstance(Value
, type('')):
1045 raise BadExpression('Type %s is %s' %(Value
, type(Value
)))
1046 Value
= Value
.strip()
1047 if Value
.startswith(TAB_UINT8
) and Value
.endswith(')'):
1048 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1050 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1052 if Value
.startswith(TAB_UINT16
) 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_UINT32
) 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_UINT64
) 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_GUID
) and Value
.endswith(')'):
1068 Value
= Value
.split('(', 1)[1][:-1].strip()
1069 if Value
[0] == '{' and Value
[-1] == '}':
1070 TmpValue
= GuidStructureStringToGuidString(Value
)
1072 raise BadExpression("Invalid GUID value string %s" % Value
)
1074 if Value
[0] == '"' and Value
[-1] == '"':
1077 Value
= str(uuid
.UUID(Value
).bytes_le
)
1078 if Value
.startswith("b'"):
1080 Value
= "'" + Value
+ "'"
1081 except ValueError as Message
:
1082 raise BadExpression(Message
)
1083 Value
, Size
= ParseFieldValue(Value
)
1085 if Value
.startswith('L"') and Value
.endswith('"'):
1087 # translate escape character
1097 Value
= (Value
<< 16) |
ord(Char
)
1098 return Value
, (len(List
) + 1) * 2
1099 if Value
.startswith('"') and Value
.endswith('"'):
1101 # translate escape character
1110 Value
= (Value
<< 8) |
ord(Char
)
1111 return Value
, len(List
) + 1
1112 if Value
.startswith("L'") and Value
.endswith("'"):
1113 # Unicode Character Constant
1114 # translate escape character
1122 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1126 Value
= (Value
<< 16) |
ord(Char
)
1127 return Value
, len(List
) * 2
1128 if Value
.startswith("'") and Value
.endswith("'"):
1129 # Character constant
1130 # translate escape character
1137 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1141 Value
= (Value
<< 8) |
ord(Char
)
1142 return Value
, len(List
)
1143 if Value
.startswith('{') and Value
.endswith('}'):
1146 List
= [Item
.strip() for Item
in Value
.split(',')]
1151 ItemValue
, Size
= ParseFieldValue(Item
)
1153 for I
in range(Size
):
1154 Value
= (Value
<< 8) |
((ItemValue
>> 8 * I
) & 0xff)
1155 return Value
, RetSize
1156 if Value
.startswith('DEVICE_PATH(') and Value
.endswith(')'):
1157 Value
= Value
.replace("DEVICE_PATH(", '').rstrip(')')
1158 Value
= Value
.strip().strip('"')
1159 return ParseDevPathValue(Value
)
1160 if Value
.lower().startswith('0x'):
1162 Value
= int(Value
, 16)
1164 raise BadExpression("invalid hex value: %s" % Value
)
1167 return Value
, (Value
.bit_length() + 7) // 8
1168 if Value
[0].isdigit():
1169 Value
= int(Value
, 10)
1172 return Value
, (Value
.bit_length() + 7) // 8
1173 if Value
.lower() == 'true':
1175 if Value
.lower() == 'false':
1181 # Analyze DSC PCD value, since there is no data type info in DSC
1182 # This function is used to match functions (AnalyzePcdData) used for retrieving PCD value from database
1183 # 1. Feature flag: TokenSpace.PcdCName|PcdValue
1184 # 2. Fix and Patch:TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1185 # 3. Dynamic default:
1186 # TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1187 # TokenSpace.PcdCName|PcdValue
1189 # TokenSpace.PcdCName|VpdOffset[|VpdValue]
1190 # TokenSpace.PcdCName|VpdOffset[|MaxSize[|VpdValue]]
1192 # TokenSpace.PcdCName|HiiString|VariableGuid|VariableOffset[|HiiValue]
1193 # PCD value needs to be located in such kind of string, and the PCD value might be an expression in which
1194 # there might have "|" operator, also in string value.
1196 # @param Setting: String contain information described above with "TokenSpace.PcdCName|" stripped
1197 # @param PcdType: PCD type: feature, fixed, dynamic default VPD HII
1198 # @param DataType: The datum type of PCD: VOID*, UNIT, BOOL
1200 # ValueList: A List contain fields described above
1201 # IsValid: True if conforming EBNF, otherwise False
1202 # Index: The index where PcdValue is in ValueList
1204 def AnalyzeDscPcd(Setting
, PcdType
, DataType
=''):
1205 FieldList
= AnalyzePcdExpression(Setting
)
1208 if PcdType
in (MODEL_PCD_FIXED_AT_BUILD
, MODEL_PCD_PATCHABLE_IN_MODULE
, MODEL_PCD_DYNAMIC_DEFAULT
, MODEL_PCD_DYNAMIC_EX_DEFAULT
):
1209 Value
= FieldList
[0]
1211 if len(FieldList
) > 1 and FieldList
[1]:
1212 DataType
= FieldList
[1]
1213 if FieldList
[1] != TAB_VOID
and StructPattern
.match(FieldList
[1]) is None:
1215 if len(FieldList
) > 2:
1219 IsValid
= (len(FieldList
) <= 1)
1221 IsValid
= (len(FieldList
) <= 3)
1225 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1229 return [str(Value
), DataType
, str(Size
)], IsValid
, 0
1230 elif PcdType
== MODEL_PCD_FEATURE_FLAG
:
1231 Value
= FieldList
[0]
1233 IsValid
= (len(FieldList
) <= 1)
1234 return [Value
, DataType
, str(Size
)], IsValid
, 0
1235 elif PcdType
in (MODEL_PCD_DYNAMIC_VPD
, MODEL_PCD_DYNAMIC_EX_VPD
):
1236 VpdOffset
= FieldList
[0]
1238 if not DataType
== TAB_VOID
:
1239 if len(FieldList
) > 1:
1240 Value
= FieldList
[1]
1242 if len(FieldList
) > 1:
1244 if len(FieldList
) > 2:
1245 Value
= FieldList
[2]
1247 IsValid
= (len(FieldList
) <= 1)
1249 IsValid
= (len(FieldList
) <= 3)
1252 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1256 return [VpdOffset
, str(Size
), Value
], IsValid
, 2
1257 elif PcdType
in (MODEL_PCD_DYNAMIC_HII
, MODEL_PCD_DYNAMIC_EX_HII
):
1258 IsValid
= (3 <= len(FieldList
) <= 5)
1259 HiiString
= FieldList
[0]
1260 Guid
= Offset
= Value
= Attribute
= ''
1261 if len(FieldList
) > 1:
1263 if len(FieldList
) > 2:
1264 Offset
= FieldList
[2]
1265 if len(FieldList
) > 3:
1266 Value
= FieldList
[3]
1267 if len(FieldList
) > 4:
1268 Attribute
= FieldList
[4]
1269 return [HiiString
, Guid
, Offset
, Value
, Attribute
], IsValid
, 3
1274 # Analyze the pcd Value, Datum type and TokenNumber.
1275 # Used to avoid split issue while the value string contain "|" character
1277 # @param[in] Setting: A String contain value/datum type/token number information;
1279 # @retval ValueList: A List contain value, datum type and toke number.
1281 def AnalyzePcdData(Setting
):
1282 ValueList
= ['', '', '']
1284 ValueRe
= re
.compile(r
'^\s*L?\".*\|.*\"')
1285 PtrValue
= ValueRe
.findall(Setting
)
1287 ValueUpdateFlag
= False
1289 if len(PtrValue
) >= 1:
1290 Setting
= re
.sub(ValueRe
, '', Setting
)
1291 ValueUpdateFlag
= True
1293 TokenList
= Setting
.split(TAB_VALUE_SPLIT
)
1294 ValueList
[0:len(TokenList
)] = TokenList
1297 ValueList
[0] = PtrValue
[0]
1301 ## check format of PCD value against its the datum type
1303 # For PCD value setting
1305 def CheckPcdDatum(Type
, Value
):
1306 if Type
== TAB_VOID
:
1307 ValueRe
= re
.compile(r
'\s*L?\".*\"\s*$')
1308 if not (((Value
.startswith('L"') or Value
.startswith('"')) and Value
.endswith('"'))
1309 or (Value
.startswith('{') and Value
.endswith('}')) or (Value
.startswith("L'") or Value
.startswith("'") and Value
.endswith("'"))
1311 return False, "Invalid value [%s] of type [%s]; must be in the form of {...} for array"\
1312 ", \"...\" or \'...\' for string, L\"...\" or L\'...\' for unicode string" % (Value
, Type
)
1313 elif ValueRe
.match(Value
):
1314 # Check the chars in UnicodeString or CString is printable
1315 if Value
.startswith("L"):
1319 Printset
= set(string
.printable
)
1320 Printset
.remove(TAB_PRINTCHAR_VT
)
1321 Printset
.add(TAB_PRINTCHAR_BS
)
1322 Printset
.add(TAB_PRINTCHAR_NUL
)
1323 if not set(Value
).issubset(Printset
):
1324 PrintList
= sorted(Printset
)
1325 return False, "Invalid PCD string value of type [%s]; must be printable chars %s." % (Type
, PrintList
)
1326 elif Type
== 'BOOLEAN':
1327 if Value
not in ['TRUE', 'True', 'true', '0x1', '0x01', '1', 'FALSE', 'False', 'false', '0x0', '0x00', '0']:
1328 return False, "Invalid value [%s] of type [%s]; must be one of TRUE, True, true, 0x1, 0x01, 1"\
1329 ", FALSE, False, false, 0x0, 0x00, 0" % (Value
, Type
)
1330 elif Type
in [TAB_UINT8
, TAB_UINT16
, TAB_UINT32
, TAB_UINT64
]:
1331 if Value
.startswith('0') and not Value
.lower().startswith('0x') and len(Value
) > 1 and Value
.lstrip('0'):
1332 Value
= Value
.lstrip('0')
1334 if Value
and int(Value
, 0) < 0:
1335 return False, "PCD can't be set to negative value[%s] for datum type [%s]" % (Value
, Type
)
1336 Value
= int(Value
, 0)
1337 if Value
> MAX_VAL_TYPE
[Type
]:
1338 return False, "Too large PCD value[%s] for datum type [%s]" % (Value
, Type
)
1340 return False, "Invalid value [%s] of type [%s];"\
1341 " must be a hexadecimal, decimal or octal in C language format." % (Value
, Type
)
1343 return True, "StructurePcd"
1347 def CommonPath(PathList
):
1348 P1
= min(PathList
).split(os
.path
.sep
)
1349 P2
= max(PathList
).split(os
.path
.sep
)
1350 for Index
in range(min(len(P1
), len(P2
))):
1351 if P1
[Index
] != P2
[Index
]:
1352 return os
.path
.sep
.join(P1
[:Index
])
1353 return os
.path
.sep
.join(P1
)
1355 class PathClass(object):
1356 def __init__(self
, File
='', Root
='', AlterRoot
='', Type
='', IsBinary
=False,
1357 Arch
='COMMON', ToolChainFamily
='', Target
='', TagName
='', ToolCode
=''):
1359 self
.File
= str(File
)
1360 if os
.path
.isabs(self
.File
):
1364 self
.Root
= str(Root
)
1365 self
.AlterRoot
= str(AlterRoot
)
1367 # Remove any '.' and '..' in path
1369 self
.Root
= mws
.getWs(self
.Root
, self
.File
)
1370 self
.Path
= os
.path
.normpath(os
.path
.join(self
.Root
, self
.File
))
1371 self
.Root
= os
.path
.normpath(CommonPath([self
.Root
, self
.Path
]))
1372 # eliminate the side-effect of 'C:'
1373 if self
.Root
[-1] == ':':
1374 self
.Root
+= os
.path
.sep
1375 # file path should not start with path separator
1376 if self
.Root
[-1] == os
.path
.sep
:
1377 self
.File
= self
.Path
[len(self
.Root
):]
1379 self
.File
= self
.Path
[len(self
.Root
) + 1:]
1381 self
.Path
= os
.path
.normpath(self
.File
)
1383 self
.SubDir
, self
.Name
= os
.path
.split(self
.File
)
1384 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1388 self
.Dir
= os
.path
.join(self
.Root
, self
.SubDir
)
1390 self
.Dir
= self
.Root
1392 self
.Dir
= self
.SubDir
1397 self
.Type
= self
.Ext
.lower()
1399 self
.IsBinary
= IsBinary
1400 self
.Target
= Target
1401 self
.TagName
= TagName
1402 self
.ToolCode
= ToolCode
1403 self
.ToolChainFamily
= ToolChainFamily
1405 ## Convert the object of this class to a string
1407 # Convert member Path of the class to a string
1409 # @retval string Formatted String
1414 ## Override __eq__ function
1416 # Check whether PathClass are the same
1418 # @retval False The two PathClass are different
1419 # @retval True The two PathClass are the same
1421 def __eq__(self
, Other
):
1422 return self
.Path
== str(Other
)
1424 ## Override __cmp__ function
1426 # Customize the comparison operation of two PathClass
1428 # @retval 0 The two PathClass are different
1429 # @retval -1 The first PathClass is less than the second PathClass
1430 # @retval 1 The first PathClass is Bigger than the second PathClass
1431 def __cmp__(self
, Other
):
1432 OtherKey
= str(Other
)
1435 if SelfKey
== OtherKey
:
1437 elif SelfKey
> OtherKey
:
1442 ## Override __hash__ function
1444 # Use Path as key in hash table
1446 # @retval string Key for hash table
1449 return hash(self
.Path
)
1453 return self
.Path
.upper()
1456 def TimeStamp(self
):
1457 return os
.stat(self
.Path
)[8]
1459 def Validate(self
, Type
='', CaseSensitive
=True):
1460 def RealPath2(File
, Dir
='', OverrideDir
=''):
1463 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(OverrideDir
, File
))]
1465 if OverrideDir
[-1] == os
.path
.sep
:
1466 return NewFile
[len(OverrideDir
):], NewFile
[0:len(OverrideDir
)]
1468 return NewFile
[len(OverrideDir
) + 1:], NewFile
[0:len(OverrideDir
)]
1469 if GlobalData
.gAllFiles
:
1470 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(Dir
, File
))]
1472 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
1473 if not os
.path
.exists(NewFile
):
1477 if Dir
[-1] == os
.path
.sep
:
1478 return NewFile
[len(Dir
):], NewFile
[0:len(Dir
)]
1480 return NewFile
[len(Dir
) + 1:], NewFile
[0:len(Dir
)]
1486 if GlobalData
.gCaseInsensitive
:
1487 CaseSensitive
= False
1488 if Type
and Type
.lower() != self
.Type
:
1489 return FILE_TYPE_MISMATCH
, '%s (expect %s but got %s)' % (self
.File
, Type
, self
.Type
)
1491 RealFile
, RealRoot
= RealPath2(self
.File
, self
.Root
, self
.AlterRoot
)
1492 if not RealRoot
and not RealFile
:
1493 RealFile
= self
.File
1495 RealFile
= os
.path
.join(self
.AlterRoot
, self
.File
)
1497 RealFile
= os
.path
.join(self
.Root
, self
.File
)
1498 if len (mws
.getPkgPath()) == 0:
1499 return FILE_NOT_FOUND
, os
.path
.join(self
.AlterRoot
, RealFile
)
1501 return FILE_NOT_FOUND
, "%s is not found in packages path:\n\t%s" % (self
.File
, '\n\t'.join(mws
.getPkgPath()))
1505 if RealRoot
!= self
.Root
or RealFile
!= self
.File
:
1506 if CaseSensitive
and (RealFile
!= self
.File
or (RealRoot
!= self
.Root
and RealRoot
!= self
.AlterRoot
)):
1507 ErrorCode
= FILE_CASE_MISMATCH
1508 ErrorInfo
= self
.File
+ '\n\t' + RealFile
+ " [in file system]"
1510 self
.SubDir
, self
.Name
= os
.path
.split(RealFile
)
1511 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1513 self
.Dir
= os
.path
.join(RealRoot
, self
.SubDir
)
1516 self
.File
= RealFile
1517 self
.Root
= RealRoot
1518 self
.Path
= os
.path
.join(RealRoot
, RealFile
)
1519 return ErrorCode
, ErrorInfo
1521 ## Parse PE image to get the required PE information.
1523 class PeImageClass():
1526 # @param File FilePath of PeImage
1528 def __init__(self
, PeFile
):
1529 self
.FileName
= PeFile
1530 self
.IsValid
= False
1533 self
.SectionAlignment
= 0
1534 self
.SectionHeaderList
= []
1537 PeObject
= open(PeFile
, 'rb')
1539 self
.ErrorInfo
= self
.FileName
+ ' can not be found\n'
1542 ByteArray
= array
.array('B')
1543 ByteArray
.fromfile(PeObject
, 0x3E)
1544 ByteList
= ByteArray
.tolist()
1545 # DOS signature should be 'MZ'
1546 if self
._ByteListToStr
(ByteList
[0x0:0x2]) != 'MZ':
1547 self
.ErrorInfo
= self
.FileName
+ ' has no valid DOS signature MZ'
1550 # Read 4 byte PE Signature
1551 PeOffset
= self
._ByteListToInt
(ByteList
[0x3C:0x3E])
1552 PeObject
.seek(PeOffset
)
1553 ByteArray
= array
.array('B')
1554 ByteArray
.fromfile(PeObject
, 4)
1555 # PE signature should be 'PE\0\0'
1556 if ByteArray
.tostring() != b
'PE\0\0':
1557 self
.ErrorInfo
= self
.FileName
+ ' has no valid PE signature PE00'
1560 # Read PE file header
1561 ByteArray
= array
.array('B')
1562 ByteArray
.fromfile(PeObject
, 0x14)
1563 ByteList
= ByteArray
.tolist()
1564 SecNumber
= self
._ByteListToInt
(ByteList
[0x2:0x4])
1566 self
.ErrorInfo
= self
.FileName
+ ' has no section header'
1569 # Read PE optional header
1570 OptionalHeaderSize
= self
._ByteListToInt
(ByteArray
[0x10:0x12])
1571 ByteArray
= array
.array('B')
1572 ByteArray
.fromfile(PeObject
, OptionalHeaderSize
)
1573 ByteList
= ByteArray
.tolist()
1574 self
.EntryPoint
= self
._ByteListToInt
(ByteList
[0x10:0x14])
1575 self
.SectionAlignment
= self
._ByteListToInt
(ByteList
[0x20:0x24])
1576 self
.Size
= self
._ByteListToInt
(ByteList
[0x38:0x3C])
1578 # Read each Section Header
1579 for Index
in range(SecNumber
):
1580 ByteArray
= array
.array('B')
1581 ByteArray
.fromfile(PeObject
, 0x28)
1582 ByteList
= ByteArray
.tolist()
1583 SecName
= self
._ByteListToStr
(ByteList
[0:8])
1584 SecVirtualSize
= self
._ByteListToInt
(ByteList
[8:12])
1585 SecRawAddress
= self
._ByteListToInt
(ByteList
[20:24])
1586 SecVirtualAddress
= self
._ByteListToInt
(ByteList
[12:16])
1587 self
.SectionHeaderList
.append((SecName
, SecVirtualAddress
, SecRawAddress
, SecVirtualSize
))
1591 def _ByteListToStr(self
, ByteList
):
1593 for index
in range(len(ByteList
)):
1594 if ByteList
[index
] == 0:
1596 String
+= chr(ByteList
[index
])
1599 def _ByteListToInt(self
, ByteList
):
1601 for index
in range(len(ByteList
) - 1, -1, -1):
1602 Value
= (Value
<< 8) |
int(ByteList
[index
])
1605 class DefaultStore():
1606 def __init__(self
, DefaultStores
):
1608 self
.DefaultStores
= DefaultStores
1609 def DefaultStoreID(self
, DefaultStoreName
):
1610 for key
, value
in self
.DefaultStores
.items():
1611 if value
== DefaultStoreName
:
1614 def GetDefaultDefault(self
):
1615 if not self
.DefaultStores
or "0" in self
.DefaultStores
:
1616 return "0", TAB_DEFAULT_STORES_DEFAULT
1618 minvalue
= min(int(value_str
) for value_str
in self
.DefaultStores
)
1619 return (str(minvalue
), self
.DefaultStores
[str(minvalue
)])
1620 def GetMin(self
, DefaultSIdList
):
1621 if not DefaultSIdList
:
1622 return TAB_DEFAULT_STORES_DEFAULT
1623 storeidset
= {storeid
for storeid
, storename
in self
.DefaultStores
.values() if storename
in DefaultSIdList
}
1626 minid
= min(storeidset
)
1627 for sid
, name
in self
.DefaultStores
.values():
1636 def __init__(self
,SkuIdentifier
='', SkuIds
=None):
1640 for SkuName
in SkuIds
:
1641 SkuId
= SkuIds
[SkuName
][0]
1642 skuid_num
= int(SkuId
, 16) if SkuId
.upper().startswith("0X") else int(SkuId
)
1643 if skuid_num
> 0xFFFFFFFFFFFFFFFF:
1644 EdkLogger
.error("build", PARAMETER_INVALID
,
1645 ExtraData
= "SKU-ID [%s] value %s exceeds the max value of UINT64"
1648 self
.AvailableSkuIds
= OrderedDict()
1650 self
.SkuIdNumberSet
= []
1651 self
.SkuData
= SkuIds
1652 self
._SkuInherit
= {}
1653 self
._SkuIdentifier
= SkuIdentifier
1654 if SkuIdentifier
== '' or SkuIdentifier
is None:
1655 self
.SkuIdSet
= ['DEFAULT']
1656 self
.SkuIdNumberSet
= ['0U']
1657 elif SkuIdentifier
== 'ALL':
1658 self
.SkuIdSet
= list(SkuIds
.keys())
1659 self
.SkuIdNumberSet
= [num
[0].strip() + 'U' for num
in SkuIds
.values()]
1661 r
= SkuIdentifier
.split('|')
1662 self
.SkuIdSet
=[(r
[k
].strip()).upper() for k
in range(len(r
))]
1665 self
.SkuIdNumberSet
= [SkuIds
[k
][0].strip() + 'U' for k
in self
.SkuIdSet
]
1667 EdkLogger
.error("build", PARAMETER_INVALID
,
1668 ExtraData
= "SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1669 % (k
, " | ".join(SkuIds
.keys())))
1670 for each
in self
.SkuIdSet
:
1672 self
.AvailableSkuIds
[each
] = SkuIds
[each
][0]
1674 EdkLogger
.error("build", PARAMETER_INVALID
,
1675 ExtraData
="SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1676 % (each
, " | ".join(SkuIds
.keys())))
1677 if self
.SkuUsageType
!= SkuClass
.SINGLE
:
1678 self
.AvailableSkuIds
.update({'DEFAULT':0, 'COMMON':0})
1680 GlobalData
.gSkuids
= (self
.SkuIdSet
)
1681 if 'COMMON' in GlobalData
.gSkuids
:
1682 GlobalData
.gSkuids
.remove('COMMON')
1683 if self
.SkuUsageType
== self
.SINGLE
:
1684 if len(GlobalData
.gSkuids
) != 1:
1685 if 'DEFAULT' in GlobalData
.gSkuids
:
1686 GlobalData
.gSkuids
.remove('DEFAULT')
1687 if GlobalData
.gSkuids
:
1688 GlobalData
.gSkuids
.sort()
1690 def GetNextSkuId(self
, skuname
):
1691 if not self
._SkuInherit
:
1692 self
._SkuInherit
= {}
1693 for item
in self
.SkuData
.values():
1694 self
._SkuInherit
[item
[1]]=item
[2] if item
[2] else "DEFAULT"
1695 return self
._SkuInherit
.get(skuname
, "DEFAULT")
1697 def GetSkuChain(self
, sku
):
1698 if sku
== "DEFAULT":
1703 nextsku
= self
.GetNextSkuId(nextsku
)
1704 skulist
.append(nextsku
)
1705 if nextsku
== "DEFAULT":
1709 def SkuOverrideOrder(self
):
1711 for skuname
in self
.SkuIdSet
:
1712 skuorderset
.append(self
.GetSkuChain(skuname
))
1715 for index
in range(max(len(item
) for item
in skuorderset
)):
1716 for subset
in skuorderset
:
1717 if index
> len(subset
)-1:
1719 if subset
[index
] in skuorder
:
1721 skuorder
.append(subset
[index
])
1726 def SkuUsageType(self
):
1727 if self
._SkuIdentifier
.upper() == "ALL":
1728 return SkuClass
.MULTIPLE
1730 if len(self
.SkuIdSet
) == 1:
1731 if self
.SkuIdSet
[0] == 'DEFAULT':
1732 return SkuClass
.DEFAULT
1733 return SkuClass
.SINGLE
1734 if len(self
.SkuIdSet
) == 2 and 'DEFAULT' in self
.SkuIdSet
:
1735 return SkuClass
.SINGLE
1736 return SkuClass
.MULTIPLE
1738 def DumpSkuIdArrary(self
):
1739 if self
.SkuUsageType
== SkuClass
.SINGLE
:
1742 for skuname
in self
.AvailableSkuIds
:
1743 if skuname
== "COMMON":
1745 while skuname
!= "DEFAULT":
1746 ArrayStrList
.append(hex(int(self
.AvailableSkuIds
[skuname
])))
1747 skuname
= self
.GetNextSkuId(skuname
)
1748 ArrayStrList
.append("0x0")
1749 return "{{{myList}}}".format(myList
=",".join(ArrayStrList
))
1752 def AvailableSkuIdSet(self
):
1753 return self
.AvailableSkuIds
1756 def SystemSkuId(self
):
1757 if self
.SkuUsageType
== SkuClass
.SINGLE
:
1758 if len(self
.SkuIdSet
) == 1:
1759 return self
.SkuIdSet
[0]
1761 return self
.SkuIdSet
[0] if self
.SkuIdSet
[0] != 'DEFAULT' else self
.SkuIdSet
[1]
1765 ## Get the integer value from string like "14U" or integer like 2
1767 # @param Input The object that may be either a integer value or a string
1769 # @retval Value The integer value that the input represents
1771 def GetIntegerValue(Input
):
1772 if not isinstance(Input
, str):
1775 if String
.endswith("U"):
1776 String
= String
[:-1]
1777 if String
.endswith("ULL"):
1778 String
= String
[:-3]
1779 if String
.endswith("LL"):
1780 String
= String
[:-2]
1782 if String
.startswith("0x") or String
.startswith("0X"):
1783 return int(String
, 16)
1790 # Pack a GUID (registry format) list into a buffer and return it
1793 return pack(PACK_PATTERN_GUID
,
1797 int(Guid
[3][-4:-2], 16),
1798 int(Guid
[3][-2:], 16),
1799 int(Guid
[4][-12:-10], 16),
1800 int(Guid
[4][-10:-8], 16),
1801 int(Guid
[4][-8:-6], 16),
1802 int(Guid
[4][-6:-4], 16),
1803 int(Guid
[4][-4:-2], 16),
1804 int(Guid
[4][-2:], 16)
1808 # Pack a GUID (byte) list into a buffer and return it
1810 def PackByteFormatGUID(Guid
):
1811 return pack(PACK_PATTERN_GUID
,
1825 ## DeepCopy dict/OrderedDict recusively
1827 # @param ori_dict a nested dict or ordereddict
1829 # @retval new dict or orderdict
1831 def CopyDict(ori_dict
):
1832 dict_type
= ori_dict
.__class
__
1833 if dict_type
not in (dict,OrderedDict
):
1835 new_dict
= dict_type()
1836 for key
in ori_dict
:
1837 if isinstance(ori_dict
[key
],(dict,OrderedDict
)):
1838 new_dict
[key
] = CopyDict(ori_dict
[key
])
1840 new_dict
[key
] = ori_dict
[key
]
1844 # Remove the c/c++ comments: // and /* */
1846 def RemoveCComments(ctext
):
1847 return re
.sub('//.*?\n|/\*.*?\*/', '\n', ctext
, flags
=re
.S
)