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
.LongFilePathSupport
import CopyLongFilePath
as CopyLong
38 from Common
.LongFilePathSupport
import LongFilePath
as LongFilePath
39 from Common
.MultipleWorkspace
import MultipleWorkspace
as mws
40 from CommonDataClass
.Exceptions
import BadExpression
41 from Common
.caching
import cached_property
43 ArrayIndex
= re
.compile("\[\s*[0-9a-fA-FxX]*\s*\]")
44 ## Regular expression used to find out place holders in string template
45 gPlaceholderPattern
= re
.compile("\$\{([^$()\s]+)\}", re
.MULTILINE | re
.UNICODE
)
47 ## regular expressions for map file processing
48 startPatternGeneral
= re
.compile("^Start[' ']+Length[' ']+Name[' ']+Class")
49 addressPatternGeneral
= re
.compile("^Address[' ']+Publics by Value[' ']+Rva\+Base")
50 valuePatternGcc
= re
.compile('^([\w_\.]+) +([\da-fA-Fx]+) +([\da-fA-Fx]+)$')
51 pcdPatternGcc
= re
.compile('^([\da-fA-Fx]+) +([\da-fA-Fx]+)')
52 secReGeneral
= re
.compile('^([\da-fA-F]+):([\da-fA-F]+) +([\da-fA-F]+)[Hh]? +([.\w\$]+) +(\w+)', re
.UNICODE
)
54 StructPattern
= re
.compile(r
'[_a-zA-Z][0-9A-Za-z_]*$')
56 ## Dictionary used to store dependencies of files
57 gDependencyDatabase
= {} # arch : {file path : [dependent files list]}
60 # If a module is built more than once with different PCDs or library classes
61 # a temporary INF file with same content is created, the temporary file is removed
66 def GetVariableOffset(mapfilepath
, efifilepath
, varnames
):
67 """ Parse map file to get variable offset in current EFI file
68 @param mapfilepath Map file absolution path
69 @param efifilepath: EFI binary file full path
70 @param varnames iteratable container whose elements are variable names to be searched
72 @return List whos elements are tuple with variable name and raw offset
76 f
= open(mapfilepath
, 'r')
82 if len(lines
) == 0: return None
83 firstline
= lines
[0].strip()
84 if (firstline
.startswith("Archive member included ") and
85 firstline
.endswith(" file (symbol)")):
86 return _parseForGCC(lines
, efifilepath
, varnames
)
87 if firstline
.startswith("# Path:"):
88 return _parseForXcode(lines
, efifilepath
, varnames
)
89 return _parseGeneral(lines
, efifilepath
, varnames
)
91 def _parseForXcode(lines
, efifilepath
, varnames
):
96 if status
== 0 and line
== "# Symbols:":
99 if status
== 1 and len(line
) != 0:
100 for varname
in varnames
:
102 # cannot pregenerate this RegEx since it uses varname from varnames.
103 m
= re
.match('^([\da-fA-FxX]+)([\s\S]*)([_]*%s)$' % varname
, line
)
105 ret
.append((varname
, m
.group(1)))
108 def _parseForGCC(lines
, efifilepath
, varnames
):
109 """ Parse map file generated by GCC linker """
113 for index
, line
in enumerate(lines
):
115 # status machine transection
116 if status
== 0 and line
== "Memory Configuration":
119 elif status
== 1 and line
== 'Linker script and memory map':
122 elif status
==2 and line
== 'START GROUP':
128 m
= valuePatternGcc
.match(line
)
130 sections
.append(m
.groups(0))
131 for varname
in varnames
:
133 m
= re
.match("^.data.(%s)" % varname
, line
)
135 m
= re
.match(".data.(%s)$" % varname
, line
)
137 Str
= lines
[index
+ 1]
139 Str
= line
[len(".data.%s" % varname
):]
141 m
= pcdPatternGcc
.match(Str
.strip())
143 varoffset
.append((varname
, int(m
.groups(0)[0], 16), int(sections
[-1][1], 16), sections
[-1][0]))
147 # get section information from efi file
148 efisecs
= PeImageClass(efifilepath
).SectionHeaderList
149 if efisecs
is None or len(efisecs
) == 0:
153 for efisec
in efisecs
:
154 for section
in sections
:
155 if section
[0].strip() == efisec
[0].strip() and section
[0].strip() == '.text':
156 redirection
= int(section
[1], 16) - efisec
[1]
159 for var
in varoffset
:
160 for efisec
in efisecs
:
161 if var
[1] >= efisec
[1] and var
[1] < efisec
[1]+efisec
[3]:
162 ret
.append((var
[0], hex(efisec
[2] + var
[1] - efisec
[1] - redirection
)))
165 def _parseGeneral(lines
, efifilepath
, varnames
):
166 status
= 0 #0 - beginning of file; 1 - PE section definition; 2 - symbol table
167 secs
= [] # key = section name
169 symRe
= re
.compile('^([\da-fA-F]+):([\da-fA-F]+) +([\.:\\\\\w\?@\$-]+) +([\da-fA-F]+)', re
.UNICODE
)
173 if startPatternGeneral
.match(line
):
176 if addressPatternGeneral
.match(line
):
179 if line
.startswith("entry point at"):
182 if status
== 1 and len(line
) != 0:
183 m
= secReGeneral
.match(line
)
184 assert m
is not None, "Fail to parse the section in map file , line is %s" % line
185 sec_no
, sec_start
, sec_length
, sec_name
, sec_class
= m
.groups(0)
186 secs
.append([int(sec_no
, 16), int(sec_start
, 16), int(sec_length
, 16), sec_name
, sec_class
])
187 if status
== 2 and len(line
) != 0:
188 for varname
in varnames
:
189 m
= symRe
.match(line
)
190 assert m
is not None, "Fail to parse the symbol in map file, line is %s" % line
191 sec_no
, sym_offset
, sym_name
, vir_addr
= m
.groups(0)
192 sec_no
= int(sec_no
, 16)
193 sym_offset
= int(sym_offset
, 16)
194 vir_addr
= int(vir_addr
, 16)
195 # cannot pregenerate this RegEx since it uses varname from varnames.
196 m2
= re
.match('^[_]*(%s)' % varname
, sym_name
)
198 # fond a binary pcd entry in map file
200 if sec
[0] == sec_no
and (sym_offset
>= sec
[1] and sym_offset
< sec
[1] + sec
[2]):
201 varoffset
.append([varname
, sec
[3], sym_offset
, vir_addr
, sec_no
])
203 if not varoffset
: return []
205 # get section information from efi file
206 efisecs
= PeImageClass(efifilepath
).SectionHeaderList
207 if efisecs
is None or len(efisecs
) == 0:
211 for var
in varoffset
:
213 for efisec
in efisecs
:
215 if var
[1].strip() == efisec
[0].strip():
216 ret
.append((var
[0], hex(efisec
[2] + var
[2])))
217 elif var
[4] == index
:
218 ret
.append((var
[0], hex(efisec
[2] + var
[2])))
222 ## Routine to process duplicated INF
224 # This function is called by following two cases:
227 # Pkg/module/module.inf
228 # Pkg/module/module.inf {
230 # FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836
233 # INF Pkg/module/module.inf
234 # INF FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836 Pkg/module/module.inf
236 # This function copies Pkg/module/module.inf to
237 # Conf/.cache/0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf
239 # @param Path Original PathClass object
240 # @param BaseName New file base name
242 # @retval return the new PathClass object
244 def ProcessDuplicatedInf(Path
, BaseName
, Workspace
):
245 Filename
= os
.path
.split(Path
.File
)[1]
247 Filename
= BaseName
+ Path
.BaseName
+ Filename
[Filename
.rfind('.'):]
249 Filename
= BaseName
+ Path
.BaseName
251 DbDir
= os
.path
.split(GlobalData
.gDatabasePath
)[0]
254 # A temporary INF is copied to database path which must have write permission
255 # The temporary will be removed at the end of build
256 # In case of name conflict, the file name is
257 # FILE_GUIDBaseName (0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf)
259 TempFullPath
= os
.path
.join(DbDir
,
261 RtPath
= PathClass(Path
.File
, Workspace
)
263 # Modify the full path to temporary path, keep other unchanged
265 # To build same module more than once, the module path with FILE_GUID overridden has
266 # the file name FILE_GUIDmodule.inf, but the relative path (self.MetaFile.File) is the real path
267 # in DSC which is used as relative path by C files and other files in INF.
268 # A trick was used: all module paths are PathClass instances, after the initialization
269 # of PathClass, the PathClass.Path is overridden by the temporary INF path.
271 # The reason for creating a temporary INF is:
272 # Platform.Modules which is the base to create ModuleAutoGen objects is a dictionary,
273 # the key is the full path of INF, the value is an object to save overridden library instances, PCDs.
274 # A different key for the same module is needed to create different output directory,
275 # retrieve overridden PCDs, library instances.
277 # The BaseName is the FILE_GUID which is also the output directory name.
280 RtPath
.Path
= TempFullPath
281 RtPath
.BaseName
= BaseName
282 RtPath
.OriginalPath
= Path
284 # If file exists, compare contents
286 if os
.path
.exists(TempFullPath
):
287 with
open(str(Path
), 'rb') as f1
, open(TempFullPath
, 'rb') as f2
:
288 if f1
.read() == f2
.read():
290 _TempInfs
.append(TempFullPath
)
291 shutil
.copy2(str(Path
), TempFullPath
)
294 ## Remove temporary created INFs whose paths were saved in _TempInfs
296 def ClearDuplicatedInf():
298 File
= _TempInfs
.pop()
299 if os
.path
.exists(File
):
302 ## Convert GUID string in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx style to C structure style
304 # @param Guid The GUID string
306 # @retval string The GUID string in C structure style
308 def GuidStringToGuidStructureString(Guid
):
309 GuidList
= Guid
.split('-')
311 for Index
in range(0, 3, 1):
312 Result
= Result
+ '0x' + GuidList
[Index
] + ', '
313 Result
= Result
+ '{0x' + GuidList
[3][0:2] + ', 0x' + GuidList
[3][2:4]
314 for Index
in range(0, 12, 2):
315 Result
= Result
+ ', 0x' + GuidList
[4][Index
:Index
+ 2]
319 ## Convert GUID structure in byte array to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
321 # @param GuidValue The GUID value in byte array
323 # @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
325 def GuidStructureByteArrayToGuidString(GuidValue
):
326 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
327 guidValueList
= guidValueString
.split(",")
328 if len(guidValueList
) != 16:
330 #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
332 return "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
333 int(guidValueList
[3], 16),
334 int(guidValueList
[2], 16),
335 int(guidValueList
[1], 16),
336 int(guidValueList
[0], 16),
337 int(guidValueList
[5], 16),
338 int(guidValueList
[4], 16),
339 int(guidValueList
[7], 16),
340 int(guidValueList
[6], 16),
341 int(guidValueList
[8], 16),
342 int(guidValueList
[9], 16),
343 int(guidValueList
[10], 16),
344 int(guidValueList
[11], 16),
345 int(guidValueList
[12], 16),
346 int(guidValueList
[13], 16),
347 int(guidValueList
[14], 16),
348 int(guidValueList
[15], 16)
353 ## Convert GUID string in C structure style to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
355 # @param GuidValue The GUID value in C structure format
357 # @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
359 def GuidStructureStringToGuidString(GuidValue
):
360 if not GlobalData
.gGuidCFormatPattern
.match(GuidValue
):
362 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
363 guidValueList
= guidValueString
.split(",")
364 if len(guidValueList
) != 11:
366 #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
368 return "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
369 int(guidValueList
[0], 16),
370 int(guidValueList
[1], 16),
371 int(guidValueList
[2], 16),
372 int(guidValueList
[3], 16),
373 int(guidValueList
[4], 16),
374 int(guidValueList
[5], 16),
375 int(guidValueList
[6], 16),
376 int(guidValueList
[7], 16),
377 int(guidValueList
[8], 16),
378 int(guidValueList
[9], 16),
379 int(guidValueList
[10], 16)
384 ## Convert GUID string in C structure style to xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx
386 # @param GuidValue The GUID value in C structure format
388 # @retval string The GUID value in xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx format
390 def GuidStructureStringToGuidValueName(GuidValue
):
391 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "")
392 guidValueList
= guidValueString
.split(",")
393 if len(guidValueList
) != 11:
394 EdkLogger
.error(None, FORMAT_INVALID
, "Invalid GUID value string [%s]" % GuidValue
)
395 return "%08x_%04x_%04x_%02x%02x_%02x%02x%02x%02x%02x%02x" % (
396 int(guidValueList
[0], 16),
397 int(guidValueList
[1], 16),
398 int(guidValueList
[2], 16),
399 int(guidValueList
[3], 16),
400 int(guidValueList
[4], 16),
401 int(guidValueList
[5], 16),
402 int(guidValueList
[6], 16),
403 int(guidValueList
[7], 16),
404 int(guidValueList
[8], 16),
405 int(guidValueList
[9], 16),
406 int(guidValueList
[10], 16)
409 ## Create directories
411 # @param Directory The directory name
413 def CreateDirectory(Directory
):
414 if Directory
is None or Directory
.strip() == "":
417 if not os
.access(Directory
, os
.F_OK
):
418 os
.makedirs(Directory
)
423 ## Remove directories, including files and sub-directories in it
425 # @param Directory The directory name
427 def RemoveDirectory(Directory
, Recursively
=False):
428 if Directory
is None or Directory
.strip() == "" or not os
.path
.exists(Directory
):
431 CurrentDirectory
= os
.getcwd()
433 for File
in os
.listdir("."):
434 if os
.path
.isdir(File
):
435 RemoveDirectory(File
, Recursively
)
438 os
.chdir(CurrentDirectory
)
441 ## Store content in file
443 # This method is used to save file only when its content is changed. This is
444 # quite useful for "make" system to decide what will be re-built and what won't.
446 # @param File The path of file
447 # @param Content The new content of the file
448 # @param IsBinaryFile The flag indicating if the file is binary file or not
450 # @retval True If the file content is changed and the file is renewed
451 # @retval False If the file content is the same
453 def SaveFileOnChange(File
, Content
, IsBinaryFile
=True, FileLock
=None):
455 # Convert to long file path format
456 File
= LongFilePath(File
)
458 if os
.path
.exists(File
):
461 with
open(File
, "rb") as f
:
462 if Content
== f
.read():
465 EdkLogger
.error(None, FILE_OPEN_FAILURE
, ExtraData
=File
)
468 with
open(File
, "r") as f
:
469 if Content
== f
.read():
472 EdkLogger
.error(None, FILE_OPEN_FAILURE
, ExtraData
=File
)
474 DirName
= os
.path
.dirname(File
)
475 if not CreateDirectory(DirName
):
476 EdkLogger
.error(None, FILE_CREATE_FAILURE
, "Could not create directory %s" % DirName
)
479 DirName
= os
.getcwd()
480 if not os
.access(DirName
, os
.W_OK
):
481 EdkLogger
.error(None, PERMISSION_FAILURE
, "Do not have write permission on directory %s" % DirName
)
487 # use default file_lock if no input new lock
489 FileLock
= GlobalData
.file_lock
494 if GlobalData
.gIsWindows
and not os
.path
.exists(File
):
496 with
open(File
, OpenMode
) as tf
:
499 if GlobalData
.gBinCacheSource
:
500 EdkLogger
.quiet("[cache error]:fails to save file with error: %s" % (X
))
502 EdkLogger
.error(None, FILE_CREATE_FAILURE
, ExtraData
='IOError %s' % X
)
508 with
open(File
, OpenMode
) as Fd
:
511 if GlobalData
.gBinCacheSource
:
512 EdkLogger
.quiet("[cache error]:fails to save file with error: %s" % (X
))
514 EdkLogger
.error(None, FILE_CREATE_FAILURE
, ExtraData
='IOError %s' % X
)
521 ## Copy source file only if it is different from the destination file
523 # This method is used to copy file only if the source file and destination
524 # file content are different. This is quite useful to avoid duplicated
527 # @param SrcFile The path of source file
528 # @param Dst The path of destination file or folder
530 # @retval True The two files content are different and the file is copied
531 # @retval False No copy really happen
533 def CopyFileOnChange(SrcFile
, Dst
, FileLock
=None):
535 # Convert to long file path format
536 SrcFile
= LongFilePath(SrcFile
)
537 Dst
= LongFilePath(Dst
)
539 if os
.path
.isdir(SrcFile
):
540 EdkLogger
.error(None, FILE_COPY_FAILURE
, ExtraData
='CopyFileOnChange SrcFile is a dir, not a file: %s' % SrcFile
)
543 if os
.path
.isdir(Dst
):
544 DstFile
= os
.path
.join(Dst
, os
.path
.basename(SrcFile
))
548 if os
.path
.exists(DstFile
) and filecmp
.cmp(SrcFile
, DstFile
, shallow
=False):
551 DirName
= os
.path
.dirname(DstFile
)
552 if not CreateDirectory(DirName
):
553 EdkLogger
.error(None, FILE_CREATE_FAILURE
, "Could not create directory %s" % DirName
)
556 DirName
= os
.getcwd()
557 if not os
.access(DirName
, os
.W_OK
):
558 EdkLogger
.error(None, PERMISSION_FAILURE
, "Do not have write permission on directory %s" % DirName
)
560 # use default file_lock if no input new lock
562 FileLock
= GlobalData
.file_lock
567 CopyLong(SrcFile
, DstFile
)
569 if GlobalData
.gBinCacheSource
:
570 EdkLogger
.quiet("[cache error]:fails to copy file with error: %s" % (X
))
572 EdkLogger
.error(None, FILE_COPY_FAILURE
, ExtraData
='IOError %s' % X
)
579 ## Retrieve and cache the real path name in file system
581 # @param Root The root directory of path relative to
583 # @retval str The path string if the path exists
584 # @retval None If path doesn't exist
590 def __init__(self
, Root
):
592 for F
in os
.listdir(Root
):
594 self
._UPPER
_CACHE
_[F
.upper()] = F
597 def __getitem__(self
, Path
):
598 Path
= Path
[len(os
.path
.commonprefix([Path
, self
._Root
])):]
601 if Path
and Path
[0] == os
.path
.sep
:
603 if Path
in self
._CACHE
_:
604 return os
.path
.join(self
._Root
, Path
)
605 UpperPath
= Path
.upper()
606 if UpperPath
in self
._UPPER
_CACHE
_:
607 return os
.path
.join(self
._Root
, self
._UPPER
_CACHE
_[UpperPath
])
611 SepIndex
= Path
.find(os
.path
.sep
)
613 Parent
= UpperPath
[:SepIndex
]
614 if Parent
not in self
._UPPER
_CACHE
_:
616 LastSepIndex
= SepIndex
617 SepIndex
= Path
.find(os
.path
.sep
, LastSepIndex
+ 1)
619 if LastSepIndex
== -1:
624 SepIndex
= LastSepIndex
626 Parent
= Path
[:SepIndex
]
627 ParentKey
= UpperPath
[:SepIndex
]
628 if ParentKey
not in self
._UPPER
_CACHE
_:
632 if Parent
in self
._CACHE
_:
635 ParentDir
= self
._UPPER
_CACHE
_[ParentKey
]
636 for F
in os
.listdir(ParentDir
):
637 Dir
= os
.path
.join(ParentDir
, F
)
638 self
._CACHE
_.add(Dir
)
639 self
._UPPER
_CACHE
_[Dir
.upper()] = Dir
641 SepIndex
= Path
.find(os
.path
.sep
, SepIndex
+ 1)
644 if Path
in self
._CACHE
_:
645 return os
.path
.join(self
._Root
, Path
)
646 elif UpperPath
in self
._UPPER
_CACHE
_:
647 return os
.path
.join(self
._Root
, self
._UPPER
_CACHE
_[UpperPath
])
650 def RealPath(File
, Dir
='', OverrideDir
=''):
651 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
652 NewFile
= GlobalData
.gAllFiles
[NewFile
]
653 if not NewFile
and OverrideDir
:
654 NewFile
= os
.path
.normpath(os
.path
.join(OverrideDir
, File
))
655 NewFile
= GlobalData
.gAllFiles
[NewFile
]
658 ## Get GUID value from given packages
660 # @param CName The CName of the GUID
661 # @param PackageList List of packages looking-up in
662 # @param Inffile The driver file
664 # @retval GuidValue if the CName is found in any given package
665 # @retval None if the CName is not found in all given packages
667 def GuidValue(CName
, PackageList
, Inffile
= None):
668 for P
in PackageList
:
669 GuidKeys
= list(P
.Guids
.keys())
670 if Inffile
and P
._PrivateGuids
:
671 if not Inffile
.startswith(P
.MetaFile
.Dir
):
672 GuidKeys
= [x
for x
in P
.Guids
if x
not in P
._PrivateGuids
]
673 if CName
in GuidKeys
:
674 return P
.Guids
[CName
]
677 ## A string template class
679 # This class implements a template for string replacement. A string template
680 # looks like following
682 # ${BEGIN} other_string ${placeholder_name} other_string ${END}
684 # The string between ${BEGIN} and ${END} will be repeated as many times as the
685 # length of "placeholder_name", which is a list passed through a dict. The
686 # "placeholder_name" is the key name of the dict. The ${BEGIN} and ${END} can
687 # be not used and, in this case, the "placeholder_name" must not a list and it
688 # will just be replaced once.
690 class TemplateString(object):
691 _REPEAT_START_FLAG
= "BEGIN"
692 _REPEAT_END_FLAG
= "END"
694 class Section(object):
695 _LIST_TYPES
= [type([]), type(set()), type((0,))]
697 def __init__(self
, TemplateSection
, PlaceHolderList
):
698 self
._Template
= TemplateSection
699 self
._PlaceHolderList
= []
701 # Split the section into sub-sections according to the position of placeholders
703 self
._SubSectionList
= []
706 # The placeholders passed in must be in the format of
708 # PlaceHolderName, PlaceHolderStartPoint, PlaceHolderEndPoint
710 for PlaceHolder
, Start
, End
in PlaceHolderList
:
711 self
._SubSectionList
.append(TemplateSection
[SubSectionStart
:Start
])
712 self
._SubSectionList
.append(TemplateSection
[Start
:End
])
713 self
._PlaceHolderList
.append(PlaceHolder
)
714 SubSectionStart
= End
715 if SubSectionStart
< len(TemplateSection
):
716 self
._SubSectionList
.append(TemplateSection
[SubSectionStart
:])
718 self
._SubSectionList
= [TemplateSection
]
721 return self
._Template
+ " : " + str(self
._PlaceHolderList
)
723 def Instantiate(self
, PlaceHolderValues
):
725 RepeatPlaceHolders
= {}
726 NonRepeatPlaceHolders
= {}
728 for PlaceHolder
in self
._PlaceHolderList
:
729 if PlaceHolder
not in PlaceHolderValues
:
731 Value
= PlaceHolderValues
[PlaceHolder
]
732 if type(Value
) in self
._LIST
_TYPES
:
734 RepeatTime
= len(Value
)
735 elif RepeatTime
!= len(Value
):
739 "${%s} has different repeat time from others!" % PlaceHolder
,
740 ExtraData
=str(self
._Template
)
742 RepeatPlaceHolders
["${%s}" % PlaceHolder
] = Value
744 NonRepeatPlaceHolders
["${%s}" % PlaceHolder
] = Value
746 if NonRepeatPlaceHolders
:
748 for S
in self
._SubSectionList
:
749 if S
not in NonRepeatPlaceHolders
:
752 StringList
.append(str(NonRepeatPlaceHolders
[S
]))
754 StringList
= self
._SubSectionList
756 if RepeatPlaceHolders
:
758 for Index
in range(RepeatTime
):
760 if S
not in RepeatPlaceHolders
:
761 TempStringList
.append(S
)
763 TempStringList
.append(str(RepeatPlaceHolders
[S
][Index
]))
764 StringList
= TempStringList
766 return "".join(StringList
)
769 def __init__(self
, Template
=None):
771 self
.IsBinary
= False
772 self
._Template
= Template
773 self
._TemplateSectionList
= self
._Parse
(Template
)
777 # @retval string The string replaced
780 return "".join(self
.String
)
782 ## Split the template string into fragments per the ${BEGIN} and ${END} flags
784 # @retval list A list of TemplateString.Section objects
786 def _Parse(self
, Template
):
791 TemplateSectionList
= []
793 MatchObj
= gPlaceholderPattern
.search(Template
, SearchFrom
)
795 if MatchEnd
<= len(Template
):
796 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:], PlaceHolderList
)
797 TemplateSectionList
.append(TemplateSection
)
800 MatchString
= MatchObj
.group(1)
801 MatchStart
= MatchObj
.start()
802 MatchEnd
= MatchObj
.end()
804 if MatchString
== self
._REPEAT
_START
_FLAG
:
805 if MatchStart
> SectionStart
:
806 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:MatchStart
], PlaceHolderList
)
807 TemplateSectionList
.append(TemplateSection
)
808 SectionStart
= MatchEnd
810 elif MatchString
== self
._REPEAT
_END
_FLAG
:
811 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:MatchStart
], PlaceHolderList
)
812 TemplateSectionList
.append(TemplateSection
)
813 SectionStart
= MatchEnd
816 PlaceHolderList
.append((MatchString
, MatchStart
- SectionStart
, MatchEnd
- SectionStart
))
817 SearchFrom
= MatchEnd
818 return TemplateSectionList
820 ## Replace the string template with dictionary of placeholders and append it to previous one
822 # @param AppendString The string template to append
823 # @param Dictionary The placeholder dictionaries
825 def Append(self
, AppendString
, Dictionary
=None):
827 SectionList
= self
._Parse
(AppendString
)
828 self
.String
.append( "".join(S
.Instantiate(Dictionary
) for S
in SectionList
))
830 if isinstance(AppendString
,list):
831 self
.String
.extend(AppendString
)
833 self
.String
.append(AppendString
)
835 ## Replace the string template with dictionary of placeholders
837 # @param Dictionary The placeholder dictionaries
839 # @retval str The string replaced with placeholder values
841 def Replace(self
, Dictionary
=None):
842 return "".join(S
.Instantiate(Dictionary
) for S
in self
._TemplateSectionList
)
844 ## Progress indicator class
846 # This class makes use of thread to print progress on console.
849 # for avoiding deadloop
851 _ProgressThread
= None
852 _CheckInterval
= 0.25
856 # @param OpenMessage The string printed before progress characters
857 # @param CloseMessage The string printed after progress characters
858 # @param ProgressChar The character used to indicate the progress
859 # @param Interval The interval in seconds between two progress characters
861 def __init__(self
, OpenMessage
="", CloseMessage
="", ProgressChar
='.', Interval
=1.0):
862 self
.PromptMessage
= OpenMessage
863 self
.CodaMessage
= CloseMessage
864 self
.ProgressChar
= ProgressChar
865 self
.Interval
= Interval
866 if Progressor
._StopFlag
is None:
867 Progressor
._StopFlag
= threading
.Event()
869 ## Start to print progress character
871 # @param OpenMessage The string printed before progress characters
873 def Start(self
, OpenMessage
=None):
874 if OpenMessage
is not None:
875 self
.PromptMessage
= OpenMessage
876 Progressor
._StopFlag
.clear()
877 if Progressor
._ProgressThread
is None:
878 Progressor
._ProgressThread
= threading
.Thread(target
=self
._ProgressThreadEntry
)
879 Progressor
._ProgressThread
.setDaemon(False)
880 Progressor
._ProgressThread
.start()
882 ## Stop printing progress character
884 # @param CloseMessage The string printed after progress characters
886 def Stop(self
, CloseMessage
=None):
887 OriginalCodaMessage
= self
.CodaMessage
888 if CloseMessage
is not None:
889 self
.CodaMessage
= CloseMessage
891 self
.CodaMessage
= OriginalCodaMessage
893 ## Thread entry method
894 def _ProgressThreadEntry(self
):
895 sys
.stdout
.write(self
.PromptMessage
+ " ")
898 while not Progressor
._StopFlag
.isSet():
900 sys
.stdout
.write(self
.ProgressChar
)
902 TimeUp
= self
.Interval
903 time
.sleep(self
._CheckInterval
)
904 TimeUp
-= self
._CheckInterval
905 sys
.stdout
.write(" " + self
.CodaMessage
+ "\n")
908 ## Abort the progress display
911 if Progressor
._StopFlag
is not None:
912 Progressor
._StopFlag
.set()
913 if Progressor
._ProgressThread
is not None:
914 Progressor
._ProgressThread
.join()
915 Progressor
._ProgressThread
= None
918 ## Dictionary using prioritized list as key
922 _TupleType
= type(())
924 _ValidWildcardList
= ['COMMON', 'DEFAULT', 'ALL', TAB_STAR
, 'PLATFORM']
926 def __init__(self
, _Single_
=False, _Level_
=2):
927 self
._Level
_ = _Level_
929 self
._Single
_ = _Single_
932 def __getitem__(self
, key
):
935 if KeyType
== self
._ListType
or KeyType
== self
._TupleType
:
939 elif self
._Level
_ > 1:
940 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
944 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
946 if FirstKey
is None or str(FirstKey
).upper() in self
._ValidWildcardList
:
947 FirstKey
= self
._Wildcard
950 return self
._GetSingleValue
(FirstKey
, RestKeys
)
952 return self
._GetAllValues
(FirstKey
, RestKeys
)
954 def _GetSingleValue(self
, FirstKey
, RestKeys
):
956 #print "%s-%s" % (FirstKey, self._Level_) ,
958 if FirstKey
== self
._Wildcard
:
959 if FirstKey
in self
.data
:
960 Value
= self
.data
[FirstKey
][RestKeys
]
962 for Key
in self
.data
:
963 Value
= self
.data
[Key
][RestKeys
]
964 if Value
is not None: break
966 if FirstKey
in self
.data
:
967 Value
= self
.data
[FirstKey
][RestKeys
]
968 if Value
is None and self
._Wildcard
in self
.data
:
970 Value
= self
.data
[self
._Wildcard
][RestKeys
]
972 if FirstKey
== self
._Wildcard
:
973 if FirstKey
in self
.data
:
974 Value
= self
.data
[FirstKey
]
976 for Key
in self
.data
:
977 Value
= self
.data
[Key
]
978 if Value
is not None: break
980 if FirstKey
in self
.data
:
981 Value
= self
.data
[FirstKey
]
982 elif self
._Wildcard
in self
.data
:
983 Value
= self
.data
[self
._Wildcard
]
986 def _GetAllValues(self
, FirstKey
, RestKeys
):
989 if FirstKey
== self
._Wildcard
:
990 for Key
in self
.data
:
991 Value
+= self
.data
[Key
][RestKeys
]
993 if FirstKey
in self
.data
:
994 Value
+= self
.data
[FirstKey
][RestKeys
]
995 if self
._Wildcard
in self
.data
:
996 Value
+= self
.data
[self
._Wildcard
][RestKeys
]
998 if FirstKey
== self
._Wildcard
:
999 for Key
in self
.data
:
1000 Value
.append(self
.data
[Key
])
1002 if FirstKey
in self
.data
:
1003 Value
.append(self
.data
[FirstKey
])
1004 if self
._Wildcard
in self
.data
:
1005 Value
.append(self
.data
[self
._Wildcard
])
1009 def __setitem__(self
, key
, value
):
1012 if KeyType
== self
._ListType
or KeyType
== self
._TupleType
:
1017 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
1020 if self
._Level
_ > 1:
1021 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
1023 if FirstKey
in self
._ValidWildcardList
:
1024 FirstKey
= self
._Wildcard
1026 if FirstKey
not in self
.data
and self
._Level
_ > 0:
1027 self
.data
[FirstKey
] = tdict(self
._Single
_, self
._Level
_ - 1)
1029 if self
._Level
_ > 1:
1030 self
.data
[FirstKey
][RestKeys
] = value
1032 self
.data
[FirstKey
] = value
1034 def SetGreedyMode(self
):
1035 self
._Single
_ = False
1036 if self
._Level
_ > 1:
1037 for Key
in self
.data
:
1038 self
.data
[Key
].SetGreedyMode()
1040 def SetSingleMode(self
):
1041 self
._Single
_ = True
1042 if self
._Level
_ > 1:
1043 for Key
in self
.data
:
1044 self
.data
[Key
].SetSingleMode()
1046 def GetKeys(self
, KeyIndex
=0):
1047 assert KeyIndex
>= 0
1049 return set(self
.data
.keys())
1052 for Key
in self
.data
:
1053 keys |
= self
.data
[Key
].GetKeys(KeyIndex
- 1)
1056 def AnalyzePcdExpression(Setting
):
1057 RanStr
= ''.join(sample(string
.ascii_letters
+ string
.digits
, 8))
1058 Setting
= Setting
.replace('\\\\', RanStr
).strip()
1059 # There might be escaped quote in a string: \", \\\" , \', \\\'
1061 # There might be '|' in string and in ( ... | ... ), replace it with '-'
1063 InSingleQuoteStr
= False
1064 InDoubleQuoteStr
= False
1066 for Index
, ch
in enumerate(Data
):
1067 if ch
== '"' and not InSingleQuoteStr
:
1068 if Data
[Index
- 1] != '\\':
1069 InDoubleQuoteStr
= not InDoubleQuoteStr
1070 elif ch
== "'" and not InDoubleQuoteStr
:
1071 if Data
[Index
- 1] != '\\':
1072 InSingleQuoteStr
= not InSingleQuoteStr
1073 elif ch
== '(' and not (InSingleQuoteStr
or InDoubleQuoteStr
):
1075 elif ch
== ')' and not (InSingleQuoteStr
or InDoubleQuoteStr
):
1078 if (Pair
> 0 or InSingleQuoteStr
or InDoubleQuoteStr
) and ch
== TAB_VALUE_SPLIT
:
1085 Pos
= NewStr
.find(TAB_VALUE_SPLIT
, StartPos
)
1087 FieldList
.append(Setting
[StartPos
:].strip())
1089 FieldList
.append(Setting
[StartPos
:Pos
].strip())
1091 for i
, ch
in enumerate(FieldList
):
1093 FieldList
[i
] = ch
.replace(RanStr
,'\\\\')
1096 def ParseFieldValue (Value
):
1097 def ParseDevPathValue (Value
):
1099 Value
.replace('\\', '/').replace(' ', '')
1101 Cmd
= 'DevicePath ' + '"' + Value
+ '"'
1103 p
= subprocess
.Popen(Cmd
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, shell
=True)
1104 out
, err
= p
.communicate()
1105 except Exception as X
:
1106 raise BadExpression("DevicePath: %s" % (str(X
)) )
1108 subprocess
._cleanup
()
1112 raise BadExpression("DevicePath: %s" % str(err
))
1114 Size
= len(out
.split())
1115 out
= ','.join(out
.split())
1116 return '{' + out
+ '}', Size
1118 if "{CODE(" in Value
:
1119 return Value
, len(Value
.split(","))
1120 if isinstance(Value
, type(0)):
1121 return Value
, (Value
.bit_length() + 7) // 8
1122 if not isinstance(Value
, type('')):
1123 raise BadExpression('Type %s is %s' %(Value
, type(Value
)))
1124 Value
= Value
.strip()
1125 if Value
.startswith(TAB_UINT8
) and Value
.endswith(')'):
1126 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1128 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1130 if Value
.startswith(TAB_UINT16
) and Value
.endswith(')'):
1131 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1133 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1135 if Value
.startswith(TAB_UINT32
) and Value
.endswith(')'):
1136 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1138 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1140 if Value
.startswith(TAB_UINT64
) and Value
.endswith(')'):
1141 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1143 raise BadExpression('Value (%s) Size larger than %d' % (Value
, Size
))
1145 if Value
.startswith(TAB_GUID
) and Value
.endswith(')'):
1146 Value
= Value
.split('(', 1)[1][:-1].strip()
1147 if Value
[0] == '{' and Value
[-1] == '}':
1148 TmpValue
= GuidStructureStringToGuidString(Value
)
1150 raise BadExpression("Invalid GUID value string %s" % Value
)
1152 if Value
[0] == '"' and Value
[-1] == '"':
1155 Value
= str(uuid
.UUID(Value
).bytes_le
)
1156 if Value
.startswith("b'"):
1158 Value
= "'" + Value
+ "'"
1159 except ValueError as Message
:
1160 raise BadExpression(Message
)
1161 Value
, Size
= ParseFieldValue(Value
)
1163 if Value
.startswith('L"') and Value
.endswith('"'):
1165 # translate escape character
1175 Value
= (Value
<< 16) |
ord(Char
)
1176 return Value
, (len(List
) + 1) * 2
1177 if Value
.startswith('"') and Value
.endswith('"'):
1179 # translate escape character
1188 Value
= (Value
<< 8) |
ord(Char
)
1189 return Value
, len(List
) + 1
1190 if Value
.startswith("L'") and Value
.endswith("'"):
1191 # Unicode Character Constant
1192 # translate escape character
1200 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1204 Value
= (Value
<< 16) |
ord(Char
)
1205 return Value
, len(List
) * 2
1206 if Value
.startswith("'") and Value
.endswith("'"):
1207 # Character constant
1208 # translate escape character
1215 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1219 Value
= (Value
<< 8) |
ord(Char
)
1220 return Value
, len(List
)
1221 if Value
.startswith('{') and Value
.endswith('}'):
1224 List
= [Item
.strip() for Item
in Value
.split(',')]
1229 ItemValue
, Size
= ParseFieldValue(Item
)
1231 for I
in range(Size
):
1232 Value
= (Value
<< 8) |
((ItemValue
>> 8 * I
) & 0xff)
1233 return Value
, RetSize
1234 if Value
.startswith('DEVICE_PATH(') and Value
.endswith(')'):
1235 Value
= Value
.replace("DEVICE_PATH(", '').rstrip(')')
1236 Value
= Value
.strip().strip('"')
1237 return ParseDevPathValue(Value
)
1238 if Value
.lower().startswith('0x'):
1240 Value
= int(Value
, 16)
1242 raise BadExpression("invalid hex value: %s" % Value
)
1245 return Value
, (Value
.bit_length() + 7) // 8
1246 if Value
[0].isdigit():
1247 Value
= int(Value
, 10)
1250 return Value
, (Value
.bit_length() + 7) // 8
1251 if Value
.lower() == 'true':
1253 if Value
.lower() == 'false':
1259 # Analyze DSC PCD value, since there is no data type info in DSC
1260 # This function is used to match functions (AnalyzePcdData) used for retrieving PCD value from database
1261 # 1. Feature flag: TokenSpace.PcdCName|PcdValue
1262 # 2. Fix and Patch:TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1263 # 3. Dynamic default:
1264 # TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1265 # TokenSpace.PcdCName|PcdValue
1267 # TokenSpace.PcdCName|VpdOffset[|VpdValue]
1268 # TokenSpace.PcdCName|VpdOffset[|MaxSize[|VpdValue]]
1270 # TokenSpace.PcdCName|HiiString|VariableGuid|VariableOffset[|HiiValue]
1271 # PCD value needs to be located in such kind of string, and the PCD value might be an expression in which
1272 # there might have "|" operator, also in string value.
1274 # @param Setting: String contain information described above with "TokenSpace.PcdCName|" stripped
1275 # @param PcdType: PCD type: feature, fixed, dynamic default VPD HII
1276 # @param DataType: The datum type of PCD: VOID*, UNIT, BOOL
1278 # ValueList: A List contain fields described above
1279 # IsValid: True if conforming EBNF, otherwise False
1280 # Index: The index where PcdValue is in ValueList
1282 def AnalyzeDscPcd(Setting
, PcdType
, DataType
=''):
1283 FieldList
= AnalyzePcdExpression(Setting
)
1286 if PcdType
in (MODEL_PCD_FIXED_AT_BUILD
, MODEL_PCD_PATCHABLE_IN_MODULE
, MODEL_PCD_DYNAMIC_DEFAULT
, MODEL_PCD_DYNAMIC_EX_DEFAULT
):
1287 Value
= FieldList
[0]
1289 if len(FieldList
) > 1 and FieldList
[1]:
1290 DataType
= FieldList
[1]
1291 if FieldList
[1] != TAB_VOID
and StructPattern
.match(FieldList
[1]) is None:
1293 if len(FieldList
) > 2:
1297 IsValid
= (len(FieldList
) <= 1)
1299 IsValid
= (len(FieldList
) <= 3)
1303 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1307 return [str(Value
), DataType
, str(Size
)], IsValid
, 0
1308 elif PcdType
== MODEL_PCD_FEATURE_FLAG
:
1309 Value
= FieldList
[0]
1311 IsValid
= (len(FieldList
) <= 1)
1312 return [Value
, DataType
, str(Size
)], IsValid
, 0
1313 elif PcdType
in (MODEL_PCD_DYNAMIC_VPD
, MODEL_PCD_DYNAMIC_EX_VPD
):
1314 VpdOffset
= FieldList
[0]
1316 if not DataType
== TAB_VOID
:
1317 if len(FieldList
) > 1:
1318 Value
= FieldList
[1]
1320 if len(FieldList
) > 1:
1322 if len(FieldList
) > 2:
1323 Value
= FieldList
[2]
1325 IsValid
= (len(FieldList
) <= 1)
1327 IsValid
= (len(FieldList
) <= 3)
1330 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1334 return [VpdOffset
, str(Size
), Value
], IsValid
, 2
1335 elif PcdType
in (MODEL_PCD_DYNAMIC_HII
, MODEL_PCD_DYNAMIC_EX_HII
):
1336 IsValid
= (3 <= len(FieldList
) <= 5)
1337 HiiString
= FieldList
[0]
1338 Guid
= Offset
= Value
= Attribute
= ''
1339 if len(FieldList
) > 1:
1341 if len(FieldList
) > 2:
1342 Offset
= FieldList
[2]
1343 if len(FieldList
) > 3:
1344 Value
= FieldList
[3]
1345 if len(FieldList
) > 4:
1346 Attribute
= FieldList
[4]
1347 return [HiiString
, Guid
, Offset
, Value
, Attribute
], IsValid
, 3
1352 # Analyze the pcd Value, Datum type and TokenNumber.
1353 # Used to avoid split issue while the value string contain "|" character
1355 # @param[in] Setting: A String contain value/datum type/token number information;
1357 # @retval ValueList: A List contain value, datum type and toke number.
1359 def AnalyzePcdData(Setting
):
1360 ValueList
= ['', '', '']
1362 ValueRe
= re
.compile(r
'^\s*L?\".*\|.*\"')
1363 PtrValue
= ValueRe
.findall(Setting
)
1365 ValueUpdateFlag
= False
1367 if len(PtrValue
) >= 1:
1368 Setting
= re
.sub(ValueRe
, '', Setting
)
1369 ValueUpdateFlag
= True
1371 TokenList
= Setting
.split(TAB_VALUE_SPLIT
)
1372 ValueList
[0:len(TokenList
)] = TokenList
1375 ValueList
[0] = PtrValue
[0]
1379 ## check format of PCD value against its the datum type
1381 # For PCD value setting
1383 def CheckPcdDatum(Type
, Value
):
1384 if Type
== TAB_VOID
:
1385 ValueRe
= re
.compile(r
'\s*L?\".*\"\s*$')
1386 if not (((Value
.startswith('L"') or Value
.startswith('"')) and Value
.endswith('"'))
1387 or (Value
.startswith('{') and Value
.endswith('}')) or (Value
.startswith("L'") or Value
.startswith("'") and Value
.endswith("'"))
1389 return False, "Invalid value [%s] of type [%s]; must be in the form of {...} for array"\
1390 ", \"...\" or \'...\' for string, L\"...\" or L\'...\' for unicode string" % (Value
, Type
)
1391 elif ValueRe
.match(Value
):
1392 # Check the chars in UnicodeString or CString is printable
1393 if Value
.startswith("L"):
1397 Printset
= set(string
.printable
)
1398 Printset
.remove(TAB_PRINTCHAR_VT
)
1399 Printset
.add(TAB_PRINTCHAR_BS
)
1400 Printset
.add(TAB_PRINTCHAR_NUL
)
1401 if not set(Value
).issubset(Printset
):
1402 PrintList
= sorted(Printset
)
1403 return False, "Invalid PCD string value of type [%s]; must be printable chars %s." % (Type
, PrintList
)
1404 elif Type
== 'BOOLEAN':
1405 if Value
not in ['TRUE', 'True', 'true', '0x1', '0x01', '1', 'FALSE', 'False', 'false', '0x0', '0x00', '0']:
1406 return False, "Invalid value [%s] of type [%s]; must be one of TRUE, True, true, 0x1, 0x01, 1"\
1407 ", FALSE, False, false, 0x0, 0x00, 0" % (Value
, Type
)
1408 elif Type
in [TAB_UINT8
, TAB_UINT16
, TAB_UINT32
, TAB_UINT64
]:
1409 if Value
.startswith('0') and not Value
.lower().startswith('0x') and len(Value
) > 1 and Value
.lstrip('0'):
1410 Value
= Value
.lstrip('0')
1412 if Value
and int(Value
, 0) < 0:
1413 return False, "PCD can't be set to negative value[%s] for datum type [%s]" % (Value
, Type
)
1414 Value
= int(Value
, 0)
1415 if Value
> MAX_VAL_TYPE
[Type
]:
1416 return False, "Too large PCD value[%s] for datum type [%s]" % (Value
, Type
)
1418 return False, "Invalid value [%s] of type [%s];"\
1419 " must be a hexadecimal, decimal or octal in C language format." % (Value
, Type
)
1421 return True, "StructurePcd"
1425 def CommonPath(PathList
):
1426 P1
= min(PathList
).split(os
.path
.sep
)
1427 P2
= max(PathList
).split(os
.path
.sep
)
1428 for Index
in range(min(len(P1
), len(P2
))):
1429 if P1
[Index
] != P2
[Index
]:
1430 return os
.path
.sep
.join(P1
[:Index
])
1431 return os
.path
.sep
.join(P1
)
1433 class PathClass(object):
1434 def __init__(self
, File
='', Root
='', AlterRoot
='', Type
='', IsBinary
=False,
1435 Arch
='COMMON', ToolChainFamily
='', Target
='', TagName
='', ToolCode
=''):
1437 self
.File
= str(File
)
1438 if os
.path
.isabs(self
.File
):
1442 self
.Root
= str(Root
)
1443 self
.AlterRoot
= str(AlterRoot
)
1445 # Remove any '.' and '..' in path
1447 self
.Root
= mws
.getWs(self
.Root
, self
.File
)
1448 self
.Path
= os
.path
.normpath(os
.path
.join(self
.Root
, self
.File
))
1449 self
.Root
= os
.path
.normpath(CommonPath([self
.Root
, self
.Path
]))
1450 # eliminate the side-effect of 'C:'
1451 if self
.Root
[-1] == ':':
1452 self
.Root
+= os
.path
.sep
1453 # file path should not start with path separator
1454 if self
.Root
[-1] == os
.path
.sep
:
1455 self
.File
= self
.Path
[len(self
.Root
):]
1457 self
.File
= self
.Path
[len(self
.Root
) + 1:]
1459 self
.Path
= os
.path
.normpath(self
.File
)
1461 self
.SubDir
, self
.Name
= os
.path
.split(self
.File
)
1462 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1466 self
.Dir
= os
.path
.join(self
.Root
, self
.SubDir
)
1468 self
.Dir
= self
.Root
1470 self
.Dir
= self
.SubDir
1475 self
.Type
= self
.Ext
.lower()
1477 self
.IsBinary
= IsBinary
1478 self
.Target
= Target
1479 self
.TagName
= TagName
1480 self
.ToolCode
= ToolCode
1481 self
.ToolChainFamily
= ToolChainFamily
1482 self
.OriginalPath
= self
1484 ## Convert the object of this class to a string
1486 # Convert member Path of the class to a string
1488 # @retval string Formatted String
1493 ## Override __eq__ function
1495 # Check whether PathClass are the same
1497 # @retval False The two PathClass are different
1498 # @retval True The two PathClass are the same
1500 def __eq__(self
, Other
):
1501 return self
.Path
== str(Other
)
1503 ## Override __cmp__ function
1505 # Customize the comparison operation of two PathClass
1507 # @retval 0 The two PathClass are different
1508 # @retval -1 The first PathClass is less than the second PathClass
1509 # @retval 1 The first PathClass is Bigger than the second PathClass
1510 def __cmp__(self
, Other
):
1511 OtherKey
= str(Other
)
1514 if SelfKey
== OtherKey
:
1516 elif SelfKey
> OtherKey
:
1521 ## Override __hash__ function
1523 # Use Path as key in hash table
1525 # @retval string Key for hash table
1528 return hash(self
.Path
)
1532 return self
.Path
.upper()
1535 def TimeStamp(self
):
1536 return os
.stat(self
.Path
)[8]
1538 def Validate(self
, Type
='', CaseSensitive
=True):
1539 def RealPath2(File
, Dir
='', OverrideDir
=''):
1542 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(OverrideDir
, File
))]
1544 if OverrideDir
[-1] == os
.path
.sep
:
1545 return NewFile
[len(OverrideDir
):], NewFile
[0:len(OverrideDir
)]
1547 return NewFile
[len(OverrideDir
) + 1:], NewFile
[0:len(OverrideDir
)]
1548 if GlobalData
.gAllFiles
:
1549 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(Dir
, File
))]
1551 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
1552 if not os
.path
.exists(NewFile
):
1556 if Dir
[-1] == os
.path
.sep
:
1557 return NewFile
[len(Dir
):], NewFile
[0:len(Dir
)]
1559 return NewFile
[len(Dir
) + 1:], NewFile
[0:len(Dir
)]
1565 if GlobalData
.gCaseInsensitive
:
1566 CaseSensitive
= False
1567 if Type
and Type
.lower() != self
.Type
:
1568 return FILE_TYPE_MISMATCH
, '%s (expect %s but got %s)' % (self
.File
, Type
, self
.Type
)
1570 RealFile
, RealRoot
= RealPath2(self
.File
, self
.Root
, self
.AlterRoot
)
1571 if not RealRoot
and not RealFile
:
1572 RealFile
= self
.File
1574 RealFile
= os
.path
.join(self
.AlterRoot
, self
.File
)
1576 RealFile
= os
.path
.join(self
.Root
, self
.File
)
1577 if len (mws
.getPkgPath()) == 0:
1578 return FILE_NOT_FOUND
, os
.path
.join(self
.AlterRoot
, RealFile
)
1580 return FILE_NOT_FOUND
, "%s is not found in packages path:\n\t%s" % (self
.File
, '\n\t'.join(mws
.getPkgPath()))
1584 if RealRoot
!= self
.Root
or RealFile
!= self
.File
:
1585 if CaseSensitive
and (RealFile
!= self
.File
or (RealRoot
!= self
.Root
and RealRoot
!= self
.AlterRoot
)):
1586 ErrorCode
= FILE_CASE_MISMATCH
1587 ErrorInfo
= self
.File
+ '\n\t' + RealFile
+ " [in file system]"
1589 self
.SubDir
, self
.Name
= os
.path
.split(RealFile
)
1590 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1592 self
.Dir
= os
.path
.join(RealRoot
, self
.SubDir
)
1595 self
.File
= RealFile
1596 self
.Root
= RealRoot
1597 self
.Path
= os
.path
.join(RealRoot
, RealFile
)
1598 return ErrorCode
, ErrorInfo
1600 ## Parse PE image to get the required PE information.
1602 class PeImageClass():
1605 # @param File FilePath of PeImage
1607 def __init__(self
, PeFile
):
1608 self
.FileName
= PeFile
1609 self
.IsValid
= False
1612 self
.SectionAlignment
= 0
1613 self
.SectionHeaderList
= []
1616 PeObject
= open(PeFile
, 'rb')
1618 self
.ErrorInfo
= self
.FileName
+ ' can not be found\n'
1621 ByteArray
= array
.array('B')
1622 ByteArray
.fromfile(PeObject
, 0x3E)
1623 ByteList
= ByteArray
.tolist()
1624 # DOS signature should be 'MZ'
1625 if self
._ByteListToStr
(ByteList
[0x0:0x2]) != 'MZ':
1626 self
.ErrorInfo
= self
.FileName
+ ' has no valid DOS signature MZ'
1629 # Read 4 byte PE Signature
1630 PeOffset
= self
._ByteListToInt
(ByteList
[0x3C:0x3E])
1631 PeObject
.seek(PeOffset
)
1632 ByteArray
= array
.array('B')
1633 ByteArray
.fromfile(PeObject
, 4)
1634 # PE signature should be 'PE\0\0'
1635 if ByteArray
.tostring() != b
'PE\0\0':
1636 self
.ErrorInfo
= self
.FileName
+ ' has no valid PE signature PE00'
1639 # Read PE file header
1640 ByteArray
= array
.array('B')
1641 ByteArray
.fromfile(PeObject
, 0x14)
1642 ByteList
= ByteArray
.tolist()
1643 SecNumber
= self
._ByteListToInt
(ByteList
[0x2:0x4])
1645 self
.ErrorInfo
= self
.FileName
+ ' has no section header'
1648 # Read PE optional header
1649 OptionalHeaderSize
= self
._ByteListToInt
(ByteArray
[0x10:0x12])
1650 ByteArray
= array
.array('B')
1651 ByteArray
.fromfile(PeObject
, OptionalHeaderSize
)
1652 ByteList
= ByteArray
.tolist()
1653 self
.EntryPoint
= self
._ByteListToInt
(ByteList
[0x10:0x14])
1654 self
.SectionAlignment
= self
._ByteListToInt
(ByteList
[0x20:0x24])
1655 self
.Size
= self
._ByteListToInt
(ByteList
[0x38:0x3C])
1657 # Read each Section Header
1658 for Index
in range(SecNumber
):
1659 ByteArray
= array
.array('B')
1660 ByteArray
.fromfile(PeObject
, 0x28)
1661 ByteList
= ByteArray
.tolist()
1662 SecName
= self
._ByteListToStr
(ByteList
[0:8])
1663 SecVirtualSize
= self
._ByteListToInt
(ByteList
[8:12])
1664 SecRawAddress
= self
._ByteListToInt
(ByteList
[20:24])
1665 SecVirtualAddress
= self
._ByteListToInt
(ByteList
[12:16])
1666 self
.SectionHeaderList
.append((SecName
, SecVirtualAddress
, SecRawAddress
, SecVirtualSize
))
1670 def _ByteListToStr(self
, ByteList
):
1672 for index
in range(len(ByteList
)):
1673 if ByteList
[index
] == 0:
1675 String
+= chr(ByteList
[index
])
1678 def _ByteListToInt(self
, ByteList
):
1680 for index
in range(len(ByteList
) - 1, -1, -1):
1681 Value
= (Value
<< 8) |
int(ByteList
[index
])
1684 class DefaultStore():
1685 def __init__(self
, DefaultStores
):
1687 self
.DefaultStores
= DefaultStores
1688 def DefaultStoreID(self
, DefaultStoreName
):
1689 for key
, value
in self
.DefaultStores
.items():
1690 if value
== DefaultStoreName
:
1693 def GetDefaultDefault(self
):
1694 if not self
.DefaultStores
or "0" in self
.DefaultStores
:
1695 return "0", TAB_DEFAULT_STORES_DEFAULT
1697 minvalue
= min(int(value_str
) for value_str
in self
.DefaultStores
)
1698 return (str(minvalue
), self
.DefaultStores
[str(minvalue
)])
1699 def GetMin(self
, DefaultSIdList
):
1700 if not DefaultSIdList
:
1701 return TAB_DEFAULT_STORES_DEFAULT
1702 storeidset
= {storeid
for storeid
, storename
in self
.DefaultStores
.values() if storename
in DefaultSIdList
}
1705 minid
= min(storeidset
)
1706 for sid
, name
in self
.DefaultStores
.values():
1715 def __init__(self
,SkuIdentifier
='', SkuIds
=None):
1719 for SkuName
in SkuIds
:
1720 SkuId
= SkuIds
[SkuName
][0]
1721 skuid_num
= int(SkuId
, 16) if SkuId
.upper().startswith("0X") else int(SkuId
)
1722 if skuid_num
> 0xFFFFFFFFFFFFFFFF:
1723 EdkLogger
.error("build", PARAMETER_INVALID
,
1724 ExtraData
= "SKU-ID [%s] value %s exceeds the max value of UINT64"
1727 self
.AvailableSkuIds
= OrderedDict()
1729 self
.SkuIdNumberSet
= []
1730 self
.SkuData
= SkuIds
1731 self
._SkuInherit
= {}
1732 self
._SkuIdentifier
= SkuIdentifier
1733 if SkuIdentifier
== '' or SkuIdentifier
is None:
1734 self
.SkuIdSet
= ['DEFAULT']
1735 self
.SkuIdNumberSet
= ['0U']
1736 elif SkuIdentifier
== 'ALL':
1737 self
.SkuIdSet
= list(SkuIds
.keys())
1738 self
.SkuIdNumberSet
= [num
[0].strip() + 'U' for num
in SkuIds
.values()]
1740 r
= SkuIdentifier
.split('|')
1741 self
.SkuIdSet
=[(r
[k
].strip()).upper() for k
in range(len(r
))]
1744 self
.SkuIdNumberSet
= [SkuIds
[k
][0].strip() + 'U' for k
in self
.SkuIdSet
]
1746 EdkLogger
.error("build", PARAMETER_INVALID
,
1747 ExtraData
= "SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1748 % (k
, " | ".join(SkuIds
.keys())))
1749 for each
in self
.SkuIdSet
:
1751 self
.AvailableSkuIds
[each
] = SkuIds
[each
][0]
1753 EdkLogger
.error("build", PARAMETER_INVALID
,
1754 ExtraData
="SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1755 % (each
, " | ".join(SkuIds
.keys())))
1756 if self
.SkuUsageType
!= SkuClass
.SINGLE
:
1757 self
.AvailableSkuIds
.update({'DEFAULT':0, 'COMMON':0})
1759 GlobalData
.gSkuids
= (self
.SkuIdSet
)
1760 if 'COMMON' in GlobalData
.gSkuids
:
1761 GlobalData
.gSkuids
.remove('COMMON')
1762 if self
.SkuUsageType
== self
.SINGLE
:
1763 if len(GlobalData
.gSkuids
) != 1:
1764 if 'DEFAULT' in GlobalData
.gSkuids
:
1765 GlobalData
.gSkuids
.remove('DEFAULT')
1766 if GlobalData
.gSkuids
:
1767 GlobalData
.gSkuids
.sort()
1769 def GetNextSkuId(self
, skuname
):
1770 if not self
._SkuInherit
:
1771 self
._SkuInherit
= {}
1772 for item
in self
.SkuData
.values():
1773 self
._SkuInherit
[item
[1]]=item
[2] if item
[2] else "DEFAULT"
1774 return self
._SkuInherit
.get(skuname
, "DEFAULT")
1776 def GetSkuChain(self
, sku
):
1777 if sku
== "DEFAULT":
1782 nextsku
= self
.GetNextSkuId(nextsku
)
1783 skulist
.append(nextsku
)
1784 if nextsku
== "DEFAULT":
1788 def SkuOverrideOrder(self
):
1790 for skuname
in self
.SkuIdSet
:
1791 skuorderset
.append(self
.GetSkuChain(skuname
))
1794 for index
in range(max(len(item
) for item
in skuorderset
)):
1795 for subset
in skuorderset
:
1796 if index
> len(subset
)-1:
1798 if subset
[index
] in skuorder
:
1800 skuorder
.append(subset
[index
])
1805 def SkuUsageType(self
):
1806 if self
._SkuIdentifier
.upper() == "ALL":
1807 return SkuClass
.MULTIPLE
1809 if len(self
.SkuIdSet
) == 1:
1810 if self
.SkuIdSet
[0] == 'DEFAULT':
1811 return SkuClass
.DEFAULT
1812 return SkuClass
.SINGLE
1813 if len(self
.SkuIdSet
) == 2 and 'DEFAULT' in self
.SkuIdSet
:
1814 return SkuClass
.SINGLE
1815 return SkuClass
.MULTIPLE
1817 def DumpSkuIdArrary(self
):
1818 if self
.SkuUsageType
== SkuClass
.SINGLE
:
1821 for skuname
in self
.AvailableSkuIds
:
1822 if skuname
== "COMMON":
1824 while skuname
!= "DEFAULT":
1825 ArrayStrList
.append(hex(int(self
.AvailableSkuIds
[skuname
])))
1826 skuname
= self
.GetNextSkuId(skuname
)
1827 ArrayStrList
.append("0x0")
1828 return "{{{myList}}}".format(myList
=",".join(ArrayStrList
))
1831 def AvailableSkuIdSet(self
):
1832 return self
.AvailableSkuIds
1835 def SystemSkuId(self
):
1836 if self
.SkuUsageType
== SkuClass
.SINGLE
:
1837 if len(self
.SkuIdSet
) == 1:
1838 return self
.SkuIdSet
[0]
1840 return self
.SkuIdSet
[0] if self
.SkuIdSet
[0] != 'DEFAULT' else self
.SkuIdSet
[1]
1844 ## Get the integer value from string like "14U" or integer like 2
1846 # @param Input The object that may be either a integer value or a string
1848 # @retval Value The integer value that the input represents
1850 def GetIntegerValue(Input
):
1851 if not isinstance(Input
, str):
1854 if String
.endswith("U"):
1855 String
= String
[:-1]
1856 if String
.endswith("ULL"):
1857 String
= String
[:-3]
1858 if String
.endswith("LL"):
1859 String
= String
[:-2]
1861 if String
.startswith("0x") or String
.startswith("0X"):
1862 return int(String
, 16)
1869 # Pack a GUID (registry format) list into a buffer and return it
1872 return pack(PACK_PATTERN_GUID
,
1876 int(Guid
[3][-4:-2], 16),
1877 int(Guid
[3][-2:], 16),
1878 int(Guid
[4][-12:-10], 16),
1879 int(Guid
[4][-10:-8], 16),
1880 int(Guid
[4][-8:-6], 16),
1881 int(Guid
[4][-6:-4], 16),
1882 int(Guid
[4][-4:-2], 16),
1883 int(Guid
[4][-2:], 16)
1887 # Pack a GUID (byte) list into a buffer and return it
1889 def PackByteFormatGUID(Guid
):
1890 return pack(PACK_PATTERN_GUID
,
1904 ## DeepCopy dict/OrderedDict recusively
1906 # @param ori_dict a nested dict or ordereddict
1908 # @retval new dict or orderdict
1910 def CopyDict(ori_dict
):
1911 dict_type
= ori_dict
.__class
__
1912 if dict_type
not in (dict,OrderedDict
):
1914 new_dict
= dict_type()
1915 for key
in ori_dict
:
1916 if isinstance(ori_dict
[key
],(dict,OrderedDict
)):
1917 new_dict
[key
] = CopyDict(ori_dict
[key
])
1919 new_dict
[key
] = ori_dict
[key
]
1923 # Remove the c/c++ comments: // and /* */
1925 def RemoveCComments(ctext
):
1926 return re
.sub('//.*?\n|/\*.*?\*/', '\n', ctext
, flags
=re
.S
)