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 file time stamp for quick re-access
58 gFileTimeStampCache
= {} # {file path : file time stamp}
60 ## Dictionary used to store dependencies of files
61 gDependencyDatabase
= {} # arch : {file path : [dependent files list]}
64 # If a module is built more than once with different PCDs or library classes
65 # a temporary INF file with same content is created, the temporary file is removed
70 def GetVariableOffset(mapfilepath
, efifilepath
, varnames
):
71 """ Parse map file to get variable offset in current EFI file
72 @param mapfilepath Map file absolution path
73 @param efifilepath: EFI binary file full path
74 @param varnames iteratable container whose elements are variable names to be searched
76 @return List whos elements are tuple with variable name and raw offset
80 f
= open(mapfilepath
, 'r')
86 if len(lines
) == 0: return None
87 firstline
= lines
[0].strip()
88 if (firstline
.startswith("Archive member included ") and
89 firstline
.endswith(" file (symbol)")):
90 return _parseForGCC(lines
, efifilepath
, varnames
)
91 if firstline
.startswith("# Path:"):
92 return _parseForXcode(lines
, efifilepath
, varnames
)
93 return _parseGeneral(lines
, efifilepath
, varnames
)
95 def _parseForXcode(lines
, efifilepath
, varnames
):
100 if status
== 0 and line
== "# Symbols:":
103 if status
== 1 and len(line
) != 0:
104 for varname
in varnames
:
106 # cannot pregenerate this RegEx since it uses varname from varnames.
107 m
= re
.match('^([\da-fA-FxX]+)([\s\S]*)([_]*%s)$' % varname
, line
)
109 ret
.append((varname
, m
.group(1)))
112 def _parseForGCC(lines
, efifilepath
, varnames
):
113 """ Parse map file generated by GCC linker """
117 for index
, line
in enumerate(lines
):
119 # status machine transection
120 if status
== 0 and line
== "Memory Configuration":
123 elif status
== 1 and line
== 'Linker script and memory map':
126 elif status
==2 and line
== 'START GROUP':
132 m
= valuePatternGcc
.match(line
)
134 sections
.append(m
.groups(0))
135 for varname
in varnames
:
137 m
= re
.match("^.data.(%s)" % varname
, line
)
139 m
= re
.match(".data.(%s)$" % varname
, line
)
141 Str
= lines
[index
+ 1]
143 Str
= line
[len(".data.%s" % varname
):]
145 m
= pcdPatternGcc
.match(Str
.strip())
147 varoffset
.append((varname
, int(m
.groups(0)[0], 16), int(sections
[-1][1], 16), sections
[-1][0]))
151 # get section information from efi file
152 efisecs
= PeImageClass(efifilepath
).SectionHeaderList
153 if efisecs
is None or len(efisecs
) == 0:
157 for efisec
in efisecs
:
158 for section
in sections
:
159 if section
[0].strip() == efisec
[0].strip() and section
[0].strip() == '.text':
160 redirection
= int(section
[1], 16) - efisec
[1]
163 for var
in varoffset
:
164 for efisec
in efisecs
:
165 if var
[1] >= efisec
[1] and var
[1] < efisec
[1]+efisec
[3]:
166 ret
.append((var
[0], hex(efisec
[2] + var
[1] - efisec
[1] - redirection
)))
169 def _parseGeneral(lines
, efifilepath
, varnames
):
170 status
= 0 #0 - beginning of file; 1 - PE section definition; 2 - symbol table
171 secs
= [] # key = section name
173 symRe
= re
.compile('^([\da-fA-F]+):([\da-fA-F]+) +([\.:\\\\\w\?@\$]+) +([\da-fA-F]+)', re
.UNICODE
)
177 if startPatternGeneral
.match(line
):
180 if addressPatternGeneral
.match(line
):
183 if line
.startswith("entry point at"):
186 if status
== 1 and len(line
) != 0:
187 m
= secReGeneral
.match(line
)
188 assert m
is not None, "Fail to parse the section in map file , line is %s" % line
189 sec_no
, sec_start
, sec_length
, sec_name
, sec_class
= m
.groups(0)
190 secs
.append([int(sec_no
, 16), int(sec_start
, 16), int(sec_length
, 16), sec_name
, sec_class
])
191 if status
== 2 and len(line
) != 0:
192 for varname
in varnames
:
193 m
= symRe
.match(line
)
194 assert m
is not None, "Fail to parse the symbol in map file, line is %s" % line
195 sec_no
, sym_offset
, sym_name
, vir_addr
= m
.groups(0)
196 sec_no
= int(sec_no
, 16)
197 sym_offset
= int(sym_offset
, 16)
198 vir_addr
= int(vir_addr
, 16)
199 # cannot pregenerate this RegEx since it uses varname from varnames.
200 m2
= re
.match('^[_]*(%s)' % varname
, sym_name
)
202 # fond a binary pcd entry in map file
204 if sec
[0] == sec_no
and (sym_offset
>= sec
[1] and sym_offset
< sec
[1] + sec
[2]):
205 varoffset
.append([varname
, sec
[3], sym_offset
, vir_addr
, sec_no
])
207 if not varoffset
: return []
209 # get section information from efi file
210 efisecs
= PeImageClass(efifilepath
).SectionHeaderList
211 if efisecs
is None or len(efisecs
) == 0:
215 for var
in varoffset
:
217 for efisec
in efisecs
:
219 if var
[1].strip() == efisec
[0].strip():
220 ret
.append((var
[0], hex(efisec
[2] + var
[2])))
221 elif var
[4] == index
:
222 ret
.append((var
[0], hex(efisec
[2] + var
[2])))
226 ## Routine to process duplicated INF
228 # This function is called by following two cases:
231 # Pkg/module/module.inf
232 # Pkg/module/module.inf {
234 # FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836
237 # INF Pkg/module/module.inf
238 # INF FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836 Pkg/module/module.inf
240 # This function copies Pkg/module/module.inf to
241 # Conf/.cache/0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf
243 # @param Path Original PathClass object
244 # @param BaseName New file base name
246 # @retval return the new PathClass object
248 def ProcessDuplicatedInf(Path
, BaseName
, Workspace
):
249 Filename
= os
.path
.split(Path
.File
)[1]
251 Filename
= BaseName
+ Path
.BaseName
+ Filename
[Filename
.rfind('.'):]
253 Filename
= BaseName
+ Path
.BaseName
256 # If -N is specified on command line, cache is disabled
257 # The directory has to be created
259 DbDir
= os
.path
.split(GlobalData
.gDatabasePath
)[0]
260 if not os
.path
.exists(DbDir
):
263 # A temporary INF is copied to database path which must have write permission
264 # The temporary will be removed at the end of build
265 # In case of name conflict, the file name is
266 # FILE_GUIDBaseName (0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf)
268 TempFullPath
= os
.path
.join(DbDir
,
270 RtPath
= PathClass(Path
.File
, Workspace
)
272 # Modify the full path to temporary path, keep other unchanged
274 # To build same module more than once, the module path with FILE_GUID overridden has
275 # the file name FILE_GUIDmodule.inf, but the relative path (self.MetaFile.File) is the real path
276 # in DSC which is used as relative path by C files and other files in INF.
277 # A trick was used: all module paths are PathClass instances, after the initialization
278 # of PathClass, the PathClass.Path is overridden by the temporary INF path.
280 # The reason for creating a temporary INF is:
281 # Platform.Modules which is the base to create ModuleAutoGen objects is a dictionary,
282 # the key is the full path of INF, the value is an object to save overridden library instances, PCDs.
283 # A different key for the same module is needed to create different output directory,
284 # retrieve overridden PCDs, library instances.
286 # The BaseName is the FILE_GUID which is also the output directory name.
289 RtPath
.Path
= TempFullPath
290 RtPath
.BaseName
= BaseName
292 # If file exists, compare contents
294 if os
.path
.exists(TempFullPath
):
295 with
open(str(Path
), 'rb') as f1
, open(TempFullPath
, 'rb') as f2
:
296 if f1
.read() == f2
.read():
298 _TempInfs
.append(TempFullPath
)
299 shutil
.copy2(str(Path
), TempFullPath
)
302 ## Remove temporary created INFs whose paths were saved in _TempInfs
304 def ClearDuplicatedInf():
306 File
= _TempInfs
.pop()
307 if os
.path
.exists(File
):
310 ## Convert GUID string in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx style to C structure style
312 # @param Guid The GUID string
314 # @retval string The GUID string in C structure style
316 def GuidStringToGuidStructureString(Guid
):
317 GuidList
= Guid
.split('-')
319 for Index
in range(0, 3, 1):
320 Result
= Result
+ '0x' + GuidList
[Index
] + ', '
321 Result
= Result
+ '{0x' + GuidList
[3][0:2] + ', 0x' + GuidList
[3][2:4]
322 for Index
in range(0, 12, 2):
323 Result
= Result
+ ', 0x' + GuidList
[4][Index
:Index
+ 2]
327 ## Convert GUID structure in byte array to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
329 # @param GuidValue The GUID value in byte array
331 # @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
333 def GuidStructureByteArrayToGuidString(GuidValue
):
334 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
335 guidValueList
= guidValueString
.split(",")
336 if len(guidValueList
) != 16:
338 #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
340 return "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
341 int(guidValueList
[3], 16),
342 int(guidValueList
[2], 16),
343 int(guidValueList
[1], 16),
344 int(guidValueList
[0], 16),
345 int(guidValueList
[5], 16),
346 int(guidValueList
[4], 16),
347 int(guidValueList
[7], 16),
348 int(guidValueList
[6], 16),
349 int(guidValueList
[8], 16),
350 int(guidValueList
[9], 16),
351 int(guidValueList
[10], 16),
352 int(guidValueList
[11], 16),
353 int(guidValueList
[12], 16),
354 int(guidValueList
[13], 16),
355 int(guidValueList
[14], 16),
356 int(guidValueList
[15], 16)
361 ## Convert GUID string in C structure style to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
363 # @param GuidValue The GUID value in C structure format
365 # @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
367 def GuidStructureStringToGuidString(GuidValue
):
368 if not GlobalData
.gGuidCFormatPattern
.match(GuidValue
):
370 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
371 guidValueList
= guidValueString
.split(",")
372 if len(guidValueList
) != 11:
374 #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
376 return "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
377 int(guidValueList
[0], 16),
378 int(guidValueList
[1], 16),
379 int(guidValueList
[2], 16),
380 int(guidValueList
[3], 16),
381 int(guidValueList
[4], 16),
382 int(guidValueList
[5], 16),
383 int(guidValueList
[6], 16),
384 int(guidValueList
[7], 16),
385 int(guidValueList
[8], 16),
386 int(guidValueList
[9], 16),
387 int(guidValueList
[10], 16)
392 ## Convert GUID string in C structure style to xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx
394 # @param GuidValue The GUID value in C structure format
396 # @retval string The GUID value in xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx format
398 def GuidStructureStringToGuidValueName(GuidValue
):
399 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "")
400 guidValueList
= guidValueString
.split(",")
401 if len(guidValueList
) != 11:
402 EdkLogger
.error(None, FORMAT_INVALID
, "Invalid GUID value string [%s]" % GuidValue
)
403 return "%08x_%04x_%04x_%02x%02x_%02x%02x%02x%02x%02x%02x" % (
404 int(guidValueList
[0], 16),
405 int(guidValueList
[1], 16),
406 int(guidValueList
[2], 16),
407 int(guidValueList
[3], 16),
408 int(guidValueList
[4], 16),
409 int(guidValueList
[5], 16),
410 int(guidValueList
[6], 16),
411 int(guidValueList
[7], 16),
412 int(guidValueList
[8], 16),
413 int(guidValueList
[9], 16),
414 int(guidValueList
[10], 16)
417 ## Create directories
419 # @param Directory The directory name
421 def CreateDirectory(Directory
):
422 if Directory
is None or Directory
.strip() == "":
425 if not os
.access(Directory
, os
.F_OK
):
426 os
.makedirs(Directory
)
431 ## Remove directories, including files and sub-directories in it
433 # @param Directory The directory name
435 def RemoveDirectory(Directory
, Recursively
=False):
436 if Directory
is None or Directory
.strip() == "" or not os
.path
.exists(Directory
):
439 CurrentDirectory
= os
.getcwd()
441 for File
in os
.listdir("."):
442 if os
.path
.isdir(File
):
443 RemoveDirectory(File
, Recursively
)
446 os
.chdir(CurrentDirectory
)
449 ## Store content in file
451 # This method is used to save file only when its content is changed. This is
452 # quite useful for "make" system to decide what will be re-built and what won't.
454 # @param File The path of file
455 # @param Content The new content of the file
456 # @param IsBinaryFile The flag indicating if the file is binary file or not
458 # @retval True If the file content is changed and the file is renewed
459 # @retval False If the file content is the same
461 def SaveFileOnChange(File
, Content
, IsBinaryFile
=True):
463 Content
= Content
.replace("\n", os
.linesep
)
465 if os
.path
.exists(File
):
467 if Content
== open(File
, "rb").read():
470 EdkLogger
.error(None, FILE_OPEN_FAILURE
, ExtraData
=File
)
472 DirName
= os
.path
.dirname(File
)
473 if not CreateDirectory(DirName
):
474 EdkLogger
.error(None, FILE_CREATE_FAILURE
, "Could not create directory %s" % DirName
)
477 DirName
= os
.getcwd()
478 if not os
.access(DirName
, os
.W_OK
):
479 EdkLogger
.error(None, PERMISSION_FAILURE
, "Do not have write permission on directory %s" % DirName
)
482 Fd
= open(File
, "wb")
486 EdkLogger
.error(None, FILE_CREATE_FAILURE
, ExtraData
='IOError %s' % X
)
490 ## Retrieve and cache the real path name in file system
492 # @param Root The root directory of path relative to
494 # @retval str The path string if the path exists
495 # @retval None If path doesn't exist
501 def __init__(self
, Root
):
503 for F
in os
.listdir(Root
):
505 self
._UPPER
_CACHE
_[F
.upper()] = F
508 def __getitem__(self
, Path
):
509 Path
= Path
[len(os
.path
.commonprefix([Path
, self
._Root
])):]
512 if Path
and Path
[0] == os
.path
.sep
:
514 if Path
in self
._CACHE
_:
515 return os
.path
.join(self
._Root
, Path
)
516 UpperPath
= Path
.upper()
517 if UpperPath
in self
._UPPER
_CACHE
_:
518 return os
.path
.join(self
._Root
, self
._UPPER
_CACHE
_[UpperPath
])
522 SepIndex
= Path
.find(os
.path
.sep
)
524 Parent
= UpperPath
[:SepIndex
]
525 if Parent
not in self
._UPPER
_CACHE
_:
527 LastSepIndex
= SepIndex
528 SepIndex
= Path
.find(os
.path
.sep
, LastSepIndex
+ 1)
530 if LastSepIndex
== -1:
535 SepIndex
= LastSepIndex
537 Parent
= Path
[:SepIndex
]
538 ParentKey
= UpperPath
[:SepIndex
]
539 if ParentKey
not in self
._UPPER
_CACHE
_:
543 if Parent
in self
._CACHE
_:
546 ParentDir
= self
._UPPER
_CACHE
_[ParentKey
]
547 for F
in os
.listdir(ParentDir
):
548 Dir
= os
.path
.join(ParentDir
, F
)
549 self
._CACHE
_.add(Dir
)
550 self
._UPPER
_CACHE
_[Dir
.upper()] = Dir
552 SepIndex
= Path
.find(os
.path
.sep
, SepIndex
+ 1)
555 if Path
in self
._CACHE
_:
556 return os
.path
.join(self
._Root
, Path
)
557 elif UpperPath
in self
._UPPER
_CACHE
_:
558 return os
.path
.join(self
._Root
, self
._UPPER
_CACHE
_[UpperPath
])
561 def RealPath(File
, Dir
='', OverrideDir
=''):
562 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
563 NewFile
= GlobalData
.gAllFiles
[NewFile
]
564 if not NewFile
and OverrideDir
:
565 NewFile
= os
.path
.normpath(os
.path
.join(OverrideDir
, File
))
566 NewFile
= GlobalData
.gAllFiles
[NewFile
]
569 ## Get GUID value from given packages
571 # @param CName The CName of the GUID
572 # @param PackageList List of packages looking-up in
573 # @param Inffile The driver file
575 # @retval GuidValue if the CName is found in any given package
576 # @retval None if the CName is not found in all given packages
578 def GuidValue(CName
, PackageList
, Inffile
= None):
579 for P
in PackageList
:
580 GuidKeys
= P
.Guids
.keys()
581 if Inffile
and P
._PrivateGuids
:
582 if not Inffile
.startswith(P
.MetaFile
.Dir
):
583 GuidKeys
= [x
for x
in P
.Guids
if x
not in P
._PrivateGuids
]
584 if CName
in GuidKeys
:
585 return P
.Guids
[CName
]
588 ## A string template class
590 # This class implements a template for string replacement. A string template
591 # looks like following
593 # ${BEGIN} other_string ${placeholder_name} other_string ${END}
595 # The string between ${BEGIN} and ${END} will be repeated as many times as the
596 # length of "placeholder_name", which is a list passed through a dict. The
597 # "placeholder_name" is the key name of the dict. The ${BEGIN} and ${END} can
598 # be not used and, in this case, the "placeholder_name" must not a list and it
599 # will just be replaced once.
601 class TemplateString(object):
602 _REPEAT_START_FLAG
= "BEGIN"
603 _REPEAT_END_FLAG
= "END"
605 class Section(object):
606 _LIST_TYPES
= [type([]), type(set()), type((0,))]
608 def __init__(self
, TemplateSection
, PlaceHolderList
):
609 self
._Template
= TemplateSection
610 self
._PlaceHolderList
= []
612 # Split the section into sub-sections according to the position of placeholders
614 self
._SubSectionList
= []
617 # The placeholders passed in must be in the format of
619 # PlaceHolderName, PlaceHolderStartPoint, PlaceHolderEndPoint
621 for PlaceHolder
, Start
, End
in PlaceHolderList
:
622 self
._SubSectionList
.append(TemplateSection
[SubSectionStart
:Start
])
623 self
._SubSectionList
.append(TemplateSection
[Start
:End
])
624 self
._PlaceHolderList
.append(PlaceHolder
)
625 SubSectionStart
= End
626 if SubSectionStart
< len(TemplateSection
):
627 self
._SubSectionList
.append(TemplateSection
[SubSectionStart
:])
629 self
._SubSectionList
= [TemplateSection
]
632 return self
._Template
+ " : " + str(self
._PlaceHolderList
)
634 def Instantiate(self
, PlaceHolderValues
):
636 RepeatPlaceHolders
= {}
637 NonRepeatPlaceHolders
= {}
639 for PlaceHolder
in self
._PlaceHolderList
:
640 if PlaceHolder
not in PlaceHolderValues
:
642 Value
= PlaceHolderValues
[PlaceHolder
]
643 if type(Value
) in self
._LIST
_TYPES
:
645 RepeatTime
= len(Value
)
646 elif RepeatTime
!= len(Value
):
650 "${%s} has different repeat time from others!" % PlaceHolder
,
651 ExtraData
=str(self
._Template
)
653 RepeatPlaceHolders
["${%s}" % PlaceHolder
] = Value
655 NonRepeatPlaceHolders
["${%s}" % PlaceHolder
] = Value
657 if NonRepeatPlaceHolders
:
659 for S
in self
._SubSectionList
:
660 if S
not in NonRepeatPlaceHolders
:
663 StringList
.append(str(NonRepeatPlaceHolders
[S
]))
665 StringList
= self
._SubSectionList
667 if RepeatPlaceHolders
:
669 for Index
in range(RepeatTime
):
671 if S
not in RepeatPlaceHolders
:
672 TempStringList
.append(S
)
674 TempStringList
.append(str(RepeatPlaceHolders
[S
][Index
]))
675 StringList
= TempStringList
677 return "".join(StringList
)
680 def __init__(self
, Template
=None):
682 self
.IsBinary
= False
683 self
._Template
= Template
684 self
._TemplateSectionList
= self
._Parse
(Template
)
688 # @retval string The string replaced
691 return "".join(self
.String
)
693 ## Split the template string into fragments per the ${BEGIN} and ${END} flags
695 # @retval list A list of TemplateString.Section objects
697 def _Parse(self
, Template
):
702 TemplateSectionList
= []
704 MatchObj
= gPlaceholderPattern
.search(Template
, SearchFrom
)
706 if MatchEnd
<= len(Template
):
707 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:], PlaceHolderList
)
708 TemplateSectionList
.append(TemplateSection
)
711 MatchString
= MatchObj
.group(1)
712 MatchStart
= MatchObj
.start()
713 MatchEnd
= MatchObj
.end()
715 if MatchString
== self
._REPEAT
_START
_FLAG
:
716 if MatchStart
> SectionStart
:
717 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:MatchStart
], PlaceHolderList
)
718 TemplateSectionList
.append(TemplateSection
)
719 SectionStart
= MatchEnd
721 elif MatchString
== self
._REPEAT
_END
_FLAG
:
722 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:MatchStart
], PlaceHolderList
)
723 TemplateSectionList
.append(TemplateSection
)
724 SectionStart
= MatchEnd
727 PlaceHolderList
.append((MatchString
, MatchStart
- SectionStart
, MatchEnd
- SectionStart
))
728 SearchFrom
= MatchEnd
729 return TemplateSectionList
731 ## Replace the string template with dictionary of placeholders and append it to previous one
733 # @param AppendString The string template to append
734 # @param Dictionary The placeholder dictionaries
736 def Append(self
, AppendString
, Dictionary
=None):
738 SectionList
= self
._Parse
(AppendString
)
739 self
.String
.append( "".join(S
.Instantiate(Dictionary
) for S
in SectionList
))
741 if isinstance(AppendString
,list):
742 self
.String
.extend(AppendString
)
744 self
.String
.append(AppendString
)
746 ## Replace the string template with dictionary of placeholders
748 # @param Dictionary The placeholder dictionaries
750 # @retval str The string replaced with placeholder values
752 def Replace(self
, Dictionary
=None):
753 return "".join(S
.Instantiate(Dictionary
) for S
in self
._TemplateSectionList
)
755 ## Progress indicator class
757 # This class makes use of thread to print progress on console.
760 # for avoiding deadloop
762 _ProgressThread
= None
763 _CheckInterval
= 0.25
767 # @param OpenMessage The string printed before progress charaters
768 # @param CloseMessage The string printed after progress charaters
769 # @param ProgressChar The charater used to indicate the progress
770 # @param Interval The interval in seconds between two progress charaters
772 def __init__(self
, OpenMessage
="", CloseMessage
="", ProgressChar
='.', Interval
=1.0):
773 self
.PromptMessage
= OpenMessage
774 self
.CodaMessage
= CloseMessage
775 self
.ProgressChar
= ProgressChar
776 self
.Interval
= Interval
777 if Progressor
._StopFlag
is None:
778 Progressor
._StopFlag
= threading
.Event()
780 ## Start to print progress charater
782 # @param OpenMessage The string printed before progress charaters
784 def Start(self
, OpenMessage
=None):
785 if OpenMessage
is not None:
786 self
.PromptMessage
= OpenMessage
787 Progressor
._StopFlag
.clear()
788 if Progressor
._ProgressThread
is None:
789 Progressor
._ProgressThread
= threading
.Thread(target
=self
._ProgressThreadEntry
)
790 Progressor
._ProgressThread
.setDaemon(False)
791 Progressor
._ProgressThread
.start()
793 ## Stop printing progress charater
795 # @param CloseMessage The string printed after progress charaters
797 def Stop(self
, CloseMessage
=None):
798 OriginalCodaMessage
= self
.CodaMessage
799 if CloseMessage
is not None:
800 self
.CodaMessage
= CloseMessage
802 self
.CodaMessage
= OriginalCodaMessage
804 ## Thread entry method
805 def _ProgressThreadEntry(self
):
806 sys
.stdout
.write(self
.PromptMessage
+ " ")
809 while not Progressor
._StopFlag
.isSet():
811 sys
.stdout
.write(self
.ProgressChar
)
813 TimeUp
= self
.Interval
814 time
.sleep(self
._CheckInterval
)
815 TimeUp
-= self
._CheckInterval
816 sys
.stdout
.write(" " + self
.CodaMessage
+ "\n")
819 ## Abort the progress display
822 if Progressor
._StopFlag
is not None:
823 Progressor
._StopFlag
.set()
824 if Progressor
._ProgressThread
is not None:
825 Progressor
._ProgressThread
.join()
826 Progressor
._ProgressThread
= None
828 ## A dict which can access its keys and/or values orderly
830 # The class implements a new kind of dict which its keys or values can be
831 # accessed in the order they are added into the dict. It guarantees the order
832 # by making use of an internal list to keep a copy of keys.
841 def __setitem__(self
, key
, value
):
842 if key
not in self
._key
_list
:
843 self
._key
_list
.append(key
)
844 dict.__setitem
__(self
, key
, value
)
847 def __delitem__(self
, key
):
848 self
._key
_list
.remove(key
)
849 dict.__delitem
__(self
, key
)
851 ## used in "for k in dict" loop to ensure the correct order
853 return self
.iterkeys()
857 return len(self
._key
_list
)
860 def __contains__(self
, key
):
861 return key
in self
._key
_list
864 def index(self
, key
):
865 return self
._key
_list
.index(key
)
868 def insert(self
, key
, newkey
, newvalue
, order
):
869 index
= self
._key
_list
.index(key
)
870 if order
== 'BEFORE':
871 self
._key
_list
.insert(index
, newkey
)
872 dict.__setitem
__(self
, newkey
, newvalue
)
873 elif order
== 'AFTER':
874 self
._key
_list
.insert(index
+ 1, newkey
)
875 dict.__setitem
__(self
, newkey
, newvalue
)
878 def append(self
, sdict
):
880 if key
not in self
._key
_list
:
881 self
._key
_list
.append(key
)
882 dict.__setitem
__(self
, key
, sdict
[key
])
884 def has_key(self
, key
):
885 return key
in self
._key
_list
892 ## Return a copy of keys
895 for key
in self
._key
_list
:
899 ## Return a copy of values
902 for key
in self
._key
_list
:
903 values
.append(self
[key
])
906 ## Return a copy of (key, value) list
909 for key
in self
._key
_list
:
910 items
.append((key
, self
[key
]))
915 return iter(self
.items())
917 ## Keys interation support
919 return iter(self
.keys())
921 ## Values interation support
922 def itervalues(self
):
923 return iter(self
.values())
925 ## Return value related to a key, and remove the (key, value) from the dict
926 def pop(self
, key
, *dv
):
928 if key
in self
._key
_list
:
930 self
.__delitem
__(key
)
935 ## Return (key, value) pair, and remove the (key, value) from the dict
937 key
= self
._key
_list
[-1]
939 self
.__delitem
__(key
)
942 def update(self
, dict=None, **kwargs
):
944 for k
, v
in dict.items():
947 for k
, v
in kwargs
.items():
950 ## Dictionary using prioritized list as key
954 _TupleType
= type(())
956 _ValidWildcardList
= ['COMMON', 'DEFAULT', 'ALL', TAB_STAR
, 'PLATFORM']
958 def __init__(self
, _Single_
=False, _Level_
=2):
959 self
._Level
_ = _Level_
961 self
._Single
_ = _Single_
964 def __getitem__(self
, key
):
967 if KeyType
== self
._ListType
or KeyType
== self
._TupleType
:
971 elif self
._Level
_ > 1:
972 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
976 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
978 if FirstKey
is None or str(FirstKey
).upper() in self
._ValidWildcardList
:
979 FirstKey
= self
._Wildcard
982 return self
._GetSingleValue
(FirstKey
, RestKeys
)
984 return self
._GetAllValues
(FirstKey
, RestKeys
)
986 def _GetSingleValue(self
, FirstKey
, RestKeys
):
988 #print "%s-%s" % (FirstKey, self._Level_) ,
990 if FirstKey
== self
._Wildcard
:
991 if FirstKey
in self
.data
:
992 Value
= self
.data
[FirstKey
][RestKeys
]
994 for Key
in self
.data
:
995 Value
= self
.data
[Key
][RestKeys
]
996 if Value
is not None: break
998 if FirstKey
in self
.data
:
999 Value
= self
.data
[FirstKey
][RestKeys
]
1000 if Value
is None and self
._Wildcard
in self
.data
:
1002 Value
= self
.data
[self
._Wildcard
][RestKeys
]
1004 if FirstKey
== self
._Wildcard
:
1005 if FirstKey
in self
.data
:
1006 Value
= self
.data
[FirstKey
]
1008 for Key
in self
.data
:
1009 Value
= self
.data
[Key
]
1010 if Value
is not None: break
1012 if FirstKey
in self
.data
:
1013 Value
= self
.data
[FirstKey
]
1014 elif self
._Wildcard
in self
.data
:
1015 Value
= self
.data
[self
._Wildcard
]
1018 def _GetAllValues(self
, FirstKey
, RestKeys
):
1020 if self
._Level
_ > 1:
1021 if FirstKey
== self
._Wildcard
:
1022 for Key
in self
.data
:
1023 Value
+= self
.data
[Key
][RestKeys
]
1025 if FirstKey
in self
.data
:
1026 Value
+= self
.data
[FirstKey
][RestKeys
]
1027 if self
._Wildcard
in self
.data
:
1028 Value
+= self
.data
[self
._Wildcard
][RestKeys
]
1030 if FirstKey
== self
._Wildcard
:
1031 for Key
in self
.data
:
1032 Value
.append(self
.data
[Key
])
1034 if FirstKey
in self
.data
:
1035 Value
.append(self
.data
[FirstKey
])
1036 if self
._Wildcard
in self
.data
:
1037 Value
.append(self
.data
[self
._Wildcard
])
1041 def __setitem__(self
, key
, value
):
1044 if KeyType
== self
._ListType
or KeyType
== self
._TupleType
:
1049 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
1052 if self
._Level
_ > 1:
1053 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
1055 if FirstKey
in self
._ValidWildcardList
:
1056 FirstKey
= self
._Wildcard
1058 if FirstKey
not in self
.data
and self
._Level
_ > 0:
1059 self
.data
[FirstKey
] = tdict(self
._Single
_, self
._Level
_ - 1)
1061 if self
._Level
_ > 1:
1062 self
.data
[FirstKey
][RestKeys
] = value
1064 self
.data
[FirstKey
] = value
1066 def SetGreedyMode(self
):
1067 self
._Single
_ = False
1068 if self
._Level
_ > 1:
1069 for Key
in self
.data
:
1070 self
.data
[Key
].SetGreedyMode()
1072 def SetSingleMode(self
):
1073 self
._Single
_ = True
1074 if self
._Level
_ > 1:
1075 for Key
in self
.data
:
1076 self
.data
[Key
].SetSingleMode()
1078 def GetKeys(self
, KeyIndex
=0):
1079 assert KeyIndex
>= 0
1081 return set(self
.data
.keys())
1084 for Key
in self
.data
:
1085 keys |
= self
.data
[Key
].GetKeys(KeyIndex
- 1)
1088 def AnalyzePcdExpression(Setting
):
1089 RanStr
= ''.join(sample(string
.ascii_letters
+ string
.digits
, 8))
1090 Setting
= Setting
.replace('\\\\', RanStr
).strip()
1091 # There might be escaped quote in a string: \", \\\" , \', \\\'
1093 # There might be '|' in string and in ( ... | ... ), replace it with '-'
1095 InSingleQuoteStr
= False
1096 InDoubleQuoteStr
= False
1098 for Index
, ch
in enumerate(Data
):
1099 if ch
== '"' and not InSingleQuoteStr
:
1100 if Data
[Index
- 1] != '\\':
1101 InDoubleQuoteStr
= not InDoubleQuoteStr
1102 elif ch
== "'" and not InDoubleQuoteStr
:
1103 if Data
[Index
- 1] != '\\':
1104 InSingleQuoteStr
= not InSingleQuoteStr
1105 elif ch
== '(' and not (InSingleQuoteStr
or InDoubleQuoteStr
):
1107 elif ch
== ')' and not (InSingleQuoteStr
or InDoubleQuoteStr
):
1110 if (Pair
> 0 or InSingleQuoteStr
or InDoubleQuoteStr
) and ch
== TAB_VALUE_SPLIT
:
1117 Pos
= NewStr
.find(TAB_VALUE_SPLIT
, StartPos
)
1119 FieldList
.append(Setting
[StartPos
:].strip())
1121 FieldList
.append(Setting
[StartPos
:Pos
].strip())
1123 for i
, ch
in enumerate(FieldList
):
1125 FieldList
[i
] = ch
.replace(RanStr
,'\\\\')
1128 def ParseFieldValue (Value
):
1129 def ParseDevPathValue (Value
):
1131 Value
.replace('\\', '/').replace(' ', '')
1133 Cmd
= 'DevicePath ' + '"' + Value
+ '"'
1135 p
= subprocess
.Popen(Cmd
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, shell
=True)
1136 out
, err
= p
.communicate()
1137 except Exception as X
:
1138 raise BadExpression("DevicePath: %s" % (str(X
)) )
1140 subprocess
._cleanup
()
1144 raise BadExpression("DevicePath: %s" % str(err
))
1145 Size
= len(out
.split())
1146 out
= ','.join(out
.split())
1147 return '{' + out
+ '}', Size
1149 if "{CODE(" in Value
:
1150 return Value
, len(Value
.split(","))
1151 if isinstance(Value
, type(0)):
1152 return Value
, (Value
.bit_length() + 7) / 8
1153 if not isinstance(Value
, type('')):
1154 raise BadExpression('Type %s is %s' %(Value
, type(Value
)))
1155 Value
= Value
.strip()
1156 if Value
.startswith(TAB_UINT8
) and Value
.endswith(')'):
1157 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1159 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1161 if Value
.startswith(TAB_UINT16
) and Value
.endswith(')'):
1162 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1164 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1166 if Value
.startswith(TAB_UINT32
) and Value
.endswith(')'):
1167 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1169 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1171 if Value
.startswith(TAB_UINT64
) and Value
.endswith(')'):
1172 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1174 raise BadExpression('Value (%s) Size larger than %d' % (Value
, Size
))
1176 if Value
.startswith(TAB_GUID
) and Value
.endswith(')'):
1177 Value
= Value
.split('(', 1)[1][:-1].strip()
1178 if Value
[0] == '{' and Value
[-1] == '}':
1179 TmpValue
= GuidStructureStringToGuidString(Value
)
1181 raise BadExpression("Invalid GUID value string %s" % Value
)
1183 if Value
[0] == '"' and Value
[-1] == '"':
1186 Value
= "'" + uuid
.UUID(Value
).get_bytes_le() + "'"
1187 except ValueError as Message
:
1188 raise BadExpression(Message
)
1189 Value
, Size
= ParseFieldValue(Value
)
1191 if Value
.startswith('L"') and Value
.endswith('"'):
1193 # translate escape character
1203 Value
= (Value
<< 16) |
ord(Char
)
1204 return Value
, (len(List
) + 1) * 2
1205 if Value
.startswith('"') and Value
.endswith('"'):
1207 # translate escape character
1216 Value
= (Value
<< 8) |
ord(Char
)
1217 return Value
, len(List
) + 1
1218 if Value
.startswith("L'") and Value
.endswith("'"):
1219 # Unicode Character Constant
1220 # translate escape character
1228 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1232 Value
= (Value
<< 16) |
ord(Char
)
1233 return Value
, len(List
) * 2
1234 if Value
.startswith("'") and Value
.endswith("'"):
1235 # Character constant
1236 # translate escape character
1243 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1247 Value
= (Value
<< 8) |
ord(Char
)
1248 return Value
, len(List
)
1249 if Value
.startswith('{') and Value
.endswith('}'):
1252 List
= [Item
.strip() for Item
in Value
.split(',')]
1257 ItemValue
, Size
= ParseFieldValue(Item
)
1259 for I
in range(Size
):
1260 Value
= (Value
<< 8) |
((ItemValue
>> 8 * I
) & 0xff)
1261 return Value
, RetSize
1262 if Value
.startswith('DEVICE_PATH(') and Value
.endswith(')'):
1263 Value
= Value
.replace("DEVICE_PATH(", '').rstrip(')')
1264 Value
= Value
.strip().strip('"')
1265 return ParseDevPathValue(Value
)
1266 if Value
.lower().startswith('0x'):
1268 Value
= int(Value
, 16)
1270 raise BadExpression("invalid hex value: %s" % Value
)
1273 return Value
, (Value
.bit_length() + 7) / 8
1274 if Value
[0].isdigit():
1275 Value
= int(Value
, 10)
1278 return Value
, (Value
.bit_length() + 7) / 8
1279 if Value
.lower() == 'true':
1281 if Value
.lower() == 'false':
1287 # Analyze DSC PCD value, since there is no data type info in DSC
1288 # This function is used to match functions (AnalyzePcdData) used for retrieving PCD value from database
1289 # 1. Feature flag: TokenSpace.PcdCName|PcdValue
1290 # 2. Fix and Patch:TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1291 # 3. Dynamic default:
1292 # TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1293 # TokenSpace.PcdCName|PcdValue
1295 # TokenSpace.PcdCName|VpdOffset[|VpdValue]
1296 # TokenSpace.PcdCName|VpdOffset[|MaxSize[|VpdValue]]
1298 # TokenSpace.PcdCName|HiiString|VariableGuid|VariableOffset[|HiiValue]
1299 # PCD value needs to be located in such kind of string, and the PCD value might be an expression in which
1300 # there might have "|" operator, also in string value.
1302 # @param Setting: String contain information described above with "TokenSpace.PcdCName|" stripped
1303 # @param PcdType: PCD type: feature, fixed, dynamic default VPD HII
1304 # @param DataType: The datum type of PCD: VOID*, UNIT, BOOL
1306 # ValueList: A List contain fields described above
1307 # IsValid: True if conforming EBNF, otherwise False
1308 # Index: The index where PcdValue is in ValueList
1310 def AnalyzeDscPcd(Setting
, PcdType
, DataType
=''):
1311 FieldList
= AnalyzePcdExpression(Setting
)
1314 if PcdType
in (MODEL_PCD_FIXED_AT_BUILD
, MODEL_PCD_PATCHABLE_IN_MODULE
, MODEL_PCD_DYNAMIC_DEFAULT
, MODEL_PCD_DYNAMIC_EX_DEFAULT
):
1315 Value
= FieldList
[0]
1317 if len(FieldList
) > 1 and FieldList
[1]:
1318 DataType
= FieldList
[1]
1319 if FieldList
[1] != TAB_VOID
and StructPattern
.match(FieldList
[1]) is None:
1321 if len(FieldList
) > 2:
1325 IsValid
= (len(FieldList
) <= 1)
1327 IsValid
= (len(FieldList
) <= 3)
1331 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1335 return [str(Value
), DataType
, str(Size
)], IsValid
, 0
1336 elif PcdType
== MODEL_PCD_FEATURE_FLAG
:
1337 Value
= FieldList
[0]
1339 IsValid
= (len(FieldList
) <= 1)
1340 return [Value
, DataType
, str(Size
)], IsValid
, 0
1341 elif PcdType
in (MODEL_PCD_DYNAMIC_VPD
, MODEL_PCD_DYNAMIC_EX_VPD
):
1342 VpdOffset
= FieldList
[0]
1344 if not DataType
== TAB_VOID
:
1345 if len(FieldList
) > 1:
1346 Value
= FieldList
[1]
1348 if len(FieldList
) > 1:
1350 if len(FieldList
) > 2:
1351 Value
= FieldList
[2]
1353 IsValid
= (len(FieldList
) <= 1)
1355 IsValid
= (len(FieldList
) <= 3)
1358 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1362 return [VpdOffset
, str(Size
), Value
], IsValid
, 2
1363 elif PcdType
in (MODEL_PCD_DYNAMIC_HII
, MODEL_PCD_DYNAMIC_EX_HII
):
1364 IsValid
= (3 <= len(FieldList
) <= 5)
1365 HiiString
= FieldList
[0]
1366 Guid
= Offset
= Value
= Attribute
= ''
1367 if len(FieldList
) > 1:
1369 if len(FieldList
) > 2:
1370 Offset
= FieldList
[2]
1371 if len(FieldList
) > 3:
1372 Value
= FieldList
[3]
1373 if len(FieldList
) > 4:
1374 Attribute
= FieldList
[4]
1375 return [HiiString
, Guid
, Offset
, Value
, Attribute
], IsValid
, 3
1380 # Analyze the pcd Value, Datum type and TokenNumber.
1381 # Used to avoid split issue while the value string contain "|" character
1383 # @param[in] Setting: A String contain value/datum type/token number information;
1385 # @retval ValueList: A List contain value, datum type and toke number.
1387 def AnalyzePcdData(Setting
):
1388 ValueList
= ['', '', '']
1390 ValueRe
= re
.compile(r
'^\s*L?\".*\|.*\"')
1391 PtrValue
= ValueRe
.findall(Setting
)
1393 ValueUpdateFlag
= False
1395 if len(PtrValue
) >= 1:
1396 Setting
= re
.sub(ValueRe
, '', Setting
)
1397 ValueUpdateFlag
= True
1399 TokenList
= Setting
.split(TAB_VALUE_SPLIT
)
1400 ValueList
[0:len(TokenList
)] = TokenList
1403 ValueList
[0] = PtrValue
[0]
1407 ## check format of PCD value against its the datum type
1409 # For PCD value setting
1411 def CheckPcdDatum(Type
, Value
):
1412 if Type
== TAB_VOID
:
1413 ValueRe
= re
.compile(r
'\s*L?\".*\"\s*$')
1414 if not (((Value
.startswith('L"') or Value
.startswith('"')) and Value
.endswith('"'))
1415 or (Value
.startswith('{') and Value
.endswith('}')) or (Value
.startswith("L'") or Value
.startswith("'") and Value
.endswith("'"))
1417 return False, "Invalid value [%s] of type [%s]; must be in the form of {...} for array"\
1418 ", \"...\" or \'...\' for string, L\"...\" or L\'...\' for unicode string" % (Value
, Type
)
1419 elif ValueRe
.match(Value
):
1420 # Check the chars in UnicodeString or CString is printable
1421 if Value
.startswith("L"):
1425 Printset
= set(string
.printable
)
1426 Printset
.remove(TAB_PRINTCHAR_VT
)
1427 Printset
.add(TAB_PRINTCHAR_BS
)
1428 Printset
.add(TAB_PRINTCHAR_NUL
)
1429 if not set(Value
).issubset(Printset
):
1430 PrintList
= sorted(Printset
)
1431 return False, "Invalid PCD string value of type [%s]; must be printable chars %s." % (Type
, PrintList
)
1432 elif Type
== 'BOOLEAN':
1433 if Value
not in ['TRUE', 'True', 'true', '0x1', '0x01', '1', 'FALSE', 'False', 'false', '0x0', '0x00', '0']:
1434 return False, "Invalid value [%s] of type [%s]; must be one of TRUE, True, true, 0x1, 0x01, 1"\
1435 ", FALSE, False, false, 0x0, 0x00, 0" % (Value
, Type
)
1436 elif Type
in [TAB_UINT8
, TAB_UINT16
, TAB_UINT32
, TAB_UINT64
]:
1437 if Value
and int(Value
, 0) < 0:
1438 return False, "PCD can't be set to negative value[%s] for datum type [%s]" % (Value
, Type
)
1440 Value
= long(Value
, 0)
1441 if Value
> MAX_VAL_TYPE
[Type
]:
1442 return False, "Too large PCD value[%s] for datum type [%s]" % (Value
, Type
)
1444 return False, "Invalid value [%s] of type [%s];"\
1445 " must be a hexadecimal, decimal or octal in C language format." % (Value
, Type
)
1447 return True, "StructurePcd"
1451 def CommonPath(PathList
):
1452 P1
= min(PathList
).split(os
.path
.sep
)
1453 P2
= max(PathList
).split(os
.path
.sep
)
1454 for Index
in xrange(min(len(P1
), len(P2
))):
1455 if P1
[Index
] != P2
[Index
]:
1456 return os
.path
.sep
.join(P1
[:Index
])
1457 return os
.path
.sep
.join(P1
)
1459 class PathClass(object):
1460 def __init__(self
, File
='', Root
='', AlterRoot
='', Type
='', IsBinary
=False,
1461 Arch
='COMMON', ToolChainFamily
='', Target
='', TagName
='', ToolCode
=''):
1463 self
.File
= str(File
)
1464 if os
.path
.isabs(self
.File
):
1468 self
.Root
= str(Root
)
1469 self
.AlterRoot
= str(AlterRoot
)
1471 # Remove any '.' and '..' in path
1473 self
.Root
= mws
.getWs(self
.Root
, self
.File
)
1474 self
.Path
= os
.path
.normpath(os
.path
.join(self
.Root
, self
.File
))
1475 self
.Root
= os
.path
.normpath(CommonPath([self
.Root
, self
.Path
]))
1476 # eliminate the side-effect of 'C:'
1477 if self
.Root
[-1] == ':':
1478 self
.Root
+= os
.path
.sep
1479 # file path should not start with path separator
1480 if self
.Root
[-1] == os
.path
.sep
:
1481 self
.File
= self
.Path
[len(self
.Root
):]
1483 self
.File
= self
.Path
[len(self
.Root
) + 1:]
1485 self
.Path
= os
.path
.normpath(self
.File
)
1487 self
.SubDir
, self
.Name
= os
.path
.split(self
.File
)
1488 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1492 self
.Dir
= os
.path
.join(self
.Root
, self
.SubDir
)
1494 self
.Dir
= self
.Root
1496 self
.Dir
= self
.SubDir
1501 self
.Type
= self
.Ext
.lower()
1503 self
.IsBinary
= IsBinary
1504 self
.Target
= Target
1505 self
.TagName
= TagName
1506 self
.ToolCode
= ToolCode
1507 self
.ToolChainFamily
= ToolChainFamily
1509 ## Convert the object of this class to a string
1511 # Convert member Path of the class to a string
1513 # @retval string Formatted String
1518 ## Override __eq__ function
1520 # Check whether PathClass are the same
1522 # @retval False The two PathClass are different
1523 # @retval True The two PathClass are the same
1525 def __eq__(self
, Other
):
1526 return self
.Path
== str(Other
)
1528 ## Override __cmp__ function
1530 # Customize the comparsion operation of two PathClass
1532 # @retval 0 The two PathClass are different
1533 # @retval -1 The first PathClass is less than the second PathClass
1534 # @retval 1 The first PathClass is Bigger than the second PathClass
1535 def __cmp__(self
, Other
):
1536 OtherKey
= str(Other
)
1539 if SelfKey
== OtherKey
:
1541 elif SelfKey
> OtherKey
:
1546 ## Override __hash__ function
1548 # Use Path as key in hash table
1550 # @retval string Key for hash table
1553 return hash(self
.Path
)
1557 return self
.Path
.upper()
1560 def TimeStamp(self
):
1561 return os
.stat(self
.Path
)[8]
1563 def Validate(self
, Type
='', CaseSensitive
=True):
1564 def RealPath2(File
, Dir
='', OverrideDir
=''):
1567 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(OverrideDir
, File
))]
1569 if OverrideDir
[-1] == os
.path
.sep
:
1570 return NewFile
[len(OverrideDir
):], NewFile
[0:len(OverrideDir
)]
1572 return NewFile
[len(OverrideDir
) + 1:], NewFile
[0:len(OverrideDir
)]
1573 if GlobalData
.gAllFiles
:
1574 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(Dir
, File
))]
1576 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
1577 if not os
.path
.exists(NewFile
):
1581 if Dir
[-1] == os
.path
.sep
:
1582 return NewFile
[len(Dir
):], NewFile
[0:len(Dir
)]
1584 return NewFile
[len(Dir
) + 1:], NewFile
[0:len(Dir
)]
1590 if GlobalData
.gCaseInsensitive
:
1591 CaseSensitive
= False
1592 if Type
and Type
.lower() != self
.Type
:
1593 return FILE_TYPE_MISMATCH
, '%s (expect %s but got %s)' % (self
.File
, Type
, self
.Type
)
1595 RealFile
, RealRoot
= RealPath2(self
.File
, self
.Root
, self
.AlterRoot
)
1596 if not RealRoot
and not RealFile
:
1597 RealFile
= self
.File
1599 RealFile
= os
.path
.join(self
.AlterRoot
, self
.File
)
1601 RealFile
= os
.path
.join(self
.Root
, self
.File
)
1602 if len (mws
.getPkgPath()) == 0:
1603 return FILE_NOT_FOUND
, os
.path
.join(self
.AlterRoot
, RealFile
)
1605 return FILE_NOT_FOUND
, "%s is not found in packages path:\n\t%s" % (self
.File
, '\n\t'.join(mws
.getPkgPath()))
1609 if RealRoot
!= self
.Root
or RealFile
!= self
.File
:
1610 if CaseSensitive
and (RealFile
!= self
.File
or (RealRoot
!= self
.Root
and RealRoot
!= self
.AlterRoot
)):
1611 ErrorCode
= FILE_CASE_MISMATCH
1612 ErrorInfo
= self
.File
+ '\n\t' + RealFile
+ " [in file system]"
1614 self
.SubDir
, self
.Name
= os
.path
.split(RealFile
)
1615 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1617 self
.Dir
= os
.path
.join(RealRoot
, self
.SubDir
)
1620 self
.File
= RealFile
1621 self
.Root
= RealRoot
1622 self
.Path
= os
.path
.join(RealRoot
, RealFile
)
1623 return ErrorCode
, ErrorInfo
1625 ## Parse PE image to get the required PE informaion.
1627 class PeImageClass():
1630 # @param File FilePath of PeImage
1632 def __init__(self
, PeFile
):
1633 self
.FileName
= PeFile
1634 self
.IsValid
= False
1637 self
.SectionAlignment
= 0
1638 self
.SectionHeaderList
= []
1641 PeObject
= open(PeFile
, 'rb')
1643 self
.ErrorInfo
= self
.FileName
+ ' can not be found\n'
1646 ByteArray
= array
.array('B')
1647 ByteArray
.fromfile(PeObject
, 0x3E)
1648 ByteList
= ByteArray
.tolist()
1649 # DOS signature should be 'MZ'
1650 if self
._ByteListToStr
(ByteList
[0x0:0x2]) != 'MZ':
1651 self
.ErrorInfo
= self
.FileName
+ ' has no valid DOS signature MZ'
1654 # Read 4 byte PE Signature
1655 PeOffset
= self
._ByteListToInt
(ByteList
[0x3C:0x3E])
1656 PeObject
.seek(PeOffset
)
1657 ByteArray
= array
.array('B')
1658 ByteArray
.fromfile(PeObject
, 4)
1659 # PE signature should be 'PE\0\0'
1660 if ByteArray
.tostring() != 'PE\0\0':
1661 self
.ErrorInfo
= self
.FileName
+ ' has no valid PE signature PE00'
1664 # Read PE file header
1665 ByteArray
= array
.array('B')
1666 ByteArray
.fromfile(PeObject
, 0x14)
1667 ByteList
= ByteArray
.tolist()
1668 SecNumber
= self
._ByteListToInt
(ByteList
[0x2:0x4])
1670 self
.ErrorInfo
= self
.FileName
+ ' has no section header'
1673 # Read PE optional header
1674 OptionalHeaderSize
= self
._ByteListToInt
(ByteArray
[0x10:0x12])
1675 ByteArray
= array
.array('B')
1676 ByteArray
.fromfile(PeObject
, OptionalHeaderSize
)
1677 ByteList
= ByteArray
.tolist()
1678 self
.EntryPoint
= self
._ByteListToInt
(ByteList
[0x10:0x14])
1679 self
.SectionAlignment
= self
._ByteListToInt
(ByteList
[0x20:0x24])
1680 self
.Size
= self
._ByteListToInt
(ByteList
[0x38:0x3C])
1682 # Read each Section Header
1683 for Index
in range(SecNumber
):
1684 ByteArray
= array
.array('B')
1685 ByteArray
.fromfile(PeObject
, 0x28)
1686 ByteList
= ByteArray
.tolist()
1687 SecName
= self
._ByteListToStr
(ByteList
[0:8])
1688 SecVirtualSize
= self
._ByteListToInt
(ByteList
[8:12])
1689 SecRawAddress
= self
._ByteListToInt
(ByteList
[20:24])
1690 SecVirtualAddress
= self
._ByteListToInt
(ByteList
[12:16])
1691 self
.SectionHeaderList
.append((SecName
, SecVirtualAddress
, SecRawAddress
, SecVirtualSize
))
1695 def _ByteListToStr(self
, ByteList
):
1697 for index
in range(len(ByteList
)):
1698 if ByteList
[index
] == 0:
1700 String
+= chr(ByteList
[index
])
1703 def _ByteListToInt(self
, ByteList
):
1705 for index
in range(len(ByteList
) - 1, -1, -1):
1706 Value
= (Value
<< 8) |
int(ByteList
[index
])
1709 class DefaultStore():
1710 def __init__(self
, DefaultStores
):
1712 self
.DefaultStores
= DefaultStores
1713 def DefaultStoreID(self
, DefaultStoreName
):
1714 for key
, value
in self
.DefaultStores
.items():
1715 if value
== DefaultStoreName
:
1718 def GetDefaultDefault(self
):
1719 if not self
.DefaultStores
or "0" in self
.DefaultStores
:
1720 return "0", TAB_DEFAULT_STORES_DEFAULT
1722 minvalue
= min(int(value_str
) for value_str
in self
.DefaultStores
)
1723 return (str(minvalue
), self
.DefaultStores
[str(minvalue
)])
1724 def GetMin(self
, DefaultSIdList
):
1725 if not DefaultSIdList
:
1726 return TAB_DEFAULT_STORES_DEFAULT
1727 storeidset
= {storeid
for storeid
, storename
in self
.DefaultStores
.values() if storename
in DefaultSIdList
}
1730 minid
= min(storeidset
)
1731 for sid
, name
in self
.DefaultStores
.values():
1740 def __init__(self
,SkuIdentifier
='', SkuIds
=None):
1744 for SkuName
in SkuIds
:
1745 SkuId
= SkuIds
[SkuName
][0]
1746 skuid_num
= int(SkuId
, 16) if SkuId
.upper().startswith("0X") else int(SkuId
)
1747 if skuid_num
> 0xFFFFFFFFFFFFFFFF:
1748 EdkLogger
.error("build", PARAMETER_INVALID
,
1749 ExtraData
= "SKU-ID [%s] value %s exceeds the max value of UINT64"
1752 self
.AvailableSkuIds
= sdict()
1754 self
.SkuIdNumberSet
= []
1755 self
.SkuData
= SkuIds
1756 self
._SkuInherit
= {}
1757 self
._SkuIdentifier
= SkuIdentifier
1758 if SkuIdentifier
== '' or SkuIdentifier
is None:
1759 self
.SkuIdSet
= ['DEFAULT']
1760 self
.SkuIdNumberSet
= ['0U']
1761 elif SkuIdentifier
== 'ALL':
1762 self
.SkuIdSet
= SkuIds
.keys()
1763 self
.SkuIdNumberSet
= [num
[0].strip() + 'U' for num
in SkuIds
.values()]
1765 r
= SkuIdentifier
.split('|')
1766 self
.SkuIdSet
=[(r
[k
].strip()).upper() for k
in range(len(r
))]
1769 self
.SkuIdNumberSet
= [SkuIds
[k
][0].strip() + 'U' for k
in self
.SkuIdSet
]
1771 EdkLogger
.error("build", PARAMETER_INVALID
,
1772 ExtraData
= "SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1773 % (k
, " | ".join(SkuIds
.keys())))
1774 for each
in self
.SkuIdSet
:
1776 self
.AvailableSkuIds
[each
] = SkuIds
[each
][0]
1778 EdkLogger
.error("build", PARAMETER_INVALID
,
1779 ExtraData
="SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1780 % (each
, " | ".join(SkuIds
.keys())))
1781 if self
.SkuUsageType
!= SkuClass
.SINGLE
:
1782 self
.AvailableSkuIds
.update({'DEFAULT':0, 'COMMON':0})
1784 GlobalData
.gSkuids
= (self
.SkuIdSet
)
1785 if 'COMMON' in GlobalData
.gSkuids
:
1786 GlobalData
.gSkuids
.remove('COMMON')
1787 if self
.SkuUsageType
== self
.SINGLE
:
1788 if len(GlobalData
.gSkuids
) != 1:
1789 if 'DEFAULT' in GlobalData
.gSkuids
:
1790 GlobalData
.gSkuids
.remove('DEFAULT')
1791 if GlobalData
.gSkuids
:
1792 GlobalData
.gSkuids
.sort()
1794 def GetNextSkuId(self
, skuname
):
1795 if not self
._SkuInherit
:
1796 self
._SkuInherit
= {}
1797 for item
in self
.SkuData
.values():
1798 self
._SkuInherit
[item
[1]]=item
[2] if item
[2] else "DEFAULT"
1799 return self
._SkuInherit
.get(skuname
, "DEFAULT")
1801 def GetSkuChain(self
, sku
):
1802 if sku
== "DEFAULT":
1807 nextsku
= self
.GetNextSkuId(nextsku
)
1808 skulist
.append(nextsku
)
1809 if nextsku
== "DEFAULT":
1813 def SkuOverrideOrder(self
):
1815 for skuname
in self
.SkuIdSet
:
1816 skuorderset
.append(self
.GetSkuChain(skuname
))
1819 for index
in range(max(len(item
) for item
in skuorderset
)):
1820 for subset
in skuorderset
:
1821 if index
> len(subset
)-1:
1823 if subset
[index
] in skuorder
:
1825 skuorder
.append(subset
[index
])
1830 def SkuUsageType(self
):
1831 if self
._SkuIdentifier
.upper() == "ALL":
1832 return SkuClass
.MULTIPLE
1834 if len(self
.SkuIdSet
) == 1:
1835 if self
.SkuIdSet
[0] == 'DEFAULT':
1836 return SkuClass
.DEFAULT
1837 return SkuClass
.SINGLE
1838 if len(self
.SkuIdSet
) == 2 and 'DEFAULT' in self
.SkuIdSet
:
1839 return SkuClass
.SINGLE
1840 return SkuClass
.MULTIPLE
1842 def DumpSkuIdArrary(self
):
1843 if self
.SkuUsageType
== SkuClass
.SINGLE
:
1846 for skuname
in self
.AvailableSkuIds
:
1847 if skuname
== "COMMON":
1849 while skuname
!= "DEFAULT":
1850 ArrayStrList
.append(hex(int(self
.AvailableSkuIds
[skuname
])))
1851 skuname
= self
.GetNextSkuId(skuname
)
1852 ArrayStrList
.append("0x0")
1853 return "{{{myList}}}".format(myList
=",".join(ArrayStrList
))
1856 def AvailableSkuIdSet(self
):
1857 return self
.AvailableSkuIds
1860 def SystemSkuId(self
):
1861 if self
.SkuUsageType
== SkuClass
.SINGLE
:
1862 if len(self
.SkuIdSet
) == 1:
1863 return self
.SkuIdSet
[0]
1865 return self
.SkuIdSet
[0] if self
.SkuIdSet
[0] != 'DEFAULT' else self
.SkuIdSet
[1]
1869 ## Get the integer value from string like "14U" or integer like 2
1871 # @param Input The object that may be either a integer value or a string
1873 # @retval Value The integer value that the input represents
1875 def GetIntegerValue(Input
):
1876 if type(Input
) in (int, long):
1879 if String
.endswith("U"):
1880 String
= String
[:-1]
1881 if String
.endswith("ULL"):
1882 String
= String
[:-3]
1883 if String
.endswith("LL"):
1884 String
= String
[:-2]
1886 if String
.startswith("0x") or String
.startswith("0X"):
1887 return int(String
, 16)
1894 # Pack a GUID (registry format) list into a buffer and return it
1897 return pack(PACK_PATTERN_GUID
,
1901 int(Guid
[3][-4:-2], 16),
1902 int(Guid
[3][-2:], 16),
1903 int(Guid
[4][-12:-10], 16),
1904 int(Guid
[4][-10:-8], 16),
1905 int(Guid
[4][-8:-6], 16),
1906 int(Guid
[4][-6:-4], 16),
1907 int(Guid
[4][-4:-2], 16),
1908 int(Guid
[4][-2:], 16)
1912 # Pack a GUID (byte) list into a buffer and return it
1914 def PackByteFormatGUID(Guid
):
1915 return pack(PACK_PATTERN_GUID
,
1929 ## DeepCopy dict/OrderedDict recusively
1931 # @param ori_dict a nested dict or ordereddict
1933 # @retval new dict or orderdict
1935 def CopyDict(ori_dict
):
1936 dict_type
= ori_dict
.__class
__
1937 if dict_type
not in (dict,OrderedDict
):
1939 new_dict
= dict_type()
1940 for key
in ori_dict
:
1941 if isinstance(ori_dict
[key
],(dict,OrderedDict
)):
1942 new_dict
[key
] = CopyDict(ori_dict
[key
])
1944 new_dict
[key
] = ori_dict
[key
]
1948 # Remove the c/c++ comments: // and /* */
1950 def RemoveCComments(ctext
):
1951 return re
.sub('//.*?\n|/\*.*?\*/', '\n', ctext
, flags
=re
.S
)