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
44 ArrayIndex
= re
.compile("\[\s*[0-9a-fA-FxX]*\s*\]")
45 ## Regular expression used to find out place holders in string template
46 gPlaceholderPattern
= re
.compile("\$\{([^$()\s]+)\}", re
.MULTILINE | re
.UNICODE
)
48 ## regular expressions for map file processing
49 startPatternGeneral
= re
.compile("^Start[' ']+Length[' ']+Name[' ']+Class")
50 addressPatternGeneral
= re
.compile("^Address[' ']+Publics by Value[' ']+Rva\+Base")
51 valuePatternGcc
= re
.compile('^([\w_\.]+) +([\da-fA-Fx]+) +([\da-fA-Fx]+)$')
52 pcdPatternGcc
= re
.compile('^([\da-fA-Fx]+) +([\da-fA-Fx]+)')
53 secReGeneral
= re
.compile('^([\da-fA-F]+):([\da-fA-F]+) +([\da-fA-F]+)[Hh]? +([.\w\$]+) +(\w+)', re
.UNICODE
)
55 StructPattern
= re
.compile(r
'[_a-zA-Z][0-9A-Za-z_]*$')
57 ## Dictionary used to store dependencies of files
58 gDependencyDatabase
= {} # arch : {file path : [dependent files list]}
61 # If a module is built more than once with different PCDs or library classes
62 # a temporary INF file with same content is created, the temporary file is removed
67 def GetVariableOffset(mapfilepath
, efifilepath
, varnames
):
68 """ Parse map file to get variable offset in current EFI file
69 @param mapfilepath Map file absolution path
70 @param efifilepath: EFI binary file full path
71 @param varnames iteratable container whose elements are variable names to be searched
73 @return List whos elements are tuple with variable name and raw offset
77 f
= open(mapfilepath
, 'r')
83 if len(lines
) == 0: return None
84 firstline
= lines
[0].strip()
85 if re
.match('^\s*Address\s*Size\s*Align\s*Out\s*In\s*Symbol\s*$', firstline
):
86 return _parseForXcodeAndClang9(lines
, efifilepath
, varnames
)
87 if (firstline
.startswith("Archive member included ") and
88 firstline
.endswith(" file (symbol)")):
89 return _parseForGCC(lines
, efifilepath
, varnames
)
90 if firstline
.startswith("# Path:"):
91 return _parseForXcodeAndClang9(lines
, efifilepath
, varnames
)
92 return _parseGeneral(lines
, efifilepath
, varnames
)
94 def _parseForXcodeAndClang9(lines
, efifilepath
, varnames
):
99 if status
== 0 and (re
.match('^\s*Address\s*Size\s*Align\s*Out\s*In\s*Symbol\s*$', line
) \
100 or line
== "# Symbols:"):
103 if status
== 1 and len(line
) != 0:
104 for varname
in varnames
:
106 # cannot pregenerate this RegEx since it uses varname from varnames.
107 m
= re
.match('^([\da-fA-FxX]+)([\s\S]*)([_]*%s)$' % varname
, line
)
109 ret
.append((varname
, m
.group(1)))
112 def _parseForGCC(lines
, efifilepath
, varnames
):
113 """ Parse map file generated by GCC linker """
117 for index
, line
in enumerate(lines
):
119 # status machine transection
120 if status
== 0 and line
== "Memory Configuration":
123 elif status
== 1 and line
== 'Linker script and memory map':
126 elif status
==2 and line
== 'START GROUP':
132 m
= valuePatternGcc
.match(line
)
134 sections
.append(m
.groups(0))
135 for varname
in varnames
:
137 m
= re
.match("^.data.(%s)" % varname
, line
)
139 m
= re
.match(".data.(%s)$" % varname
, line
)
141 Str
= lines
[index
+ 1]
143 Str
= line
[len(".data.%s" % varname
):]
145 m
= pcdPatternGcc
.match(Str
.strip())
147 varoffset
.append((varname
, int(m
.groups(0)[0], 16), int(sections
[-1][1], 16), sections
[-1][0]))
151 # get section information from efi file
152 efisecs
= PeImageClass(efifilepath
).SectionHeaderList
153 if efisecs
is None or len(efisecs
) == 0:
157 for efisec
in efisecs
:
158 for section
in sections
:
159 if section
[0].strip() == efisec
[0].strip() and section
[0].strip() == '.text':
160 redirection
= int(section
[1], 16) - efisec
[1]
163 for var
in varoffset
:
164 for efisec
in efisecs
:
165 if var
[1] >= efisec
[1] and var
[1] < efisec
[1]+efisec
[3]:
166 ret
.append((var
[0], hex(efisec
[2] + var
[1] - efisec
[1] - redirection
)))
169 def _parseGeneral(lines
, efifilepath
, varnames
):
170 status
= 0 #0 - beginning of file; 1 - PE section definition; 2 - symbol table
171 secs
= [] # key = section name
173 symRe
= re
.compile('^([\da-fA-F]+):([\da-fA-F]+) +([\.:\\\\\w\?@\$-]+) +([\da-fA-F]+)', re
.UNICODE
)
177 if startPatternGeneral
.match(line
):
180 if addressPatternGeneral
.match(line
):
183 if line
.startswith("entry point at"):
186 if status
== 1 and len(line
) != 0:
187 m
= secReGeneral
.match(line
)
188 assert m
is not None, "Fail to parse the section in map file , line is %s" % line
189 sec_no
, sec_start
, sec_length
, sec_name
, sec_class
= m
.groups(0)
190 secs
.append([int(sec_no
, 16), int(sec_start
, 16), int(sec_length
, 16), sec_name
, sec_class
])
191 if status
== 2 and len(line
) != 0:
192 for varname
in varnames
:
193 m
= symRe
.match(line
)
194 assert m
is not None, "Fail to parse the symbol in map file, line is %s" % line
195 sec_no
, sym_offset
, sym_name
, vir_addr
= m
.groups(0)
196 sec_no
= int(sec_no
, 16)
197 sym_offset
= int(sym_offset
, 16)
198 vir_addr
= int(vir_addr
, 16)
199 # cannot pregenerate this RegEx since it uses varname from varnames.
200 m2
= re
.match('^[_]*(%s)' % varname
, sym_name
)
202 # fond a binary pcd entry in map file
204 if sec
[0] == sec_no
and (sym_offset
>= sec
[1] and sym_offset
< sec
[1] + sec
[2]):
205 varoffset
.append([varname
, sec
[3], sym_offset
, vir_addr
, sec_no
])
207 if not varoffset
: return []
209 # get section information from efi file
210 efisecs
= PeImageClass(efifilepath
).SectionHeaderList
211 if efisecs
is None or len(efisecs
) == 0:
215 for var
in varoffset
:
217 for efisec
in efisecs
:
219 if var
[1].strip() == efisec
[0].strip():
220 ret
.append((var
[0], hex(efisec
[2] + var
[2])))
221 elif var
[4] == index
:
222 ret
.append((var
[0], hex(efisec
[2] + var
[2])))
226 ## Routine to process duplicated INF
228 # This function is called by following two cases:
231 # Pkg/module/module.inf
232 # Pkg/module/module.inf {
234 # FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836
237 # INF Pkg/module/module.inf
238 # INF FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836 Pkg/module/module.inf
240 # This function copies Pkg/module/module.inf to
241 # Conf/.cache/0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf
243 # @param Path Original PathClass object
244 # @param BaseName New file base name
246 # @retval return the new PathClass object
248 def ProcessDuplicatedInf(Path
, BaseName
, Workspace
):
249 Filename
= os
.path
.split(Path
.File
)[1]
251 Filename
= BaseName
+ Path
.BaseName
+ Filename
[Filename
.rfind('.'):]
253 Filename
= BaseName
+ Path
.BaseName
255 DbDir
= os
.path
.split(GlobalData
.gDatabasePath
)[0]
258 # A temporary INF is copied to database path which must have write permission
259 # The temporary will be removed at the end of build
260 # In case of name conflict, the file name is
261 # FILE_GUIDBaseName (0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf)
263 TempFullPath
= os
.path
.join(DbDir
,
265 RtPath
= PathClass(Path
.File
, Workspace
)
267 # Modify the full path to temporary path, keep other unchanged
269 # To build same module more than once, the module path with FILE_GUID overridden has
270 # the file name FILE_GUIDmodule.inf, but the relative path (self.MetaFile.File) is the real path
271 # in DSC which is used as relative path by C files and other files in INF.
272 # A trick was used: all module paths are PathClass instances, after the initialization
273 # of PathClass, the PathClass.Path is overridden by the temporary INF path.
275 # The reason for creating a temporary INF is:
276 # Platform.Modules which is the base to create ModuleAutoGen objects is a dictionary,
277 # the key is the full path of INF, the value is an object to save overridden library instances, PCDs.
278 # A different key for the same module is needed to create different output directory,
279 # retrieve overridden PCDs, library instances.
281 # The BaseName is the FILE_GUID which is also the output directory name.
284 RtPath
.Path
= TempFullPath
285 RtPath
.BaseName
= BaseName
286 RtPath
.OriginalPath
= Path
288 # If file exists, compare contents
290 if os
.path
.exists(TempFullPath
):
291 with
open(str(Path
), 'rb') as f1
, open(TempFullPath
, 'rb') as f2
:
292 if f1
.read() == f2
.read():
294 _TempInfs
.append(TempFullPath
)
295 shutil
.copy2(str(Path
), TempFullPath
)
298 ## Remove temporary created INFs whose paths were saved in _TempInfs
300 def ClearDuplicatedInf():
302 File
= _TempInfs
.pop()
303 if os
.path
.exists(File
):
306 ## Convert GUID string in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx style to C structure style
308 # @param Guid The GUID string
310 # @retval string The GUID string in C structure style
312 def GuidStringToGuidStructureString(Guid
):
313 GuidList
= Guid
.split('-')
315 for Index
in range(0, 3, 1):
316 Result
= Result
+ '0x' + GuidList
[Index
] + ', '
317 Result
= Result
+ '{0x' + GuidList
[3][0:2] + ', 0x' + GuidList
[3][2:4]
318 for Index
in range(0, 12, 2):
319 Result
= Result
+ ', 0x' + GuidList
[4][Index
:Index
+ 2]
323 ## Convert GUID structure in byte array to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
325 # @param GuidValue The GUID value in byte array
327 # @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
329 def GuidStructureByteArrayToGuidString(GuidValue
):
330 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
331 guidValueList
= guidValueString
.split(",")
332 if len(guidValueList
) != 16:
334 #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
336 return "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
337 int(guidValueList
[3], 16),
338 int(guidValueList
[2], 16),
339 int(guidValueList
[1], 16),
340 int(guidValueList
[0], 16),
341 int(guidValueList
[5], 16),
342 int(guidValueList
[4], 16),
343 int(guidValueList
[7], 16),
344 int(guidValueList
[6], 16),
345 int(guidValueList
[8], 16),
346 int(guidValueList
[9], 16),
347 int(guidValueList
[10], 16),
348 int(guidValueList
[11], 16),
349 int(guidValueList
[12], 16),
350 int(guidValueList
[13], 16),
351 int(guidValueList
[14], 16),
352 int(guidValueList
[15], 16)
357 ## Convert GUID string in C structure style to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
359 # @param GuidValue The GUID value in C structure format
361 # @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
363 def GuidStructureStringToGuidString(GuidValue
):
364 if not GlobalData
.gGuidCFormatPattern
.match(GuidValue
):
366 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
367 guidValueList
= guidValueString
.split(",")
368 if len(guidValueList
) != 11:
370 #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
372 return "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
373 int(guidValueList
[0], 16),
374 int(guidValueList
[1], 16),
375 int(guidValueList
[2], 16),
376 int(guidValueList
[3], 16),
377 int(guidValueList
[4], 16),
378 int(guidValueList
[5], 16),
379 int(guidValueList
[6], 16),
380 int(guidValueList
[7], 16),
381 int(guidValueList
[8], 16),
382 int(guidValueList
[9], 16),
383 int(guidValueList
[10], 16)
388 ## Convert GUID string in C structure style to xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx
390 # @param GuidValue The GUID value in C structure format
392 # @retval string The GUID value in xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx format
394 def GuidStructureStringToGuidValueName(GuidValue
):
395 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "")
396 guidValueList
= guidValueString
.split(",")
397 if len(guidValueList
) != 11:
398 EdkLogger
.error(None, FORMAT_INVALID
, "Invalid GUID value string [%s]" % GuidValue
)
399 return "%08x_%04x_%04x_%02x%02x_%02x%02x%02x%02x%02x%02x" % (
400 int(guidValueList
[0], 16),
401 int(guidValueList
[1], 16),
402 int(guidValueList
[2], 16),
403 int(guidValueList
[3], 16),
404 int(guidValueList
[4], 16),
405 int(guidValueList
[5], 16),
406 int(guidValueList
[6], 16),
407 int(guidValueList
[7], 16),
408 int(guidValueList
[8], 16),
409 int(guidValueList
[9], 16),
410 int(guidValueList
[10], 16)
413 ## Create directories
415 # @param Directory The directory name
417 def CreateDirectory(Directory
):
418 if Directory
is None or Directory
.strip() == "":
421 if not os
.access(Directory
, os
.F_OK
):
422 os
.makedirs(Directory
)
427 ## Remove directories, including files and sub-directories in it
429 # @param Directory The directory name
431 def RemoveDirectory(Directory
, Recursively
=False):
432 if Directory
is None or Directory
.strip() == "" or not os
.path
.exists(Directory
):
435 CurrentDirectory
= os
.getcwd()
437 for File
in os
.listdir("."):
438 if os
.path
.isdir(File
):
439 RemoveDirectory(File
, Recursively
)
442 os
.chdir(CurrentDirectory
)
445 ## Store content in file
447 # This method is used to save file only when its content is changed. This is
448 # quite useful for "make" system to decide what will be re-built and what won't.
450 # @param File The path of file
451 # @param Content The new content of the file
452 # @param IsBinaryFile The flag indicating if the file is binary file or not
454 # @retval True If the file content is changed and the file is renewed
455 # @retval False If the file content is the same
457 def SaveFileOnChange(File
, Content
, IsBinaryFile
=True, FileLock
=None):
459 # Convert to long file path format
460 File
= LongFilePath(File
)
462 if os
.path
.exists(File
):
465 with
open(File
, "rb") as f
:
466 if Content
== f
.read():
469 EdkLogger
.error(None, FILE_OPEN_FAILURE
, ExtraData
=File
)
472 with
open(File
, "r") as f
:
473 if Content
== f
.read():
476 EdkLogger
.error(None, FILE_OPEN_FAILURE
, ExtraData
=File
)
478 DirName
= os
.path
.dirname(File
)
479 if not CreateDirectory(DirName
):
480 EdkLogger
.error(None, FILE_CREATE_FAILURE
, "Could not create directory %s" % DirName
)
483 DirName
= os
.getcwd()
484 if not os
.access(DirName
, os
.W_OK
):
485 EdkLogger
.error(None, PERMISSION_FAILURE
, "Do not have write permission on directory %s" % DirName
)
491 # use default file_lock if no input new lock
493 FileLock
= GlobalData
.file_lock
498 if GlobalData
.gIsWindows
and not os
.path
.exists(File
):
500 with
open(File
, OpenMode
) as tf
:
503 if GlobalData
.gBinCacheSource
:
504 EdkLogger
.quiet("[cache error]:fails to save file with error: %s" % (X
))
506 EdkLogger
.error(None, FILE_CREATE_FAILURE
, ExtraData
='IOError %s' % X
)
512 with
open(File
, OpenMode
) as Fd
:
515 if GlobalData
.gBinCacheSource
:
516 EdkLogger
.quiet("[cache error]:fails to save file with error: %s" % (X
))
518 EdkLogger
.error(None, FILE_CREATE_FAILURE
, ExtraData
='IOError %s' % X
)
525 ## Copy source file only if it is different from the destination file
527 # This method is used to copy file only if the source file and destination
528 # file content are different. This is quite useful to avoid duplicated
531 # @param SrcFile The path of source file
532 # @param Dst The path of destination file or folder
534 # @retval True The two files content are different and the file is copied
535 # @retval False No copy really happen
537 def CopyFileOnChange(SrcFile
, Dst
, FileLock
=None):
539 # Convert to long file path format
540 SrcFile
= LongFilePath(SrcFile
)
541 Dst
= LongFilePath(Dst
)
543 if os
.path
.isdir(SrcFile
):
544 EdkLogger
.error(None, FILE_COPY_FAILURE
, ExtraData
='CopyFileOnChange SrcFile is a dir, not a file: %s' % SrcFile
)
547 if os
.path
.isdir(Dst
):
548 DstFile
= os
.path
.join(Dst
, os
.path
.basename(SrcFile
))
552 if os
.path
.exists(DstFile
) and filecmp
.cmp(SrcFile
, DstFile
, shallow
=False):
555 DirName
= os
.path
.dirname(DstFile
)
556 if not CreateDirectory(DirName
):
557 EdkLogger
.error(None, FILE_CREATE_FAILURE
, "Could not create directory %s" % DirName
)
560 DirName
= os
.getcwd()
561 if not os
.access(DirName
, os
.W_OK
):
562 EdkLogger
.error(None, PERMISSION_FAILURE
, "Do not have write permission on directory %s" % DirName
)
564 # use default file_lock if no input new lock
566 FileLock
= GlobalData
.file_lock
571 CopyLong(SrcFile
, DstFile
)
573 if GlobalData
.gBinCacheSource
:
574 EdkLogger
.quiet("[cache error]:fails to copy file with error: %s" % (X
))
576 EdkLogger
.error(None, FILE_COPY_FAILURE
, ExtraData
='IOError %s' % X
)
583 ## Retrieve and cache the real path name in file system
585 # @param Root The root directory of path relative to
587 # @retval str The path string if the path exists
588 # @retval None If path doesn't exist
594 def __init__(self
, Root
):
596 for F
in os
.listdir(Root
):
598 self
._UPPER
_CACHE
_[F
.upper()] = F
601 def __getitem__(self
, Path
):
602 Path
= Path
[len(os
.path
.commonprefix([Path
, self
._Root
])):]
605 if Path
and Path
[0] == os
.path
.sep
:
607 if Path
in self
._CACHE
_:
608 return os
.path
.join(self
._Root
, Path
)
609 UpperPath
= Path
.upper()
610 if UpperPath
in self
._UPPER
_CACHE
_:
611 return os
.path
.join(self
._Root
, self
._UPPER
_CACHE
_[UpperPath
])
615 SepIndex
= Path
.find(os
.path
.sep
)
617 Parent
= UpperPath
[:SepIndex
]
618 if Parent
not in self
._UPPER
_CACHE
_:
620 LastSepIndex
= SepIndex
621 SepIndex
= Path
.find(os
.path
.sep
, LastSepIndex
+ 1)
623 if LastSepIndex
== -1:
628 SepIndex
= LastSepIndex
630 Parent
= Path
[:SepIndex
]
631 ParentKey
= UpperPath
[:SepIndex
]
632 if ParentKey
not in self
._UPPER
_CACHE
_:
636 if Parent
in self
._CACHE
_:
639 ParentDir
= self
._UPPER
_CACHE
_[ParentKey
]
640 for F
in os
.listdir(ParentDir
):
641 Dir
= os
.path
.join(ParentDir
, F
)
642 self
._CACHE
_.add(Dir
)
643 self
._UPPER
_CACHE
_[Dir
.upper()] = Dir
645 SepIndex
= Path
.find(os
.path
.sep
, SepIndex
+ 1)
648 if Path
in self
._CACHE
_:
649 return os
.path
.join(self
._Root
, Path
)
650 elif UpperPath
in self
._UPPER
_CACHE
_:
651 return os
.path
.join(self
._Root
, self
._UPPER
_CACHE
_[UpperPath
])
654 def RealPath(File
, Dir
='', OverrideDir
=''):
655 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
656 NewFile
= GlobalData
.gAllFiles
[NewFile
]
657 if not NewFile
and OverrideDir
:
658 NewFile
= os
.path
.normpath(os
.path
.join(OverrideDir
, File
))
659 NewFile
= GlobalData
.gAllFiles
[NewFile
]
662 ## Get GUID value from given packages
664 # @param CName The CName of the GUID
665 # @param PackageList List of packages looking-up in
666 # @param Inffile The driver file
668 # @retval GuidValue if the CName is found in any given package
669 # @retval None if the CName is not found in all given packages
671 def GuidValue(CName
, PackageList
, Inffile
= None):
672 for P
in PackageList
:
673 GuidKeys
= list(P
.Guids
.keys())
674 if Inffile
and P
._PrivateGuids
:
675 if not Inffile
.startswith(P
.MetaFile
.Dir
):
676 GuidKeys
= [x
for x
in P
.Guids
if x
not in P
._PrivateGuids
]
677 if CName
in GuidKeys
:
678 return P
.Guids
[CName
]
681 ## A string template class
683 # This class implements a template for string replacement. A string template
684 # looks like following
686 # ${BEGIN} other_string ${placeholder_name} other_string ${END}
688 # The string between ${BEGIN} and ${END} will be repeated as many times as the
689 # length of "placeholder_name", which is a list passed through a dict. The
690 # "placeholder_name" is the key name of the dict. The ${BEGIN} and ${END} can
691 # be not used and, in this case, the "placeholder_name" must not a list and it
692 # will just be replaced once.
694 class TemplateString(object):
695 _REPEAT_START_FLAG
= "BEGIN"
696 _REPEAT_END_FLAG
= "END"
698 class Section(object):
699 _LIST_TYPES
= [type([]), type(set()), type((0,))]
701 def __init__(self
, TemplateSection
, PlaceHolderList
):
702 self
._Template
= TemplateSection
703 self
._PlaceHolderList
= []
705 # Split the section into sub-sections according to the position of placeholders
707 self
._SubSectionList
= []
710 # The placeholders passed in must be in the format of
712 # PlaceHolderName, PlaceHolderStartPoint, PlaceHolderEndPoint
714 for PlaceHolder
, Start
, End
in PlaceHolderList
:
715 self
._SubSectionList
.append(TemplateSection
[SubSectionStart
:Start
])
716 self
._SubSectionList
.append(TemplateSection
[Start
:End
])
717 self
._PlaceHolderList
.append(PlaceHolder
)
718 SubSectionStart
= End
719 if SubSectionStart
< len(TemplateSection
):
720 self
._SubSectionList
.append(TemplateSection
[SubSectionStart
:])
722 self
._SubSectionList
= [TemplateSection
]
725 return self
._Template
+ " : " + str(self
._PlaceHolderList
)
727 def Instantiate(self
, PlaceHolderValues
):
729 RepeatPlaceHolders
= {}
730 NonRepeatPlaceHolders
= {}
732 for PlaceHolder
in self
._PlaceHolderList
:
733 if PlaceHolder
not in PlaceHolderValues
:
735 Value
= PlaceHolderValues
[PlaceHolder
]
736 if type(Value
) in self
._LIST
_TYPES
:
738 RepeatTime
= len(Value
)
739 elif RepeatTime
!= len(Value
):
743 "${%s} has different repeat time from others!" % PlaceHolder
,
744 ExtraData
=str(self
._Template
)
746 RepeatPlaceHolders
["${%s}" % PlaceHolder
] = Value
748 NonRepeatPlaceHolders
["${%s}" % PlaceHolder
] = Value
750 if NonRepeatPlaceHolders
:
752 for S
in self
._SubSectionList
:
753 if S
not in NonRepeatPlaceHolders
:
756 StringList
.append(str(NonRepeatPlaceHolders
[S
]))
758 StringList
= self
._SubSectionList
760 if RepeatPlaceHolders
:
762 for Index
in range(RepeatTime
):
764 if S
not in RepeatPlaceHolders
:
765 TempStringList
.append(S
)
767 TempStringList
.append(str(RepeatPlaceHolders
[S
][Index
]))
768 StringList
= TempStringList
770 return "".join(StringList
)
773 def __init__(self
, Template
=None):
775 self
.IsBinary
= False
776 self
._Template
= Template
777 self
._TemplateSectionList
= self
._Parse
(Template
)
781 # @retval string The string replaced
784 return "".join(self
.String
)
786 ## Split the template string into fragments per the ${BEGIN} and ${END} flags
788 # @retval list A list of TemplateString.Section objects
790 def _Parse(self
, Template
):
795 TemplateSectionList
= []
797 MatchObj
= gPlaceholderPattern
.search(Template
, SearchFrom
)
799 if MatchEnd
<= len(Template
):
800 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:], PlaceHolderList
)
801 TemplateSectionList
.append(TemplateSection
)
804 MatchString
= MatchObj
.group(1)
805 MatchStart
= MatchObj
.start()
806 MatchEnd
= MatchObj
.end()
808 if MatchString
== self
._REPEAT
_START
_FLAG
:
809 if MatchStart
> SectionStart
:
810 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:MatchStart
], PlaceHolderList
)
811 TemplateSectionList
.append(TemplateSection
)
812 SectionStart
= MatchEnd
814 elif MatchString
== self
._REPEAT
_END
_FLAG
:
815 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:MatchStart
], PlaceHolderList
)
816 TemplateSectionList
.append(TemplateSection
)
817 SectionStart
= MatchEnd
820 PlaceHolderList
.append((MatchString
, MatchStart
- SectionStart
, MatchEnd
- SectionStart
))
821 SearchFrom
= MatchEnd
822 return TemplateSectionList
824 ## Replace the string template with dictionary of placeholders and append it to previous one
826 # @param AppendString The string template to append
827 # @param Dictionary The placeholder dictionaries
829 def Append(self
, AppendString
, Dictionary
=None):
831 SectionList
= self
._Parse
(AppendString
)
832 self
.String
.append( "".join(S
.Instantiate(Dictionary
) for S
in SectionList
))
834 if isinstance(AppendString
,list):
835 self
.String
.extend(AppendString
)
837 self
.String
.append(AppendString
)
839 ## Replace the string template with dictionary of placeholders
841 # @param Dictionary The placeholder dictionaries
843 # @retval str The string replaced with placeholder values
845 def Replace(self
, Dictionary
=None):
846 return "".join(S
.Instantiate(Dictionary
) for S
in self
._TemplateSectionList
)
848 ## Progress indicator class
850 # This class makes use of thread to print progress on console.
853 # for avoiding deadloop
855 _ProgressThread
= None
856 _CheckInterval
= 0.25
860 # @param OpenMessage The string printed before progress characters
861 # @param CloseMessage The string printed after progress characters
862 # @param ProgressChar The character used to indicate the progress
863 # @param Interval The interval in seconds between two progress characters
865 def __init__(self
, OpenMessage
="", CloseMessage
="", ProgressChar
='.', Interval
=1.0):
866 self
.PromptMessage
= OpenMessage
867 self
.CodaMessage
= CloseMessage
868 self
.ProgressChar
= ProgressChar
869 self
.Interval
= Interval
870 if Progressor
._StopFlag
is None:
871 Progressor
._StopFlag
= threading
.Event()
873 ## Start to print progress character
875 # @param OpenMessage The string printed before progress characters
877 def Start(self
, OpenMessage
=None):
878 if OpenMessage
is not None:
879 self
.PromptMessage
= OpenMessage
880 Progressor
._StopFlag
.clear()
881 if Progressor
._ProgressThread
is None:
882 Progressor
._ProgressThread
= threading
.Thread(target
=self
._ProgressThreadEntry
)
883 Progressor
._ProgressThread
.setDaemon(False)
884 Progressor
._ProgressThread
.start()
886 ## Stop printing progress character
888 # @param CloseMessage The string printed after progress characters
890 def Stop(self
, CloseMessage
=None):
891 OriginalCodaMessage
= self
.CodaMessage
892 if CloseMessage
is not None:
893 self
.CodaMessage
= CloseMessage
895 self
.CodaMessage
= OriginalCodaMessage
897 ## Thread entry method
898 def _ProgressThreadEntry(self
):
899 sys
.stdout
.write(self
.PromptMessage
+ " ")
902 while not Progressor
._StopFlag
.isSet():
904 sys
.stdout
.write(self
.ProgressChar
)
906 TimeUp
= self
.Interval
907 time
.sleep(self
._CheckInterval
)
908 TimeUp
-= self
._CheckInterval
909 sys
.stdout
.write(" " + self
.CodaMessage
+ "\n")
912 ## Abort the progress display
915 if Progressor
._StopFlag
is not None:
916 Progressor
._StopFlag
.set()
917 if Progressor
._ProgressThread
is not None:
918 Progressor
._ProgressThread
.join()
919 Progressor
._ProgressThread
= None
922 ## Dictionary using prioritized list as key
926 _TupleType
= type(())
928 _ValidWildcardList
= ['COMMON', 'DEFAULT', 'ALL', TAB_STAR
, 'PLATFORM']
930 def __init__(self
, _Single_
=False, _Level_
=2):
931 self
._Level
_ = _Level_
933 self
._Single
_ = _Single_
936 def __getitem__(self
, key
):
939 if KeyType
== self
._ListType
or KeyType
== self
._TupleType
:
943 elif self
._Level
_ > 1:
944 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
948 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
950 if FirstKey
is None or str(FirstKey
).upper() in self
._ValidWildcardList
:
951 FirstKey
= self
._Wildcard
954 return self
._GetSingleValue
(FirstKey
, RestKeys
)
956 return self
._GetAllValues
(FirstKey
, RestKeys
)
958 def _GetSingleValue(self
, FirstKey
, RestKeys
):
960 #print "%s-%s" % (FirstKey, self._Level_) ,
962 if FirstKey
== self
._Wildcard
:
963 if FirstKey
in self
.data
:
964 Value
= self
.data
[FirstKey
][RestKeys
]
966 for Key
in self
.data
:
967 Value
= self
.data
[Key
][RestKeys
]
968 if Value
is not None: break
970 if FirstKey
in self
.data
:
971 Value
= self
.data
[FirstKey
][RestKeys
]
972 if Value
is None and self
._Wildcard
in self
.data
:
974 Value
= self
.data
[self
._Wildcard
][RestKeys
]
976 if FirstKey
== self
._Wildcard
:
977 if FirstKey
in self
.data
:
978 Value
= self
.data
[FirstKey
]
980 for Key
in self
.data
:
981 Value
= self
.data
[Key
]
982 if Value
is not None: break
984 if FirstKey
in self
.data
:
985 Value
= self
.data
[FirstKey
]
986 elif self
._Wildcard
in self
.data
:
987 Value
= self
.data
[self
._Wildcard
]
990 def _GetAllValues(self
, FirstKey
, RestKeys
):
993 if FirstKey
== self
._Wildcard
:
994 for Key
in self
.data
:
995 Value
+= self
.data
[Key
][RestKeys
]
997 if FirstKey
in self
.data
:
998 Value
+= self
.data
[FirstKey
][RestKeys
]
999 if self
._Wildcard
in self
.data
:
1000 Value
+= self
.data
[self
._Wildcard
][RestKeys
]
1002 if FirstKey
== self
._Wildcard
:
1003 for Key
in self
.data
:
1004 Value
.append(self
.data
[Key
])
1006 if FirstKey
in self
.data
:
1007 Value
.append(self
.data
[FirstKey
])
1008 if self
._Wildcard
in self
.data
:
1009 Value
.append(self
.data
[self
._Wildcard
])
1013 def __setitem__(self
, key
, value
):
1016 if KeyType
== self
._ListType
or KeyType
== self
._TupleType
:
1021 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
1024 if self
._Level
_ > 1:
1025 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
1027 if FirstKey
in self
._ValidWildcardList
:
1028 FirstKey
= self
._Wildcard
1030 if FirstKey
not in self
.data
and self
._Level
_ > 0:
1031 self
.data
[FirstKey
] = tdict(self
._Single
_, self
._Level
_ - 1)
1033 if self
._Level
_ > 1:
1034 self
.data
[FirstKey
][RestKeys
] = value
1036 self
.data
[FirstKey
] = value
1038 def SetGreedyMode(self
):
1039 self
._Single
_ = False
1040 if self
._Level
_ > 1:
1041 for Key
in self
.data
:
1042 self
.data
[Key
].SetGreedyMode()
1044 def SetSingleMode(self
):
1045 self
._Single
_ = True
1046 if self
._Level
_ > 1:
1047 for Key
in self
.data
:
1048 self
.data
[Key
].SetSingleMode()
1050 def GetKeys(self
, KeyIndex
=0):
1051 assert KeyIndex
>= 0
1053 return set(self
.data
.keys())
1056 for Key
in self
.data
:
1057 keys |
= self
.data
[Key
].GetKeys(KeyIndex
- 1)
1060 def AnalyzePcdExpression(Setting
):
1061 RanStr
= ''.join(sample(string
.ascii_letters
+ string
.digits
, 8))
1062 Setting
= Setting
.replace('\\\\', RanStr
).strip()
1063 # There might be escaped quote in a string: \", \\\" , \', \\\'
1065 # There might be '|' in string and in ( ... | ... ), replace it with '-'
1067 InSingleQuoteStr
= False
1068 InDoubleQuoteStr
= False
1070 for Index
, ch
in enumerate(Data
):
1071 if ch
== '"' and not InSingleQuoteStr
:
1072 if Data
[Index
- 1] != '\\':
1073 InDoubleQuoteStr
= not InDoubleQuoteStr
1074 elif ch
== "'" and not InDoubleQuoteStr
:
1075 if Data
[Index
- 1] != '\\':
1076 InSingleQuoteStr
= not InSingleQuoteStr
1077 elif ch
== '(' and not (InSingleQuoteStr
or InDoubleQuoteStr
):
1079 elif ch
== ')' and not (InSingleQuoteStr
or InDoubleQuoteStr
):
1082 if (Pair
> 0 or InSingleQuoteStr
or InDoubleQuoteStr
) and ch
== TAB_VALUE_SPLIT
:
1089 Pos
= NewStr
.find(TAB_VALUE_SPLIT
, StartPos
)
1091 FieldList
.append(Setting
[StartPos
:].strip())
1093 FieldList
.append(Setting
[StartPos
:Pos
].strip())
1095 for i
, ch
in enumerate(FieldList
):
1097 FieldList
[i
] = ch
.replace(RanStr
,'\\\\')
1100 def ParseFieldValue (Value
):
1101 def ParseDevPathValue (Value
):
1103 Value
.replace('\\', '/').replace(' ', '')
1105 Cmd
= 'DevicePath ' + '"' + Value
+ '"'
1107 p
= subprocess
.Popen(Cmd
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, shell
=True)
1108 out
, err
= p
.communicate()
1109 except Exception as X
:
1110 raise BadExpression("DevicePath: %s" % (str(X
)) )
1112 subprocess
._cleanup
()
1116 raise BadExpression("DevicePath: %s" % str(err
))
1118 Size
= len(out
.split())
1119 out
= ','.join(out
.split())
1120 return '{' + out
+ '}', Size
1122 if "{CODE(" in Value
:
1123 return Value
, len(Value
.split(","))
1124 if isinstance(Value
, type(0)):
1125 return Value
, (Value
.bit_length() + 7) // 8
1126 if not isinstance(Value
, type('')):
1127 raise BadExpression('Type %s is %s' %(Value
, type(Value
)))
1128 Value
= Value
.strip()
1129 if Value
.startswith(TAB_UINT8
) and Value
.endswith(')'):
1130 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1132 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1134 if Value
.startswith(TAB_UINT16
) and Value
.endswith(')'):
1135 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1137 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1139 if Value
.startswith(TAB_UINT32
) and Value
.endswith(')'):
1140 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1142 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1144 if Value
.startswith(TAB_UINT64
) and Value
.endswith(')'):
1145 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1147 raise BadExpression('Value (%s) Size larger than %d' % (Value
, Size
))
1149 if Value
.startswith(TAB_GUID
) and Value
.endswith(')'):
1150 Value
= Value
.split('(', 1)[1][:-1].strip()
1151 if Value
[0] == '{' and Value
[-1] == '}':
1152 TmpValue
= GuidStructureStringToGuidString(Value
)
1154 raise BadExpression("Invalid GUID value string %s" % Value
)
1156 if Value
[0] == '"' and Value
[-1] == '"':
1159 Value
= uuid
.UUID(Value
).bytes_le
1160 ValueL
, ValueH
= struct
.unpack('2Q', Value
)
1161 Value
= (ValueH
<< 64 ) | ValueL
1163 except ValueError as Message
:
1164 raise BadExpression(Message
)
1166 if Value
.startswith('L"') and Value
.endswith('"'):
1168 # translate escape character
1178 Value
= (Value
<< 16) |
ord(Char
)
1179 return Value
, (len(List
) + 1) * 2
1180 if Value
.startswith('"') and Value
.endswith('"'):
1182 # translate escape character
1191 Value
= (Value
<< 8) |
ord(Char
)
1192 return Value
, len(List
) + 1
1193 if Value
.startswith("L'") and Value
.endswith("'"):
1194 # Unicode Character Constant
1195 # translate escape character
1203 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1207 Value
= (Value
<< 16) |
ord(Char
)
1208 return Value
, len(List
) * 2
1209 if Value
.startswith("'") and Value
.endswith("'"):
1210 # Character constant
1211 # translate escape character
1218 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1222 Value
= (Value
<< 8) |
ord(Char
)
1223 return Value
, len(List
)
1224 if Value
.startswith('{') and Value
.endswith('}'):
1227 List
= [Item
.strip() for Item
in Value
.split(',')]
1232 ItemValue
, Size
= ParseFieldValue(Item
)
1234 for I
in range(Size
):
1235 Value
= (Value
<< 8) |
((ItemValue
>> 8 * I
) & 0xff)
1236 return Value
, RetSize
1237 if Value
.startswith('DEVICE_PATH(') and Value
.endswith(')'):
1238 Value
= Value
.replace("DEVICE_PATH(", '').rstrip(')')
1239 Value
= Value
.strip().strip('"')
1240 return ParseDevPathValue(Value
)
1241 if Value
.lower().startswith('0x'):
1243 Value
= int(Value
, 16)
1245 raise BadExpression("invalid hex value: %s" % Value
)
1248 return Value
, (Value
.bit_length() + 7) // 8
1249 if Value
[0].isdigit():
1250 Value
= int(Value
, 10)
1253 return Value
, (Value
.bit_length() + 7) // 8
1254 if Value
.lower() == 'true':
1256 if Value
.lower() == 'false':
1262 # Analyze DSC PCD value, since there is no data type info in DSC
1263 # This function is used to match functions (AnalyzePcdData) used for retrieving PCD value from database
1264 # 1. Feature flag: TokenSpace.PcdCName|PcdValue
1265 # 2. Fix and Patch:TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1266 # 3. Dynamic default:
1267 # TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1268 # TokenSpace.PcdCName|PcdValue
1270 # TokenSpace.PcdCName|VpdOffset[|VpdValue]
1271 # TokenSpace.PcdCName|VpdOffset[|MaxSize[|VpdValue]]
1273 # TokenSpace.PcdCName|HiiString|VariableGuid|VariableOffset[|HiiValue]
1274 # PCD value needs to be located in such kind of string, and the PCD value might be an expression in which
1275 # there might have "|" operator, also in string value.
1277 # @param Setting: String contain information described above with "TokenSpace.PcdCName|" stripped
1278 # @param PcdType: PCD type: feature, fixed, dynamic default VPD HII
1279 # @param DataType: The datum type of PCD: VOID*, UNIT, BOOL
1281 # ValueList: A List contain fields described above
1282 # IsValid: True if conforming EBNF, otherwise False
1283 # Index: The index where PcdValue is in ValueList
1285 def AnalyzeDscPcd(Setting
, PcdType
, DataType
=''):
1286 FieldList
= AnalyzePcdExpression(Setting
)
1289 if PcdType
in (MODEL_PCD_FIXED_AT_BUILD
, MODEL_PCD_PATCHABLE_IN_MODULE
, MODEL_PCD_DYNAMIC_DEFAULT
, MODEL_PCD_DYNAMIC_EX_DEFAULT
):
1290 Value
= FieldList
[0]
1292 if len(FieldList
) > 1 and FieldList
[1]:
1293 DataType
= FieldList
[1]
1294 if FieldList
[1] != TAB_VOID
and StructPattern
.match(FieldList
[1]) is None:
1296 if len(FieldList
) > 2:
1300 IsValid
= (len(FieldList
) <= 1)
1302 IsValid
= (len(FieldList
) <= 3)
1306 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1310 return [str(Value
), DataType
, str(Size
)], IsValid
, 0
1311 elif PcdType
== MODEL_PCD_FEATURE_FLAG
:
1312 Value
= FieldList
[0]
1314 IsValid
= (len(FieldList
) <= 1)
1315 return [Value
, DataType
, str(Size
)], IsValid
, 0
1316 elif PcdType
in (MODEL_PCD_DYNAMIC_VPD
, MODEL_PCD_DYNAMIC_EX_VPD
):
1317 VpdOffset
= FieldList
[0]
1319 if not DataType
== TAB_VOID
:
1320 if len(FieldList
) > 1:
1321 Value
= FieldList
[1]
1323 if len(FieldList
) > 1:
1325 if len(FieldList
) > 2:
1326 Value
= FieldList
[2]
1328 IsValid
= (len(FieldList
) <= 1)
1330 IsValid
= (len(FieldList
) <= 3)
1333 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1337 return [VpdOffset
, str(Size
), Value
], IsValid
, 2
1338 elif PcdType
in (MODEL_PCD_DYNAMIC_HII
, MODEL_PCD_DYNAMIC_EX_HII
):
1339 IsValid
= (3 <= len(FieldList
) <= 5)
1340 HiiString
= FieldList
[0]
1341 Guid
= Offset
= Value
= Attribute
= ''
1342 if len(FieldList
) > 1:
1344 if len(FieldList
) > 2:
1345 Offset
= FieldList
[2]
1346 if len(FieldList
) > 3:
1347 Value
= FieldList
[3]
1348 if len(FieldList
) > 4:
1349 Attribute
= FieldList
[4]
1350 return [HiiString
, Guid
, Offset
, Value
, Attribute
], IsValid
, 3
1355 # Analyze the pcd Value, Datum type and TokenNumber.
1356 # Used to avoid split issue while the value string contain "|" character
1358 # @param[in] Setting: A String contain value/datum type/token number information;
1360 # @retval ValueList: A List contain value, datum type and toke number.
1362 def AnalyzePcdData(Setting
):
1363 ValueList
= ['', '', '']
1365 ValueRe
= re
.compile(r
'^\s*L?\".*\|.*\"')
1366 PtrValue
= ValueRe
.findall(Setting
)
1368 ValueUpdateFlag
= False
1370 if len(PtrValue
) >= 1:
1371 Setting
= re
.sub(ValueRe
, '', Setting
)
1372 ValueUpdateFlag
= True
1374 TokenList
= Setting
.split(TAB_VALUE_SPLIT
)
1375 ValueList
[0:len(TokenList
)] = TokenList
1378 ValueList
[0] = PtrValue
[0]
1382 ## check format of PCD value against its the datum type
1384 # For PCD value setting
1386 def CheckPcdDatum(Type
, Value
):
1387 if Type
== TAB_VOID
:
1388 ValueRe
= re
.compile(r
'\s*L?\".*\"\s*$')
1389 if not (((Value
.startswith('L"') or Value
.startswith('"')) and Value
.endswith('"'))
1390 or (Value
.startswith('{') and Value
.endswith('}')) or (Value
.startswith("L'") or Value
.startswith("'") and Value
.endswith("'"))
1392 return False, "Invalid value [%s] of type [%s]; must be in the form of {...} for array"\
1393 ", \"...\" or \'...\' for string, L\"...\" or L\'...\' for unicode string" % (Value
, Type
)
1394 elif ValueRe
.match(Value
):
1395 # Check the chars in UnicodeString or CString is printable
1396 if Value
.startswith("L"):
1400 Printset
= set(string
.printable
)
1401 Printset
.remove(TAB_PRINTCHAR_VT
)
1402 Printset
.add(TAB_PRINTCHAR_BS
)
1403 Printset
.add(TAB_PRINTCHAR_NUL
)
1404 if not set(Value
).issubset(Printset
):
1405 PrintList
= sorted(Printset
)
1406 return False, "Invalid PCD string value of type [%s]; must be printable chars %s." % (Type
, PrintList
)
1407 elif Type
== 'BOOLEAN':
1408 if Value
not in ['TRUE', 'True', 'true', '0x1', '0x01', '1', 'FALSE', 'False', 'false', '0x0', '0x00', '0']:
1409 return False, "Invalid value [%s] of type [%s]; must be one of TRUE, True, true, 0x1, 0x01, 1"\
1410 ", FALSE, False, false, 0x0, 0x00, 0" % (Value
, Type
)
1411 elif Type
in [TAB_UINT8
, TAB_UINT16
, TAB_UINT32
, TAB_UINT64
]:
1412 if Value
.startswith('0') and not Value
.lower().startswith('0x') and len(Value
) > 1 and Value
.lstrip('0'):
1413 Value
= Value
.lstrip('0')
1415 if Value
and int(Value
, 0) < 0:
1416 return False, "PCD can't be set to negative value[%s] for datum type [%s]" % (Value
, Type
)
1417 Value
= int(Value
, 0)
1418 if Value
> MAX_VAL_TYPE
[Type
]:
1419 return False, "Too large PCD value[%s] for datum type [%s]" % (Value
, Type
)
1421 return False, "Invalid value [%s] of type [%s];"\
1422 " must be a hexadecimal, decimal or octal in C language format." % (Value
, Type
)
1424 return True, "StructurePcd"
1428 def CommonPath(PathList
):
1429 P1
= min(PathList
).split(os
.path
.sep
)
1430 P2
= max(PathList
).split(os
.path
.sep
)
1431 for Index
in range(min(len(P1
), len(P2
))):
1432 if P1
[Index
] != P2
[Index
]:
1433 return os
.path
.sep
.join(P1
[:Index
])
1434 return os
.path
.sep
.join(P1
)
1436 class PathClass(object):
1437 def __init__(self
, File
='', Root
='', AlterRoot
='', Type
='', IsBinary
=False,
1438 Arch
='COMMON', ToolChainFamily
='', Target
='', TagName
='', ToolCode
=''):
1440 self
.File
= str(File
)
1441 if os
.path
.isabs(self
.File
):
1445 self
.Root
= str(Root
)
1446 self
.AlterRoot
= str(AlterRoot
)
1448 # Remove any '.' and '..' in path
1450 self
.Root
= mws
.getWs(self
.Root
, self
.File
)
1451 self
.Path
= os
.path
.normpath(os
.path
.join(self
.Root
, self
.File
))
1452 self
.Root
= os
.path
.normpath(CommonPath([self
.Root
, self
.Path
]))
1453 # eliminate the side-effect of 'C:'
1454 if self
.Root
[-1] == ':':
1455 self
.Root
+= os
.path
.sep
1456 # file path should not start with path separator
1457 if self
.Root
[-1] == os
.path
.sep
:
1458 self
.File
= self
.Path
[len(self
.Root
):]
1460 self
.File
= self
.Path
[len(self
.Root
) + 1:]
1462 self
.Path
= os
.path
.normpath(self
.File
)
1464 self
.SubDir
, self
.Name
= os
.path
.split(self
.File
)
1465 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1469 self
.Dir
= os
.path
.join(self
.Root
, self
.SubDir
)
1471 self
.Dir
= self
.Root
1473 self
.Dir
= self
.SubDir
1478 self
.Type
= self
.Ext
.lower()
1480 self
.IsBinary
= IsBinary
1481 self
.Target
= Target
1482 self
.TagName
= TagName
1483 self
.ToolCode
= ToolCode
1484 self
.ToolChainFamily
= ToolChainFamily
1485 self
.OriginalPath
= self
1487 ## Convert the object of this class to a string
1489 # Convert member Path of the class to a string
1491 # @retval string Formatted String
1496 ## Override __eq__ function
1498 # Check whether PathClass are the same
1500 # @retval False The two PathClass are different
1501 # @retval True The two PathClass are the same
1503 def __eq__(self
, Other
):
1504 return self
.Path
== str(Other
)
1506 ## Override __cmp__ function
1508 # Customize the comparison operation of two PathClass
1510 # @retval 0 The two PathClass are different
1511 # @retval -1 The first PathClass is less than the second PathClass
1512 # @retval 1 The first PathClass is Bigger than the second PathClass
1513 def __cmp__(self
, Other
):
1514 OtherKey
= str(Other
)
1517 if SelfKey
== OtherKey
:
1519 elif SelfKey
> OtherKey
:
1524 ## Override __hash__ function
1526 # Use Path as key in hash table
1528 # @retval string Key for hash table
1531 return hash(self
.Path
)
1535 return self
.Path
.upper()
1538 def TimeStamp(self
):
1539 return os
.stat(self
.Path
)[8]
1541 def Validate(self
, Type
='', CaseSensitive
=True):
1542 def RealPath2(File
, Dir
='', OverrideDir
=''):
1545 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(OverrideDir
, File
))]
1547 if OverrideDir
[-1] == os
.path
.sep
:
1548 return NewFile
[len(OverrideDir
):], NewFile
[0:len(OverrideDir
)]
1550 return NewFile
[len(OverrideDir
) + 1:], NewFile
[0:len(OverrideDir
)]
1551 if GlobalData
.gAllFiles
:
1552 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(Dir
, File
))]
1554 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
1555 if not os
.path
.exists(NewFile
):
1559 if Dir
[-1] == os
.path
.sep
:
1560 return NewFile
[len(Dir
):], NewFile
[0:len(Dir
)]
1562 return NewFile
[len(Dir
) + 1:], NewFile
[0:len(Dir
)]
1568 if GlobalData
.gCaseInsensitive
:
1569 CaseSensitive
= False
1570 if Type
and Type
.lower() != self
.Type
:
1571 return FILE_TYPE_MISMATCH
, '%s (expect %s but got %s)' % (self
.File
, Type
, self
.Type
)
1573 RealFile
, RealRoot
= RealPath2(self
.File
, self
.Root
, self
.AlterRoot
)
1574 if not RealRoot
and not RealFile
:
1575 RealFile
= self
.File
1577 RealFile
= os
.path
.join(self
.AlterRoot
, self
.File
)
1579 RealFile
= os
.path
.join(self
.Root
, self
.File
)
1580 if len (mws
.getPkgPath()) == 0:
1581 return FILE_NOT_FOUND
, os
.path
.join(self
.AlterRoot
, RealFile
)
1583 return FILE_NOT_FOUND
, "%s is not found in packages path:\n\t%s" % (self
.File
, '\n\t'.join(mws
.getPkgPath()))
1587 if RealRoot
!= self
.Root
or RealFile
!= self
.File
:
1588 if CaseSensitive
and (RealFile
!= self
.File
or (RealRoot
!= self
.Root
and RealRoot
!= self
.AlterRoot
)):
1589 ErrorCode
= FILE_CASE_MISMATCH
1590 ErrorInfo
= self
.File
+ '\n\t' + RealFile
+ " [in file system]"
1592 self
.SubDir
, self
.Name
= os
.path
.split(RealFile
)
1593 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1595 self
.Dir
= os
.path
.join(RealRoot
, self
.SubDir
)
1598 self
.File
= RealFile
1599 self
.Root
= RealRoot
1600 self
.Path
= os
.path
.join(RealRoot
, RealFile
)
1601 return ErrorCode
, ErrorInfo
1603 ## Parse PE image to get the required PE information.
1605 class PeImageClass():
1608 # @param File FilePath of PeImage
1610 def __init__(self
, PeFile
):
1611 self
.FileName
= PeFile
1612 self
.IsValid
= False
1615 self
.SectionAlignment
= 0
1616 self
.SectionHeaderList
= []
1619 PeObject
= open(PeFile
, 'rb')
1621 self
.ErrorInfo
= self
.FileName
+ ' can not be found\n'
1624 ByteArray
= array
.array('B')
1625 ByteArray
.fromfile(PeObject
, 0x3E)
1626 ByteList
= ByteArray
.tolist()
1627 # DOS signature should be 'MZ'
1628 if self
._ByteListToStr
(ByteList
[0x0:0x2]) != 'MZ':
1629 self
.ErrorInfo
= self
.FileName
+ ' has no valid DOS signature MZ'
1632 # Read 4 byte PE Signature
1633 PeOffset
= self
._ByteListToInt
(ByteList
[0x3C:0x3E])
1634 PeObject
.seek(PeOffset
)
1635 ByteArray
= array
.array('B')
1636 ByteArray
.fromfile(PeObject
, 4)
1637 # PE signature should be 'PE\0\0'
1638 if ByteArray
.tolist() != [ord('P'), ord('E'), 0, 0]:
1639 self
.ErrorInfo
= self
.FileName
+ ' has no valid PE signature PE00'
1642 # Read PE file header
1643 ByteArray
= array
.array('B')
1644 ByteArray
.fromfile(PeObject
, 0x14)
1645 ByteList
= ByteArray
.tolist()
1646 SecNumber
= self
._ByteListToInt
(ByteList
[0x2:0x4])
1648 self
.ErrorInfo
= self
.FileName
+ ' has no section header'
1651 # Read PE optional header
1652 OptionalHeaderSize
= self
._ByteListToInt
(ByteArray
[0x10:0x12])
1653 ByteArray
= array
.array('B')
1654 ByteArray
.fromfile(PeObject
, OptionalHeaderSize
)
1655 ByteList
= ByteArray
.tolist()
1656 self
.EntryPoint
= self
._ByteListToInt
(ByteList
[0x10:0x14])
1657 self
.SectionAlignment
= self
._ByteListToInt
(ByteList
[0x20:0x24])
1658 self
.Size
= self
._ByteListToInt
(ByteList
[0x38:0x3C])
1660 # Read each Section Header
1661 for Index
in range(SecNumber
):
1662 ByteArray
= array
.array('B')
1663 ByteArray
.fromfile(PeObject
, 0x28)
1664 ByteList
= ByteArray
.tolist()
1665 SecName
= self
._ByteListToStr
(ByteList
[0:8])
1666 SecVirtualSize
= self
._ByteListToInt
(ByteList
[8:12])
1667 SecRawAddress
= self
._ByteListToInt
(ByteList
[20:24])
1668 SecVirtualAddress
= self
._ByteListToInt
(ByteList
[12:16])
1669 self
.SectionHeaderList
.append((SecName
, SecVirtualAddress
, SecRawAddress
, SecVirtualSize
))
1673 def _ByteListToStr(self
, ByteList
):
1675 for index
in range(len(ByteList
)):
1676 if ByteList
[index
] == 0:
1678 String
+= chr(ByteList
[index
])
1681 def _ByteListToInt(self
, ByteList
):
1683 for index
in range(len(ByteList
) - 1, -1, -1):
1684 Value
= (Value
<< 8) |
int(ByteList
[index
])
1687 class DefaultStore():
1688 def __init__(self
, DefaultStores
):
1690 self
.DefaultStores
= DefaultStores
1691 def DefaultStoreID(self
, DefaultStoreName
):
1692 for key
, value
in self
.DefaultStores
.items():
1693 if value
== DefaultStoreName
:
1696 def GetDefaultDefault(self
):
1697 if not self
.DefaultStores
or "0" in self
.DefaultStores
:
1698 return "0", TAB_DEFAULT_STORES_DEFAULT
1700 minvalue
= min(int(value_str
) for value_str
in self
.DefaultStores
)
1701 return (str(minvalue
), self
.DefaultStores
[str(minvalue
)])
1702 def GetMin(self
, DefaultSIdList
):
1703 if not DefaultSIdList
:
1704 return TAB_DEFAULT_STORES_DEFAULT
1705 storeidset
= {storeid
for storeid
, storename
in self
.DefaultStores
.values() if storename
in DefaultSIdList
}
1708 minid
= min(storeidset
)
1709 for sid
, name
in self
.DefaultStores
.values():
1718 def __init__(self
,SkuIdentifier
='', SkuIds
=None):
1722 for SkuName
in SkuIds
:
1723 SkuId
= SkuIds
[SkuName
][0]
1724 skuid_num
= int(SkuId
, 16) if SkuId
.upper().startswith("0X") else int(SkuId
)
1725 if skuid_num
> 0xFFFFFFFFFFFFFFFF:
1726 EdkLogger
.error("build", PARAMETER_INVALID
,
1727 ExtraData
= "SKU-ID [%s] value %s exceeds the max value of UINT64"
1730 self
.AvailableSkuIds
= OrderedDict()
1732 self
.SkuIdNumberSet
= []
1733 self
.SkuData
= SkuIds
1734 self
._SkuInherit
= {}
1735 self
._SkuIdentifier
= SkuIdentifier
1736 if SkuIdentifier
== '' or SkuIdentifier
is None:
1737 self
.SkuIdSet
= ['DEFAULT']
1738 self
.SkuIdNumberSet
= ['0U']
1739 elif SkuIdentifier
== 'ALL':
1740 self
.SkuIdSet
= list(SkuIds
.keys())
1741 self
.SkuIdNumberSet
= [num
[0].strip() + 'U' for num
in SkuIds
.values()]
1743 r
= SkuIdentifier
.split('|')
1744 self
.SkuIdSet
=[(r
[k
].strip()).upper() for k
in range(len(r
))]
1747 self
.SkuIdNumberSet
= [SkuIds
[k
][0].strip() + 'U' for k
in self
.SkuIdSet
]
1749 EdkLogger
.error("build", PARAMETER_INVALID
,
1750 ExtraData
= "SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1751 % (k
, " | ".join(SkuIds
.keys())))
1752 for each
in self
.SkuIdSet
:
1754 self
.AvailableSkuIds
[each
] = SkuIds
[each
][0]
1756 EdkLogger
.error("build", PARAMETER_INVALID
,
1757 ExtraData
="SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1758 % (each
, " | ".join(SkuIds
.keys())))
1759 if self
.SkuUsageType
!= SkuClass
.SINGLE
:
1760 self
.AvailableSkuIds
.update({'DEFAULT':0, 'COMMON':0})
1762 GlobalData
.gSkuids
= (self
.SkuIdSet
)
1763 if 'COMMON' in GlobalData
.gSkuids
:
1764 GlobalData
.gSkuids
.remove('COMMON')
1765 if self
.SkuUsageType
== self
.SINGLE
:
1766 if len(GlobalData
.gSkuids
) != 1:
1767 if 'DEFAULT' in GlobalData
.gSkuids
:
1768 GlobalData
.gSkuids
.remove('DEFAULT')
1769 if GlobalData
.gSkuids
:
1770 GlobalData
.gSkuids
.sort()
1772 def GetNextSkuId(self
, skuname
):
1773 if not self
._SkuInherit
:
1774 self
._SkuInherit
= {}
1775 for item
in self
.SkuData
.values():
1776 self
._SkuInherit
[item
[1]]=item
[2] if item
[2] else "DEFAULT"
1777 return self
._SkuInherit
.get(skuname
, "DEFAULT")
1779 def GetSkuChain(self
, sku
):
1780 if sku
== "DEFAULT":
1785 nextsku
= self
.GetNextSkuId(nextsku
)
1786 skulist
.append(nextsku
)
1787 if nextsku
== "DEFAULT":
1791 def SkuOverrideOrder(self
):
1793 for skuname
in self
.SkuIdSet
:
1794 skuorderset
.append(self
.GetSkuChain(skuname
))
1797 for index
in range(max(len(item
) for item
in skuorderset
)):
1798 for subset
in skuorderset
:
1799 if index
> len(subset
)-1:
1801 if subset
[index
] in skuorder
:
1803 skuorder
.append(subset
[index
])
1808 def SkuUsageType(self
):
1809 if self
._SkuIdentifier
.upper() == "ALL":
1810 return SkuClass
.MULTIPLE
1812 if len(self
.SkuIdSet
) == 1:
1813 if self
.SkuIdSet
[0] == 'DEFAULT':
1814 return SkuClass
.DEFAULT
1815 return SkuClass
.SINGLE
1816 if len(self
.SkuIdSet
) == 2 and 'DEFAULT' in self
.SkuIdSet
:
1817 return SkuClass
.SINGLE
1818 return SkuClass
.MULTIPLE
1820 def DumpSkuIdArrary(self
):
1821 if self
.SkuUsageType
== SkuClass
.SINGLE
:
1824 for skuname
in self
.AvailableSkuIds
:
1825 if skuname
== "COMMON":
1827 while skuname
!= "DEFAULT":
1828 ArrayStrList
.append(hex(int(self
.AvailableSkuIds
[skuname
])))
1829 skuname
= self
.GetNextSkuId(skuname
)
1830 ArrayStrList
.append("0x0")
1831 return "{{{myList}}}".format(myList
=",".join(ArrayStrList
))
1834 def AvailableSkuIdSet(self
):
1835 return self
.AvailableSkuIds
1838 def SystemSkuId(self
):
1839 if self
.SkuUsageType
== SkuClass
.SINGLE
:
1840 if len(self
.SkuIdSet
) == 1:
1841 return self
.SkuIdSet
[0]
1843 return self
.SkuIdSet
[0] if self
.SkuIdSet
[0] != 'DEFAULT' else self
.SkuIdSet
[1]
1847 ## Get the integer value from string like "14U" or integer like 2
1849 # @param Input The object that may be either a integer value or a string
1851 # @retval Value The integer value that the input represents
1853 def GetIntegerValue(Input
):
1854 if not isinstance(Input
, str):
1857 if String
.endswith("U"):
1858 String
= String
[:-1]
1859 if String
.endswith("ULL"):
1860 String
= String
[:-3]
1861 if String
.endswith("LL"):
1862 String
= String
[:-2]
1864 if String
.startswith("0x") or String
.startswith("0X"):
1865 return int(String
, 16)
1872 # Pack a GUID (registry format) list into a buffer and return it
1875 return pack(PACK_PATTERN_GUID
,
1879 int(Guid
[3][-4:-2], 16),
1880 int(Guid
[3][-2:], 16),
1881 int(Guid
[4][-12:-10], 16),
1882 int(Guid
[4][-10:-8], 16),
1883 int(Guid
[4][-8:-6], 16),
1884 int(Guid
[4][-6:-4], 16),
1885 int(Guid
[4][-4:-2], 16),
1886 int(Guid
[4][-2:], 16)
1890 # Pack a GUID (byte) list into a buffer and return it
1892 def PackByteFormatGUID(Guid
):
1893 return pack(PACK_PATTERN_GUID
,
1907 ## DeepCopy dict/OrderedDict recusively
1909 # @param ori_dict a nested dict or ordereddict
1911 # @retval new dict or orderdict
1913 def CopyDict(ori_dict
):
1914 dict_type
= ori_dict
.__class
__
1915 if dict_type
not in (dict,OrderedDict
):
1917 new_dict
= dict_type()
1918 for key
in ori_dict
:
1919 if isinstance(ori_dict
[key
],(dict,OrderedDict
)):
1920 new_dict
[key
] = CopyDict(ori_dict
[key
])
1922 new_dict
[key
] = ori_dict
[key
]
1926 # Remove the c/c++ comments: // and /* */
1928 def RemoveCComments(ctext
):
1929 return re
.sub('//.*?\n|/\*.*?\*/', '\n', ctext
, flags
=re
.S
)