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 UserDict
import IterableUserDict
28 from UserList
import UserList
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):
458 Content
= Content
.replace("\n", os
.linesep
)
460 if os
.path
.exists(File
):
462 if isinstance(Content
, bytes
):
463 with
open(File
, "rb") as f
:
464 if Content
== f
.read():
467 with
open(File
, "r") as f
:
468 if Content
== f
.read():
471 EdkLogger
.error(None, FILE_OPEN_FAILURE
, ExtraData
=File
)
473 DirName
= os
.path
.dirname(File
)
474 if not CreateDirectory(DirName
):
475 EdkLogger
.error(None, FILE_CREATE_FAILURE
, "Could not create directory %s" % DirName
)
478 DirName
= os
.getcwd()
479 if not os
.access(DirName
, os
.W_OK
):
480 EdkLogger
.error(None, PERMISSION_FAILURE
, "Do not have write permission on directory %s" % DirName
)
483 if GlobalData
.gIsWindows
:
485 from .PyUtility
import SaveFileToDisk
486 if not SaveFileToDisk(File
, Content
):
487 EdkLogger
.error(None, FILE_CREATE_FAILURE
, ExtraData
=File
)
489 if isinstance(Content
, bytes
):
490 with
open(File
, "wb") as Fd
:
493 with
open(File
, "w") as Fd
:
496 if isinstance(Content
, bytes
):
497 with
open(File
, "wb") as Fd
:
500 with
open(File
, "w") as Fd
:
503 EdkLogger
.error(None, FILE_CREATE_FAILURE
, ExtraData
='IOError %s' % X
)
507 ## Make a Python object persistent on file system
509 # @param Data The object to be stored in file
510 # @param File The path of file to store the object
512 def DataDump(Data
, File
):
515 Fd
= open(File
, 'wb')
516 pickle
.dump(Data
, Fd
, pickle
.HIGHEST_PROTOCOL
)
518 EdkLogger
.error("", FILE_OPEN_FAILURE
, ExtraData
=File
, RaiseError
=False)
523 ## Restore a Python object from a file
525 # @param File The path of file stored the object
527 # @retval object A python object
528 # @retval None If failure in file operation
530 def DataRestore(File
):
534 Fd
= open(File
, 'rb')
535 Data
= pickle
.load(Fd
)
536 except Exception as e
:
537 EdkLogger
.verbose("Failed to load [%s]\n\t%s" % (File
, str(e
)))
544 ## Retrieve and cache the real path name in file system
546 # @param Root The root directory of path relative to
548 # @retval str The path string if the path exists
549 # @retval None If path doesn't exist
555 def __init__(self
, Root
):
557 for F
in os
.listdir(Root
):
559 self
._UPPER
_CACHE
_[F
.upper()] = F
562 def __getitem__(self
, Path
):
563 Path
= Path
[len(os
.path
.commonprefix([Path
, self
._Root
])):]
566 if Path
and Path
[0] == os
.path
.sep
:
568 if Path
in self
._CACHE
_:
569 return os
.path
.join(self
._Root
, Path
)
570 UpperPath
= Path
.upper()
571 if UpperPath
in self
._UPPER
_CACHE
_:
572 return os
.path
.join(self
._Root
, self
._UPPER
_CACHE
_[UpperPath
])
576 SepIndex
= Path
.find(os
.path
.sep
)
578 Parent
= UpperPath
[:SepIndex
]
579 if Parent
not in self
._UPPER
_CACHE
_:
581 LastSepIndex
= SepIndex
582 SepIndex
= Path
.find(os
.path
.sep
, LastSepIndex
+ 1)
584 if LastSepIndex
== -1:
589 SepIndex
= LastSepIndex
591 Parent
= Path
[:SepIndex
]
592 ParentKey
= UpperPath
[:SepIndex
]
593 if ParentKey
not in self
._UPPER
_CACHE
_:
597 if Parent
in self
._CACHE
_:
600 ParentDir
= self
._UPPER
_CACHE
_[ParentKey
]
601 for F
in os
.listdir(ParentDir
):
602 Dir
= os
.path
.join(ParentDir
, F
)
603 self
._CACHE
_.add(Dir
)
604 self
._UPPER
_CACHE
_[Dir
.upper()] = Dir
606 SepIndex
= Path
.find(os
.path
.sep
, SepIndex
+ 1)
609 if Path
in self
._CACHE
_:
610 return os
.path
.join(self
._Root
, Path
)
611 elif UpperPath
in self
._UPPER
_CACHE
_:
612 return os
.path
.join(self
._Root
, self
._UPPER
_CACHE
_[UpperPath
])
615 def RealPath(File
, Dir
='', OverrideDir
=''):
616 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
617 NewFile
= GlobalData
.gAllFiles
[NewFile
]
618 if not NewFile
and OverrideDir
:
619 NewFile
= os
.path
.normpath(os
.path
.join(OverrideDir
, File
))
620 NewFile
= GlobalData
.gAllFiles
[NewFile
]
623 def RealPath2(File
, Dir
='', OverrideDir
=''):
626 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(OverrideDir
, File
))]
628 if OverrideDir
[-1] == os
.path
.sep
:
629 return NewFile
[len(OverrideDir
):], NewFile
[0:len(OverrideDir
)]
631 return NewFile
[len(OverrideDir
) + 1:], NewFile
[0:len(OverrideDir
)]
632 if GlobalData
.gAllFiles
:
633 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(Dir
, File
))]
635 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
636 if not os
.path
.exists(NewFile
):
640 if Dir
[-1] == os
.path
.sep
:
641 return NewFile
[len(Dir
):], NewFile
[0:len(Dir
)]
643 return NewFile
[len(Dir
) + 1:], NewFile
[0:len(Dir
)]
649 ## Get GUID value from given packages
651 # @param CName The CName of the GUID
652 # @param PackageList List of packages looking-up in
653 # @param Inffile The driver file
655 # @retval GuidValue if the CName is found in any given package
656 # @retval None if the CName is not found in all given packages
658 def GuidValue(CName
, PackageList
, Inffile
= None):
659 for P
in PackageList
:
660 GuidKeys
= P
.Guids
.keys()
661 if Inffile
and P
._PrivateGuids
:
662 if not Inffile
.startswith(P
.MetaFile
.Dir
):
663 GuidKeys
= [x
for x
in P
.Guids
if x
not in P
._PrivateGuids
]
664 if CName
in GuidKeys
:
665 return P
.Guids
[CName
]
668 ## Get Protocol value from given packages
670 # @param CName The CName of the GUID
671 # @param PackageList List of packages looking-up in
672 # @param Inffile The driver file
674 # @retval GuidValue if the CName is found in any given package
675 # @retval None if the CName is not found in all given packages
677 def ProtocolValue(CName
, PackageList
, Inffile
= None):
678 for P
in PackageList
:
679 ProtocolKeys
= P
.Protocols
.keys()
680 if Inffile
and P
._PrivateProtocols
:
681 if not Inffile
.startswith(P
.MetaFile
.Dir
):
682 ProtocolKeys
= [x
for x
in P
.Protocols
if x
not in P
._PrivateProtocols
]
683 if CName
in ProtocolKeys
:
684 return P
.Protocols
[CName
]
687 ## Get PPI value from given packages
689 # @param CName The CName of the GUID
690 # @param PackageList List of packages looking-up in
691 # @param Inffile The driver file
693 # @retval GuidValue if the CName is found in any given package
694 # @retval None if the CName is not found in all given packages
696 def PpiValue(CName
, PackageList
, Inffile
= None):
697 for P
in PackageList
:
698 PpiKeys
= P
.Ppis
.keys()
699 if Inffile
and P
._PrivatePpis
:
700 if not Inffile
.startswith(P
.MetaFile
.Dir
):
701 PpiKeys
= [x
for x
in P
.Ppis
if x
not in P
._PrivatePpis
]
706 ## A string template class
708 # This class implements a template for string replacement. A string template
709 # looks like following
711 # ${BEGIN} other_string ${placeholder_name} other_string ${END}
713 # The string between ${BEGIN} and ${END} will be repeated as many times as the
714 # length of "placeholder_name", which is a list passed through a dict. The
715 # "placeholder_name" is the key name of the dict. The ${BEGIN} and ${END} can
716 # be not used and, in this case, the "placeholder_name" must not a list and it
717 # will just be replaced once.
719 class TemplateString(object):
720 _REPEAT_START_FLAG
= "BEGIN"
721 _REPEAT_END_FLAG
= "END"
723 class Section(object):
724 _LIST_TYPES
= [type([]), type(set()), type((0,))]
726 def __init__(self
, TemplateSection
, PlaceHolderList
):
727 self
._Template
= TemplateSection
728 self
._PlaceHolderList
= []
730 # Split the section into sub-sections according to the position of placeholders
732 self
._SubSectionList
= []
735 # The placeholders passed in must be in the format of
737 # PlaceHolderName, PlaceHolderStartPoint, PlaceHolderEndPoint
739 for PlaceHolder
, Start
, End
in PlaceHolderList
:
740 self
._SubSectionList
.append(TemplateSection
[SubSectionStart
:Start
])
741 self
._SubSectionList
.append(TemplateSection
[Start
:End
])
742 self
._PlaceHolderList
.append(PlaceHolder
)
743 SubSectionStart
= End
744 if SubSectionStart
< len(TemplateSection
):
745 self
._SubSectionList
.append(TemplateSection
[SubSectionStart
:])
747 self
._SubSectionList
= [TemplateSection
]
750 return self
._Template
+ " : " + str(self
._PlaceHolderList
)
752 def Instantiate(self
, PlaceHolderValues
):
754 RepeatPlaceHolders
= {}
755 NonRepeatPlaceHolders
= {}
757 for PlaceHolder
in self
._PlaceHolderList
:
758 if PlaceHolder
not in PlaceHolderValues
:
760 Value
= PlaceHolderValues
[PlaceHolder
]
761 if type(Value
) in self
._LIST
_TYPES
:
763 RepeatTime
= len(Value
)
764 elif RepeatTime
!= len(Value
):
768 "${%s} has different repeat time from others!" % PlaceHolder
,
769 ExtraData
=str(self
._Template
)
771 RepeatPlaceHolders
["${%s}" % PlaceHolder
] = Value
773 NonRepeatPlaceHolders
["${%s}" % PlaceHolder
] = Value
775 if NonRepeatPlaceHolders
:
777 for S
in self
._SubSectionList
:
778 if S
not in NonRepeatPlaceHolders
:
781 StringList
.append(str(NonRepeatPlaceHolders
[S
]))
783 StringList
= self
._SubSectionList
785 if RepeatPlaceHolders
:
787 for Index
in range(RepeatTime
):
789 if S
not in RepeatPlaceHolders
:
790 TempStringList
.append(S
)
792 TempStringList
.append(str(RepeatPlaceHolders
[S
][Index
]))
793 StringList
= TempStringList
795 return "".join(StringList
)
798 def __init__(self
, Template
=None):
800 self
.IsBinary
= False
801 self
._Template
= Template
802 self
._TemplateSectionList
= self
._Parse
(Template
)
806 # @retval string The string replaced
811 ## Split the template string into fragments per the ${BEGIN} and ${END} flags
813 # @retval list A list of TemplateString.Section objects
815 def _Parse(self
, Template
):
820 TemplateSectionList
= []
822 MatchObj
= gPlaceholderPattern
.search(Template
, SearchFrom
)
824 if MatchEnd
<= len(Template
):
825 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:], PlaceHolderList
)
826 TemplateSectionList
.append(TemplateSection
)
829 MatchString
= MatchObj
.group(1)
830 MatchStart
= MatchObj
.start()
831 MatchEnd
= MatchObj
.end()
833 if MatchString
== self
._REPEAT
_START
_FLAG
:
834 if MatchStart
> SectionStart
:
835 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:MatchStart
], PlaceHolderList
)
836 TemplateSectionList
.append(TemplateSection
)
837 SectionStart
= MatchEnd
839 elif MatchString
== self
._REPEAT
_END
_FLAG
:
840 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:MatchStart
], PlaceHolderList
)
841 TemplateSectionList
.append(TemplateSection
)
842 SectionStart
= MatchEnd
845 PlaceHolderList
.append((MatchString
, MatchStart
- SectionStart
, MatchEnd
- SectionStart
))
846 SearchFrom
= MatchEnd
847 return TemplateSectionList
849 ## Replace the string template with dictionary of placeholders and append it to previous one
851 # @param AppendString The string template to append
852 # @param Dictionary The placeholder dictionaries
854 def Append(self
, AppendString
, Dictionary
=None):
856 SectionList
= self
._Parse
(AppendString
)
857 self
.String
+= "".join(S
.Instantiate(Dictionary
) for S
in SectionList
)
859 self
.String
+= AppendString
861 ## Replace the string template with dictionary of placeholders
863 # @param Dictionary The placeholder dictionaries
865 # @retval str The string replaced with placeholder values
867 def Replace(self
, Dictionary
=None):
868 return "".join(S
.Instantiate(Dictionary
) for S
in self
._TemplateSectionList
)
870 ## Progress indicator class
872 # This class makes use of thread to print progress on console.
875 # for avoiding deadloop
877 _ProgressThread
= None
878 _CheckInterval
= 0.25
882 # @param OpenMessage The string printed before progress charaters
883 # @param CloseMessage The string printed after progress charaters
884 # @param ProgressChar The charater used to indicate the progress
885 # @param Interval The interval in seconds between two progress charaters
887 def __init__(self
, OpenMessage
="", CloseMessage
="", ProgressChar
='.', Interval
=1.0):
888 self
.PromptMessage
= OpenMessage
889 self
.CodaMessage
= CloseMessage
890 self
.ProgressChar
= ProgressChar
891 self
.Interval
= Interval
892 if Progressor
._StopFlag
is None:
893 Progressor
._StopFlag
= threading
.Event()
895 ## Start to print progress charater
897 # @param OpenMessage The string printed before progress charaters
899 def Start(self
, OpenMessage
=None):
900 if OpenMessage
is not None:
901 self
.PromptMessage
= OpenMessage
902 Progressor
._StopFlag
.clear()
903 if Progressor
._ProgressThread
is None:
904 Progressor
._ProgressThread
= threading
.Thread(target
=self
._ProgressThreadEntry
)
905 Progressor
._ProgressThread
.setDaemon(False)
906 Progressor
._ProgressThread
.start()
908 ## Stop printing progress charater
910 # @param CloseMessage The string printed after progress charaters
912 def Stop(self
, CloseMessage
=None):
913 OriginalCodaMessage
= self
.CodaMessage
914 if CloseMessage
is not None:
915 self
.CodaMessage
= CloseMessage
917 self
.CodaMessage
= OriginalCodaMessage
919 ## Thread entry method
920 def _ProgressThreadEntry(self
):
921 sys
.stdout
.write(self
.PromptMessage
+ " ")
924 while not Progressor
._StopFlag
.isSet():
926 sys
.stdout
.write(self
.ProgressChar
)
928 TimeUp
= self
.Interval
929 time
.sleep(self
._CheckInterval
)
930 TimeUp
-= self
._CheckInterval
931 sys
.stdout
.write(" " + self
.CodaMessage
+ "\n")
934 ## Abort the progress display
937 if Progressor
._StopFlag
is not None:
938 Progressor
._StopFlag
.set()
939 if Progressor
._ProgressThread
is not None:
940 Progressor
._ProgressThread
.join()
941 Progressor
._ProgressThread
= None
943 ## A dict which can access its keys and/or values orderly
945 # The class implements a new kind of dict which its keys or values can be
946 # accessed in the order they are added into the dict. It guarantees the order
947 # by making use of an internal list to keep a copy of keys.
949 class sdict(IterableUserDict
):
952 IterableUserDict
.__init
__(self
)
956 def __setitem__(self
, key
, value
):
957 if key
not in self
._key
_list
:
958 self
._key
_list
.append(key
)
959 IterableUserDict
.__setitem
__(self
, key
, value
)
962 def __delitem__(self
, key
):
963 self
._key
_list
.remove(key
)
964 IterableUserDict
.__delitem
__(self
, key
)
966 ## used in "for k in dict" loop to ensure the correct order
968 return self
.iterkeys()
972 return len(self
._key
_list
)
975 def __contains__(self
, key
):
976 return key
in self
._key
_list
979 def index(self
, key
):
980 return self
._key
_list
.index(key
)
983 def insert(self
, key
, newkey
, newvalue
, order
):
984 index
= self
._key
_list
.index(key
)
985 if order
== 'BEFORE':
986 self
._key
_list
.insert(index
, newkey
)
987 IterableUserDict
.__setitem
__(self
, newkey
, newvalue
)
988 elif order
== 'AFTER':
989 self
._key
_list
.insert(index
+ 1, newkey
)
990 IterableUserDict
.__setitem
__(self
, newkey
, newvalue
)
993 def append(self
, sdict
):
995 if key
not in self
._key
_list
:
996 self
._key
_list
.append(key
)
997 IterableUserDict
.__setitem
__(self
, key
, sdict
[key
])
999 def has_key(self
, key
):
1000 return key
in self
._key
_list
1005 IterableUserDict
.clear(self
)
1007 ## Return a copy of keys
1010 for key
in self
._key
_list
:
1014 ## Return a copy of values
1017 for key
in self
._key
_list
:
1018 values
.append(self
[key
])
1021 ## Return a copy of (key, value) list
1024 for key
in self
._key
_list
:
1025 items
.append((key
, self
[key
]))
1028 ## Iteration support
1029 def iteritems(self
):
1030 return iter(self
.items())
1032 ## Keys interation support
1036 ## Values interation support
1037 def itervalues(self
):
1038 return self
.values()
1040 ## Return value related to a key, and remove the (key, value) from the dict
1041 def pop(self
, key
, *dv
):
1043 if key
in self
._key
_list
:
1045 self
.__delitem
__(key
)
1050 ## Return (key, value) pair, and remove the (key, value) from the dict
1052 key
= self
._key
_list
[-1]
1054 self
.__delitem
__(key
)
1057 def update(self
, dict=None, **kwargs
):
1058 if dict is not None:
1059 for k
, v
in dict.items():
1062 for k
, v
in kwargs
.items():
1065 ## Dictionary with restricted keys
1069 def __init__(self
, KeyList
):
1071 dict.__setitem
__(self
, Key
, "")
1074 def __setitem__(self
, key
, value
):
1076 EdkLogger
.error("RestrictedDict", ATTRIBUTE_SET_FAILURE
, "Key [%s] is not allowed" % key
,
1077 ExtraData
=", ".join(dict.keys(self
)))
1078 dict.__setitem
__(self
, key
, value
)
1081 def __getitem__(self
, key
):
1084 return dict.__getitem
__(self
, key
)
1087 def __delitem__(self
, key
):
1088 EdkLogger
.error("RestrictedDict", ATTRIBUTE_ACCESS_DENIED
, ExtraData
="del")
1093 self
.__setitem
__(Key
, "")
1095 ## Return value related to a key, and remove the (key, value) from the dict
1096 def pop(self
, key
, *dv
):
1097 EdkLogger
.error("RestrictedDict", ATTRIBUTE_ACCESS_DENIED
, ExtraData
="pop")
1099 ## Return (key, value) pair, and remove the (key, value) from the dict
1101 EdkLogger
.error("RestrictedDict", ATTRIBUTE_ACCESS_DENIED
, ExtraData
="popitem")
1103 ## Dictionary using prioritized list as key
1106 _ListType
= type([])
1107 _TupleType
= type(())
1108 _Wildcard
= 'COMMON'
1109 _ValidWildcardList
= ['COMMON', 'DEFAULT', 'ALL', '*', 'PLATFORM']
1111 def __init__(self
, _Single_
=False, _Level_
=2):
1112 self
._Level
_ = _Level_
1114 self
._Single
_ = _Single_
1117 def __getitem__(self
, key
):
1120 if KeyType
== self
._ListType
or KeyType
== self
._TupleType
:
1124 elif self
._Level
_ > 1:
1125 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
1128 if self
._Level
_ > 1:
1129 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
1131 if FirstKey
is None or str(FirstKey
).upper() in self
._ValidWildcardList
:
1132 FirstKey
= self
._Wildcard
1135 return self
._GetSingleValue
(FirstKey
, RestKeys
)
1137 return self
._GetAllValues
(FirstKey
, RestKeys
)
1139 def _GetSingleValue(self
, FirstKey
, RestKeys
):
1141 #print "%s-%s" % (FirstKey, self._Level_) ,
1142 if self
._Level
_ > 1:
1143 if FirstKey
== self
._Wildcard
:
1144 if FirstKey
in self
.data
:
1145 Value
= self
.data
[FirstKey
][RestKeys
]
1147 for Key
in self
.data
:
1148 Value
= self
.data
[Key
][RestKeys
]
1149 if Value
is not None: break
1151 if FirstKey
in self
.data
:
1152 Value
= self
.data
[FirstKey
][RestKeys
]
1153 if Value
is None and self
._Wildcard
in self
.data
:
1155 Value
= self
.data
[self
._Wildcard
][RestKeys
]
1157 if FirstKey
== self
._Wildcard
:
1158 if FirstKey
in self
.data
:
1159 Value
= self
.data
[FirstKey
]
1161 for Key
in self
.data
:
1162 Value
= self
.data
[Key
]
1163 if Value
is not None: break
1165 if FirstKey
in self
.data
:
1166 Value
= self
.data
[FirstKey
]
1167 elif self
._Wildcard
in self
.data
:
1168 Value
= self
.data
[self
._Wildcard
]
1171 def _GetAllValues(self
, FirstKey
, RestKeys
):
1173 if self
._Level
_ > 1:
1174 if FirstKey
== self
._Wildcard
:
1175 for Key
in self
.data
:
1176 Value
+= self
.data
[Key
][RestKeys
]
1178 if FirstKey
in self
.data
:
1179 Value
+= self
.data
[FirstKey
][RestKeys
]
1180 if self
._Wildcard
in self
.data
:
1181 Value
+= self
.data
[self
._Wildcard
][RestKeys
]
1183 if FirstKey
== self
._Wildcard
:
1184 for Key
in self
.data
:
1185 Value
.append(self
.data
[Key
])
1187 if FirstKey
in self
.data
:
1188 Value
.append(self
.data
[FirstKey
])
1189 if self
._Wildcard
in self
.data
:
1190 Value
.append(self
.data
[self
._Wildcard
])
1194 def __setitem__(self
, key
, value
):
1197 if KeyType
== self
._ListType
or KeyType
== self
._TupleType
:
1202 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
1205 if self
._Level
_ > 1:
1206 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
1208 if FirstKey
in self
._ValidWildcardList
:
1209 FirstKey
= self
._Wildcard
1211 if FirstKey
not in self
.data
and self
._Level
_ > 0:
1212 self
.data
[FirstKey
] = tdict(self
._Single
_, self
._Level
_ - 1)
1214 if self
._Level
_ > 1:
1215 self
.data
[FirstKey
][RestKeys
] = value
1217 self
.data
[FirstKey
] = value
1219 def SetGreedyMode(self
):
1220 self
._Single
_ = False
1221 if self
._Level
_ > 1:
1222 for Key
in self
.data
:
1223 self
.data
[Key
].SetGreedyMode()
1225 def SetSingleMode(self
):
1226 self
._Single
_ = True
1227 if self
._Level
_ > 1:
1228 for Key
in self
.data
:
1229 self
.data
[Key
].SetSingleMode()
1231 def GetKeys(self
, KeyIndex
=0):
1232 assert KeyIndex
>= 0
1234 return set(self
.data
.keys())
1237 for Key
in self
.data
:
1238 keys |
= self
.data
[Key
].GetKeys(KeyIndex
- 1)
1241 def IsFieldValueAnArray (Value
):
1242 Value
= Value
.strip()
1243 if Value
.startswith(TAB_GUID
) and Value
.endswith(')'):
1245 if Value
.startswith('L"') and Value
.endswith('"') and len(list(Value
[2:-1])) > 1:
1247 if Value
[0] == '"' and Value
[-1] == '"' and len(list(Value
[1:-1])) > 1:
1249 if Value
[0] == '{' and Value
[-1] == '}':
1251 if Value
.startswith("L'") and Value
.endswith("'") and len(list(Value
[2:-1])) > 1:
1253 if Value
[0] == "'" and Value
[-1] == "'" and len(list(Value
[1:-1])) > 1:
1257 def AnalyzePcdExpression(Setting
):
1258 Setting
= Setting
.strip()
1259 # There might be escaped quote in a string: \", \\\" , \', \\\'
1261 # There might be '|' in string and in ( ... | ... ), replace it with '-'
1263 InSingleQuoteStr
= False
1264 InDoubleQuoteStr
= False
1266 for Index
, ch
in enumerate(Data
):
1267 if ch
== '"' and not InSingleQuoteStr
:
1268 if Data
[Index
- 1] != '\\':
1269 InDoubleQuoteStr
= not InDoubleQuoteStr
1270 elif ch
== "'" and not InDoubleQuoteStr
:
1271 if Data
[Index
- 1] != '\\':
1272 InSingleQuoteStr
= not InSingleQuoteStr
1273 elif ch
== '(' and not (InSingleQuoteStr
or InDoubleQuoteStr
):
1275 elif ch
== ')' and not (InSingleQuoteStr
or InDoubleQuoteStr
):
1278 if (Pair
> 0 or InSingleQuoteStr
or InDoubleQuoteStr
) and ch
== TAB_VALUE_SPLIT
:
1285 Pos
= NewStr
.find(TAB_VALUE_SPLIT
, StartPos
)
1287 FieldList
.append(Setting
[StartPos
:].strip())
1289 FieldList
.append(Setting
[StartPos
:Pos
].strip())
1294 def ParseDevPathValue (Value
):
1296 Value
.replace('\\', '/').replace(' ', '')
1298 Cmd
= 'DevicePath ' + '"' + Value
+ '"'
1300 p
= subprocess
.Popen(Cmd
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, shell
=True)
1301 out
, err
= p
.communicate()
1302 except Exception as X
:
1303 raise BadExpression("DevicePath: %s" % (str(X
)) )
1305 subprocess
._cleanup
()
1309 raise BadExpression("DevicePath: %s" % str(err
))
1310 Size
= len(out
.split())
1311 out
= ','.join(out
.split())
1312 return '{' + out
+ '}', Size
1314 def ParseFieldValue (Value
):
1315 if isinstance(Value
, type(0)):
1316 return Value
, (Value
.bit_length() + 7) / 8
1317 if not isinstance(Value
, type('')):
1318 raise BadExpression('Type %s is %s' %(Value
, type(Value
)))
1319 Value
= Value
.strip()
1320 if Value
.startswith(TAB_UINT8
) and Value
.endswith(')'):
1321 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1323 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1325 if Value
.startswith(TAB_UINT16
) and Value
.endswith(')'):
1326 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1328 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1330 if Value
.startswith(TAB_UINT32
) and Value
.endswith(')'):
1331 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1333 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1335 if Value
.startswith(TAB_UINT64
) and Value
.endswith(')'):
1336 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1338 raise BadExpression('Value (%s) Size larger than %d' % (Value
, Size
))
1340 if Value
.startswith(TAB_GUID
) and Value
.endswith(')'):
1341 Value
= Value
.split('(', 1)[1][:-1].strip()
1342 if Value
[0] == '{' and Value
[-1] == '}':
1343 TmpValue
= GuidStructureStringToGuidString(Value
)
1345 raise BadExpression("Invalid GUID value string %s" % Value
)
1347 if Value
[0] == '"' and Value
[-1] == '"':
1350 Value
= "'" + uuid
.UUID(Value
).get_bytes_le() + "'"
1351 except ValueError as Message
:
1352 raise BadExpression(Message
)
1353 Value
, Size
= ParseFieldValue(Value
)
1355 if Value
.startswith('L"') and Value
.endswith('"'):
1357 # translate escape character
1367 Value
= (Value
<< 16) |
ord(Char
)
1368 return Value
, (len(List
) + 1) * 2
1369 if Value
.startswith('"') and Value
.endswith('"'):
1371 # translate escape character
1380 Value
= (Value
<< 8) |
ord(Char
)
1381 return Value
, len(List
) + 1
1382 if Value
.startswith("L'") and Value
.endswith("'"):
1383 # Unicode Character Constant
1384 # translate escape character
1392 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1396 Value
= (Value
<< 16) |
ord(Char
)
1397 return Value
, len(List
) * 2
1398 if Value
.startswith("'") and Value
.endswith("'"):
1399 # Character constant
1400 # translate escape character
1407 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1411 Value
= (Value
<< 8) |
ord(Char
)
1412 return Value
, len(List
)
1413 if Value
.startswith('{') and Value
.endswith('}'):
1416 List
= [Item
.strip() for Item
in Value
.split(',')]
1421 ItemValue
, Size
= ParseFieldValue(Item
)
1423 for I
in range(Size
):
1424 Value
= (Value
<< 8) |
((ItemValue
>> 8 * I
) & 0xff)
1425 return Value
, RetSize
1426 if Value
.startswith('DEVICE_PATH(') and Value
.endswith(')'):
1427 Value
= Value
.replace("DEVICE_PATH(", '').rstrip(')')
1428 Value
= Value
.strip().strip('"')
1429 return ParseDevPathValue(Value
)
1430 if Value
.lower().startswith('0x'):
1432 Value
= int(Value
, 16)
1434 raise BadExpression("invalid hex value: %s" % Value
)
1437 return Value
, (Value
.bit_length() + 7) / 8
1438 if Value
[0].isdigit():
1439 Value
= int(Value
, 10)
1442 return Value
, (Value
.bit_length() + 7) / 8
1443 if Value
.lower() == 'true':
1445 if Value
.lower() == 'false':
1451 # Analyze DSC PCD value, since there is no data type info in DSC
1452 # This function is used to match functions (AnalyzePcdData) used for retrieving PCD value from database
1453 # 1. Feature flag: TokenSpace.PcdCName|PcdValue
1454 # 2. Fix and Patch:TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1455 # 3. Dynamic default:
1456 # TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1457 # TokenSpace.PcdCName|PcdValue
1459 # TokenSpace.PcdCName|VpdOffset[|VpdValue]
1460 # TokenSpace.PcdCName|VpdOffset[|MaxSize[|VpdValue]]
1462 # TokenSpace.PcdCName|HiiString|VariableGuid|VariableOffset[|HiiValue]
1463 # PCD value needs to be located in such kind of string, and the PCD value might be an expression in which
1464 # there might have "|" operator, also in string value.
1466 # @param Setting: String contain information described above with "TokenSpace.PcdCName|" stripped
1467 # @param PcdType: PCD type: feature, fixed, dynamic default VPD HII
1468 # @param DataType: The datum type of PCD: VOID*, UNIT, BOOL
1470 # ValueList: A List contain fields described above
1471 # IsValid: True if conforming EBNF, otherwise False
1472 # Index: The index where PcdValue is in ValueList
1474 def AnalyzeDscPcd(Setting
, PcdType
, DataType
=''):
1475 FieldList
= AnalyzePcdExpression(Setting
)
1478 if PcdType
in (MODEL_PCD_FIXED_AT_BUILD
, MODEL_PCD_PATCHABLE_IN_MODULE
, MODEL_PCD_DYNAMIC_DEFAULT
, MODEL_PCD_DYNAMIC_EX_DEFAULT
):
1479 Value
= FieldList
[0]
1481 if len(FieldList
) > 1 and FieldList
[1]:
1482 DataType
= FieldList
[1]
1483 if FieldList
[1] != TAB_VOID
:
1485 if len(FieldList
) > 2:
1489 IsValid
= (len(FieldList
) <= 1)
1491 IsValid
= (len(FieldList
) <= 3)
1495 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1499 return [str(Value
), DataType
, str(Size
)], IsValid
, 0
1500 elif PcdType
== MODEL_PCD_FEATURE_FLAG
:
1501 Value
= FieldList
[0]
1503 IsValid
= (len(FieldList
) <= 1)
1504 return [Value
, DataType
, str(Size
)], IsValid
, 0
1505 elif PcdType
in (MODEL_PCD_DYNAMIC_VPD
, MODEL_PCD_DYNAMIC_EX_VPD
):
1506 VpdOffset
= FieldList
[0]
1508 if not DataType
== TAB_VOID
:
1509 if len(FieldList
) > 1:
1510 Value
= FieldList
[1]
1512 if len(FieldList
) > 1:
1514 if len(FieldList
) > 2:
1515 Value
= FieldList
[2]
1517 IsValid
= (len(FieldList
) <= 1)
1519 IsValid
= (len(FieldList
) <= 3)
1522 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1526 return [VpdOffset
, str(Size
), Value
], IsValid
, 2
1527 elif PcdType
in (MODEL_PCD_DYNAMIC_HII
, MODEL_PCD_DYNAMIC_EX_HII
):
1528 IsValid
= (3 <= len(FieldList
) <= 5)
1529 HiiString
= FieldList
[0]
1530 Guid
= Offset
= Value
= Attribute
= ''
1531 if len(FieldList
) > 1:
1533 if len(FieldList
) > 2:
1534 Offset
= FieldList
[2]
1535 if len(FieldList
) > 3:
1536 Value
= FieldList
[3]
1539 if len(FieldList
) > 4:
1540 Attribute
= FieldList
[4]
1541 return [HiiString
, Guid
, Offset
, Value
, Attribute
], IsValid
, 3
1546 # Analyze the pcd Value, Datum type and TokenNumber.
1547 # Used to avoid split issue while the value string contain "|" character
1549 # @param[in] Setting: A String contain value/datum type/token number information;
1551 # @retval ValueList: A List contain value, datum type and toke number.
1553 def AnalyzePcdData(Setting
):
1554 ValueList
= ['', '', '']
1556 ValueRe
= re
.compile(r
'^\s*L?\".*\|.*\"')
1557 PtrValue
= ValueRe
.findall(Setting
)
1559 ValueUpdateFlag
= False
1561 if len(PtrValue
) >= 1:
1562 Setting
= re
.sub(ValueRe
, '', Setting
)
1563 ValueUpdateFlag
= True
1565 TokenList
= Setting
.split(TAB_VALUE_SPLIT
)
1566 ValueList
[0:len(TokenList
)] = TokenList
1569 ValueList
[0] = PtrValue
[0]
1573 ## check format of PCD value against its the datum type
1575 # For PCD value setting
1577 def CheckPcdDatum(Type
, Value
):
1578 if Type
== TAB_VOID
:
1579 ValueRe
= re
.compile(r
'\s*L?\".*\"\s*$')
1580 if not (((Value
.startswith('L"') or Value
.startswith('"')) and Value
.endswith('"'))
1581 or (Value
.startswith('{') and Value
.endswith('}')) or (Value
.startswith("L'") or Value
.startswith("'") and Value
.endswith("'"))
1583 return False, "Invalid value [%s] of type [%s]; must be in the form of {...} for array"\
1584 ", \"...\" or \'...\' for string, L\"...\" or L\'...\' for unicode string" % (Value
, Type
)
1585 elif ValueRe
.match(Value
):
1586 # Check the chars in UnicodeString or CString is printable
1587 if Value
.startswith("L"):
1591 Printset
= set(string
.printable
)
1592 Printset
.remove(TAB_PRINTCHAR_VT
)
1593 Printset
.add(TAB_PRINTCHAR_BS
)
1594 Printset
.add(TAB_PRINTCHAR_NUL
)
1595 if not set(Value
).issubset(Printset
):
1596 PrintList
= sorted(Printset
)
1597 return False, "Invalid PCD string value of type [%s]; must be printable chars %s." % (Type
, PrintList
)
1598 elif Type
== 'BOOLEAN':
1599 if Value
not in ['TRUE', 'True', 'true', '0x1', '0x01', '1', 'FALSE', 'False', 'false', '0x0', '0x00', '0']:
1600 return False, "Invalid value [%s] of type [%s]; must be one of TRUE, True, true, 0x1, 0x01, 1"\
1601 ", FALSE, False, false, 0x0, 0x00, 0" % (Value
, Type
)
1602 elif Type
in [TAB_UINT8
, TAB_UINT16
, TAB_UINT32
, TAB_UINT64
]:
1603 if Value
and int(Value
, 0) < 0:
1604 return False, "PCD can't be set to negative value[%s] for datum type [%s]" % (Value
, Type
)
1606 Value
= int(Value
, 0)
1607 if Value
> MAX_VAL_TYPE
[Type
]:
1608 return False, "Too large PCD value[%s] for datum type [%s]" % (Value
, Type
)
1610 return False, "Invalid value [%s] of type [%s];"\
1611 " must be a hexadecimal, decimal or octal in C language format." % (Value
, Type
)
1613 return True, "StructurePcd"
1617 ## Split command line option string to list
1619 # subprocess.Popen needs the args to be a sequence. Otherwise there's problem
1620 # in non-windows platform to launch command
1622 def SplitOption(OptionString
):
1627 for Index
in range(0, len(OptionString
)):
1628 CurrentChar
= OptionString
[Index
]
1629 if CurrentChar
in ['"', "'"]:
1630 if QuotationMark
== CurrentChar
:
1632 elif QuotationMark
== "":
1633 QuotationMark
= CurrentChar
1638 if CurrentChar
in ["/", "-"] and LastChar
in [" ", "\t", "\r", "\n"]:
1639 if Index
> OptionStart
:
1640 OptionList
.append(OptionString
[OptionStart
:Index
- 1])
1642 LastChar
= CurrentChar
1643 OptionList
.append(OptionString
[OptionStart
:])
1646 def CommonPath(PathList
):
1647 P1
= min(PathList
).split(os
.path
.sep
)
1648 P2
= max(PathList
).split(os
.path
.sep
)
1649 for Index
in range(min(len(P1
), len(P2
))):
1650 if P1
[Index
] != P2
[Index
]:
1651 return os
.path
.sep
.join(P1
[:Index
])
1652 return os
.path
.sep
.join(P1
)
1655 # Convert string to C format array
1657 def ConvertStringToByteArray(Value
):
1658 Value
= Value
.strip()
1662 if not Value
.endswith('}'):
1664 Value
= Value
.replace(' ', '').replace('{', '').replace('}', '')
1665 ValFields
= Value
.split(',')
1667 for Index
in range(len(ValFields
)):
1668 ValFields
[Index
] = str(int(ValFields
[Index
], 0))
1671 Value
= '{' + ','.join(ValFields
) + '}'
1675 if Value
.startswith('L"'):
1676 if not Value
.endswith('"'):
1680 elif not Value
.startswith('"') or not Value
.endswith('"'):
1683 Value
= eval(Value
) # translate escape character
1685 for Index
in range(0, len(Value
)):
1687 NewValue
= NewValue
+ str(ord(Value
[Index
]) % 0x10000) + ','
1689 NewValue
= NewValue
+ str(ord(Value
[Index
]) % 0x100) + ','
1690 Value
= NewValue
+ '0}'
1693 class PathClass(object):
1694 def __init__(self
, File
='', Root
='', AlterRoot
='', Type
='', IsBinary
=False,
1695 Arch
='COMMON', ToolChainFamily
='', Target
='', TagName
='', ToolCode
=''):
1697 self
.File
= str(File
)
1698 if os
.path
.isabs(self
.File
):
1702 self
.Root
= str(Root
)
1703 self
.AlterRoot
= str(AlterRoot
)
1705 # Remove any '.' and '..' in path
1707 self
.Root
= mws
.getWs(self
.Root
, self
.File
)
1708 self
.Path
= os
.path
.normpath(os
.path
.join(self
.Root
, self
.File
))
1709 self
.Root
= os
.path
.normpath(CommonPath([self
.Root
, self
.Path
]))
1710 # eliminate the side-effect of 'C:'
1711 if self
.Root
[-1] == ':':
1712 self
.Root
+= os
.path
.sep
1713 # file path should not start with path separator
1714 if self
.Root
[-1] == os
.path
.sep
:
1715 self
.File
= self
.Path
[len(self
.Root
):]
1717 self
.File
= self
.Path
[len(self
.Root
) + 1:]
1719 self
.Path
= os
.path
.normpath(self
.File
)
1721 self
.SubDir
, self
.Name
= os
.path
.split(self
.File
)
1722 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1726 self
.Dir
= os
.path
.join(self
.Root
, self
.SubDir
)
1728 self
.Dir
= self
.Root
1730 self
.Dir
= self
.SubDir
1735 self
.Type
= self
.Ext
.lower()
1737 self
.IsBinary
= IsBinary
1738 self
.Target
= Target
1739 self
.TagName
= TagName
1740 self
.ToolCode
= ToolCode
1741 self
.ToolChainFamily
= ToolChainFamily
1743 ## Convert the object of this class to a string
1745 # Convert member Path of the class to a string
1747 # @retval string Formatted String
1752 ## Override __eq__ function
1754 # Check whether PathClass are the same
1756 # @retval False The two PathClass are different
1757 # @retval True The two PathClass are the same
1759 def __eq__(self
, Other
):
1760 if isinstance(Other
, type(self
)):
1761 return self
.Path
== Other
.Path
1763 return self
.Path
== str(Other
)
1765 ## Override __cmp__ function
1767 # Customize the comparsion operation of two PathClass
1769 # @retval 0 The two PathClass are different
1770 # @retval -1 The first PathClass is less than the second PathClass
1771 # @retval 1 The first PathClass is Bigger than the second PathClass
1772 def __cmp__(self
, Other
):
1773 if isinstance(Other
, type(self
)):
1774 OtherKey
= Other
.Path
1776 OtherKey
= str(Other
)
1779 if SelfKey
== OtherKey
:
1781 elif SelfKey
> OtherKey
:
1786 ## Override __hash__ function
1788 # Use Path as key in hash table
1790 # @retval string Key for hash table
1793 return hash(self
.Path
)
1797 return self
.Path
.upper()
1800 def TimeStamp(self
):
1801 return os
.stat(self
.Path
)[8]
1803 def Validate(self
, Type
='', CaseSensitive
=True):
1804 if GlobalData
.gCaseInsensitive
:
1805 CaseSensitive
= False
1806 if Type
and Type
.lower() != self
.Type
:
1807 return FILE_TYPE_MISMATCH
, '%s (expect %s but got %s)' % (self
.File
, Type
, self
.Type
)
1809 RealFile
, RealRoot
= RealPath2(self
.File
, self
.Root
, self
.AlterRoot
)
1810 if not RealRoot
and not RealFile
:
1811 RealFile
= self
.File
1813 RealFile
= os
.path
.join(self
.AlterRoot
, self
.File
)
1815 RealFile
= os
.path
.join(self
.Root
, self
.File
)
1816 if len (mws
.getPkgPath()) == 0:
1817 return FILE_NOT_FOUND
, os
.path
.join(self
.AlterRoot
, RealFile
)
1819 return FILE_NOT_FOUND
, "%s is not found in packages path:\n\t%s" % (self
.File
, '\n\t'.join(mws
.getPkgPath()))
1823 if RealRoot
!= self
.Root
or RealFile
!= self
.File
:
1824 if CaseSensitive
and (RealFile
!= self
.File
or (RealRoot
!= self
.Root
and RealRoot
!= self
.AlterRoot
)):
1825 ErrorCode
= FILE_CASE_MISMATCH
1826 ErrorInfo
= self
.File
+ '\n\t' + RealFile
+ " [in file system]"
1828 self
.SubDir
, self
.Name
= os
.path
.split(RealFile
)
1829 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1831 self
.Dir
= os
.path
.join(RealRoot
, self
.SubDir
)
1834 self
.File
= RealFile
1835 self
.Root
= RealRoot
1836 self
.Path
= os
.path
.join(RealRoot
, RealFile
)
1837 return ErrorCode
, ErrorInfo
1839 ## Parse PE image to get the required PE informaion.
1841 class PeImageClass():
1844 # @param File FilePath of PeImage
1846 def __init__(self
, PeFile
):
1847 self
.FileName
= PeFile
1848 self
.IsValid
= False
1851 self
.SectionAlignment
= 0
1852 self
.SectionHeaderList
= []
1855 PeObject
= open(PeFile
, 'rb')
1857 self
.ErrorInfo
= self
.FileName
+ ' can not be found\n'
1860 ByteArray
= array
.array('B')
1861 ByteArray
.fromfile(PeObject
, 0x3E)
1862 ByteList
= ByteArray
.tolist()
1863 # DOS signature should be 'MZ'
1864 if self
._ByteListToStr
(ByteList
[0x0:0x2]) != 'MZ':
1865 self
.ErrorInfo
= self
.FileName
+ ' has no valid DOS signature MZ'
1868 # Read 4 byte PE Signature
1869 PeOffset
= self
._ByteListToInt
(ByteList
[0x3C:0x3E])
1870 PeObject
.seek(PeOffset
)
1871 ByteArray
= array
.array('B')
1872 ByteArray
.fromfile(PeObject
, 4)
1873 # PE signature should be 'PE\0\0'
1874 if ByteArray
.tostring() != 'PE\0\0':
1875 self
.ErrorInfo
= self
.FileName
+ ' has no valid PE signature PE00'
1878 # Read PE file header
1879 ByteArray
= array
.array('B')
1880 ByteArray
.fromfile(PeObject
, 0x14)
1881 ByteList
= ByteArray
.tolist()
1882 SecNumber
= self
._ByteListToInt
(ByteList
[0x2:0x4])
1884 self
.ErrorInfo
= self
.FileName
+ ' has no section header'
1887 # Read PE optional header
1888 OptionalHeaderSize
= self
._ByteListToInt
(ByteArray
[0x10:0x12])
1889 ByteArray
= array
.array('B')
1890 ByteArray
.fromfile(PeObject
, OptionalHeaderSize
)
1891 ByteList
= ByteArray
.tolist()
1892 self
.EntryPoint
= self
._ByteListToInt
(ByteList
[0x10:0x14])
1893 self
.SectionAlignment
= self
._ByteListToInt
(ByteList
[0x20:0x24])
1894 self
.Size
= self
._ByteListToInt
(ByteList
[0x38:0x3C])
1896 # Read each Section Header
1897 for Index
in range(SecNumber
):
1898 ByteArray
= array
.array('B')
1899 ByteArray
.fromfile(PeObject
, 0x28)
1900 ByteList
= ByteArray
.tolist()
1901 SecName
= self
._ByteListToStr
(ByteList
[0:8])
1902 SecVirtualSize
= self
._ByteListToInt
(ByteList
[8:12])
1903 SecRawAddress
= self
._ByteListToInt
(ByteList
[20:24])
1904 SecVirtualAddress
= self
._ByteListToInt
(ByteList
[12:16])
1905 self
.SectionHeaderList
.append((SecName
, SecVirtualAddress
, SecRawAddress
, SecVirtualSize
))
1909 def _ByteListToStr(self
, ByteList
):
1911 for index
in range(len(ByteList
)):
1912 if ByteList
[index
] == 0:
1914 String
+= chr(ByteList
[index
])
1917 def _ByteListToInt(self
, ByteList
):
1919 for index
in range(len(ByteList
) - 1, -1, -1):
1920 Value
= (Value
<< 8) |
int(ByteList
[index
])
1923 class DefaultStore():
1924 def __init__(self
, DefaultStores
):
1926 self
.DefaultStores
= DefaultStores
1927 def DefaultStoreID(self
, DefaultStoreName
):
1928 for key
, value
in self
.DefaultStores
.items():
1929 if value
== DefaultStoreName
:
1932 def GetDefaultDefault(self
):
1933 if not self
.DefaultStores
or "0" in self
.DefaultStores
:
1934 return "0", TAB_DEFAULT_STORES_DEFAULT
1936 minvalue
= min(int(value_str
) for value_str
in self
.DefaultStores
)
1937 return (str(minvalue
), self
.DefaultStores
[str(minvalue
)])
1938 def GetMin(self
, DefaultSIdList
):
1939 if not DefaultSIdList
:
1940 return TAB_DEFAULT_STORES_DEFAULT
1941 storeidset
= {storeid
for storeid
, storename
in self
.DefaultStores
.values() if storename
in DefaultSIdList
}
1944 minid
= min(storeidset
)
1945 for sid
, name
in self
.DefaultStores
.values():
1954 def __init__(self
,SkuIdentifier
='', SkuIds
=None):
1958 for SkuName
in SkuIds
:
1959 SkuId
= SkuIds
[SkuName
][0]
1960 skuid_num
= int(SkuId
, 16) if SkuId
.upper().startswith("0X") else int(SkuId
)
1961 if skuid_num
> 0xFFFFFFFFFFFFFFFF:
1962 EdkLogger
.error("build", PARAMETER_INVALID
,
1963 ExtraData
= "SKU-ID [%s] value %s exceeds the max value of UINT64"
1966 self
.AvailableSkuIds
= sdict()
1968 self
.SkuIdNumberSet
= []
1969 self
.SkuData
= SkuIds
1970 self
._SkuInherit
= {}
1971 self
._SkuIdentifier
= SkuIdentifier
1972 if SkuIdentifier
== '' or SkuIdentifier
is None:
1973 self
.SkuIdSet
= ['DEFAULT']
1974 self
.SkuIdNumberSet
= ['0U']
1975 elif SkuIdentifier
== 'ALL':
1976 self
.SkuIdSet
= SkuIds
.keys()
1977 self
.SkuIdNumberSet
= [num
[0].strip() + 'U' for num
in SkuIds
.values()]
1979 r
= SkuIdentifier
.split('|')
1980 self
.SkuIdSet
=[(r
[k
].strip()).upper() for k
in range(len(r
))]
1983 self
.SkuIdNumberSet
= [SkuIds
[k
][0].strip() + 'U' for k
in self
.SkuIdSet
]
1985 EdkLogger
.error("build", PARAMETER_INVALID
,
1986 ExtraData
= "SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1987 % (k
, " | ".join(SkuIds
.keys())))
1988 for each
in self
.SkuIdSet
:
1990 self
.AvailableSkuIds
[each
] = SkuIds
[each
][0]
1992 EdkLogger
.error("build", PARAMETER_INVALID
,
1993 ExtraData
="SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1994 % (each
, " | ".join(SkuIds
.keys())))
1995 if self
.SkuUsageType
!= SkuClass
.SINGLE
:
1996 self
.AvailableSkuIds
.update({'DEFAULT':0, 'COMMON':0})
1998 GlobalData
.gSkuids
= (self
.SkuIdSet
)
1999 if 'COMMON' in GlobalData
.gSkuids
:
2000 GlobalData
.gSkuids
.remove('COMMON')
2001 if self
.SkuUsageType
== self
.SINGLE
:
2002 if len(GlobalData
.gSkuids
) != 1:
2003 if 'DEFAULT' in GlobalData
.gSkuids
:
2004 GlobalData
.gSkuids
.remove('DEFAULT')
2005 if GlobalData
.gSkuids
:
2006 GlobalData
.gSkuids
.sort()
2008 def GetNextSkuId(self
, skuname
):
2009 if not self
._SkuInherit
:
2010 self
._SkuInherit
= {}
2011 for item
in self
.SkuData
.values():
2012 self
._SkuInherit
[item
[1]]=item
[2] if item
[2] else "DEFAULT"
2013 return self
._SkuInherit
.get(skuname
, "DEFAULT")
2015 def GetSkuChain(self
, sku
):
2016 if sku
== "DEFAULT":
2021 nextsku
= self
.GetNextSkuId(nextsku
)
2022 skulist
.append(nextsku
)
2023 if nextsku
== "DEFAULT":
2027 def SkuOverrideOrder(self
):
2029 for skuname
in self
.SkuIdSet
:
2030 skuorderset
.append(self
.GetSkuChain(skuname
))
2033 for index
in range(max(len(item
) for item
in skuorderset
)):
2034 for subset
in skuorderset
:
2035 if index
> len(subset
)-1:
2037 if subset
[index
] in skuorder
:
2039 skuorder
.append(subset
[index
])
2044 def SkuUsageType(self
):
2045 if self
._SkuIdentifier
.upper() == "ALL":
2046 return SkuClass
.MULTIPLE
2048 if len(self
.SkuIdSet
) == 1:
2049 if self
.SkuIdSet
[0] == 'DEFAULT':
2050 return SkuClass
.DEFAULT
2051 return SkuClass
.SINGLE
2052 if len(self
.SkuIdSet
) == 2 and 'DEFAULT' in self
.SkuIdSet
:
2053 return SkuClass
.SINGLE
2054 return SkuClass
.MULTIPLE
2056 def DumpSkuIdArrary(self
):
2057 if self
.SkuUsageType
== SkuClass
.SINGLE
:
2060 for skuname
in self
.AvailableSkuIds
:
2061 if skuname
== "COMMON":
2063 while skuname
!= "DEFAULT":
2064 ArrayStrList
.append(hex(int(self
.AvailableSkuIds
[skuname
])))
2065 skuname
= self
.GetNextSkuId(skuname
)
2066 ArrayStrList
.append("0x0")
2067 return "{{{myList}}}".format(myList
=",".join(ArrayStrList
))
2070 def AvailableSkuIdSet(self
):
2071 return self
.AvailableSkuIds
2074 def SystemSkuId(self
):
2075 if self
.SkuUsageType
== SkuClass
.SINGLE
:
2076 if len(self
.SkuIdSet
) == 1:
2077 return self
.SkuIdSet
[0]
2079 return self
.SkuIdSet
[0] if self
.SkuIdSet
[0] != 'DEFAULT' else self
.SkuIdSet
[1]
2084 # Pack a registry format GUID
2086 def PackRegistryFormatGuid(Guid
):
2087 return PackGUID(Guid
.split('-'))
2089 ## Get the integer value from string like "14U" or integer like 2
2091 # @param Input The object that may be either a integer value or a string
2093 # @retval Value The integer value that the input represents
2095 def GetIntegerValue(Input
):
2096 if isinstance(Input
, int):
2099 if String
.endswith("U"):
2100 String
= String
[:-1]
2101 if String
.endswith("ULL"):
2102 String
= String
[:-3]
2103 if String
.endswith("LL"):
2104 String
= String
[:-2]
2106 if String
.startswith("0x") or String
.startswith("0X"):
2107 return int(String
, 16)
2114 # Pack a GUID (registry format) list into a buffer and return it
2117 return pack(PACK_PATTERN_GUID
,
2121 int(Guid
[3][-4:-2], 16),
2122 int(Guid
[3][-2:], 16),
2123 int(Guid
[4][-12:-10], 16),
2124 int(Guid
[4][-10:-8], 16),
2125 int(Guid
[4][-8:-6], 16),
2126 int(Guid
[4][-6:-4], 16),
2127 int(Guid
[4][-4:-2], 16),
2128 int(Guid
[4][-2:], 16)
2132 # Pack a GUID (byte) list into a buffer and return it
2134 def PackByteFormatGUID(Guid
):
2135 return pack(PACK_PATTERN_GUID
,
2151 # This acts like the main() function for the script, unless it is 'import'ed into another
2154 if __name__
== '__main__':