2 # Common routines used by all tools
4 # Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
5 # This program and the accompanying materials
6 # are licensed and made available under the terms and conditions of the BSD License
7 # which accompanies this distribution. The full text of the license may be found at
8 # http://opensource.org/licenses/bsd-license.php
10 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
17 import Common
.LongFilePathOs
as os
26 from struct
import pack
27 from collections
import UserDict
as IterableUserDict
28 from collections
import OrderedDict
30 from Common
import EdkLogger
as EdkLogger
31 from Common
import GlobalData
as GlobalData
32 from .DataType
import *
33 from .BuildToolError
import *
34 from CommonDataClass
.DataClass
import *
35 from .Parsing
import GetSplitValueList
36 from Common
.LongFilePathSupport
import OpenLongFilePath
as open
37 from Common
.MultipleWorkspace
import MultipleWorkspace
as mws
39 from CommonDataClass
.Exceptions
import BadExpression
40 from Common
.caching
import cached_property
42 ## Regular expression used to find out place holders in string template
43 gPlaceholderPattern
= re
.compile("\$\{([^$()\s]+)\}", re
.MULTILINE | re
.UNICODE
)
45 ## regular expressions for map file processing
46 startPatternGeneral
= re
.compile("^Start[' ']+Length[' ']+Name[' ']+Class")
47 addressPatternGeneral
= re
.compile("^Address[' ']+Publics by Value[' ']+Rva\+Base")
48 valuePatternGcc
= re
.compile('^([\w_\.]+) +([\da-fA-Fx]+) +([\da-fA-Fx]+)$')
49 pcdPatternGcc
= re
.compile('^([\da-fA-Fx]+) +([\da-fA-Fx]+)')
50 secReGeneral
= re
.compile('^([\da-fA-F]+):([\da-fA-F]+) +([\da-fA-F]+)[Hh]? +([.\w\$]+) +(\w+)', re
.UNICODE
)
52 ## Dictionary used to store file time stamp for quick re-access
53 gFileTimeStampCache
= {} # {file path : file time stamp}
55 ## Dictionary used to store dependencies of files
56 gDependencyDatabase
= {} # arch : {file path : [dependent files list]}
59 # If a module is built more than once with different PCDs or library classes
60 # a temporary INF file with same content is created, the temporary file is removed
65 def GetVariableOffset(mapfilepath
, efifilepath
, varnames
):
66 """ Parse map file to get variable offset in current EFI file
67 @param mapfilepath Map file absolution path
68 @param efifilepath: EFI binary file full path
69 @param varnames iteratable container whose elements are variable names to be searched
71 @return List whos elements are tuple with variable name and raw offset
75 f
= open(mapfilepath
, 'r')
81 if len(lines
) == 0: return None
82 firstline
= lines
[0].strip()
83 if (firstline
.startswith("Archive member included ") and
84 firstline
.endswith(" file (symbol)")):
85 return _parseForGCC(lines
, efifilepath
, varnames
)
86 if firstline
.startswith("# Path:"):
87 return _parseForXcode(lines
, efifilepath
, varnames
)
88 return _parseGeneral(lines
, efifilepath
, varnames
)
90 def _parseForXcode(lines
, efifilepath
, varnames
):
95 if status
== 0 and line
== "# Symbols:":
98 if status
== 1 and len(line
) != 0:
99 for varname
in varnames
:
101 # cannot pregenerate this RegEx since it uses varname from varnames.
102 m
= re
.match('^([\da-fA-FxX]+)([\s\S]*)([_]*%s)$' % varname
, line
)
104 ret
.append((varname
, m
.group(1)))
107 def _parseForGCC(lines
, efifilepath
, varnames
):
108 """ Parse map file generated by GCC linker """
112 for index
, line
in enumerate(lines
):
114 # status machine transection
115 if status
== 0 and line
== "Memory Configuration":
118 elif status
== 1 and line
== 'Linker script and memory map':
121 elif status
==2 and line
== 'START GROUP':
127 m
= valuePatternGcc
.match(line
)
129 sections
.append(m
.groups(0))
130 for varname
in varnames
:
132 m
= re
.match("^.data.(%s)" % varname
, line
)
134 m
= re
.match(".data.(%s)$" % varname
, line
)
136 Str
= lines
[index
+ 1]
138 Str
= line
[len(".data.%s" % varname
):]
140 m
= pcdPatternGcc
.match(Str
.strip())
142 varoffset
.append((varname
, int(m
.groups(0)[0], 16), int(sections
[-1][1], 16), sections
[-1][0]))
146 # get section information from efi file
147 efisecs
= PeImageClass(efifilepath
).SectionHeaderList
148 if efisecs
is None or len(efisecs
) == 0:
152 for efisec
in efisecs
:
153 for section
in sections
:
154 if section
[0].strip() == efisec
[0].strip() and section
[0].strip() == '.text':
155 redirection
= int(section
[1], 16) - efisec
[1]
158 for var
in varoffset
:
159 for efisec
in efisecs
:
160 if var
[1] >= efisec
[1] and var
[1] < efisec
[1]+efisec
[3]:
161 ret
.append((var
[0], hex(efisec
[2] + var
[1] - efisec
[1] - redirection
)))
164 def _parseGeneral(lines
, efifilepath
, varnames
):
165 status
= 0 #0 - beginning of file; 1 - PE section definition; 2 - symbol table
166 secs
= [] # key = section name
168 symRe
= re
.compile('^([\da-fA-F]+):([\da-fA-F]+) +([\.:\\\\\w\?@\$]+) +([\da-fA-F]+)', re
.UNICODE
)
172 if startPatternGeneral
.match(line
):
175 if addressPatternGeneral
.match(line
):
178 if line
.startswith("entry point at"):
181 if status
== 1 and len(line
) != 0:
182 m
= secReGeneral
.match(line
)
183 assert m
is not None, "Fail to parse the section in map file , line is %s" % line
184 sec_no
, sec_start
, sec_length
, sec_name
, sec_class
= m
.groups(0)
185 secs
.append([int(sec_no
, 16), int(sec_start
, 16), int(sec_length
, 16), sec_name
, sec_class
])
186 if status
== 2 and len(line
) != 0:
187 for varname
in varnames
:
188 m
= symRe
.match(line
)
189 assert m
is not None, "Fail to parse the symbol in map file, line is %s" % line
190 sec_no
, sym_offset
, sym_name
, vir_addr
= m
.groups(0)
191 sec_no
= int(sec_no
, 16)
192 sym_offset
= int(sym_offset
, 16)
193 vir_addr
= int(vir_addr
, 16)
194 # cannot pregenerate this RegEx since it uses varname from varnames.
195 m2
= re
.match('^[_]*(%s)' % varname
, sym_name
)
197 # fond a binary pcd entry in map file
199 if sec
[0] == sec_no
and (sym_offset
>= sec
[1] and sym_offset
< sec
[1] + sec
[2]):
200 varoffset
.append([varname
, sec
[3], sym_offset
, vir_addr
, sec_no
])
202 if not varoffset
: return []
204 # get section information from efi file
205 efisecs
= PeImageClass(efifilepath
).SectionHeaderList
206 if efisecs
is None or len(efisecs
) == 0:
210 for var
in varoffset
:
212 for efisec
in efisecs
:
214 if var
[1].strip() == efisec
[0].strip():
215 ret
.append((var
[0], hex(efisec
[2] + var
[2])))
216 elif var
[4] == index
:
217 ret
.append((var
[0], hex(efisec
[2] + var
[2])))
221 ## Routine to process duplicated INF
223 # This function is called by following two cases:
226 # Pkg/module/module.inf
227 # Pkg/module/module.inf {
229 # FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836
232 # INF Pkg/module/module.inf
233 # INF FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836 Pkg/module/module.inf
235 # This function copies Pkg/module/module.inf to
236 # Conf/.cache/0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf
238 # @param Path Original PathClass object
239 # @param BaseName New file base name
241 # @retval return the new PathClass object
243 def ProcessDuplicatedInf(Path
, BaseName
, Workspace
):
244 Filename
= os
.path
.split(Path
.File
)[1]
246 Filename
= BaseName
+ Path
.BaseName
+ Filename
[Filename
.rfind('.'):]
248 Filename
= BaseName
+ Path
.BaseName
251 # If -N is specified on command line, cache is disabled
252 # The directory has to be created
254 DbDir
= os
.path
.split(GlobalData
.gDatabasePath
)[0]
255 if not os
.path
.exists(DbDir
):
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
287 # If file exists, compare contents
289 if os
.path
.exists(TempFullPath
):
290 with
open(str(Path
), 'rb') as f1
, open(TempFullPath
, 'rb') as f2
:
291 if f1
.read() == f2
.read():
293 _TempInfs
.append(TempFullPath
)
294 shutil
.copy2(str(Path
), TempFullPath
)
297 ## Remove temporary created INFs whose paths were saved in _TempInfs
299 def ClearDuplicatedInf():
301 File
= _TempInfs
.pop()
302 if os
.path
.exists(File
):
305 ## Convert GUID string in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx style to C structure style
307 # @param Guid The GUID string
309 # @retval string The GUID string in C structure style
311 def GuidStringToGuidStructureString(Guid
):
312 GuidList
= Guid
.split('-')
314 for Index
in range(0, 3, 1):
315 Result
= Result
+ '0x' + GuidList
[Index
] + ', '
316 Result
= Result
+ '{0x' + GuidList
[3][0:2] + ', 0x' + GuidList
[3][2:4]
317 for Index
in range(0, 12, 2):
318 Result
= Result
+ ', 0x' + GuidList
[4][Index
:Index
+ 2]
322 ## Convert GUID structure in byte array to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
324 # @param GuidValue The GUID value in byte array
326 # @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
328 def GuidStructureByteArrayToGuidString(GuidValue
):
329 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
330 guidValueList
= guidValueString
.split(",")
331 if len(guidValueList
) != 16:
333 #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
335 return "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
336 int(guidValueList
[3], 16),
337 int(guidValueList
[2], 16),
338 int(guidValueList
[1], 16),
339 int(guidValueList
[0], 16),
340 int(guidValueList
[5], 16),
341 int(guidValueList
[4], 16),
342 int(guidValueList
[7], 16),
343 int(guidValueList
[6], 16),
344 int(guidValueList
[8], 16),
345 int(guidValueList
[9], 16),
346 int(guidValueList
[10], 16),
347 int(guidValueList
[11], 16),
348 int(guidValueList
[12], 16),
349 int(guidValueList
[13], 16),
350 int(guidValueList
[14], 16),
351 int(guidValueList
[15], 16)
356 ## Convert GUID string in C structure style to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
358 # @param GuidValue The GUID value in C structure format
360 # @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
362 def GuidStructureStringToGuidString(GuidValue
):
363 if not GlobalData
.gGuidCFormatPattern
.match(GuidValue
):
365 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
366 guidValueList
= guidValueString
.split(",")
367 if len(guidValueList
) != 11:
369 #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
371 return "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
372 int(guidValueList
[0], 16),
373 int(guidValueList
[1], 16),
374 int(guidValueList
[2], 16),
375 int(guidValueList
[3], 16),
376 int(guidValueList
[4], 16),
377 int(guidValueList
[5], 16),
378 int(guidValueList
[6], 16),
379 int(guidValueList
[7], 16),
380 int(guidValueList
[8], 16),
381 int(guidValueList
[9], 16),
382 int(guidValueList
[10], 16)
387 ## Convert GUID string in C structure style to xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx
389 # @param GuidValue The GUID value in C structure format
391 # @retval string The GUID value in xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx format
393 def GuidStructureStringToGuidValueName(GuidValue
):
394 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "")
395 guidValueList
= guidValueString
.split(",")
396 if len(guidValueList
) != 11:
397 EdkLogger
.error(None, FORMAT_INVALID
, "Invalid GUID value string [%s]" % GuidValue
)
398 return "%08x_%04x_%04x_%02x%02x_%02x%02x%02x%02x%02x%02x" % (
399 int(guidValueList
[0], 16),
400 int(guidValueList
[1], 16),
401 int(guidValueList
[2], 16),
402 int(guidValueList
[3], 16),
403 int(guidValueList
[4], 16),
404 int(guidValueList
[5], 16),
405 int(guidValueList
[6], 16),
406 int(guidValueList
[7], 16),
407 int(guidValueList
[8], 16),
408 int(guidValueList
[9], 16),
409 int(guidValueList
[10], 16)
412 ## Create directories
414 # @param Directory The directory name
416 def CreateDirectory(Directory
):
417 if Directory
is None or Directory
.strip() == "":
420 if not os
.access(Directory
, os
.F_OK
):
421 os
.makedirs(Directory
)
426 ## Remove directories, including files and sub-directories in it
428 # @param Directory The directory name
430 def RemoveDirectory(Directory
, Recursively
=False):
431 if Directory
is None or Directory
.strip() == "" or not os
.path
.exists(Directory
):
434 CurrentDirectory
= os
.getcwd()
436 for File
in os
.listdir("."):
437 if os
.path
.isdir(File
):
438 RemoveDirectory(File
, Recursively
)
441 os
.chdir(CurrentDirectory
)
444 ## Store content in file
446 # This method is used to save file only when its content is changed. This is
447 # quite useful for "make" system to decide what will be re-built and what won't.
449 # @param File The path of file
450 # @param Content The new content of the file
451 # @param IsBinaryFile The flag indicating if the file is binary file or not
453 # @retval True If the file content is changed and the file is renewed
454 # @retval False If the file content is the same
456 def SaveFileOnChange(File
, Content
, IsBinaryFile
=True):
457 if os
.path
.exists(File
):
459 if isinstance(Content
, bytes
):
460 with
open(File
, "rb") as f
:
461 if Content
== f
.read():
464 with
open(File
, "r") as f
:
465 if Content
== f
.read():
468 EdkLogger
.error(None, FILE_OPEN_FAILURE
, ExtraData
=File
)
470 DirName
= os
.path
.dirname(File
)
471 if not CreateDirectory(DirName
):
472 EdkLogger
.error(None, FILE_CREATE_FAILURE
, "Could not create directory %s" % DirName
)
475 DirName
= os
.getcwd()
476 if not os
.access(DirName
, os
.W_OK
):
477 EdkLogger
.error(None, PERMISSION_FAILURE
, "Do not have write permission on directory %s" % DirName
)
480 if GlobalData
.gIsWindows
:
482 from .PyUtility
import SaveFileToDisk
483 if not SaveFileToDisk(File
, Content
):
484 EdkLogger
.error(None, FILE_CREATE_FAILURE
, ExtraData
=File
)
486 if isinstance(Content
, bytes
):
487 with
open(File
, "wb") as Fd
:
490 with
open(File
, "w") as Fd
:
493 if isinstance(Content
, bytes
):
494 with
open(File
, "wb") as Fd
:
497 with
open(File
, "w") as Fd
:
500 EdkLogger
.error(None, FILE_CREATE_FAILURE
, ExtraData
='IOError %s' % X
)
504 ## Make a Python object persistent on file system
506 # @param Data The object to be stored in file
507 # @param File The path of file to store the object
509 def DataDump(Data
, File
):
512 Fd
= open(File
, 'wb')
513 pickle
.dump(Data
, Fd
, pickle
.HIGHEST_PROTOCOL
)
515 EdkLogger
.error("", FILE_OPEN_FAILURE
, ExtraData
=File
, RaiseError
=False)
520 ## Restore a Python object from a file
522 # @param File The path of file stored the object
524 # @retval object A python object
525 # @retval None If failure in file operation
527 def DataRestore(File
):
531 Fd
= open(File
, 'rb')
532 Data
= pickle
.load(Fd
)
533 except Exception as e
:
534 EdkLogger
.verbose("Failed to load [%s]\n\t%s" % (File
, str(e
)))
541 ## Retrieve and cache the real path name in file system
543 # @param Root The root directory of path relative to
545 # @retval str The path string if the path exists
546 # @retval None If path doesn't exist
552 def __init__(self
, Root
):
554 for F
in os
.listdir(Root
):
556 self
._UPPER
_CACHE
_[F
.upper()] = F
559 def __getitem__(self
, Path
):
560 Path
= Path
[len(os
.path
.commonprefix([Path
, self
._Root
])):]
563 if Path
and Path
[0] == os
.path
.sep
:
565 if Path
in self
._CACHE
_:
566 return os
.path
.join(self
._Root
, Path
)
567 UpperPath
= Path
.upper()
568 if UpperPath
in self
._UPPER
_CACHE
_:
569 return os
.path
.join(self
._Root
, self
._UPPER
_CACHE
_[UpperPath
])
573 SepIndex
= Path
.find(os
.path
.sep
)
575 Parent
= UpperPath
[:SepIndex
]
576 if Parent
not in self
._UPPER
_CACHE
_:
578 LastSepIndex
= SepIndex
579 SepIndex
= Path
.find(os
.path
.sep
, LastSepIndex
+ 1)
581 if LastSepIndex
== -1:
586 SepIndex
= LastSepIndex
588 Parent
= Path
[:SepIndex
]
589 ParentKey
= UpperPath
[:SepIndex
]
590 if ParentKey
not in self
._UPPER
_CACHE
_:
594 if Parent
in self
._CACHE
_:
597 ParentDir
= self
._UPPER
_CACHE
_[ParentKey
]
598 for F
in os
.listdir(ParentDir
):
599 Dir
= os
.path
.join(ParentDir
, F
)
600 self
._CACHE
_.add(Dir
)
601 self
._UPPER
_CACHE
_[Dir
.upper()] = Dir
603 SepIndex
= Path
.find(os
.path
.sep
, SepIndex
+ 1)
606 if Path
in self
._CACHE
_:
607 return os
.path
.join(self
._Root
, Path
)
608 elif UpperPath
in self
._UPPER
_CACHE
_:
609 return os
.path
.join(self
._Root
, self
._UPPER
_CACHE
_[UpperPath
])
612 def RealPath(File
, Dir
='', OverrideDir
=''):
613 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
614 NewFile
= GlobalData
.gAllFiles
[NewFile
]
615 if not NewFile
and OverrideDir
:
616 NewFile
= os
.path
.normpath(os
.path
.join(OverrideDir
, File
))
617 NewFile
= GlobalData
.gAllFiles
[NewFile
]
620 def RealPath2(File
, Dir
='', OverrideDir
=''):
623 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(OverrideDir
, File
))]
625 if OverrideDir
[-1] == os
.path
.sep
:
626 return NewFile
[len(OverrideDir
):], NewFile
[0:len(OverrideDir
)]
628 return NewFile
[len(OverrideDir
) + 1:], NewFile
[0:len(OverrideDir
)]
629 if GlobalData
.gAllFiles
:
630 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(Dir
, File
))]
632 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
633 if not os
.path
.exists(NewFile
):
637 if Dir
[-1] == os
.path
.sep
:
638 return NewFile
[len(Dir
):], NewFile
[0:len(Dir
)]
640 return NewFile
[len(Dir
) + 1:], NewFile
[0:len(Dir
)]
646 ## Get GUID value from given packages
648 # @param CName The CName of the GUID
649 # @param PackageList List of packages looking-up in
650 # @param Inffile The driver file
652 # @retval GuidValue if the CName is found in any given package
653 # @retval None if the CName is not found in all given packages
655 def GuidValue(CName
, PackageList
, Inffile
= None):
656 for P
in PackageList
:
657 GuidKeys
= list(P
.Guids
.keys())
658 if Inffile
and P
._PrivateGuids
:
659 if not Inffile
.startswith(P
.MetaFile
.Dir
):
660 GuidKeys
= [x
for x
in P
.Guids
if x
not in P
._PrivateGuids
]
661 if CName
in GuidKeys
:
662 return P
.Guids
[CName
]
665 ## Get Protocol value from given packages
667 # @param CName The CName of the GUID
668 # @param PackageList List of packages looking-up in
669 # @param Inffile The driver file
671 # @retval GuidValue if the CName is found in any given package
672 # @retval None if the CName is not found in all given packages
674 def ProtocolValue(CName
, PackageList
, Inffile
= None):
675 for P
in PackageList
:
676 ProtocolKeys
= list(P
.Protocols
.keys())
677 if Inffile
and P
._PrivateProtocols
:
678 if not Inffile
.startswith(P
.MetaFile
.Dir
):
679 ProtocolKeys
= [x
for x
in P
.Protocols
if x
not in P
._PrivateProtocols
]
680 if CName
in ProtocolKeys
:
681 return P
.Protocols
[CName
]
684 ## Get PPI value from given packages
686 # @param CName The CName of the GUID
687 # @param PackageList List of packages looking-up in
688 # @param Inffile The driver file
690 # @retval GuidValue if the CName is found in any given package
691 # @retval None if the CName is not found in all given packages
693 def PpiValue(CName
, PackageList
, Inffile
= None):
694 for P
in PackageList
:
695 PpiKeys
= list(P
.Ppis
.keys())
696 if Inffile
and P
._PrivatePpis
:
697 if not Inffile
.startswith(P
.MetaFile
.Dir
):
698 PpiKeys
= [x
for x
in P
.Ppis
if x
not in P
._PrivatePpis
]
703 ## A string template class
705 # This class implements a template for string replacement. A string template
706 # looks like following
708 # ${BEGIN} other_string ${placeholder_name} other_string ${END}
710 # The string between ${BEGIN} and ${END} will be repeated as many times as the
711 # length of "placeholder_name", which is a list passed through a dict. The
712 # "placeholder_name" is the key name of the dict. The ${BEGIN} and ${END} can
713 # be not used and, in this case, the "placeholder_name" must not a list and it
714 # will just be replaced once.
716 class TemplateString(object):
717 _REPEAT_START_FLAG
= "BEGIN"
718 _REPEAT_END_FLAG
= "END"
720 class Section(object):
721 _LIST_TYPES
= [type([]), type(set()), type((0,))]
723 def __init__(self
, TemplateSection
, PlaceHolderList
):
724 self
._Template
= TemplateSection
725 self
._PlaceHolderList
= []
727 # Split the section into sub-sections according to the position of placeholders
729 self
._SubSectionList
= []
732 # The placeholders passed in must be in the format of
734 # PlaceHolderName, PlaceHolderStartPoint, PlaceHolderEndPoint
736 for PlaceHolder
, Start
, End
in PlaceHolderList
:
737 self
._SubSectionList
.append(TemplateSection
[SubSectionStart
:Start
])
738 self
._SubSectionList
.append(TemplateSection
[Start
:End
])
739 self
._PlaceHolderList
.append(PlaceHolder
)
740 SubSectionStart
= End
741 if SubSectionStart
< len(TemplateSection
):
742 self
._SubSectionList
.append(TemplateSection
[SubSectionStart
:])
744 self
._SubSectionList
= [TemplateSection
]
747 return self
._Template
+ " : " + str(self
._PlaceHolderList
)
749 def Instantiate(self
, PlaceHolderValues
):
751 RepeatPlaceHolders
= {}
752 NonRepeatPlaceHolders
= {}
754 for PlaceHolder
in self
._PlaceHolderList
:
755 if PlaceHolder
not in PlaceHolderValues
:
757 Value
= PlaceHolderValues
[PlaceHolder
]
758 if type(Value
) in self
._LIST
_TYPES
:
760 RepeatTime
= len(Value
)
761 elif RepeatTime
!= len(Value
):
765 "${%s} has different repeat time from others!" % PlaceHolder
,
766 ExtraData
=str(self
._Template
)
768 RepeatPlaceHolders
["${%s}" % PlaceHolder
] = Value
770 NonRepeatPlaceHolders
["${%s}" % PlaceHolder
] = Value
772 if NonRepeatPlaceHolders
:
774 for S
in self
._SubSectionList
:
775 if S
not in NonRepeatPlaceHolders
:
778 StringList
.append(str(NonRepeatPlaceHolders
[S
]))
780 StringList
= self
._SubSectionList
782 if RepeatPlaceHolders
:
784 for Index
in range(RepeatTime
):
786 if S
not in RepeatPlaceHolders
:
787 TempStringList
.append(S
)
789 TempStringList
.append(str(RepeatPlaceHolders
[S
][Index
]))
790 StringList
= TempStringList
792 return "".join(StringList
)
795 def __init__(self
, Template
=None):
797 self
.IsBinary
= False
798 self
._Template
= Template
799 self
._TemplateSectionList
= self
._Parse
(Template
)
803 # @retval string The string replaced
808 ## Split the template string into fragments per the ${BEGIN} and ${END} flags
810 # @retval list A list of TemplateString.Section objects
812 def _Parse(self
, Template
):
817 TemplateSectionList
= []
819 MatchObj
= gPlaceholderPattern
.search(Template
, SearchFrom
)
821 if MatchEnd
<= len(Template
):
822 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:], PlaceHolderList
)
823 TemplateSectionList
.append(TemplateSection
)
826 MatchString
= MatchObj
.group(1)
827 MatchStart
= MatchObj
.start()
828 MatchEnd
= MatchObj
.end()
830 if MatchString
== self
._REPEAT
_START
_FLAG
:
831 if MatchStart
> SectionStart
:
832 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:MatchStart
], PlaceHolderList
)
833 TemplateSectionList
.append(TemplateSection
)
834 SectionStart
= MatchEnd
836 elif MatchString
== self
._REPEAT
_END
_FLAG
:
837 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:MatchStart
], PlaceHolderList
)
838 TemplateSectionList
.append(TemplateSection
)
839 SectionStart
= MatchEnd
842 PlaceHolderList
.append((MatchString
, MatchStart
- SectionStart
, MatchEnd
- SectionStart
))
843 SearchFrom
= MatchEnd
844 return TemplateSectionList
846 ## Replace the string template with dictionary of placeholders and append it to previous one
848 # @param AppendString The string template to append
849 # @param Dictionary The placeholder dictionaries
851 def Append(self
, AppendString
, Dictionary
=None):
853 SectionList
= self
._Parse
(AppendString
)
854 self
.String
+= "".join(S
.Instantiate(Dictionary
) for S
in SectionList
)
856 self
.String
+= AppendString
858 ## Replace the string template with dictionary of placeholders
860 # @param Dictionary The placeholder dictionaries
862 # @retval str The string replaced with placeholder values
864 def Replace(self
, Dictionary
=None):
865 return "".join(S
.Instantiate(Dictionary
) for S
in self
._TemplateSectionList
)
867 ## Progress indicator class
869 # This class makes use of thread to print progress on console.
872 # for avoiding deadloop
874 _ProgressThread
= None
875 _CheckInterval
= 0.25
879 # @param OpenMessage The string printed before progress charaters
880 # @param CloseMessage The string printed after progress charaters
881 # @param ProgressChar The charater used to indicate the progress
882 # @param Interval The interval in seconds between two progress charaters
884 def __init__(self
, OpenMessage
="", CloseMessage
="", ProgressChar
='.', Interval
=1.0):
885 self
.PromptMessage
= OpenMessage
886 self
.CodaMessage
= CloseMessage
887 self
.ProgressChar
= ProgressChar
888 self
.Interval
= Interval
889 if Progressor
._StopFlag
is None:
890 Progressor
._StopFlag
= threading
.Event()
892 ## Start to print progress charater
894 # @param OpenMessage The string printed before progress charaters
896 def Start(self
, OpenMessage
=None):
897 if OpenMessage
is not None:
898 self
.PromptMessage
= OpenMessage
899 Progressor
._StopFlag
.clear()
900 if Progressor
._ProgressThread
is None:
901 Progressor
._ProgressThread
= threading
.Thread(target
=self
._ProgressThreadEntry
)
902 Progressor
._ProgressThread
.setDaemon(False)
903 Progressor
._ProgressThread
.start()
905 ## Stop printing progress charater
907 # @param CloseMessage The string printed after progress charaters
909 def Stop(self
, CloseMessage
=None):
910 OriginalCodaMessage
= self
.CodaMessage
911 if CloseMessage
is not None:
912 self
.CodaMessage
= CloseMessage
914 self
.CodaMessage
= OriginalCodaMessage
916 ## Thread entry method
917 def _ProgressThreadEntry(self
):
918 sys
.stdout
.write(self
.PromptMessage
+ " ")
921 while not Progressor
._StopFlag
.isSet():
923 sys
.stdout
.write(self
.ProgressChar
)
925 TimeUp
= self
.Interval
926 time
.sleep(self
._CheckInterval
)
927 TimeUp
-= self
._CheckInterval
928 sys
.stdout
.write(" " + self
.CodaMessage
+ "\n")
931 ## Abort the progress display
934 if Progressor
._StopFlag
is not None:
935 Progressor
._StopFlag
.set()
936 if Progressor
._ProgressThread
is not None:
937 Progressor
._ProgressThread
.join()
938 Progressor
._ProgressThread
= None
940 ## A dict which can access its keys and/or values orderly
942 # The class implements a new kind of dict which its keys or values can be
943 # accessed in the order they are added into the dict. It guarantees the order
944 # by making use of an internal list to keep a copy of keys.
946 class sdict(IterableUserDict
):
949 IterableUserDict
.__init
__(self
)
953 def __setitem__(self
, key
, value
):
954 if key
not in self
._key
_list
:
955 self
._key
_list
.append(key
)
956 IterableUserDict
.__setitem
__(self
, key
, value
)
959 def __delitem__(self
, key
):
960 self
._key
_list
.remove(key
)
961 IterableUserDict
.__delitem
__(self
, key
)
963 ## used in "for k in dict" loop to ensure the correct order
965 return self
.iterkeys()
969 return len(self
._key
_list
)
972 def __contains__(self
, key
):
973 return key
in self
._key
_list
976 def index(self
, key
):
977 return self
._key
_list
.index(key
)
980 def insert(self
, key
, newkey
, newvalue
, order
):
981 index
= self
._key
_list
.index(key
)
982 if order
== 'BEFORE':
983 self
._key
_list
.insert(index
, newkey
)
984 IterableUserDict
.__setitem
__(self
, newkey
, newvalue
)
985 elif order
== 'AFTER':
986 self
._key
_list
.insert(index
+ 1, newkey
)
987 IterableUserDict
.__setitem
__(self
, newkey
, newvalue
)
990 def append(self
, sdict
):
991 for key
in sdict
.keys():
992 if key
not in self
._key
_list
:
993 self
._key
_list
.append(key
)
994 IterableUserDict
.__setitem
__(self
, key
, sdict
[key
])
996 def has_key(self
, key
):
997 return key
in self
._key
_list
1002 IterableUserDict
.clear(self
)
1004 ## Return a copy of keys
1007 for key
in self
._key
_list
:
1011 ## Return a copy of values
1014 for key
in self
._key
_list
:
1015 values
.append(self
[key
])
1018 ## Return a copy of (key, value) list
1021 for key
in self
._key
_list
:
1022 items
.append((key
, self
[key
]))
1025 ## Iteration support
1026 def iteritems(self
):
1027 return iter(self
.items())
1029 ## Keys interation support
1033 ## Values interation support
1034 def itervalues(self
):
1035 return self
.values()
1037 ## Return value related to a key, and remove the (key, value) from the dict
1038 def pop(self
, key
, *dv
):
1040 if key
in self
._key
_list
:
1042 self
.__delitem
__(key
)
1047 ## Return (key, value) pair, and remove the (key, value) from the dict
1049 key
= self
._key
_list
[-1]
1051 self
.__delitem
__(key
)
1054 def update(self
, dict=None, **kwargs
):
1055 if dict is not None:
1056 for k
, v
in dict.items():
1059 for k
, v
in kwargs
.items():
1062 ## Dictionary with restricted keys
1066 def __init__(self
, KeyList
):
1068 dict.__setitem
__(self
, Key
, "")
1071 def __setitem__(self
, key
, value
):
1073 EdkLogger
.error("RestrictedDict", ATTRIBUTE_SET_FAILURE
, "Key [%s] is not allowed" % key
,
1074 ExtraData
=", ".join(dict.keys(self
)))
1075 dict.__setitem
__(self
, key
, value
)
1078 def __getitem__(self
, key
):
1081 return dict.__getitem
__(self
, key
)
1084 def __delitem__(self
, key
):
1085 EdkLogger
.error("RestrictedDict", ATTRIBUTE_ACCESS_DENIED
, ExtraData
="del")
1090 self
.__setitem
__(Key
, "")
1092 ## Return value related to a key, and remove the (key, value) from the dict
1093 def pop(self
, key
, *dv
):
1094 EdkLogger
.error("RestrictedDict", ATTRIBUTE_ACCESS_DENIED
, ExtraData
="pop")
1096 ## Return (key, value) pair, and remove the (key, value) from the dict
1098 EdkLogger
.error("RestrictedDict", ATTRIBUTE_ACCESS_DENIED
, ExtraData
="popitem")
1100 ## Dictionary using prioritized list as key
1103 _ListType
= type([])
1104 _TupleType
= type(())
1105 _Wildcard
= 'COMMON'
1106 _ValidWildcardList
= ['COMMON', 'DEFAULT', 'ALL', '*', 'PLATFORM']
1108 def __init__(self
, _Single_
=False, _Level_
=2):
1109 self
._Level
_ = _Level_
1111 self
._Single
_ = _Single_
1114 def __getitem__(self
, key
):
1117 if KeyType
== self
._ListType
or KeyType
== self
._TupleType
:
1121 elif self
._Level
_ > 1:
1122 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
1125 if self
._Level
_ > 1:
1126 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
1128 if FirstKey
is None or str(FirstKey
).upper() in self
._ValidWildcardList
:
1129 FirstKey
= self
._Wildcard
1132 return self
._GetSingleValue
(FirstKey
, RestKeys
)
1134 return self
._GetAllValues
(FirstKey
, RestKeys
)
1136 def _GetSingleValue(self
, FirstKey
, RestKeys
):
1138 #print "%s-%s" % (FirstKey, self._Level_) ,
1139 if self
._Level
_ > 1:
1140 if FirstKey
== self
._Wildcard
:
1141 if FirstKey
in self
.data
:
1142 Value
= self
.data
[FirstKey
][RestKeys
]
1144 for Key
in self
.data
:
1145 Value
= self
.data
[Key
][RestKeys
]
1146 if Value
is not None: break
1148 if FirstKey
in self
.data
:
1149 Value
= self
.data
[FirstKey
][RestKeys
]
1150 if Value
is None and self
._Wildcard
in self
.data
:
1152 Value
= self
.data
[self
._Wildcard
][RestKeys
]
1154 if FirstKey
== self
._Wildcard
:
1155 if FirstKey
in self
.data
:
1156 Value
= self
.data
[FirstKey
]
1158 for Key
in self
.data
:
1159 Value
= self
.data
[Key
]
1160 if Value
is not None: break
1162 if FirstKey
in self
.data
:
1163 Value
= self
.data
[FirstKey
]
1164 elif self
._Wildcard
in self
.data
:
1165 Value
= self
.data
[self
._Wildcard
]
1168 def _GetAllValues(self
, FirstKey
, RestKeys
):
1170 if self
._Level
_ > 1:
1171 if FirstKey
== self
._Wildcard
:
1172 for Key
in self
.data
:
1173 Value
+= self
.data
[Key
][RestKeys
]
1175 if FirstKey
in self
.data
:
1176 Value
+= self
.data
[FirstKey
][RestKeys
]
1177 if self
._Wildcard
in self
.data
:
1178 Value
+= self
.data
[self
._Wildcard
][RestKeys
]
1180 if FirstKey
== self
._Wildcard
:
1181 for Key
in self
.data
:
1182 Value
.append(self
.data
[Key
])
1184 if FirstKey
in self
.data
:
1185 Value
.append(self
.data
[FirstKey
])
1186 if self
._Wildcard
in self
.data
:
1187 Value
.append(self
.data
[self
._Wildcard
])
1191 def __setitem__(self
, key
, value
):
1194 if KeyType
== self
._ListType
or KeyType
== self
._TupleType
:
1199 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
1202 if self
._Level
_ > 1:
1203 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
1205 if FirstKey
in self
._ValidWildcardList
:
1206 FirstKey
= self
._Wildcard
1208 if FirstKey
not in self
.data
and self
._Level
_ > 0:
1209 self
.data
[FirstKey
] = tdict(self
._Single
_, self
._Level
_ - 1)
1211 if self
._Level
_ > 1:
1212 self
.data
[FirstKey
][RestKeys
] = value
1214 self
.data
[FirstKey
] = value
1216 def SetGreedyMode(self
):
1217 self
._Single
_ = False
1218 if self
._Level
_ > 1:
1219 for Key
in self
.data
:
1220 self
.data
[Key
].SetGreedyMode()
1222 def SetSingleMode(self
):
1223 self
._Single
_ = True
1224 if self
._Level
_ > 1:
1225 for Key
in self
.data
:
1226 self
.data
[Key
].SetSingleMode()
1228 def GetKeys(self
, KeyIndex
=0):
1229 assert KeyIndex
>= 0
1231 return set(self
.data
.keys())
1234 for Key
in self
.data
:
1235 keys |
= self
.data
[Key
].GetKeys(KeyIndex
- 1)
1238 def IsFieldValueAnArray (Value
):
1239 Value
= Value
.strip()
1240 if Value
.startswith(TAB_GUID
) and Value
.endswith(')'):
1242 if Value
.startswith('L"') and Value
.endswith('"') and len(list(Value
[2:-1])) > 1:
1244 if Value
[0] == '"' and Value
[-1] == '"' and len(list(Value
[1:-1])) > 1:
1246 if Value
[0] == '{' and Value
[-1] == '}':
1248 if Value
.startswith("L'") and Value
.endswith("'") and len(list(Value
[2:-1])) > 1:
1250 if Value
[0] == "'" and Value
[-1] == "'" and len(list(Value
[1:-1])) > 1:
1254 def AnalyzePcdExpression(Setting
):
1255 Setting
= Setting
.strip()
1256 # There might be escaped quote in a string: \", \\\" , \', \\\'
1258 # There might be '|' in string and in ( ... | ... ), replace it with '-'
1260 InSingleQuoteStr
= False
1261 InDoubleQuoteStr
= False
1263 for Index
, ch
in enumerate(Data
):
1264 if ch
== '"' and not InSingleQuoteStr
:
1265 if Data
[Index
- 1] != '\\':
1266 InDoubleQuoteStr
= not InDoubleQuoteStr
1267 elif ch
== "'" and not InDoubleQuoteStr
:
1268 if Data
[Index
- 1] != '\\':
1269 InSingleQuoteStr
= not InSingleQuoteStr
1270 elif ch
== '(' and not (InSingleQuoteStr
or InDoubleQuoteStr
):
1272 elif ch
== ')' and not (InSingleQuoteStr
or InDoubleQuoteStr
):
1275 if (Pair
> 0 or InSingleQuoteStr
or InDoubleQuoteStr
) and ch
== TAB_VALUE_SPLIT
:
1282 Pos
= NewStr
.find(TAB_VALUE_SPLIT
, StartPos
)
1284 FieldList
.append(Setting
[StartPos
:].strip())
1286 FieldList
.append(Setting
[StartPos
:Pos
].strip())
1291 def ParseDevPathValue (Value
):
1293 Value
.replace('\\', '/').replace(' ', '')
1295 Cmd
= 'DevicePath ' + '"' + Value
+ '"'
1297 p
= subprocess
.Popen(Cmd
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, shell
=True)
1298 out
, err
= p
.communicate()
1299 except Exception as X
:
1300 raise BadExpression("DevicePath: %s" % (str(X
)) )
1302 subprocess
._cleanup
()
1306 raise BadExpression("DevicePath: %s" % str(err
))
1307 Size
= len(out
.split())
1308 out
= ','.join(out
.decode(encoding
='utf-8', errors
='ignore').split())
1309 return '{' + out
+ '}', Size
1311 def ParseFieldValue (Value
):
1312 if isinstance(Value
, type(0)):
1313 return Value
, (Value
.bit_length() + 7) // 8
1314 if not isinstance(Value
, type('')):
1315 raise BadExpression('Type %s is %s' %(Value
, type(Value
)))
1316 Value
= Value
.strip()
1317 if Value
.startswith(TAB_UINT8
) and Value
.endswith(')'):
1318 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1320 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1322 if Value
.startswith(TAB_UINT16
) and Value
.endswith(')'):
1323 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1325 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1327 if Value
.startswith(TAB_UINT32
) and Value
.endswith(')'):
1328 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1330 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1332 if Value
.startswith(TAB_UINT64
) and Value
.endswith(')'):
1333 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1335 raise BadExpression('Value (%s) Size larger than %d' % (Value
, Size
))
1337 if Value
.startswith(TAB_GUID
) and Value
.endswith(')'):
1338 Value
= Value
.split('(', 1)[1][:-1].strip()
1339 if Value
[0] == '{' and Value
[-1] == '}':
1340 TmpValue
= GuidStructureStringToGuidString(Value
)
1342 raise BadExpression("Invalid GUID value string %s" % Value
)
1344 if Value
[0] == '"' and Value
[-1] == '"':
1347 Value
= "{" + ','.join([str(i
) for i
in uuid
.UUID(Value
).bytes_le
]) + "}"
1348 except ValueError as Message
:
1349 raise BadExpression(Message
)
1350 Value
, Size
= ParseFieldValue(Value
)
1352 if Value
.startswith('L"') and Value
.endswith('"'):
1354 # translate escape character
1364 Value
= (Value
<< 16) |
ord(Char
)
1365 return Value
, (len(List
) + 1) * 2
1366 if Value
.startswith('"') and Value
.endswith('"'):
1368 # translate escape character
1377 Value
= (Value
<< 8) |
ord(Char
)
1378 return Value
, len(List
) + 1
1379 if Value
.startswith("L'") and Value
.endswith("'"):
1380 # Unicode Character Constant
1381 # translate escape character
1389 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1393 Value
= (Value
<< 16) |
ord(Char
)
1394 return Value
, len(List
) * 2
1395 if Value
.startswith("'") and Value
.endswith("'"):
1396 # Character constant
1397 # translate escape character
1404 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1408 Value
= (Value
<< 8) |
ord(Char
)
1409 return Value
, len(List
)
1410 if Value
.startswith('{') and Value
.endswith('}'):
1413 List
= [Item
.strip() for Item
in Value
.split(',')]
1418 ItemValue
, Size
= ParseFieldValue(Item
)
1420 for I
in range(Size
):
1421 Value
= (Value
<< 8) |
((ItemValue
>> 8 * I
) & 0xff)
1422 return Value
, RetSize
1423 if Value
.startswith('DEVICE_PATH(') and Value
.endswith(')'):
1424 Value
= Value
.replace("DEVICE_PATH(", '').rstrip(')')
1425 Value
= Value
.strip().strip('"')
1426 return ParseDevPathValue(Value
)
1427 if Value
.lower().startswith('0x'):
1429 Value
= int(Value
, 16)
1431 raise BadExpression("invalid hex value: %s" % Value
)
1434 return Value
, (Value
.bit_length() + 7) // 8
1435 if Value
[0].isdigit():
1436 Value
= int(Value
, 10)
1439 return Value
, (Value
.bit_length() + 7) // 8
1440 if Value
.lower() == 'true':
1442 if Value
.lower() == 'false':
1448 # Analyze DSC PCD value, since there is no data type info in DSC
1449 # This function is used to match functions (AnalyzePcdData) used for retrieving PCD value from database
1450 # 1. Feature flag: TokenSpace.PcdCName|PcdValue
1451 # 2. Fix and Patch:TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1452 # 3. Dynamic default:
1453 # TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1454 # TokenSpace.PcdCName|PcdValue
1456 # TokenSpace.PcdCName|VpdOffset[|VpdValue]
1457 # TokenSpace.PcdCName|VpdOffset[|MaxSize[|VpdValue]]
1459 # TokenSpace.PcdCName|HiiString|VariableGuid|VariableOffset[|HiiValue]
1460 # PCD value needs to be located in such kind of string, and the PCD value might be an expression in which
1461 # there might have "|" operator, also in string value.
1463 # @param Setting: String contain information described above with "TokenSpace.PcdCName|" stripped
1464 # @param PcdType: PCD type: feature, fixed, dynamic default VPD HII
1465 # @param DataType: The datum type of PCD: VOID*, UNIT, BOOL
1467 # ValueList: A List contain fields described above
1468 # IsValid: True if conforming EBNF, otherwise False
1469 # Index: The index where PcdValue is in ValueList
1471 def AnalyzeDscPcd(Setting
, PcdType
, DataType
=''):
1472 FieldList
= AnalyzePcdExpression(Setting
)
1475 if PcdType
in (MODEL_PCD_FIXED_AT_BUILD
, MODEL_PCD_PATCHABLE_IN_MODULE
, MODEL_PCD_DYNAMIC_DEFAULT
, MODEL_PCD_DYNAMIC_EX_DEFAULT
):
1476 Value
= FieldList
[0]
1478 if len(FieldList
) > 1 and FieldList
[1]:
1479 DataType
= FieldList
[1]
1480 if FieldList
[1] != TAB_VOID
:
1482 if len(FieldList
) > 2:
1486 IsValid
= (len(FieldList
) <= 1)
1488 IsValid
= (len(FieldList
) <= 3)
1492 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1496 return [str(Value
), DataType
, str(Size
)], IsValid
, 0
1497 elif PcdType
== MODEL_PCD_FEATURE_FLAG
:
1498 Value
= FieldList
[0]
1500 IsValid
= (len(FieldList
) <= 1)
1501 return [Value
, DataType
, str(Size
)], IsValid
, 0
1502 elif PcdType
in (MODEL_PCD_DYNAMIC_VPD
, MODEL_PCD_DYNAMIC_EX_VPD
):
1503 VpdOffset
= FieldList
[0]
1505 if not DataType
== TAB_VOID
:
1506 if len(FieldList
) > 1:
1507 Value
= FieldList
[1]
1509 if len(FieldList
) > 1:
1511 if len(FieldList
) > 2:
1512 Value
= FieldList
[2]
1514 IsValid
= (len(FieldList
) <= 1)
1516 IsValid
= (len(FieldList
) <= 3)
1519 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1523 return [VpdOffset
, str(Size
), Value
], IsValid
, 2
1524 elif PcdType
in (MODEL_PCD_DYNAMIC_HII
, MODEL_PCD_DYNAMIC_EX_HII
):
1525 IsValid
= (3 <= len(FieldList
) <= 5)
1526 HiiString
= FieldList
[0]
1527 Guid
= Offset
= Value
= Attribute
= ''
1528 if len(FieldList
) > 1:
1530 if len(FieldList
) > 2:
1531 Offset
= FieldList
[2]
1532 if len(FieldList
) > 3:
1533 Value
= FieldList
[3]
1536 if len(FieldList
) > 4:
1537 Attribute
= FieldList
[4]
1538 return [HiiString
, Guid
, Offset
, Value
, Attribute
], IsValid
, 3
1543 # Analyze the pcd Value, Datum type and TokenNumber.
1544 # Used to avoid split issue while the value string contain "|" character
1546 # @param[in] Setting: A String contain value/datum type/token number information;
1548 # @retval ValueList: A List contain value, datum type and toke number.
1550 def AnalyzePcdData(Setting
):
1551 ValueList
= ['', '', '']
1553 ValueRe
= re
.compile(r
'^\s*L?\".*\|.*\"')
1554 PtrValue
= ValueRe
.findall(Setting
)
1556 ValueUpdateFlag
= False
1558 if len(PtrValue
) >= 1:
1559 Setting
= re
.sub(ValueRe
, '', Setting
)
1560 ValueUpdateFlag
= True
1562 TokenList
= Setting
.split(TAB_VALUE_SPLIT
)
1563 ValueList
[0:len(TokenList
)] = TokenList
1566 ValueList
[0] = PtrValue
[0]
1570 ## check format of PCD value against its the datum type
1572 # For PCD value setting
1574 def CheckPcdDatum(Type
, Value
):
1575 if Type
== TAB_VOID
:
1576 ValueRe
= re
.compile(r
'\s*L?\".*\"\s*$')
1577 if not (((Value
.startswith('L"') or Value
.startswith('"')) and Value
.endswith('"'))
1578 or (Value
.startswith('{') and Value
.endswith('}')) or (Value
.startswith("L'") or Value
.startswith("'") and Value
.endswith("'"))
1580 return False, "Invalid value [%s] of type [%s]; must be in the form of {...} for array"\
1581 ", \"...\" or \'...\' for string, L\"...\" or L\'...\' for unicode string" % (Value
, Type
)
1582 elif ValueRe
.match(Value
):
1583 # Check the chars in UnicodeString or CString is printable
1584 if Value
.startswith("L"):
1588 Printset
= set(string
.printable
)
1589 Printset
.remove(TAB_PRINTCHAR_VT
)
1590 Printset
.add(TAB_PRINTCHAR_BS
)
1591 Printset
.add(TAB_PRINTCHAR_NUL
)
1592 if not set(Value
).issubset(Printset
):
1593 PrintList
= sorted(Printset
)
1594 return False, "Invalid PCD string value of type [%s]; must be printable chars %s." % (Type
, PrintList
)
1595 elif Type
== 'BOOLEAN':
1596 if Value
not in ['TRUE', 'True', 'true', '0x1', '0x01', '1', 'FALSE', 'False', 'false', '0x0', '0x00', '0']:
1597 return False, "Invalid value [%s] of type [%s]; must be one of TRUE, True, true, 0x1, 0x01, 1"\
1598 ", FALSE, False, false, 0x0, 0x00, 0" % (Value
, Type
)
1599 elif Type
in [TAB_UINT8
, TAB_UINT16
, TAB_UINT32
, TAB_UINT64
]:
1604 Val
= int(Value
.lstrip('0'))
1606 return False, "Invalid value [%s] of type [%s];" \
1607 " must be a hexadecimal, decimal or octal in C language format." % (Value
, Type
)
1608 if Val
> MAX_VAL_TYPE
[Type
]:
1609 return False, "Too large PCD value[%s] for datum type [%s]" % (Value
, Type
)
1611 return False, "PCD can't be set to negative value[%s] for datum type [%s]" % (Value
, Type
)
1614 return True, "StructurePcd"
1618 ## Split command line option string to list
1620 # subprocess.Popen needs the args to be a sequence. Otherwise there's problem
1621 # in non-windows platform to launch command
1623 def SplitOption(OptionString
):
1628 for Index
in range(0, len(OptionString
)):
1629 CurrentChar
= OptionString
[Index
]
1630 if CurrentChar
in ['"', "'"]:
1631 if QuotationMark
== CurrentChar
:
1633 elif QuotationMark
== "":
1634 QuotationMark
= CurrentChar
1639 if CurrentChar
in ["/", "-"] and LastChar
in [" ", "\t", "\r", "\n"]:
1640 if Index
> OptionStart
:
1641 OptionList
.append(OptionString
[OptionStart
:Index
- 1])
1643 LastChar
= CurrentChar
1644 OptionList
.append(OptionString
[OptionStart
:])
1647 def CommonPath(PathList
):
1648 P1
= min(PathList
).split(os
.path
.sep
)
1649 P2
= max(PathList
).split(os
.path
.sep
)
1650 for Index
in range(min(len(P1
), len(P2
))):
1651 if P1
[Index
] != P2
[Index
]:
1652 return os
.path
.sep
.join(P1
[:Index
])
1653 return os
.path
.sep
.join(P1
)
1656 # Convert string to C format array
1658 def ConvertStringToByteArray(Value
):
1659 Value
= Value
.strip()
1663 if not Value
.endswith('}'):
1665 Value
= Value
.replace(' ', '').replace('{', '').replace('}', '')
1666 ValFields
= Value
.split(',')
1668 for Index
in range(len(ValFields
)):
1669 ValFields
[Index
] = str(int(ValFields
[Index
], 0))
1672 Value
= '{' + ','.join(ValFields
) + '}'
1676 if Value
.startswith('L"'):
1677 if not Value
.endswith('"'):
1681 elif not Value
.startswith('"') or not Value
.endswith('"'):
1684 Value
= eval(Value
) # translate escape character
1686 for Index
in range(0, len(Value
)):
1688 NewValue
= NewValue
+ str(ord(Value
[Index
]) % 0x10000) + ','
1690 NewValue
= NewValue
+ str(ord(Value
[Index
]) % 0x100) + ','
1691 Value
= NewValue
+ '0}'
1694 class PathClass(object):
1695 def __init__(self
, File
='', Root
='', AlterRoot
='', Type
='', IsBinary
=False,
1696 Arch
='COMMON', ToolChainFamily
='', Target
='', TagName
='', ToolCode
=''):
1698 self
.File
= str(File
)
1699 if os
.path
.isabs(self
.File
):
1703 self
.Root
= str(Root
)
1704 self
.AlterRoot
= str(AlterRoot
)
1706 # Remove any '.' and '..' in path
1708 self
.Root
= mws
.getWs(self
.Root
, self
.File
)
1709 self
.Path
= os
.path
.normpath(os
.path
.join(self
.Root
, self
.File
))
1710 self
.Root
= os
.path
.normpath(CommonPath([self
.Root
, self
.Path
]))
1711 # eliminate the side-effect of 'C:'
1712 if self
.Root
[-1] == ':':
1713 self
.Root
+= os
.path
.sep
1714 # file path should not start with path separator
1715 if self
.Root
[-1] == os
.path
.sep
:
1716 self
.File
= self
.Path
[len(self
.Root
):]
1718 self
.File
= self
.Path
[len(self
.Root
) + 1:]
1720 self
.Path
= os
.path
.normpath(self
.File
)
1722 self
.SubDir
, self
.Name
= os
.path
.split(self
.File
)
1723 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1727 self
.Dir
= os
.path
.join(self
.Root
, self
.SubDir
)
1729 self
.Dir
= self
.Root
1731 self
.Dir
= self
.SubDir
1736 self
.Type
= self
.Ext
.lower()
1738 self
.IsBinary
= IsBinary
1739 self
.Target
= Target
1740 self
.TagName
= TagName
1741 self
.ToolCode
= ToolCode
1742 self
.ToolChainFamily
= ToolChainFamily
1744 ## Convert the object of this class to a string
1746 # Convert member Path of the class to a string
1748 # @retval string Formatted String
1753 ## Override __eq__ function
1755 # Check whether PathClass are the same
1757 # @retval False The two PathClass are different
1758 # @retval True The two PathClass are the same
1760 def __eq__(self
, Other
):
1761 if isinstance(Other
, type(self
)):
1762 return self
.Path
== Other
.Path
1764 return self
.Path
== str(Other
)
1766 ## Override __cmp__ function
1768 # Customize the comparsion operation of two PathClass
1770 # @retval 0 The two PathClass are different
1771 # @retval -1 The first PathClass is less than the second PathClass
1772 # @retval 1 The first PathClass is Bigger than the second PathClass
1773 def __cmp__(self
, Other
):
1774 if isinstance(Other
, type(self
)):
1775 OtherKey
= Other
.Path
1777 OtherKey
= str(Other
)
1780 if SelfKey
== OtherKey
:
1782 elif SelfKey
> OtherKey
:
1787 ## Override __hash__ function
1789 # Use Path as key in hash table
1791 # @retval string Key for hash table
1794 return hash(self
.Path
)
1798 return self
.Path
.upper()
1801 def TimeStamp(self
):
1802 return os
.stat(self
.Path
)[8]
1804 def Validate(self
, Type
='', CaseSensitive
=True):
1805 if GlobalData
.gCaseInsensitive
:
1806 CaseSensitive
= False
1807 if Type
and Type
.lower() != self
.Type
:
1808 return FILE_TYPE_MISMATCH
, '%s (expect %s but got %s)' % (self
.File
, Type
, self
.Type
)
1810 RealFile
, RealRoot
= RealPath2(self
.File
, self
.Root
, self
.AlterRoot
)
1811 if not RealRoot
and not RealFile
:
1812 RealFile
= self
.File
1814 RealFile
= os
.path
.join(self
.AlterRoot
, self
.File
)
1816 RealFile
= os
.path
.join(self
.Root
, self
.File
)
1817 if len (mws
.getPkgPath()) == 0:
1818 return FILE_NOT_FOUND
, os
.path
.join(self
.AlterRoot
, RealFile
)
1820 return FILE_NOT_FOUND
, "%s is not found in packages path:\n\t%s" % (self
.File
, '\n\t'.join(mws
.getPkgPath()))
1824 if RealRoot
!= self
.Root
or RealFile
!= self
.File
:
1825 if CaseSensitive
and (RealFile
!= self
.File
or (RealRoot
!= self
.Root
and RealRoot
!= self
.AlterRoot
)):
1826 ErrorCode
= FILE_CASE_MISMATCH
1827 ErrorInfo
= self
.File
+ '\n\t' + RealFile
+ " [in file system]"
1829 self
.SubDir
, self
.Name
= os
.path
.split(RealFile
)
1830 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1832 self
.Dir
= os
.path
.join(RealRoot
, self
.SubDir
)
1835 self
.File
= RealFile
1836 self
.Root
= RealRoot
1837 self
.Path
= os
.path
.join(RealRoot
, RealFile
)
1838 return ErrorCode
, ErrorInfo
1840 ## Parse PE image to get the required PE informaion.
1842 class PeImageClass():
1845 # @param File FilePath of PeImage
1847 def __init__(self
, PeFile
):
1848 self
.FileName
= PeFile
1849 self
.IsValid
= False
1852 self
.SectionAlignment
= 0
1853 self
.SectionHeaderList
= []
1856 PeObject
= open(PeFile
, 'rb')
1858 self
.ErrorInfo
= self
.FileName
+ ' can not be found\n'
1861 ByteArray
= array
.array('B')
1862 ByteArray
.fromfile(PeObject
, 0x3E)
1863 ByteList
= ByteArray
.tolist()
1864 # DOS signature should be 'MZ'
1865 if self
._ByteListToStr
(ByteList
[0x0:0x2]) != 'MZ':
1866 self
.ErrorInfo
= self
.FileName
+ ' has no valid DOS signature MZ'
1869 # Read 4 byte PE Signature
1870 PeOffset
= self
._ByteListToInt
(ByteList
[0x3C:0x3E])
1871 PeObject
.seek(PeOffset
)
1872 ByteArray
= array
.array('B')
1873 ByteArray
.fromfile(PeObject
, 4)
1874 # PE signature should be 'PE\0\0'
1875 if ByteArray
.tostring() != b
'PE\0\0':
1876 self
.ErrorInfo
= self
.FileName
+ ' has no valid PE signature PE00'
1879 # Read PE file header
1880 ByteArray
= array
.array('B')
1881 ByteArray
.fromfile(PeObject
, 0x14)
1882 ByteList
= ByteArray
.tolist()
1883 SecNumber
= self
._ByteListToInt
(ByteList
[0x2:0x4])
1885 self
.ErrorInfo
= self
.FileName
+ ' has no section header'
1888 # Read PE optional header
1889 OptionalHeaderSize
= self
._ByteListToInt
(ByteArray
[0x10:0x12])
1890 ByteArray
= array
.array('B')
1891 ByteArray
.fromfile(PeObject
, OptionalHeaderSize
)
1892 ByteList
= ByteArray
.tolist()
1893 self
.EntryPoint
= self
._ByteListToInt
(ByteList
[0x10:0x14])
1894 self
.SectionAlignment
= self
._ByteListToInt
(ByteList
[0x20:0x24])
1895 self
.Size
= self
._ByteListToInt
(ByteList
[0x38:0x3C])
1897 # Read each Section Header
1898 for Index
in range(SecNumber
):
1899 ByteArray
= array
.array('B')
1900 ByteArray
.fromfile(PeObject
, 0x28)
1901 ByteList
= ByteArray
.tolist()
1902 SecName
= self
._ByteListToStr
(ByteList
[0:8])
1903 SecVirtualSize
= self
._ByteListToInt
(ByteList
[8:12])
1904 SecRawAddress
= self
._ByteListToInt
(ByteList
[20:24])
1905 SecVirtualAddress
= self
._ByteListToInt
(ByteList
[12:16])
1906 self
.SectionHeaderList
.append((SecName
, SecVirtualAddress
, SecRawAddress
, SecVirtualSize
))
1910 def _ByteListToStr(self
, ByteList
):
1912 for index
in range(len(ByteList
)):
1913 if ByteList
[index
] == 0:
1915 String
+= chr(ByteList
[index
])
1918 def _ByteListToInt(self
, ByteList
):
1920 for index
in range(len(ByteList
) - 1, -1, -1):
1921 Value
= (Value
<< 8) |
int(ByteList
[index
])
1924 class DefaultStore():
1925 def __init__(self
, DefaultStores
):
1927 self
.DefaultStores
= DefaultStores
1928 def DefaultStoreID(self
, DefaultStoreName
):
1929 for key
, value
in self
.DefaultStores
.items():
1930 if value
== DefaultStoreName
:
1933 def GetDefaultDefault(self
):
1934 if not self
.DefaultStores
or "0" in self
.DefaultStores
:
1935 return "0", TAB_DEFAULT_STORES_DEFAULT
1937 minvalue
= min(int(value_str
) for value_str
in self
.DefaultStores
)
1938 return (str(minvalue
), self
.DefaultStores
[str(minvalue
)])
1939 def GetMin(self
, DefaultSIdList
):
1940 if not DefaultSIdList
:
1941 return TAB_DEFAULT_STORES_DEFAULT
1942 storeidset
= {storeid
for storeid
, storename
in self
.DefaultStores
.values() if storename
in DefaultSIdList
}
1945 minid
= min(storeidset
)
1946 for sid
, name
in self
.DefaultStores
.values():
1955 def __init__(self
,SkuIdentifier
='', SkuIds
=None):
1959 for SkuName
in SkuIds
:
1960 SkuId
= SkuIds
[SkuName
][0]
1961 skuid_num
= int(SkuId
, 16) if SkuId
.upper().startswith("0X") else int(SkuId
)
1962 if skuid_num
> 0xFFFFFFFFFFFFFFFF:
1963 EdkLogger
.error("build", PARAMETER_INVALID
,
1964 ExtraData
= "SKU-ID [%s] value %s exceeds the max value of UINT64"
1967 self
.AvailableSkuIds
= OrderedDict()
1969 self
.SkuIdNumberSet
= []
1970 self
.SkuData
= SkuIds
1971 self
._SkuInherit
= {}
1972 self
._SkuIdentifier
= SkuIdentifier
1973 if SkuIdentifier
== '' or SkuIdentifier
is None:
1974 self
.SkuIdSet
= ['DEFAULT']
1975 self
.SkuIdNumberSet
= ['0U']
1976 elif SkuIdentifier
== 'ALL':
1977 self
.SkuIdSet
= list(SkuIds
.keys())
1978 self
.SkuIdNumberSet
= [num
[0].strip() + 'U' for num
in SkuIds
.values()]
1980 r
= SkuIdentifier
.split('|')
1981 self
.SkuIdSet
=[(r
[k
].strip()).upper() for k
in range(len(r
))]
1984 self
.SkuIdNumberSet
= [SkuIds
[k
][0].strip() + 'U' for k
in self
.SkuIdSet
]
1986 EdkLogger
.error("build", PARAMETER_INVALID
,
1987 ExtraData
= "SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1988 % (k
, " | ".join(SkuIds
.keys())))
1989 for each
in self
.SkuIdSet
:
1991 self
.AvailableSkuIds
[each
] = SkuIds
[each
][0]
1993 EdkLogger
.error("build", PARAMETER_INVALID
,
1994 ExtraData
="SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1995 % (each
, " | ".join(SkuIds
.keys())))
1996 if self
.SkuUsageType
!= SkuClass
.SINGLE
:
1997 self
.AvailableSkuIds
.update({'DEFAULT':0, 'COMMON':0})
1999 GlobalData
.gSkuids
= (self
.SkuIdSet
)
2000 if 'COMMON' in GlobalData
.gSkuids
:
2001 GlobalData
.gSkuids
.remove('COMMON')
2002 if self
.SkuUsageType
== self
.SINGLE
:
2003 if len(GlobalData
.gSkuids
) != 1:
2004 if 'DEFAULT' in GlobalData
.gSkuids
:
2005 GlobalData
.gSkuids
.remove('DEFAULT')
2006 if GlobalData
.gSkuids
:
2007 GlobalData
.gSkuids
.sort()
2009 def GetNextSkuId(self
, skuname
):
2010 if not self
._SkuInherit
:
2011 self
._SkuInherit
= {}
2012 for item
in self
.SkuData
.values():
2013 self
._SkuInherit
[item
[1]]=item
[2] if item
[2] else "DEFAULT"
2014 return self
._SkuInherit
.get(skuname
, "DEFAULT")
2016 def GetSkuChain(self
, sku
):
2017 if sku
== "DEFAULT":
2022 nextsku
= self
.GetNextSkuId(nextsku
)
2023 skulist
.append(nextsku
)
2024 if nextsku
== "DEFAULT":
2028 def SkuOverrideOrder(self
):
2030 for skuname
in self
.SkuIdSet
:
2031 skuorderset
.append(self
.GetSkuChain(skuname
))
2034 for index
in range(max(len(item
) for item
in skuorderset
)):
2035 for subset
in skuorderset
:
2036 if index
> len(subset
)-1:
2038 if subset
[index
] in skuorder
:
2040 skuorder
.append(subset
[index
])
2045 def SkuUsageType(self
):
2046 if self
._SkuIdentifier
.upper() == "ALL":
2047 return SkuClass
.MULTIPLE
2049 if len(self
.SkuIdSet
) == 1:
2050 if self
.SkuIdSet
[0] == 'DEFAULT':
2051 return SkuClass
.DEFAULT
2052 return SkuClass
.SINGLE
2053 if len(self
.SkuIdSet
) == 2 and 'DEFAULT' in self
.SkuIdSet
:
2054 return SkuClass
.SINGLE
2055 return SkuClass
.MULTIPLE
2057 def DumpSkuIdArrary(self
):
2058 if self
.SkuUsageType
== SkuClass
.SINGLE
:
2061 for skuname
in self
.AvailableSkuIds
:
2062 if skuname
== "COMMON":
2064 while skuname
!= "DEFAULT":
2065 ArrayStrList
.append(hex(int(self
.AvailableSkuIds
[skuname
])))
2066 skuname
= self
.GetNextSkuId(skuname
)
2067 ArrayStrList
.append("0x0")
2068 return "{{{myList}}}".format(myList
=",".join(ArrayStrList
))
2071 def AvailableSkuIdSet(self
):
2072 return self
.AvailableSkuIds
2075 def SystemSkuId(self
):
2076 if self
.SkuUsageType
== SkuClass
.SINGLE
:
2077 if len(self
.SkuIdSet
) == 1:
2078 return self
.SkuIdSet
[0]
2080 return self
.SkuIdSet
[0] if self
.SkuIdSet
[0] != 'DEFAULT' else self
.SkuIdSet
[1]
2085 # Pack a registry format GUID
2087 def PackRegistryFormatGuid(Guid
):
2088 return PackGUID(Guid
.split('-'))
2090 ## Get the integer value from string like "14U" or integer like 2
2092 # @param Input The object that may be either a integer value or a string
2094 # @retval Value The integer value that the input represents
2096 def GetIntegerValue(Input
):
2097 if isinstance(Input
, int):
2100 if String
.endswith("U"):
2101 String
= String
[:-1]
2102 if String
.endswith("ULL"):
2103 String
= String
[:-3]
2104 if String
.endswith("LL"):
2105 String
= String
[:-2]
2107 if String
.startswith("0x") or String
.startswith("0X"):
2108 return int(String
, 16)
2115 # Pack a GUID (registry format) list into a buffer and return it
2118 return pack(PACK_PATTERN_GUID
,
2122 int(Guid
[3][-4:-2], 16),
2123 int(Guid
[3][-2:], 16),
2124 int(Guid
[4][-12:-10], 16),
2125 int(Guid
[4][-10:-8], 16),
2126 int(Guid
[4][-8:-6], 16),
2127 int(Guid
[4][-6:-4], 16),
2128 int(Guid
[4][-4:-2], 16),
2129 int(Guid
[4][-2:], 16)
2133 # Pack a GUID (byte) list into a buffer and return it
2135 def PackByteFormatGUID(Guid
):
2136 return pack(PACK_PATTERN_GUID
,
2152 # This acts like the main() function for the script, unless it is 'import'ed into another
2155 if __name__
== '__main__':