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 Size
= len(out
.split())
1036 out
= ','.join(out
.split())
1037 return '{' + out
+ '}', Size
1039 if "{CODE(" in Value
:
1040 return Value
, len(Value
.split(","))
1041 if isinstance(Value
, type(0)):
1042 return Value
, (Value
.bit_length() + 7) // 8
1043 if not isinstance(Value
, type('')):
1044 raise BadExpression('Type %s is %s' %(Value
, type(Value
)))
1045 Value
= Value
.strip()
1046 if Value
.startswith(TAB_UINT8
) and Value
.endswith(')'):
1047 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1049 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1051 if Value
.startswith(TAB_UINT16
) and Value
.endswith(')'):
1052 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1054 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1056 if Value
.startswith(TAB_UINT32
) and Value
.endswith(')'):
1057 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1059 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1061 if Value
.startswith(TAB_UINT64
) and Value
.endswith(')'):
1062 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1064 raise BadExpression('Value (%s) Size larger than %d' % (Value
, Size
))
1066 if Value
.startswith(TAB_GUID
) and Value
.endswith(')'):
1067 Value
= Value
.split('(', 1)[1][:-1].strip()
1068 if Value
[0] == '{' and Value
[-1] == '}':
1069 TmpValue
= GuidStructureStringToGuidString(Value
)
1071 raise BadExpression("Invalid GUID value string %s" % Value
)
1073 if Value
[0] == '"' and Value
[-1] == '"':
1076 Value
= str(uuid
.UUID(Value
).bytes_le
)
1077 if Value
.startswith("b'"):
1079 Value
= "'" + Value
+ "'"
1080 except ValueError as Message
:
1081 raise BadExpression(Message
)
1082 Value
, Size
= ParseFieldValue(Value
)
1084 if Value
.startswith('L"') and Value
.endswith('"'):
1086 # translate escape character
1096 Value
= (Value
<< 16) |
ord(Char
)
1097 return Value
, (len(List
) + 1) * 2
1098 if Value
.startswith('"') and Value
.endswith('"'):
1100 # translate escape character
1109 Value
= (Value
<< 8) |
ord(Char
)
1110 return Value
, len(List
) + 1
1111 if Value
.startswith("L'") and Value
.endswith("'"):
1112 # Unicode Character Constant
1113 # translate escape character
1121 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1125 Value
= (Value
<< 16) |
ord(Char
)
1126 return Value
, len(List
) * 2
1127 if Value
.startswith("'") and Value
.endswith("'"):
1128 # Character constant
1129 # translate escape character
1136 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1140 Value
= (Value
<< 8) |
ord(Char
)
1141 return Value
, len(List
)
1142 if Value
.startswith('{') and Value
.endswith('}'):
1145 List
= [Item
.strip() for Item
in Value
.split(',')]
1150 ItemValue
, Size
= ParseFieldValue(Item
)
1152 for I
in range(Size
):
1153 Value
= (Value
<< 8) |
((ItemValue
>> 8 * I
) & 0xff)
1154 return Value
, RetSize
1155 if Value
.startswith('DEVICE_PATH(') and Value
.endswith(')'):
1156 Value
= Value
.replace("DEVICE_PATH(", '').rstrip(')')
1157 Value
= Value
.strip().strip('"')
1158 return ParseDevPathValue(Value
)
1159 if Value
.lower().startswith('0x'):
1161 Value
= int(Value
, 16)
1163 raise BadExpression("invalid hex value: %s" % Value
)
1166 return Value
, (Value
.bit_length() + 7) // 8
1167 if Value
[0].isdigit():
1168 Value
= int(Value
, 10)
1171 return Value
, (Value
.bit_length() + 7) // 8
1172 if Value
.lower() == 'true':
1174 if Value
.lower() == 'false':
1180 # Analyze DSC PCD value, since there is no data type info in DSC
1181 # This function is used to match functions (AnalyzePcdData) used for retrieving PCD value from database
1182 # 1. Feature flag: TokenSpace.PcdCName|PcdValue
1183 # 2. Fix and Patch:TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1184 # 3. Dynamic default:
1185 # TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1186 # TokenSpace.PcdCName|PcdValue
1188 # TokenSpace.PcdCName|VpdOffset[|VpdValue]
1189 # TokenSpace.PcdCName|VpdOffset[|MaxSize[|VpdValue]]
1191 # TokenSpace.PcdCName|HiiString|VariableGuid|VariableOffset[|HiiValue]
1192 # PCD value needs to be located in such kind of string, and the PCD value might be an expression in which
1193 # there might have "|" operator, also in string value.
1195 # @param Setting: String contain information described above with "TokenSpace.PcdCName|" stripped
1196 # @param PcdType: PCD type: feature, fixed, dynamic default VPD HII
1197 # @param DataType: The datum type of PCD: VOID*, UNIT, BOOL
1199 # ValueList: A List contain fields described above
1200 # IsValid: True if conforming EBNF, otherwise False
1201 # Index: The index where PcdValue is in ValueList
1203 def AnalyzeDscPcd(Setting
, PcdType
, DataType
=''):
1204 FieldList
= AnalyzePcdExpression(Setting
)
1207 if PcdType
in (MODEL_PCD_FIXED_AT_BUILD
, MODEL_PCD_PATCHABLE_IN_MODULE
, MODEL_PCD_DYNAMIC_DEFAULT
, MODEL_PCD_DYNAMIC_EX_DEFAULT
):
1208 Value
= FieldList
[0]
1210 if len(FieldList
) > 1 and FieldList
[1]:
1211 DataType
= FieldList
[1]
1212 if FieldList
[1] != TAB_VOID
and StructPattern
.match(FieldList
[1]) is None:
1214 if len(FieldList
) > 2:
1218 IsValid
= (len(FieldList
) <= 1)
1220 IsValid
= (len(FieldList
) <= 3)
1224 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1228 return [str(Value
), DataType
, str(Size
)], IsValid
, 0
1229 elif PcdType
== MODEL_PCD_FEATURE_FLAG
:
1230 Value
= FieldList
[0]
1232 IsValid
= (len(FieldList
) <= 1)
1233 return [Value
, DataType
, str(Size
)], IsValid
, 0
1234 elif PcdType
in (MODEL_PCD_DYNAMIC_VPD
, MODEL_PCD_DYNAMIC_EX_VPD
):
1235 VpdOffset
= FieldList
[0]
1237 if not DataType
== TAB_VOID
:
1238 if len(FieldList
) > 1:
1239 Value
= FieldList
[1]
1241 if len(FieldList
) > 1:
1243 if len(FieldList
) > 2:
1244 Value
= FieldList
[2]
1246 IsValid
= (len(FieldList
) <= 1)
1248 IsValid
= (len(FieldList
) <= 3)
1251 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1255 return [VpdOffset
, str(Size
), Value
], IsValid
, 2
1256 elif PcdType
in (MODEL_PCD_DYNAMIC_HII
, MODEL_PCD_DYNAMIC_EX_HII
):
1257 IsValid
= (3 <= len(FieldList
) <= 5)
1258 HiiString
= FieldList
[0]
1259 Guid
= Offset
= Value
= Attribute
= ''
1260 if len(FieldList
) > 1:
1262 if len(FieldList
) > 2:
1263 Offset
= FieldList
[2]
1264 if len(FieldList
) > 3:
1265 Value
= FieldList
[3]
1266 if len(FieldList
) > 4:
1267 Attribute
= FieldList
[4]
1268 return [HiiString
, Guid
, Offset
, Value
, Attribute
], IsValid
, 3
1273 # Analyze the pcd Value, Datum type and TokenNumber.
1274 # Used to avoid split issue while the value string contain "|" character
1276 # @param[in] Setting: A String contain value/datum type/token number information;
1278 # @retval ValueList: A List contain value, datum type and toke number.
1280 def AnalyzePcdData(Setting
):
1281 ValueList
= ['', '', '']
1283 ValueRe
= re
.compile(r
'^\s*L?\".*\|.*\"')
1284 PtrValue
= ValueRe
.findall(Setting
)
1286 ValueUpdateFlag
= False
1288 if len(PtrValue
) >= 1:
1289 Setting
= re
.sub(ValueRe
, '', Setting
)
1290 ValueUpdateFlag
= True
1292 TokenList
= Setting
.split(TAB_VALUE_SPLIT
)
1293 ValueList
[0:len(TokenList
)] = TokenList
1296 ValueList
[0] = PtrValue
[0]
1300 ## check format of PCD value against its the datum type
1302 # For PCD value setting
1304 def CheckPcdDatum(Type
, Value
):
1305 if Type
== TAB_VOID
:
1306 ValueRe
= re
.compile(r
'\s*L?\".*\"\s*$')
1307 if not (((Value
.startswith('L"') or Value
.startswith('"')) and Value
.endswith('"'))
1308 or (Value
.startswith('{') and Value
.endswith('}')) or (Value
.startswith("L'") or Value
.startswith("'") and Value
.endswith("'"))
1310 return False, "Invalid value [%s] of type [%s]; must be in the form of {...} for array"\
1311 ", \"...\" or \'...\' for string, L\"...\" or L\'...\' for unicode string" % (Value
, Type
)
1312 elif ValueRe
.match(Value
):
1313 # Check the chars in UnicodeString or CString is printable
1314 if Value
.startswith("L"):
1318 Printset
= set(string
.printable
)
1319 Printset
.remove(TAB_PRINTCHAR_VT
)
1320 Printset
.add(TAB_PRINTCHAR_BS
)
1321 Printset
.add(TAB_PRINTCHAR_NUL
)
1322 if not set(Value
).issubset(Printset
):
1323 PrintList
= sorted(Printset
)
1324 return False, "Invalid PCD string value of type [%s]; must be printable chars %s." % (Type
, PrintList
)
1325 elif Type
== 'BOOLEAN':
1326 if Value
not in ['TRUE', 'True', 'true', '0x1', '0x01', '1', 'FALSE', 'False', 'false', '0x0', '0x00', '0']:
1327 return False, "Invalid value [%s] of type [%s]; must be one of TRUE, True, true, 0x1, 0x01, 1"\
1328 ", FALSE, False, false, 0x0, 0x00, 0" % (Value
, Type
)
1329 elif Type
in [TAB_UINT8
, TAB_UINT16
, TAB_UINT32
, TAB_UINT64
]:
1330 if Value
.startswith('0') and not Value
.lower().startswith('0x') and len(Value
) > 1 and Value
.lstrip('0'):
1331 Value
= Value
.lstrip('0')
1333 if Value
and int(Value
, 0) < 0:
1334 return False, "PCD can't be set to negative value[%s] for datum type [%s]" % (Value
, Type
)
1335 Value
= int(Value
, 0)
1336 if Value
> MAX_VAL_TYPE
[Type
]:
1337 return False, "Too large PCD value[%s] for datum type [%s]" % (Value
, Type
)
1339 return False, "Invalid value [%s] of type [%s];"\
1340 " must be a hexadecimal, decimal or octal in C language format." % (Value
, Type
)
1342 return True, "StructurePcd"
1346 def CommonPath(PathList
):
1347 P1
= min(PathList
).split(os
.path
.sep
)
1348 P2
= max(PathList
).split(os
.path
.sep
)
1349 for Index
in range(min(len(P1
), len(P2
))):
1350 if P1
[Index
] != P2
[Index
]:
1351 return os
.path
.sep
.join(P1
[:Index
])
1352 return os
.path
.sep
.join(P1
)
1354 class PathClass(object):
1355 def __init__(self
, File
='', Root
='', AlterRoot
='', Type
='', IsBinary
=False,
1356 Arch
='COMMON', ToolChainFamily
='', Target
='', TagName
='', ToolCode
=''):
1358 self
.File
= str(File
)
1359 if os
.path
.isabs(self
.File
):
1363 self
.Root
= str(Root
)
1364 self
.AlterRoot
= str(AlterRoot
)
1366 # Remove any '.' and '..' in path
1368 self
.Root
= mws
.getWs(self
.Root
, self
.File
)
1369 self
.Path
= os
.path
.normpath(os
.path
.join(self
.Root
, self
.File
))
1370 self
.Root
= os
.path
.normpath(CommonPath([self
.Root
, self
.Path
]))
1371 # eliminate the side-effect of 'C:'
1372 if self
.Root
[-1] == ':':
1373 self
.Root
+= os
.path
.sep
1374 # file path should not start with path separator
1375 if self
.Root
[-1] == os
.path
.sep
:
1376 self
.File
= self
.Path
[len(self
.Root
):]
1378 self
.File
= self
.Path
[len(self
.Root
) + 1:]
1380 self
.Path
= os
.path
.normpath(self
.File
)
1382 self
.SubDir
, self
.Name
= os
.path
.split(self
.File
)
1383 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1387 self
.Dir
= os
.path
.join(self
.Root
, self
.SubDir
)
1389 self
.Dir
= self
.Root
1391 self
.Dir
= self
.SubDir
1396 self
.Type
= self
.Ext
.lower()
1398 self
.IsBinary
= IsBinary
1399 self
.Target
= Target
1400 self
.TagName
= TagName
1401 self
.ToolCode
= ToolCode
1402 self
.ToolChainFamily
= ToolChainFamily
1404 ## Convert the object of this class to a string
1406 # Convert member Path of the class to a string
1408 # @retval string Formatted String
1413 ## Override __eq__ function
1415 # Check whether PathClass are the same
1417 # @retval False The two PathClass are different
1418 # @retval True The two PathClass are the same
1420 def __eq__(self
, Other
):
1421 return self
.Path
== str(Other
)
1423 ## Override __cmp__ function
1425 # Customize the comparison operation of two PathClass
1427 # @retval 0 The two PathClass are different
1428 # @retval -1 The first PathClass is less than the second PathClass
1429 # @retval 1 The first PathClass is Bigger than the second PathClass
1430 def __cmp__(self
, Other
):
1431 OtherKey
= str(Other
)
1434 if SelfKey
== OtherKey
:
1436 elif SelfKey
> OtherKey
:
1441 ## Override __hash__ function
1443 # Use Path as key in hash table
1445 # @retval string Key for hash table
1448 return hash(self
.Path
)
1452 return self
.Path
.upper()
1455 def TimeStamp(self
):
1456 return os
.stat(self
.Path
)[8]
1458 def Validate(self
, Type
='', CaseSensitive
=True):
1459 def RealPath2(File
, Dir
='', OverrideDir
=''):
1462 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(OverrideDir
, File
))]
1464 if OverrideDir
[-1] == os
.path
.sep
:
1465 return NewFile
[len(OverrideDir
):], NewFile
[0:len(OverrideDir
)]
1467 return NewFile
[len(OverrideDir
) + 1:], NewFile
[0:len(OverrideDir
)]
1468 if GlobalData
.gAllFiles
:
1469 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(Dir
, File
))]
1471 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
1472 if not os
.path
.exists(NewFile
):
1476 if Dir
[-1] == os
.path
.sep
:
1477 return NewFile
[len(Dir
):], NewFile
[0:len(Dir
)]
1479 return NewFile
[len(Dir
) + 1:], NewFile
[0:len(Dir
)]
1485 if GlobalData
.gCaseInsensitive
:
1486 CaseSensitive
= False
1487 if Type
and Type
.lower() != self
.Type
:
1488 return FILE_TYPE_MISMATCH
, '%s (expect %s but got %s)' % (self
.File
, Type
, self
.Type
)
1490 RealFile
, RealRoot
= RealPath2(self
.File
, self
.Root
, self
.AlterRoot
)
1491 if not RealRoot
and not RealFile
:
1492 RealFile
= self
.File
1494 RealFile
= os
.path
.join(self
.AlterRoot
, self
.File
)
1496 RealFile
= os
.path
.join(self
.Root
, self
.File
)
1497 if len (mws
.getPkgPath()) == 0:
1498 return FILE_NOT_FOUND
, os
.path
.join(self
.AlterRoot
, RealFile
)
1500 return FILE_NOT_FOUND
, "%s is not found in packages path:\n\t%s" % (self
.File
, '\n\t'.join(mws
.getPkgPath()))
1504 if RealRoot
!= self
.Root
or RealFile
!= self
.File
:
1505 if CaseSensitive
and (RealFile
!= self
.File
or (RealRoot
!= self
.Root
and RealRoot
!= self
.AlterRoot
)):
1506 ErrorCode
= FILE_CASE_MISMATCH
1507 ErrorInfo
= self
.File
+ '\n\t' + RealFile
+ " [in file system]"
1509 self
.SubDir
, self
.Name
= os
.path
.split(RealFile
)
1510 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1512 self
.Dir
= os
.path
.join(RealRoot
, self
.SubDir
)
1515 self
.File
= RealFile
1516 self
.Root
= RealRoot
1517 self
.Path
= os
.path
.join(RealRoot
, RealFile
)
1518 return ErrorCode
, ErrorInfo
1520 ## Parse PE image to get the required PE information.
1522 class PeImageClass():
1525 # @param File FilePath of PeImage
1527 def __init__(self
, PeFile
):
1528 self
.FileName
= PeFile
1529 self
.IsValid
= False
1532 self
.SectionAlignment
= 0
1533 self
.SectionHeaderList
= []
1536 PeObject
= open(PeFile
, 'rb')
1538 self
.ErrorInfo
= self
.FileName
+ ' can not be found\n'
1541 ByteArray
= array
.array('B')
1542 ByteArray
.fromfile(PeObject
, 0x3E)
1543 ByteList
= ByteArray
.tolist()
1544 # DOS signature should be 'MZ'
1545 if self
._ByteListToStr
(ByteList
[0x0:0x2]) != 'MZ':
1546 self
.ErrorInfo
= self
.FileName
+ ' has no valid DOS signature MZ'
1549 # Read 4 byte PE Signature
1550 PeOffset
= self
._ByteListToInt
(ByteList
[0x3C:0x3E])
1551 PeObject
.seek(PeOffset
)
1552 ByteArray
= array
.array('B')
1553 ByteArray
.fromfile(PeObject
, 4)
1554 # PE signature should be 'PE\0\0'
1555 if ByteArray
.tostring() != b
'PE\0\0':
1556 self
.ErrorInfo
= self
.FileName
+ ' has no valid PE signature PE00'
1559 # Read PE file header
1560 ByteArray
= array
.array('B')
1561 ByteArray
.fromfile(PeObject
, 0x14)
1562 ByteList
= ByteArray
.tolist()
1563 SecNumber
= self
._ByteListToInt
(ByteList
[0x2:0x4])
1565 self
.ErrorInfo
= self
.FileName
+ ' has no section header'
1568 # Read PE optional header
1569 OptionalHeaderSize
= self
._ByteListToInt
(ByteArray
[0x10:0x12])
1570 ByteArray
= array
.array('B')
1571 ByteArray
.fromfile(PeObject
, OptionalHeaderSize
)
1572 ByteList
= ByteArray
.tolist()
1573 self
.EntryPoint
= self
._ByteListToInt
(ByteList
[0x10:0x14])
1574 self
.SectionAlignment
= self
._ByteListToInt
(ByteList
[0x20:0x24])
1575 self
.Size
= self
._ByteListToInt
(ByteList
[0x38:0x3C])
1577 # Read each Section Header
1578 for Index
in range(SecNumber
):
1579 ByteArray
= array
.array('B')
1580 ByteArray
.fromfile(PeObject
, 0x28)
1581 ByteList
= ByteArray
.tolist()
1582 SecName
= self
._ByteListToStr
(ByteList
[0:8])
1583 SecVirtualSize
= self
._ByteListToInt
(ByteList
[8:12])
1584 SecRawAddress
= self
._ByteListToInt
(ByteList
[20:24])
1585 SecVirtualAddress
= self
._ByteListToInt
(ByteList
[12:16])
1586 self
.SectionHeaderList
.append((SecName
, SecVirtualAddress
, SecRawAddress
, SecVirtualSize
))
1590 def _ByteListToStr(self
, ByteList
):
1592 for index
in range(len(ByteList
)):
1593 if ByteList
[index
] == 0:
1595 String
+= chr(ByteList
[index
])
1598 def _ByteListToInt(self
, ByteList
):
1600 for index
in range(len(ByteList
) - 1, -1, -1):
1601 Value
= (Value
<< 8) |
int(ByteList
[index
])
1604 class DefaultStore():
1605 def __init__(self
, DefaultStores
):
1607 self
.DefaultStores
= DefaultStores
1608 def DefaultStoreID(self
, DefaultStoreName
):
1609 for key
, value
in self
.DefaultStores
.items():
1610 if value
== DefaultStoreName
:
1613 def GetDefaultDefault(self
):
1614 if not self
.DefaultStores
or "0" in self
.DefaultStores
:
1615 return "0", TAB_DEFAULT_STORES_DEFAULT
1617 minvalue
= min(int(value_str
) for value_str
in self
.DefaultStores
)
1618 return (str(minvalue
), self
.DefaultStores
[str(minvalue
)])
1619 def GetMin(self
, DefaultSIdList
):
1620 if not DefaultSIdList
:
1621 return TAB_DEFAULT_STORES_DEFAULT
1622 storeidset
= {storeid
for storeid
, storename
in self
.DefaultStores
.values() if storename
in DefaultSIdList
}
1625 minid
= min(storeidset
)
1626 for sid
, name
in self
.DefaultStores
.values():
1635 def __init__(self
,SkuIdentifier
='', SkuIds
=None):
1639 for SkuName
in SkuIds
:
1640 SkuId
= SkuIds
[SkuName
][0]
1641 skuid_num
= int(SkuId
, 16) if SkuId
.upper().startswith("0X") else int(SkuId
)
1642 if skuid_num
> 0xFFFFFFFFFFFFFFFF:
1643 EdkLogger
.error("build", PARAMETER_INVALID
,
1644 ExtraData
= "SKU-ID [%s] value %s exceeds the max value of UINT64"
1647 self
.AvailableSkuIds
= OrderedDict()
1649 self
.SkuIdNumberSet
= []
1650 self
.SkuData
= SkuIds
1651 self
._SkuInherit
= {}
1652 self
._SkuIdentifier
= SkuIdentifier
1653 if SkuIdentifier
== '' or SkuIdentifier
is None:
1654 self
.SkuIdSet
= ['DEFAULT']
1655 self
.SkuIdNumberSet
= ['0U']
1656 elif SkuIdentifier
== 'ALL':
1657 self
.SkuIdSet
= list(SkuIds
.keys())
1658 self
.SkuIdNumberSet
= [num
[0].strip() + 'U' for num
in SkuIds
.values()]
1660 r
= SkuIdentifier
.split('|')
1661 self
.SkuIdSet
=[(r
[k
].strip()).upper() for k
in range(len(r
))]
1664 self
.SkuIdNumberSet
= [SkuIds
[k
][0].strip() + 'U' for k
in self
.SkuIdSet
]
1666 EdkLogger
.error("build", PARAMETER_INVALID
,
1667 ExtraData
= "SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1668 % (k
, " | ".join(SkuIds
.keys())))
1669 for each
in self
.SkuIdSet
:
1671 self
.AvailableSkuIds
[each
] = SkuIds
[each
][0]
1673 EdkLogger
.error("build", PARAMETER_INVALID
,
1674 ExtraData
="SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1675 % (each
, " | ".join(SkuIds
.keys())))
1676 if self
.SkuUsageType
!= SkuClass
.SINGLE
:
1677 self
.AvailableSkuIds
.update({'DEFAULT':0, 'COMMON':0})
1679 GlobalData
.gSkuids
= (self
.SkuIdSet
)
1680 if 'COMMON' in GlobalData
.gSkuids
:
1681 GlobalData
.gSkuids
.remove('COMMON')
1682 if self
.SkuUsageType
== self
.SINGLE
:
1683 if len(GlobalData
.gSkuids
) != 1:
1684 if 'DEFAULT' in GlobalData
.gSkuids
:
1685 GlobalData
.gSkuids
.remove('DEFAULT')
1686 if GlobalData
.gSkuids
:
1687 GlobalData
.gSkuids
.sort()
1689 def GetNextSkuId(self
, skuname
):
1690 if not self
._SkuInherit
:
1691 self
._SkuInherit
= {}
1692 for item
in self
.SkuData
.values():
1693 self
._SkuInherit
[item
[1]]=item
[2] if item
[2] else "DEFAULT"
1694 return self
._SkuInherit
.get(skuname
, "DEFAULT")
1696 def GetSkuChain(self
, sku
):
1697 if sku
== "DEFAULT":
1702 nextsku
= self
.GetNextSkuId(nextsku
)
1703 skulist
.append(nextsku
)
1704 if nextsku
== "DEFAULT":
1708 def SkuOverrideOrder(self
):
1710 for skuname
in self
.SkuIdSet
:
1711 skuorderset
.append(self
.GetSkuChain(skuname
))
1714 for index
in range(max(len(item
) for item
in skuorderset
)):
1715 for subset
in skuorderset
:
1716 if index
> len(subset
)-1:
1718 if subset
[index
] in skuorder
:
1720 skuorder
.append(subset
[index
])
1725 def SkuUsageType(self
):
1726 if self
._SkuIdentifier
.upper() == "ALL":
1727 return SkuClass
.MULTIPLE
1729 if len(self
.SkuIdSet
) == 1:
1730 if self
.SkuIdSet
[0] == 'DEFAULT':
1731 return SkuClass
.DEFAULT
1732 return SkuClass
.SINGLE
1733 if len(self
.SkuIdSet
) == 2 and 'DEFAULT' in self
.SkuIdSet
:
1734 return SkuClass
.SINGLE
1735 return SkuClass
.MULTIPLE
1737 def DumpSkuIdArrary(self
):
1738 if self
.SkuUsageType
== SkuClass
.SINGLE
:
1741 for skuname
in self
.AvailableSkuIds
:
1742 if skuname
== "COMMON":
1744 while skuname
!= "DEFAULT":
1745 ArrayStrList
.append(hex(int(self
.AvailableSkuIds
[skuname
])))
1746 skuname
= self
.GetNextSkuId(skuname
)
1747 ArrayStrList
.append("0x0")
1748 return "{{{myList}}}".format(myList
=",".join(ArrayStrList
))
1751 def AvailableSkuIdSet(self
):
1752 return self
.AvailableSkuIds
1755 def SystemSkuId(self
):
1756 if self
.SkuUsageType
== SkuClass
.SINGLE
:
1757 if len(self
.SkuIdSet
) == 1:
1758 return self
.SkuIdSet
[0]
1760 return self
.SkuIdSet
[0] if self
.SkuIdSet
[0] != 'DEFAULT' else self
.SkuIdSet
[1]
1764 ## Get the integer value from string like "14U" or integer like 2
1766 # @param Input The object that may be either a integer value or a string
1768 # @retval Value The integer value that the input represents
1770 def GetIntegerValue(Input
):
1771 if not isinstance(Input
, str):
1774 if String
.endswith("U"):
1775 String
= String
[:-1]
1776 if String
.endswith("ULL"):
1777 String
= String
[:-3]
1778 if String
.endswith("LL"):
1779 String
= String
[:-2]
1781 if String
.startswith("0x") or String
.startswith("0X"):
1782 return int(String
, 16)
1789 # Pack a GUID (registry format) list into a buffer and return it
1792 return pack(PACK_PATTERN_GUID
,
1796 int(Guid
[3][-4:-2], 16),
1797 int(Guid
[3][-2:], 16),
1798 int(Guid
[4][-12:-10], 16),
1799 int(Guid
[4][-10:-8], 16),
1800 int(Guid
[4][-8:-6], 16),
1801 int(Guid
[4][-6:-4], 16),
1802 int(Guid
[4][-4:-2], 16),
1803 int(Guid
[4][-2:], 16)
1807 # Pack a GUID (byte) list into a buffer and return it
1809 def PackByteFormatGUID(Guid
):
1810 return pack(PACK_PATTERN_GUID
,
1824 ## DeepCopy dict/OrderedDict recusively
1826 # @param ori_dict a nested dict or ordereddict
1828 # @retval new dict or orderdict
1830 def CopyDict(ori_dict
):
1831 dict_type
= ori_dict
.__class
__
1832 if dict_type
not in (dict,OrderedDict
):
1834 new_dict
= dict_type()
1835 for key
in ori_dict
:
1836 if isinstance(ori_dict
[key
],(dict,OrderedDict
)):
1837 new_dict
[key
] = CopyDict(ori_dict
[key
])
1839 new_dict
[key
] = ori_dict
[key
]
1843 # Remove the c/c++ comments: // and /* */
1845 def RemoveCComments(ctext
):
1846 return re
.sub('//.*?\n|/\*.*?\*/', '\n', ctext
, flags
=re
.S
)