2 # Common routines used by all tools
4 # Copyright (c) 2007 - 2019, Intel Corporation. All rights reserved.<BR>
5 # SPDX-License-Identifier: BSD-2-Clause-Patent
11 from __future__
import absolute_import
22 from random
import sample
23 from struct
import pack
27 from collections
import OrderedDict
29 import Common
.LongFilePathOs
as os
30 from Common
import EdkLogger
as EdkLogger
31 from Common
import GlobalData
as GlobalData
32 from Common
.DataType
import *
33 from Common
.BuildToolError
import *
34 from CommonDataClass
.DataClass
import *
35 from Common
.Parsing
import GetSplitValueList
36 from Common
.LongFilePathSupport
import OpenLongFilePath
as open
37 from Common
.MultipleWorkspace
import MultipleWorkspace
as mws
38 from CommonDataClass
.Exceptions
import BadExpression
39 from Common
.caching
import cached_property
41 ArrayIndex
= re
.compile("\[\s*[0-9a-fA-FxX]*\s*\]")
42 ## Regular expression used to find out place holders in string template
43 gPlaceholderPattern
= re
.compile("\$\{([^$()\s]+)\}", re
.MULTILINE | re
.UNICODE
)
45 ## regular expressions for map file processing
46 startPatternGeneral
= re
.compile("^Start[' ']+Length[' ']+Name[' ']+Class")
47 addressPatternGeneral
= re
.compile("^Address[' ']+Publics by Value[' ']+Rva\+Base")
48 valuePatternGcc
= re
.compile('^([\w_\.]+) +([\da-fA-Fx]+) +([\da-fA-Fx]+)$')
49 pcdPatternGcc
= re
.compile('^([\da-fA-Fx]+) +([\da-fA-Fx]+)')
50 secReGeneral
= re
.compile('^([\da-fA-F]+):([\da-fA-F]+) +([\da-fA-F]+)[Hh]? +([.\w\$]+) +(\w+)', re
.UNICODE
)
52 StructPattern
= re
.compile(r
'[_a-zA-Z][0-9A-Za-z_]*$')
54 ## Dictionary used to store dependencies of files
55 gDependencyDatabase
= {} # arch : {file path : [dependent files list]}
58 # If a module is built more than once with different PCDs or library classes
59 # a temporary INF file with same content is created, the temporary file is removed
64 def GetVariableOffset(mapfilepath
, efifilepath
, varnames
):
65 """ Parse map file to get variable offset in current EFI file
66 @param mapfilepath Map file absolution path
67 @param efifilepath: EFI binary file full path
68 @param varnames iteratable container whose elements are variable names to be searched
70 @return List whos elements are tuple with variable name and raw offset
74 f
= open(mapfilepath
, 'r')
80 if len(lines
) == 0: return None
81 firstline
= lines
[0].strip()
82 if (firstline
.startswith("Archive member included ") and
83 firstline
.endswith(" file (symbol)")):
84 return _parseForGCC(lines
, efifilepath
, varnames
)
85 if firstline
.startswith("# Path:"):
86 return _parseForXcode(lines
, efifilepath
, varnames
)
87 return _parseGeneral(lines
, efifilepath
, varnames
)
89 def _parseForXcode(lines
, efifilepath
, varnames
):
94 if status
== 0 and line
== "# Symbols:":
97 if status
== 1 and len(line
) != 0:
98 for varname
in varnames
:
100 # cannot pregenerate this RegEx since it uses varname from varnames.
101 m
= re
.match('^([\da-fA-FxX]+)([\s\S]*)([_]*%s)$' % varname
, line
)
103 ret
.append((varname
, m
.group(1)))
106 def _parseForGCC(lines
, efifilepath
, varnames
):
107 """ Parse map file generated by GCC linker """
111 for index
, line
in enumerate(lines
):
113 # status machine transection
114 if status
== 0 and line
== "Memory Configuration":
117 elif status
== 1 and line
== 'Linker script and memory map':
120 elif status
==2 and line
== 'START GROUP':
126 m
= valuePatternGcc
.match(line
)
128 sections
.append(m
.groups(0))
129 for varname
in varnames
:
131 m
= re
.match("^.data.(%s)" % varname
, line
)
133 m
= re
.match(".data.(%s)$" % varname
, line
)
135 Str
= lines
[index
+ 1]
137 Str
= line
[len(".data.%s" % varname
):]
139 m
= pcdPatternGcc
.match(Str
.strip())
141 varoffset
.append((varname
, int(m
.groups(0)[0], 16), int(sections
[-1][1], 16), sections
[-1][0]))
145 # get section information from efi file
146 efisecs
= PeImageClass(efifilepath
).SectionHeaderList
147 if efisecs
is None or len(efisecs
) == 0:
151 for efisec
in efisecs
:
152 for section
in sections
:
153 if section
[0].strip() == efisec
[0].strip() and section
[0].strip() == '.text':
154 redirection
= int(section
[1], 16) - efisec
[1]
157 for var
in varoffset
:
158 for efisec
in efisecs
:
159 if var
[1] >= efisec
[1] and var
[1] < efisec
[1]+efisec
[3]:
160 ret
.append((var
[0], hex(efisec
[2] + var
[1] - efisec
[1] - redirection
)))
163 def _parseGeneral(lines
, efifilepath
, varnames
):
164 status
= 0 #0 - beginning of file; 1 - PE section definition; 2 - symbol table
165 secs
= [] # key = section name
167 symRe
= re
.compile('^([\da-fA-F]+):([\da-fA-F]+) +([\.:\\\\\w\?@\$]+) +([\da-fA-F]+)', re
.UNICODE
)
171 if startPatternGeneral
.match(line
):
174 if addressPatternGeneral
.match(line
):
177 if line
.startswith("entry point at"):
180 if status
== 1 and len(line
) != 0:
181 m
= secReGeneral
.match(line
)
182 assert m
is not None, "Fail to parse the section in map file , line is %s" % line
183 sec_no
, sec_start
, sec_length
, sec_name
, sec_class
= m
.groups(0)
184 secs
.append([int(sec_no
, 16), int(sec_start
, 16), int(sec_length
, 16), sec_name
, sec_class
])
185 if status
== 2 and len(line
) != 0:
186 for varname
in varnames
:
187 m
= symRe
.match(line
)
188 assert m
is not None, "Fail to parse the symbol in map file, line is %s" % line
189 sec_no
, sym_offset
, sym_name
, vir_addr
= m
.groups(0)
190 sec_no
= int(sec_no
, 16)
191 sym_offset
= int(sym_offset
, 16)
192 vir_addr
= int(vir_addr
, 16)
193 # cannot pregenerate this RegEx since it uses varname from varnames.
194 m2
= re
.match('^[_]*(%s)' % varname
, sym_name
)
196 # fond a binary pcd entry in map file
198 if sec
[0] == sec_no
and (sym_offset
>= sec
[1] and sym_offset
< sec
[1] + sec
[2]):
199 varoffset
.append([varname
, sec
[3], sym_offset
, vir_addr
, sec_no
])
201 if not varoffset
: return []
203 # get section information from efi file
204 efisecs
= PeImageClass(efifilepath
).SectionHeaderList
205 if efisecs
is None or len(efisecs
) == 0:
209 for var
in varoffset
:
211 for efisec
in efisecs
:
213 if var
[1].strip() == efisec
[0].strip():
214 ret
.append((var
[0], hex(efisec
[2] + var
[2])))
215 elif var
[4] == index
:
216 ret
.append((var
[0], hex(efisec
[2] + var
[2])))
220 ## Routine to process duplicated INF
222 # This function is called by following two cases:
225 # Pkg/module/module.inf
226 # Pkg/module/module.inf {
228 # FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836
231 # INF Pkg/module/module.inf
232 # INF FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836 Pkg/module/module.inf
234 # This function copies Pkg/module/module.inf to
235 # Conf/.cache/0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf
237 # @param Path Original PathClass object
238 # @param BaseName New file base name
240 # @retval return the new PathClass object
242 def ProcessDuplicatedInf(Path
, BaseName
, Workspace
):
243 Filename
= os
.path
.split(Path
.File
)[1]
245 Filename
= BaseName
+ Path
.BaseName
+ Filename
[Filename
.rfind('.'):]
247 Filename
= BaseName
+ Path
.BaseName
249 DbDir
= os
.path
.split(GlobalData
.gDatabasePath
)[0]
252 # A temporary INF is copied to database path which must have write permission
253 # The temporary will be removed at the end of build
254 # In case of name conflict, the file name is
255 # FILE_GUIDBaseName (0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf)
257 TempFullPath
= os
.path
.join(DbDir
,
259 RtPath
= PathClass(Path
.File
, Workspace
)
261 # Modify the full path to temporary path, keep other unchanged
263 # To build same module more than once, the module path with FILE_GUID overridden has
264 # the file name FILE_GUIDmodule.inf, but the relative path (self.MetaFile.File) is the real path
265 # in DSC which is used as relative path by C files and other files in INF.
266 # A trick was used: all module paths are PathClass instances, after the initialization
267 # of PathClass, the PathClass.Path is overridden by the temporary INF path.
269 # The reason for creating a temporary INF is:
270 # Platform.Modules which is the base to create ModuleAutoGen objects is a dictionary,
271 # the key is the full path of INF, the value is an object to save overridden library instances, PCDs.
272 # A different key for the same module is needed to create different output directory,
273 # retrieve overridden PCDs, library instances.
275 # The BaseName is the FILE_GUID which is also the output directory name.
278 RtPath
.Path
= TempFullPath
279 RtPath
.BaseName
= BaseName
280 RtPath
.OriginalPath
= Path
282 # If file exists, compare contents
284 if os
.path
.exists(TempFullPath
):
285 with
open(str(Path
), 'rb') as f1
, open(TempFullPath
, 'rb') as f2
:
286 if f1
.read() == f2
.read():
288 _TempInfs
.append(TempFullPath
)
289 shutil
.copy2(str(Path
), TempFullPath
)
292 ## Remove temporary created INFs whose paths were saved in _TempInfs
294 def ClearDuplicatedInf():
296 File
= _TempInfs
.pop()
297 if os
.path
.exists(File
):
300 ## Convert GUID string in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx style to C structure style
302 # @param Guid The GUID string
304 # @retval string The GUID string in C structure style
306 def GuidStringToGuidStructureString(Guid
):
307 GuidList
= Guid
.split('-')
309 for Index
in range(0, 3, 1):
310 Result
= Result
+ '0x' + GuidList
[Index
] + ', '
311 Result
= Result
+ '{0x' + GuidList
[3][0:2] + ', 0x' + GuidList
[3][2:4]
312 for Index
in range(0, 12, 2):
313 Result
= Result
+ ', 0x' + GuidList
[4][Index
:Index
+ 2]
317 ## Convert GUID structure in byte array to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
319 # @param GuidValue The GUID value in byte array
321 # @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
323 def GuidStructureByteArrayToGuidString(GuidValue
):
324 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
325 guidValueList
= guidValueString
.split(",")
326 if len(guidValueList
) != 16:
328 #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
330 return "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
331 int(guidValueList
[3], 16),
332 int(guidValueList
[2], 16),
333 int(guidValueList
[1], 16),
334 int(guidValueList
[0], 16),
335 int(guidValueList
[5], 16),
336 int(guidValueList
[4], 16),
337 int(guidValueList
[7], 16),
338 int(guidValueList
[6], 16),
339 int(guidValueList
[8], 16),
340 int(guidValueList
[9], 16),
341 int(guidValueList
[10], 16),
342 int(guidValueList
[11], 16),
343 int(guidValueList
[12], 16),
344 int(guidValueList
[13], 16),
345 int(guidValueList
[14], 16),
346 int(guidValueList
[15], 16)
351 ## Convert GUID string in C structure style to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
353 # @param GuidValue The GUID value in C structure format
355 # @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
357 def GuidStructureStringToGuidString(GuidValue
):
358 if not GlobalData
.gGuidCFormatPattern
.match(GuidValue
):
360 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
361 guidValueList
= guidValueString
.split(",")
362 if len(guidValueList
) != 11:
364 #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
366 return "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
367 int(guidValueList
[0], 16),
368 int(guidValueList
[1], 16),
369 int(guidValueList
[2], 16),
370 int(guidValueList
[3], 16),
371 int(guidValueList
[4], 16),
372 int(guidValueList
[5], 16),
373 int(guidValueList
[6], 16),
374 int(guidValueList
[7], 16),
375 int(guidValueList
[8], 16),
376 int(guidValueList
[9], 16),
377 int(guidValueList
[10], 16)
382 ## Convert GUID string in C structure style to xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx
384 # @param GuidValue The GUID value in C structure format
386 # @retval string The GUID value in xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx format
388 def GuidStructureStringToGuidValueName(GuidValue
):
389 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "")
390 guidValueList
= guidValueString
.split(",")
391 if len(guidValueList
) != 11:
392 EdkLogger
.error(None, FORMAT_INVALID
, "Invalid GUID value string [%s]" % GuidValue
)
393 return "%08x_%04x_%04x_%02x%02x_%02x%02x%02x%02x%02x%02x" % (
394 int(guidValueList
[0], 16),
395 int(guidValueList
[1], 16),
396 int(guidValueList
[2], 16),
397 int(guidValueList
[3], 16),
398 int(guidValueList
[4], 16),
399 int(guidValueList
[5], 16),
400 int(guidValueList
[6], 16),
401 int(guidValueList
[7], 16),
402 int(guidValueList
[8], 16),
403 int(guidValueList
[9], 16),
404 int(guidValueList
[10], 16)
407 ## Create directories
409 # @param Directory The directory name
411 def CreateDirectory(Directory
):
412 if Directory
is None or Directory
.strip() == "":
415 if not os
.access(Directory
, os
.F_OK
):
416 os
.makedirs(Directory
)
421 ## Remove directories, including files and sub-directories in it
423 # @param Directory The directory name
425 def RemoveDirectory(Directory
, Recursively
=False):
426 if Directory
is None or Directory
.strip() == "" or not os
.path
.exists(Directory
):
429 CurrentDirectory
= os
.getcwd()
431 for File
in os
.listdir("."):
432 if os
.path
.isdir(File
):
433 RemoveDirectory(File
, Recursively
)
436 os
.chdir(CurrentDirectory
)
439 ## Store content in file
441 # This method is used to save file only when its content is changed. This is
442 # quite useful for "make" system to decide what will be re-built and what won't.
444 # @param File The path of file
445 # @param Content The new content of the file
446 # @param IsBinaryFile The flag indicating if the file is binary file or not
448 # @retval True If the file content is changed and the file is renewed
449 # @retval False If the file content is the same
451 def SaveFileOnChange(File
, Content
, IsBinaryFile
=True):
453 if os
.path
.exists(File
):
456 with
open(File
, "rb") as f
:
457 if Content
== f
.read():
460 EdkLogger
.error(None, FILE_OPEN_FAILURE
, ExtraData
=File
)
463 with
open(File
, "r") as f
:
464 if Content
== f
.read():
467 EdkLogger
.error(None, FILE_OPEN_FAILURE
, ExtraData
=File
)
469 DirName
= os
.path
.dirname(File
)
470 if not CreateDirectory(DirName
):
471 EdkLogger
.error(None, FILE_CREATE_FAILURE
, "Could not create directory %s" % DirName
)
474 DirName
= os
.getcwd()
475 if not os
.access(DirName
, os
.W_OK
):
476 EdkLogger
.error(None, PERMISSION_FAILURE
, "Do not have write permission on directory %s" % DirName
)
482 if GlobalData
.gIsWindows
and not os
.path
.exists(File
):
483 # write temp file, then rename the temp file to the real file
484 # to make sure the file be immediate saved to disk
485 with tempfile
.NamedTemporaryFile(OpenMode
, dir=os
.path
.dirname(File
), delete
=False) as tf
:
489 os
.rename(tempname
, File
)
491 EdkLogger
.error(None, FILE_CREATE_FAILURE
, ExtraData
='IOError %s' % X
)
494 with
open(File
, OpenMode
) as Fd
:
497 EdkLogger
.error(None, FILE_CREATE_FAILURE
, ExtraData
='IOError %s' % X
)
501 ## Copy source file only if it is different from the destination file
503 # This method is used to copy file only if the source file and destination
504 # file content are different. This is quite useful to avoid duplicated
507 # @param SrcFile The path of source file
508 # @param Dst The path of destination file or folder
510 # @retval True The two files content are different and the file is copied
511 # @retval False No copy really happen
513 def CopyFileOnChange(SrcFile
, Dst
):
514 if not os
.path
.exists(SrcFile
):
517 if os
.path
.isdir(Dst
):
518 DstFile
= os
.path
.join(Dst
, os
.path
.basename(SrcFile
))
522 if os
.path
.exists(DstFile
) and filecmp
.cmp(SrcFile
, DstFile
, shallow
=False):
525 DirName
= os
.path
.dirname(DstFile
)
526 if not CreateDirectory(DirName
):
527 EdkLogger
.error(None, FILE_CREATE_FAILURE
, "Could not create directory %s" % DirName
)
530 DirName
= os
.getcwd()
531 if not os
.access(DirName
, os
.W_OK
):
532 EdkLogger
.error(None, PERMISSION_FAILURE
, "Do not have write permission on directory %s" % DirName
)
534 # os.replace and os.rename are the atomic operations in python 3 and 2.
535 # we use these two atomic operations to ensure the file copy is atomic:
536 # copy the src to a temp file in the dst same folder firstly, then
537 # replace or rename the temp file to the destination file.
538 with tempfile
.NamedTemporaryFile(dir=DirName
, delete
=False) as tf
:
539 shutil
.copy(SrcFile
, tf
.name
)
542 if hasattr(os
, 'replace'):
543 os
.replace(tempname
, DstFile
)
545 # os.rename reqire to remove the dst on Windows, otherwise OSError will be raised.
546 if GlobalData
.gIsWindows
and os
.path
.exists(DstFile
):
548 os
.rename(tempname
, DstFile
)
551 EdkLogger
.error(None, FILE_COPY_FAILURE
, ExtraData
='IOError %s' % X
)
555 ## Retrieve and cache the real path name in file system
557 # @param Root The root directory of path relative to
559 # @retval str The path string if the path exists
560 # @retval None If path doesn't exist
566 def __init__(self
, Root
):
568 for F
in os
.listdir(Root
):
570 self
._UPPER
_CACHE
_[F
.upper()] = F
573 def __getitem__(self
, Path
):
574 Path
= Path
[len(os
.path
.commonprefix([Path
, self
._Root
])):]
577 if Path
and Path
[0] == os
.path
.sep
:
579 if Path
in self
._CACHE
_:
580 return os
.path
.join(self
._Root
, Path
)
581 UpperPath
= Path
.upper()
582 if UpperPath
in self
._UPPER
_CACHE
_:
583 return os
.path
.join(self
._Root
, self
._UPPER
_CACHE
_[UpperPath
])
587 SepIndex
= Path
.find(os
.path
.sep
)
589 Parent
= UpperPath
[:SepIndex
]
590 if Parent
not in self
._UPPER
_CACHE
_:
592 LastSepIndex
= SepIndex
593 SepIndex
= Path
.find(os
.path
.sep
, LastSepIndex
+ 1)
595 if LastSepIndex
== -1:
600 SepIndex
= LastSepIndex
602 Parent
= Path
[:SepIndex
]
603 ParentKey
= UpperPath
[:SepIndex
]
604 if ParentKey
not in self
._UPPER
_CACHE
_:
608 if Parent
in self
._CACHE
_:
611 ParentDir
= self
._UPPER
_CACHE
_[ParentKey
]
612 for F
in os
.listdir(ParentDir
):
613 Dir
= os
.path
.join(ParentDir
, F
)
614 self
._CACHE
_.add(Dir
)
615 self
._UPPER
_CACHE
_[Dir
.upper()] = Dir
617 SepIndex
= Path
.find(os
.path
.sep
, SepIndex
+ 1)
620 if Path
in self
._CACHE
_:
621 return os
.path
.join(self
._Root
, Path
)
622 elif UpperPath
in self
._UPPER
_CACHE
_:
623 return os
.path
.join(self
._Root
, self
._UPPER
_CACHE
_[UpperPath
])
626 def RealPath(File
, Dir
='', OverrideDir
=''):
627 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
628 NewFile
= GlobalData
.gAllFiles
[NewFile
]
629 if not NewFile
and OverrideDir
:
630 NewFile
= os
.path
.normpath(os
.path
.join(OverrideDir
, File
))
631 NewFile
= GlobalData
.gAllFiles
[NewFile
]
634 ## Get GUID value from given packages
636 # @param CName The CName of the GUID
637 # @param PackageList List of packages looking-up in
638 # @param Inffile The driver file
640 # @retval GuidValue if the CName is found in any given package
641 # @retval None if the CName is not found in all given packages
643 def GuidValue(CName
, PackageList
, Inffile
= None):
644 for P
in PackageList
:
645 GuidKeys
= list(P
.Guids
.keys())
646 if Inffile
and P
._PrivateGuids
:
647 if not Inffile
.startswith(P
.MetaFile
.Dir
):
648 GuidKeys
= [x
for x
in P
.Guids
if x
not in P
._PrivateGuids
]
649 if CName
in GuidKeys
:
650 return P
.Guids
[CName
]
654 ## A string template class
656 # This class implements a template for string replacement. A string template
657 # looks like following
659 # ${BEGIN} other_string ${placeholder_name} other_string ${END}
661 # The string between ${BEGIN} and ${END} will be repeated as many times as the
662 # length of "placeholder_name", which is a list passed through a dict. The
663 # "placeholder_name" is the key name of the dict. The ${BEGIN} and ${END} can
664 # be not used and, in this case, the "placeholder_name" must not a list and it
665 # will just be replaced once.
667 class TemplateString(object):
668 _REPEAT_START_FLAG
= "BEGIN"
669 _REPEAT_END_FLAG
= "END"
671 class Section(object):
672 _LIST_TYPES
= [type([]), type(set()), type((0,))]
674 def __init__(self
, TemplateSection
, PlaceHolderList
):
675 self
._Template
= TemplateSection
676 self
._PlaceHolderList
= []
678 # Split the section into sub-sections according to the position of placeholders
680 self
._SubSectionList
= []
683 # The placeholders passed in must be in the format of
685 # PlaceHolderName, PlaceHolderStartPoint, PlaceHolderEndPoint
687 for PlaceHolder
, Start
, End
in PlaceHolderList
:
688 self
._SubSectionList
.append(TemplateSection
[SubSectionStart
:Start
])
689 self
._SubSectionList
.append(TemplateSection
[Start
:End
])
690 self
._PlaceHolderList
.append(PlaceHolder
)
691 SubSectionStart
= End
692 if SubSectionStart
< len(TemplateSection
):
693 self
._SubSectionList
.append(TemplateSection
[SubSectionStart
:])
695 self
._SubSectionList
= [TemplateSection
]
698 return self
._Template
+ " : " + str(self
._PlaceHolderList
)
700 def Instantiate(self
, PlaceHolderValues
):
702 RepeatPlaceHolders
= {}
703 NonRepeatPlaceHolders
= {}
705 for PlaceHolder
in self
._PlaceHolderList
:
706 if PlaceHolder
not in PlaceHolderValues
:
708 Value
= PlaceHolderValues
[PlaceHolder
]
709 if type(Value
) in self
._LIST
_TYPES
:
711 RepeatTime
= len(Value
)
712 elif RepeatTime
!= len(Value
):
716 "${%s} has different repeat time from others!" % PlaceHolder
,
717 ExtraData
=str(self
._Template
)
719 RepeatPlaceHolders
["${%s}" % PlaceHolder
] = Value
721 NonRepeatPlaceHolders
["${%s}" % PlaceHolder
] = Value
723 if NonRepeatPlaceHolders
:
725 for S
in self
._SubSectionList
:
726 if S
not in NonRepeatPlaceHolders
:
729 StringList
.append(str(NonRepeatPlaceHolders
[S
]))
731 StringList
= self
._SubSectionList
733 if RepeatPlaceHolders
:
735 for Index
in range(RepeatTime
):
737 if S
not in RepeatPlaceHolders
:
738 TempStringList
.append(S
)
740 TempStringList
.append(str(RepeatPlaceHolders
[S
][Index
]))
741 StringList
= TempStringList
743 return "".join(StringList
)
746 def __init__(self
, Template
=None):
748 self
.IsBinary
= False
749 self
._Template
= Template
750 self
._TemplateSectionList
= self
._Parse
(Template
)
754 # @retval string The string replaced
757 return "".join(self
.String
)
759 ## Split the template string into fragments per the ${BEGIN} and ${END} flags
761 # @retval list A list of TemplateString.Section objects
763 def _Parse(self
, Template
):
768 TemplateSectionList
= []
770 MatchObj
= gPlaceholderPattern
.search(Template
, SearchFrom
)
772 if MatchEnd
<= len(Template
):
773 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:], PlaceHolderList
)
774 TemplateSectionList
.append(TemplateSection
)
777 MatchString
= MatchObj
.group(1)
778 MatchStart
= MatchObj
.start()
779 MatchEnd
= MatchObj
.end()
781 if MatchString
== self
._REPEAT
_START
_FLAG
:
782 if MatchStart
> SectionStart
:
783 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:MatchStart
], PlaceHolderList
)
784 TemplateSectionList
.append(TemplateSection
)
785 SectionStart
= MatchEnd
787 elif MatchString
== self
._REPEAT
_END
_FLAG
:
788 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:MatchStart
], PlaceHolderList
)
789 TemplateSectionList
.append(TemplateSection
)
790 SectionStart
= MatchEnd
793 PlaceHolderList
.append((MatchString
, MatchStart
- SectionStart
, MatchEnd
- SectionStart
))
794 SearchFrom
= MatchEnd
795 return TemplateSectionList
797 ## Replace the string template with dictionary of placeholders and append it to previous one
799 # @param AppendString The string template to append
800 # @param Dictionary The placeholder dictionaries
802 def Append(self
, AppendString
, Dictionary
=None):
804 SectionList
= self
._Parse
(AppendString
)
805 self
.String
.append( "".join(S
.Instantiate(Dictionary
) for S
in SectionList
))
807 if isinstance(AppendString
,list):
808 self
.String
.extend(AppendString
)
810 self
.String
.append(AppendString
)
812 ## Replace the string template with dictionary of placeholders
814 # @param Dictionary The placeholder dictionaries
816 # @retval str The string replaced with placeholder values
818 def Replace(self
, Dictionary
=None):
819 return "".join(S
.Instantiate(Dictionary
) for S
in self
._TemplateSectionList
)
821 ## Progress indicator class
823 # This class makes use of thread to print progress on console.
826 # for avoiding deadloop
828 _ProgressThread
= None
829 _CheckInterval
= 0.25
833 # @param OpenMessage The string printed before progress characters
834 # @param CloseMessage The string printed after progress characters
835 # @param ProgressChar The character used to indicate the progress
836 # @param Interval The interval in seconds between two progress characters
838 def __init__(self
, OpenMessage
="", CloseMessage
="", ProgressChar
='.', Interval
=1.0):
839 self
.PromptMessage
= OpenMessage
840 self
.CodaMessage
= CloseMessage
841 self
.ProgressChar
= ProgressChar
842 self
.Interval
= Interval
843 if Progressor
._StopFlag
is None:
844 Progressor
._StopFlag
= threading
.Event()
846 ## Start to print progress character
848 # @param OpenMessage The string printed before progress characters
850 def Start(self
, OpenMessage
=None):
851 if OpenMessage
is not None:
852 self
.PromptMessage
= OpenMessage
853 Progressor
._StopFlag
.clear()
854 if Progressor
._ProgressThread
is None:
855 Progressor
._ProgressThread
= threading
.Thread(target
=self
._ProgressThreadEntry
)
856 Progressor
._ProgressThread
.setDaemon(False)
857 Progressor
._ProgressThread
.start()
859 ## Stop printing progress character
861 # @param CloseMessage The string printed after progress characters
863 def Stop(self
, CloseMessage
=None):
864 OriginalCodaMessage
= self
.CodaMessage
865 if CloseMessage
is not None:
866 self
.CodaMessage
= CloseMessage
868 self
.CodaMessage
= OriginalCodaMessage
870 ## Thread entry method
871 def _ProgressThreadEntry(self
):
872 sys
.stdout
.write(self
.PromptMessage
+ " ")
875 while not Progressor
._StopFlag
.isSet():
877 sys
.stdout
.write(self
.ProgressChar
)
879 TimeUp
= self
.Interval
880 time
.sleep(self
._CheckInterval
)
881 TimeUp
-= self
._CheckInterval
882 sys
.stdout
.write(" " + self
.CodaMessage
+ "\n")
885 ## Abort the progress display
888 if Progressor
._StopFlag
is not None:
889 Progressor
._StopFlag
.set()
890 if Progressor
._ProgressThread
is not None:
891 Progressor
._ProgressThread
.join()
892 Progressor
._ProgressThread
= None
895 ## Dictionary using prioritized list as key
899 _TupleType
= type(())
901 _ValidWildcardList
= ['COMMON', 'DEFAULT', 'ALL', TAB_STAR
, 'PLATFORM']
903 def __init__(self
, _Single_
=False, _Level_
=2):
904 self
._Level
_ = _Level_
906 self
._Single
_ = _Single_
909 def __getitem__(self
, key
):
912 if KeyType
== self
._ListType
or KeyType
== self
._TupleType
:
916 elif self
._Level
_ > 1:
917 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
921 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
923 if FirstKey
is None or str(FirstKey
).upper() in self
._ValidWildcardList
:
924 FirstKey
= self
._Wildcard
927 return self
._GetSingleValue
(FirstKey
, RestKeys
)
929 return self
._GetAllValues
(FirstKey
, RestKeys
)
931 def _GetSingleValue(self
, FirstKey
, RestKeys
):
933 #print "%s-%s" % (FirstKey, self._Level_) ,
935 if FirstKey
== self
._Wildcard
:
936 if FirstKey
in self
.data
:
937 Value
= self
.data
[FirstKey
][RestKeys
]
939 for Key
in self
.data
:
940 Value
= self
.data
[Key
][RestKeys
]
941 if Value
is not None: break
943 if FirstKey
in self
.data
:
944 Value
= self
.data
[FirstKey
][RestKeys
]
945 if Value
is None and self
._Wildcard
in self
.data
:
947 Value
= self
.data
[self
._Wildcard
][RestKeys
]
949 if FirstKey
== self
._Wildcard
:
950 if FirstKey
in self
.data
:
951 Value
= self
.data
[FirstKey
]
953 for Key
in self
.data
:
954 Value
= self
.data
[Key
]
955 if Value
is not None: break
957 if FirstKey
in self
.data
:
958 Value
= self
.data
[FirstKey
]
959 elif self
._Wildcard
in self
.data
:
960 Value
= self
.data
[self
._Wildcard
]
963 def _GetAllValues(self
, FirstKey
, RestKeys
):
966 if FirstKey
== self
._Wildcard
:
967 for Key
in self
.data
:
968 Value
+= self
.data
[Key
][RestKeys
]
970 if FirstKey
in self
.data
:
971 Value
+= self
.data
[FirstKey
][RestKeys
]
972 if self
._Wildcard
in self
.data
:
973 Value
+= self
.data
[self
._Wildcard
][RestKeys
]
975 if FirstKey
== self
._Wildcard
:
976 for Key
in self
.data
:
977 Value
.append(self
.data
[Key
])
979 if FirstKey
in self
.data
:
980 Value
.append(self
.data
[FirstKey
])
981 if self
._Wildcard
in self
.data
:
982 Value
.append(self
.data
[self
._Wildcard
])
986 def __setitem__(self
, key
, value
):
989 if KeyType
== self
._ListType
or KeyType
== self
._TupleType
:
994 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
998 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
1000 if FirstKey
in self
._ValidWildcardList
:
1001 FirstKey
= self
._Wildcard
1003 if FirstKey
not in self
.data
and self
._Level
_ > 0:
1004 self
.data
[FirstKey
] = tdict(self
._Single
_, self
._Level
_ - 1)
1006 if self
._Level
_ > 1:
1007 self
.data
[FirstKey
][RestKeys
] = value
1009 self
.data
[FirstKey
] = value
1011 def SetGreedyMode(self
):
1012 self
._Single
_ = False
1013 if self
._Level
_ > 1:
1014 for Key
in self
.data
:
1015 self
.data
[Key
].SetGreedyMode()
1017 def SetSingleMode(self
):
1018 self
._Single
_ = True
1019 if self
._Level
_ > 1:
1020 for Key
in self
.data
:
1021 self
.data
[Key
].SetSingleMode()
1023 def GetKeys(self
, KeyIndex
=0):
1024 assert KeyIndex
>= 0
1026 return set(self
.data
.keys())
1029 for Key
in self
.data
:
1030 keys |
= self
.data
[Key
].GetKeys(KeyIndex
- 1)
1033 def AnalyzePcdExpression(Setting
):
1034 RanStr
= ''.join(sample(string
.ascii_letters
+ string
.digits
, 8))
1035 Setting
= Setting
.replace('\\\\', RanStr
).strip()
1036 # There might be escaped quote in a string: \", \\\" , \', \\\'
1038 # There might be '|' in string and in ( ... | ... ), replace it with '-'
1040 InSingleQuoteStr
= False
1041 InDoubleQuoteStr
= False
1043 for Index
, ch
in enumerate(Data
):
1044 if ch
== '"' and not InSingleQuoteStr
:
1045 if Data
[Index
- 1] != '\\':
1046 InDoubleQuoteStr
= not InDoubleQuoteStr
1047 elif ch
== "'" and not InDoubleQuoteStr
:
1048 if Data
[Index
- 1] != '\\':
1049 InSingleQuoteStr
= not InSingleQuoteStr
1050 elif ch
== '(' and not (InSingleQuoteStr
or InDoubleQuoteStr
):
1052 elif ch
== ')' and not (InSingleQuoteStr
or InDoubleQuoteStr
):
1055 if (Pair
> 0 or InSingleQuoteStr
or InDoubleQuoteStr
) and ch
== TAB_VALUE_SPLIT
:
1062 Pos
= NewStr
.find(TAB_VALUE_SPLIT
, StartPos
)
1064 FieldList
.append(Setting
[StartPos
:].strip())
1066 FieldList
.append(Setting
[StartPos
:Pos
].strip())
1068 for i
, ch
in enumerate(FieldList
):
1070 FieldList
[i
] = ch
.replace(RanStr
,'\\\\')
1073 def ParseFieldValue (Value
):
1074 def ParseDevPathValue (Value
):
1076 Value
.replace('\\', '/').replace(' ', '')
1078 Cmd
= 'DevicePath ' + '"' + Value
+ '"'
1080 p
= subprocess
.Popen(Cmd
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, shell
=True)
1081 out
, err
= p
.communicate()
1082 except Exception as X
:
1083 raise BadExpression("DevicePath: %s" % (str(X
)) )
1085 subprocess
._cleanup
()
1089 raise BadExpression("DevicePath: %s" % str(err
))
1090 out
= out
.decode(encoding
='utf-8', errors
='ignore')
1091 Size
= len(out
.split())
1092 out
= ','.join(out
.split())
1093 return '{' + out
+ '}', Size
1095 if "{CODE(" in Value
:
1096 return Value
, len(Value
.split(","))
1097 if isinstance(Value
, type(0)):
1098 return Value
, (Value
.bit_length() + 7) // 8
1099 if not isinstance(Value
, type('')):
1100 raise BadExpression('Type %s is %s' %(Value
, type(Value
)))
1101 Value
= Value
.strip()
1102 if Value
.startswith(TAB_UINT8
) and Value
.endswith(')'):
1103 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1105 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1107 if Value
.startswith(TAB_UINT16
) and Value
.endswith(')'):
1108 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1110 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1112 if Value
.startswith(TAB_UINT32
) and Value
.endswith(')'):
1113 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1115 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1117 if Value
.startswith(TAB_UINT64
) and Value
.endswith(')'):
1118 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1120 raise BadExpression('Value (%s) Size larger than %d' % (Value
, Size
))
1122 if Value
.startswith(TAB_GUID
) and Value
.endswith(')'):
1123 Value
= Value
.split('(', 1)[1][:-1].strip()
1124 if Value
[0] == '{' and Value
[-1] == '}':
1125 TmpValue
= GuidStructureStringToGuidString(Value
)
1127 raise BadExpression("Invalid GUID value string %s" % Value
)
1129 if Value
[0] == '"' and Value
[-1] == '"':
1132 Value
= str(uuid
.UUID(Value
).bytes_le
)
1133 if Value
.startswith("b'"):
1135 Value
= "'" + Value
+ "'"
1136 except ValueError as Message
:
1137 raise BadExpression(Message
)
1138 Value
, Size
= ParseFieldValue(Value
)
1140 if Value
.startswith('L"') and Value
.endswith('"'):
1142 # translate escape character
1152 Value
= (Value
<< 16) |
ord(Char
)
1153 return Value
, (len(List
) + 1) * 2
1154 if Value
.startswith('"') and Value
.endswith('"'):
1156 # translate escape character
1165 Value
= (Value
<< 8) |
ord(Char
)
1166 return Value
, len(List
) + 1
1167 if Value
.startswith("L'") and Value
.endswith("'"):
1168 # Unicode Character Constant
1169 # translate escape character
1177 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1181 Value
= (Value
<< 16) |
ord(Char
)
1182 return Value
, len(List
) * 2
1183 if Value
.startswith("'") and Value
.endswith("'"):
1184 # Character constant
1185 # translate escape character
1192 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1196 Value
= (Value
<< 8) |
ord(Char
)
1197 return Value
, len(List
)
1198 if Value
.startswith('{') and Value
.endswith('}'):
1201 List
= [Item
.strip() for Item
in Value
.split(',')]
1206 ItemValue
, Size
= ParseFieldValue(Item
)
1208 for I
in range(Size
):
1209 Value
= (Value
<< 8) |
((ItemValue
>> 8 * I
) & 0xff)
1210 return Value
, RetSize
1211 if Value
.startswith('DEVICE_PATH(') and Value
.endswith(')'):
1212 Value
= Value
.replace("DEVICE_PATH(", '').rstrip(')')
1213 Value
= Value
.strip().strip('"')
1214 return ParseDevPathValue(Value
)
1215 if Value
.lower().startswith('0x'):
1217 Value
= int(Value
, 16)
1219 raise BadExpression("invalid hex value: %s" % Value
)
1222 return Value
, (Value
.bit_length() + 7) // 8
1223 if Value
[0].isdigit():
1224 Value
= int(Value
, 10)
1227 return Value
, (Value
.bit_length() + 7) // 8
1228 if Value
.lower() == 'true':
1230 if Value
.lower() == 'false':
1236 # Analyze DSC PCD value, since there is no data type info in DSC
1237 # This function is used to match functions (AnalyzePcdData) used for retrieving PCD value from database
1238 # 1. Feature flag: TokenSpace.PcdCName|PcdValue
1239 # 2. Fix and Patch:TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1240 # 3. Dynamic default:
1241 # TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1242 # TokenSpace.PcdCName|PcdValue
1244 # TokenSpace.PcdCName|VpdOffset[|VpdValue]
1245 # TokenSpace.PcdCName|VpdOffset[|MaxSize[|VpdValue]]
1247 # TokenSpace.PcdCName|HiiString|VariableGuid|VariableOffset[|HiiValue]
1248 # PCD value needs to be located in such kind of string, and the PCD value might be an expression in which
1249 # there might have "|" operator, also in string value.
1251 # @param Setting: String contain information described above with "TokenSpace.PcdCName|" stripped
1252 # @param PcdType: PCD type: feature, fixed, dynamic default VPD HII
1253 # @param DataType: The datum type of PCD: VOID*, UNIT, BOOL
1255 # ValueList: A List contain fields described above
1256 # IsValid: True if conforming EBNF, otherwise False
1257 # Index: The index where PcdValue is in ValueList
1259 def AnalyzeDscPcd(Setting
, PcdType
, DataType
=''):
1260 FieldList
= AnalyzePcdExpression(Setting
)
1263 if PcdType
in (MODEL_PCD_FIXED_AT_BUILD
, MODEL_PCD_PATCHABLE_IN_MODULE
, MODEL_PCD_DYNAMIC_DEFAULT
, MODEL_PCD_DYNAMIC_EX_DEFAULT
):
1264 Value
= FieldList
[0]
1266 if len(FieldList
) > 1 and FieldList
[1]:
1267 DataType
= FieldList
[1]
1268 if FieldList
[1] != TAB_VOID
and StructPattern
.match(FieldList
[1]) is None:
1270 if len(FieldList
) > 2:
1274 IsValid
= (len(FieldList
) <= 1)
1276 IsValid
= (len(FieldList
) <= 3)
1280 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1284 return [str(Value
), DataType
, str(Size
)], IsValid
, 0
1285 elif PcdType
== MODEL_PCD_FEATURE_FLAG
:
1286 Value
= FieldList
[0]
1288 IsValid
= (len(FieldList
) <= 1)
1289 return [Value
, DataType
, str(Size
)], IsValid
, 0
1290 elif PcdType
in (MODEL_PCD_DYNAMIC_VPD
, MODEL_PCD_DYNAMIC_EX_VPD
):
1291 VpdOffset
= FieldList
[0]
1293 if not DataType
== TAB_VOID
:
1294 if len(FieldList
) > 1:
1295 Value
= FieldList
[1]
1297 if len(FieldList
) > 1:
1299 if len(FieldList
) > 2:
1300 Value
= FieldList
[2]
1302 IsValid
= (len(FieldList
) <= 1)
1304 IsValid
= (len(FieldList
) <= 3)
1307 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1311 return [VpdOffset
, str(Size
), Value
], IsValid
, 2
1312 elif PcdType
in (MODEL_PCD_DYNAMIC_HII
, MODEL_PCD_DYNAMIC_EX_HII
):
1313 IsValid
= (3 <= len(FieldList
) <= 5)
1314 HiiString
= FieldList
[0]
1315 Guid
= Offset
= Value
= Attribute
= ''
1316 if len(FieldList
) > 1:
1318 if len(FieldList
) > 2:
1319 Offset
= FieldList
[2]
1320 if len(FieldList
) > 3:
1321 Value
= FieldList
[3]
1322 if len(FieldList
) > 4:
1323 Attribute
= FieldList
[4]
1324 return [HiiString
, Guid
, Offset
, Value
, Attribute
], IsValid
, 3
1329 # Analyze the pcd Value, Datum type and TokenNumber.
1330 # Used to avoid split issue while the value string contain "|" character
1332 # @param[in] Setting: A String contain value/datum type/token number information;
1334 # @retval ValueList: A List contain value, datum type and toke number.
1336 def AnalyzePcdData(Setting
):
1337 ValueList
= ['', '', '']
1339 ValueRe
= re
.compile(r
'^\s*L?\".*\|.*\"')
1340 PtrValue
= ValueRe
.findall(Setting
)
1342 ValueUpdateFlag
= False
1344 if len(PtrValue
) >= 1:
1345 Setting
= re
.sub(ValueRe
, '', Setting
)
1346 ValueUpdateFlag
= True
1348 TokenList
= Setting
.split(TAB_VALUE_SPLIT
)
1349 ValueList
[0:len(TokenList
)] = TokenList
1352 ValueList
[0] = PtrValue
[0]
1356 ## check format of PCD value against its the datum type
1358 # For PCD value setting
1360 def CheckPcdDatum(Type
, Value
):
1361 if Type
== TAB_VOID
:
1362 ValueRe
= re
.compile(r
'\s*L?\".*\"\s*$')
1363 if not (((Value
.startswith('L"') or Value
.startswith('"')) and Value
.endswith('"'))
1364 or (Value
.startswith('{') and Value
.endswith('}')) or (Value
.startswith("L'") or Value
.startswith("'") and Value
.endswith("'"))
1366 return False, "Invalid value [%s] of type [%s]; must be in the form of {...} for array"\
1367 ", \"...\" or \'...\' for string, L\"...\" or L\'...\' for unicode string" % (Value
, Type
)
1368 elif ValueRe
.match(Value
):
1369 # Check the chars in UnicodeString or CString is printable
1370 if Value
.startswith("L"):
1374 Printset
= set(string
.printable
)
1375 Printset
.remove(TAB_PRINTCHAR_VT
)
1376 Printset
.add(TAB_PRINTCHAR_BS
)
1377 Printset
.add(TAB_PRINTCHAR_NUL
)
1378 if not set(Value
).issubset(Printset
):
1379 PrintList
= sorted(Printset
)
1380 return False, "Invalid PCD string value of type [%s]; must be printable chars %s." % (Type
, PrintList
)
1381 elif Type
== 'BOOLEAN':
1382 if Value
not in ['TRUE', 'True', 'true', '0x1', '0x01', '1', 'FALSE', 'False', 'false', '0x0', '0x00', '0']:
1383 return False, "Invalid value [%s] of type [%s]; must be one of TRUE, True, true, 0x1, 0x01, 1"\
1384 ", FALSE, False, false, 0x0, 0x00, 0" % (Value
, Type
)
1385 elif Type
in [TAB_UINT8
, TAB_UINT16
, TAB_UINT32
, TAB_UINT64
]:
1386 if Value
.startswith('0') and not Value
.lower().startswith('0x') and len(Value
) > 1 and Value
.lstrip('0'):
1387 Value
= Value
.lstrip('0')
1389 if Value
and int(Value
, 0) < 0:
1390 return False, "PCD can't be set to negative value[%s] for datum type [%s]" % (Value
, Type
)
1391 Value
= int(Value
, 0)
1392 if Value
> MAX_VAL_TYPE
[Type
]:
1393 return False, "Too large PCD value[%s] for datum type [%s]" % (Value
, Type
)
1395 return False, "Invalid value [%s] of type [%s];"\
1396 " must be a hexadecimal, decimal or octal in C language format." % (Value
, Type
)
1398 return True, "StructurePcd"
1402 def CommonPath(PathList
):
1403 P1
= min(PathList
).split(os
.path
.sep
)
1404 P2
= max(PathList
).split(os
.path
.sep
)
1405 for Index
in range(min(len(P1
), len(P2
))):
1406 if P1
[Index
] != P2
[Index
]:
1407 return os
.path
.sep
.join(P1
[:Index
])
1408 return os
.path
.sep
.join(P1
)
1410 class PathClass(object):
1411 def __init__(self
, File
='', Root
='', AlterRoot
='', Type
='', IsBinary
=False,
1412 Arch
='COMMON', ToolChainFamily
='', Target
='', TagName
='', ToolCode
=''):
1414 self
.File
= str(File
)
1415 if os
.path
.isabs(self
.File
):
1419 self
.Root
= str(Root
)
1420 self
.AlterRoot
= str(AlterRoot
)
1422 # Remove any '.' and '..' in path
1424 self
.Root
= mws
.getWs(self
.Root
, self
.File
)
1425 self
.Path
= os
.path
.normpath(os
.path
.join(self
.Root
, self
.File
))
1426 self
.Root
= os
.path
.normpath(CommonPath([self
.Root
, self
.Path
]))
1427 # eliminate the side-effect of 'C:'
1428 if self
.Root
[-1] == ':':
1429 self
.Root
+= os
.path
.sep
1430 # file path should not start with path separator
1431 if self
.Root
[-1] == os
.path
.sep
:
1432 self
.File
= self
.Path
[len(self
.Root
):]
1434 self
.File
= self
.Path
[len(self
.Root
) + 1:]
1436 self
.Path
= os
.path
.normpath(self
.File
)
1438 self
.SubDir
, self
.Name
= os
.path
.split(self
.File
)
1439 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1443 self
.Dir
= os
.path
.join(self
.Root
, self
.SubDir
)
1445 self
.Dir
= self
.Root
1447 self
.Dir
= self
.SubDir
1452 self
.Type
= self
.Ext
.lower()
1454 self
.IsBinary
= IsBinary
1455 self
.Target
= Target
1456 self
.TagName
= TagName
1457 self
.ToolCode
= ToolCode
1458 self
.ToolChainFamily
= ToolChainFamily
1459 self
.OriginalPath
= self
1461 ## Convert the object of this class to a string
1463 # Convert member Path of the class to a string
1465 # @retval string Formatted String
1470 ## Override __eq__ function
1472 # Check whether PathClass are the same
1474 # @retval False The two PathClass are different
1475 # @retval True The two PathClass are the same
1477 def __eq__(self
, Other
):
1478 return self
.Path
== str(Other
)
1480 ## Override __cmp__ function
1482 # Customize the comparison operation of two PathClass
1484 # @retval 0 The two PathClass are different
1485 # @retval -1 The first PathClass is less than the second PathClass
1486 # @retval 1 The first PathClass is Bigger than the second PathClass
1487 def __cmp__(self
, Other
):
1488 OtherKey
= str(Other
)
1491 if SelfKey
== OtherKey
:
1493 elif SelfKey
> OtherKey
:
1498 ## Override __hash__ function
1500 # Use Path as key in hash table
1502 # @retval string Key for hash table
1505 return hash(self
.Path
)
1509 return self
.Path
.upper()
1512 def TimeStamp(self
):
1513 return os
.stat(self
.Path
)[8]
1515 def Validate(self
, Type
='', CaseSensitive
=True):
1516 def RealPath2(File
, Dir
='', OverrideDir
=''):
1519 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(OverrideDir
, File
))]
1521 if OverrideDir
[-1] == os
.path
.sep
:
1522 return NewFile
[len(OverrideDir
):], NewFile
[0:len(OverrideDir
)]
1524 return NewFile
[len(OverrideDir
) + 1:], NewFile
[0:len(OverrideDir
)]
1525 if GlobalData
.gAllFiles
:
1526 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(Dir
, File
))]
1528 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
1529 if not os
.path
.exists(NewFile
):
1533 if Dir
[-1] == os
.path
.sep
:
1534 return NewFile
[len(Dir
):], NewFile
[0:len(Dir
)]
1536 return NewFile
[len(Dir
) + 1:], NewFile
[0:len(Dir
)]
1542 if GlobalData
.gCaseInsensitive
:
1543 CaseSensitive
= False
1544 if Type
and Type
.lower() != self
.Type
:
1545 return FILE_TYPE_MISMATCH
, '%s (expect %s but got %s)' % (self
.File
, Type
, self
.Type
)
1547 RealFile
, RealRoot
= RealPath2(self
.File
, self
.Root
, self
.AlterRoot
)
1548 if not RealRoot
and not RealFile
:
1549 RealFile
= self
.File
1551 RealFile
= os
.path
.join(self
.AlterRoot
, self
.File
)
1553 RealFile
= os
.path
.join(self
.Root
, self
.File
)
1554 if len (mws
.getPkgPath()) == 0:
1555 return FILE_NOT_FOUND
, os
.path
.join(self
.AlterRoot
, RealFile
)
1557 return FILE_NOT_FOUND
, "%s is not found in packages path:\n\t%s" % (self
.File
, '\n\t'.join(mws
.getPkgPath()))
1561 if RealRoot
!= self
.Root
or RealFile
!= self
.File
:
1562 if CaseSensitive
and (RealFile
!= self
.File
or (RealRoot
!= self
.Root
and RealRoot
!= self
.AlterRoot
)):
1563 ErrorCode
= FILE_CASE_MISMATCH
1564 ErrorInfo
= self
.File
+ '\n\t' + RealFile
+ " [in file system]"
1566 self
.SubDir
, self
.Name
= os
.path
.split(RealFile
)
1567 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1569 self
.Dir
= os
.path
.join(RealRoot
, self
.SubDir
)
1572 self
.File
= RealFile
1573 self
.Root
= RealRoot
1574 self
.Path
= os
.path
.join(RealRoot
, RealFile
)
1575 return ErrorCode
, ErrorInfo
1577 ## Parse PE image to get the required PE information.
1579 class PeImageClass():
1582 # @param File FilePath of PeImage
1584 def __init__(self
, PeFile
):
1585 self
.FileName
= PeFile
1586 self
.IsValid
= False
1589 self
.SectionAlignment
= 0
1590 self
.SectionHeaderList
= []
1593 PeObject
= open(PeFile
, 'rb')
1595 self
.ErrorInfo
= self
.FileName
+ ' can not be found\n'
1598 ByteArray
= array
.array('B')
1599 ByteArray
.fromfile(PeObject
, 0x3E)
1600 ByteList
= ByteArray
.tolist()
1601 # DOS signature should be 'MZ'
1602 if self
._ByteListToStr
(ByteList
[0x0:0x2]) != 'MZ':
1603 self
.ErrorInfo
= self
.FileName
+ ' has no valid DOS signature MZ'
1606 # Read 4 byte PE Signature
1607 PeOffset
= self
._ByteListToInt
(ByteList
[0x3C:0x3E])
1608 PeObject
.seek(PeOffset
)
1609 ByteArray
= array
.array('B')
1610 ByteArray
.fromfile(PeObject
, 4)
1611 # PE signature should be 'PE\0\0'
1612 if ByteArray
.tostring() != b
'PE\0\0':
1613 self
.ErrorInfo
= self
.FileName
+ ' has no valid PE signature PE00'
1616 # Read PE file header
1617 ByteArray
= array
.array('B')
1618 ByteArray
.fromfile(PeObject
, 0x14)
1619 ByteList
= ByteArray
.tolist()
1620 SecNumber
= self
._ByteListToInt
(ByteList
[0x2:0x4])
1622 self
.ErrorInfo
= self
.FileName
+ ' has no section header'
1625 # Read PE optional header
1626 OptionalHeaderSize
= self
._ByteListToInt
(ByteArray
[0x10:0x12])
1627 ByteArray
= array
.array('B')
1628 ByteArray
.fromfile(PeObject
, OptionalHeaderSize
)
1629 ByteList
= ByteArray
.tolist()
1630 self
.EntryPoint
= self
._ByteListToInt
(ByteList
[0x10:0x14])
1631 self
.SectionAlignment
= self
._ByteListToInt
(ByteList
[0x20:0x24])
1632 self
.Size
= self
._ByteListToInt
(ByteList
[0x38:0x3C])
1634 # Read each Section Header
1635 for Index
in range(SecNumber
):
1636 ByteArray
= array
.array('B')
1637 ByteArray
.fromfile(PeObject
, 0x28)
1638 ByteList
= ByteArray
.tolist()
1639 SecName
= self
._ByteListToStr
(ByteList
[0:8])
1640 SecVirtualSize
= self
._ByteListToInt
(ByteList
[8:12])
1641 SecRawAddress
= self
._ByteListToInt
(ByteList
[20:24])
1642 SecVirtualAddress
= self
._ByteListToInt
(ByteList
[12:16])
1643 self
.SectionHeaderList
.append((SecName
, SecVirtualAddress
, SecRawAddress
, SecVirtualSize
))
1647 def _ByteListToStr(self
, ByteList
):
1649 for index
in range(len(ByteList
)):
1650 if ByteList
[index
] == 0:
1652 String
+= chr(ByteList
[index
])
1655 def _ByteListToInt(self
, ByteList
):
1657 for index
in range(len(ByteList
) - 1, -1, -1):
1658 Value
= (Value
<< 8) |
int(ByteList
[index
])
1661 class DefaultStore():
1662 def __init__(self
, DefaultStores
):
1664 self
.DefaultStores
= DefaultStores
1665 def DefaultStoreID(self
, DefaultStoreName
):
1666 for key
, value
in self
.DefaultStores
.items():
1667 if value
== DefaultStoreName
:
1670 def GetDefaultDefault(self
):
1671 if not self
.DefaultStores
or "0" in self
.DefaultStores
:
1672 return "0", TAB_DEFAULT_STORES_DEFAULT
1674 minvalue
= min(int(value_str
) for value_str
in self
.DefaultStores
)
1675 return (str(minvalue
), self
.DefaultStores
[str(minvalue
)])
1676 def GetMin(self
, DefaultSIdList
):
1677 if not DefaultSIdList
:
1678 return TAB_DEFAULT_STORES_DEFAULT
1679 storeidset
= {storeid
for storeid
, storename
in self
.DefaultStores
.values() if storename
in DefaultSIdList
}
1682 minid
= min(storeidset
)
1683 for sid
, name
in self
.DefaultStores
.values():
1692 def __init__(self
,SkuIdentifier
='', SkuIds
=None):
1696 for SkuName
in SkuIds
:
1697 SkuId
= SkuIds
[SkuName
][0]
1698 skuid_num
= int(SkuId
, 16) if SkuId
.upper().startswith("0X") else int(SkuId
)
1699 if skuid_num
> 0xFFFFFFFFFFFFFFFF:
1700 EdkLogger
.error("build", PARAMETER_INVALID
,
1701 ExtraData
= "SKU-ID [%s] value %s exceeds the max value of UINT64"
1704 self
.AvailableSkuIds
= OrderedDict()
1706 self
.SkuIdNumberSet
= []
1707 self
.SkuData
= SkuIds
1708 self
._SkuInherit
= {}
1709 self
._SkuIdentifier
= SkuIdentifier
1710 if SkuIdentifier
== '' or SkuIdentifier
is None:
1711 self
.SkuIdSet
= ['DEFAULT']
1712 self
.SkuIdNumberSet
= ['0U']
1713 elif SkuIdentifier
== 'ALL':
1714 self
.SkuIdSet
= list(SkuIds
.keys())
1715 self
.SkuIdNumberSet
= [num
[0].strip() + 'U' for num
in SkuIds
.values()]
1717 r
= SkuIdentifier
.split('|')
1718 self
.SkuIdSet
=[(r
[k
].strip()).upper() for k
in range(len(r
))]
1721 self
.SkuIdNumberSet
= [SkuIds
[k
][0].strip() + 'U' for k
in self
.SkuIdSet
]
1723 EdkLogger
.error("build", PARAMETER_INVALID
,
1724 ExtraData
= "SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1725 % (k
, " | ".join(SkuIds
.keys())))
1726 for each
in self
.SkuIdSet
:
1728 self
.AvailableSkuIds
[each
] = SkuIds
[each
][0]
1730 EdkLogger
.error("build", PARAMETER_INVALID
,
1731 ExtraData
="SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1732 % (each
, " | ".join(SkuIds
.keys())))
1733 if self
.SkuUsageType
!= SkuClass
.SINGLE
:
1734 self
.AvailableSkuIds
.update({'DEFAULT':0, 'COMMON':0})
1736 GlobalData
.gSkuids
= (self
.SkuIdSet
)
1737 if 'COMMON' in GlobalData
.gSkuids
:
1738 GlobalData
.gSkuids
.remove('COMMON')
1739 if self
.SkuUsageType
== self
.SINGLE
:
1740 if len(GlobalData
.gSkuids
) != 1:
1741 if 'DEFAULT' in GlobalData
.gSkuids
:
1742 GlobalData
.gSkuids
.remove('DEFAULT')
1743 if GlobalData
.gSkuids
:
1744 GlobalData
.gSkuids
.sort()
1746 def GetNextSkuId(self
, skuname
):
1747 if not self
._SkuInherit
:
1748 self
._SkuInherit
= {}
1749 for item
in self
.SkuData
.values():
1750 self
._SkuInherit
[item
[1]]=item
[2] if item
[2] else "DEFAULT"
1751 return self
._SkuInherit
.get(skuname
, "DEFAULT")
1753 def GetSkuChain(self
, sku
):
1754 if sku
== "DEFAULT":
1759 nextsku
= self
.GetNextSkuId(nextsku
)
1760 skulist
.append(nextsku
)
1761 if nextsku
== "DEFAULT":
1765 def SkuOverrideOrder(self
):
1767 for skuname
in self
.SkuIdSet
:
1768 skuorderset
.append(self
.GetSkuChain(skuname
))
1771 for index
in range(max(len(item
) for item
in skuorderset
)):
1772 for subset
in skuorderset
:
1773 if index
> len(subset
)-1:
1775 if subset
[index
] in skuorder
:
1777 skuorder
.append(subset
[index
])
1782 def SkuUsageType(self
):
1783 if self
._SkuIdentifier
.upper() == "ALL":
1784 return SkuClass
.MULTIPLE
1786 if len(self
.SkuIdSet
) == 1:
1787 if self
.SkuIdSet
[0] == 'DEFAULT':
1788 return SkuClass
.DEFAULT
1789 return SkuClass
.SINGLE
1790 if len(self
.SkuIdSet
) == 2 and 'DEFAULT' in self
.SkuIdSet
:
1791 return SkuClass
.SINGLE
1792 return SkuClass
.MULTIPLE
1794 def DumpSkuIdArrary(self
):
1795 if self
.SkuUsageType
== SkuClass
.SINGLE
:
1798 for skuname
in self
.AvailableSkuIds
:
1799 if skuname
== "COMMON":
1801 while skuname
!= "DEFAULT":
1802 ArrayStrList
.append(hex(int(self
.AvailableSkuIds
[skuname
])))
1803 skuname
= self
.GetNextSkuId(skuname
)
1804 ArrayStrList
.append("0x0")
1805 return "{{{myList}}}".format(myList
=",".join(ArrayStrList
))
1808 def AvailableSkuIdSet(self
):
1809 return self
.AvailableSkuIds
1812 def SystemSkuId(self
):
1813 if self
.SkuUsageType
== SkuClass
.SINGLE
:
1814 if len(self
.SkuIdSet
) == 1:
1815 return self
.SkuIdSet
[0]
1817 return self
.SkuIdSet
[0] if self
.SkuIdSet
[0] != 'DEFAULT' else self
.SkuIdSet
[1]
1821 ## Get the integer value from string like "14U" or integer like 2
1823 # @param Input The object that may be either a integer value or a string
1825 # @retval Value The integer value that the input represents
1827 def GetIntegerValue(Input
):
1828 if not isinstance(Input
, str):
1831 if String
.endswith("U"):
1832 String
= String
[:-1]
1833 if String
.endswith("ULL"):
1834 String
= String
[:-3]
1835 if String
.endswith("LL"):
1836 String
= String
[:-2]
1838 if String
.startswith("0x") or String
.startswith("0X"):
1839 return int(String
, 16)
1846 # Pack a GUID (registry format) list into a buffer and return it
1849 return pack(PACK_PATTERN_GUID
,
1853 int(Guid
[3][-4:-2], 16),
1854 int(Guid
[3][-2:], 16),
1855 int(Guid
[4][-12:-10], 16),
1856 int(Guid
[4][-10:-8], 16),
1857 int(Guid
[4][-8:-6], 16),
1858 int(Guid
[4][-6:-4], 16),
1859 int(Guid
[4][-4:-2], 16),
1860 int(Guid
[4][-2:], 16)
1864 # Pack a GUID (byte) list into a buffer and return it
1866 def PackByteFormatGUID(Guid
):
1867 return pack(PACK_PATTERN_GUID
,
1881 ## DeepCopy dict/OrderedDict recusively
1883 # @param ori_dict a nested dict or ordereddict
1885 # @retval new dict or orderdict
1887 def CopyDict(ori_dict
):
1888 dict_type
= ori_dict
.__class
__
1889 if dict_type
not in (dict,OrderedDict
):
1891 new_dict
= dict_type()
1892 for key
in ori_dict
:
1893 if isinstance(ori_dict
[key
],(dict,OrderedDict
)):
1894 new_dict
[key
] = CopyDict(ori_dict
[key
])
1896 new_dict
[key
] = ori_dict
[key
]
1900 # Remove the c/c++ comments: // and /* */
1902 def RemoveCComments(ctext
):
1903 return re
.sub('//.*?\n|/\*.*?\*/', '\n', ctext
, flags
=re
.S
)