2 # Common routines used by all tools
4 # Copyright (c) 2007 - 2019, Intel Corporation. All rights reserved.<BR>
5 # SPDX-License-Identifier: BSD-2-Clause-Patent
11 from __future__
import absolute_import
22 from random
import sample
23 from struct
import pack
27 from collections
import OrderedDict
29 import Common
.LongFilePathOs
as os
30 from Common
import EdkLogger
as EdkLogger
31 from Common
import GlobalData
as GlobalData
32 from Common
.DataType
import *
33 from Common
.BuildToolError
import *
34 from CommonDataClass
.DataClass
import *
35 from Common
.Parsing
import GetSplitValueList
36 from Common
.LongFilePathSupport
import OpenLongFilePath
as open
37 from Common
.MultipleWorkspace
import MultipleWorkspace
as mws
38 from CommonDataClass
.Exceptions
import BadExpression
39 from Common
.caching
import cached_property
41 ArrayIndex
= re
.compile("\[\s*[0-9a-fA-FxX]*\s*\]")
42 ## Regular expression used to find out place holders in string template
43 gPlaceholderPattern
= re
.compile("\$\{([^$()\s]+)\}", re
.MULTILINE | re
.UNICODE
)
45 ## regular expressions for map file processing
46 startPatternGeneral
= re
.compile("^Start[' ']+Length[' ']+Name[' ']+Class")
47 addressPatternGeneral
= re
.compile("^Address[' ']+Publics by Value[' ']+Rva\+Base")
48 valuePatternGcc
= re
.compile('^([\w_\.]+) +([\da-fA-Fx]+) +([\da-fA-Fx]+)$')
49 pcdPatternGcc
= re
.compile('^([\da-fA-Fx]+) +([\da-fA-Fx]+)')
50 secReGeneral
= re
.compile('^([\da-fA-F]+):([\da-fA-F]+) +([\da-fA-F]+)[Hh]? +([.\w\$]+) +(\w+)', re
.UNICODE
)
52 StructPattern
= re
.compile(r
'[_a-zA-Z][0-9A-Za-z_]*$')
54 ## Dictionary used to store dependencies of files
55 gDependencyDatabase
= {} # arch : {file path : [dependent files list]}
58 # If a module is built more than once with different PCDs or library classes
59 # a temporary INF file with same content is created, the temporary file is removed
64 def GetVariableOffset(mapfilepath
, efifilepath
, varnames
):
65 """ Parse map file to get variable offset in current EFI file
66 @param mapfilepath Map file absolution path
67 @param efifilepath: EFI binary file full path
68 @param varnames iteratable container whose elements are variable names to be searched
70 @return List whos elements are tuple with variable name and raw offset
74 f
= open(mapfilepath
, 'r')
80 if len(lines
) == 0: return None
81 firstline
= lines
[0].strip()
82 if (firstline
.startswith("Archive member included ") and
83 firstline
.endswith(" file (symbol)")):
84 return _parseForGCC(lines
, efifilepath
, varnames
)
85 if firstline
.startswith("# Path:"):
86 return _parseForXcode(lines
, efifilepath
, varnames
)
87 return _parseGeneral(lines
, efifilepath
, varnames
)
89 def _parseForXcode(lines
, efifilepath
, varnames
):
94 if status
== 0 and line
== "# Symbols:":
97 if status
== 1 and len(line
) != 0:
98 for varname
in varnames
:
100 # cannot pregenerate this RegEx since it uses varname from varnames.
101 m
= re
.match('^([\da-fA-FxX]+)([\s\S]*)([_]*%s)$' % varname
, line
)
103 ret
.append((varname
, m
.group(1)))
106 def _parseForGCC(lines
, efifilepath
, varnames
):
107 """ Parse map file generated by GCC linker """
111 for index
, line
in enumerate(lines
):
113 # status machine transection
114 if status
== 0 and line
== "Memory Configuration":
117 elif status
== 1 and line
== 'Linker script and memory map':
120 elif status
==2 and line
== 'START GROUP':
126 m
= valuePatternGcc
.match(line
)
128 sections
.append(m
.groups(0))
129 for varname
in varnames
:
131 m
= re
.match("^.data.(%s)" % varname
, line
)
133 m
= re
.match(".data.(%s)$" % varname
, line
)
135 Str
= lines
[index
+ 1]
137 Str
= line
[len(".data.%s" % varname
):]
139 m
= pcdPatternGcc
.match(Str
.strip())
141 varoffset
.append((varname
, int(m
.groups(0)[0], 16), int(sections
[-1][1], 16), sections
[-1][0]))
145 # get section information from efi file
146 efisecs
= PeImageClass(efifilepath
).SectionHeaderList
147 if efisecs
is None or len(efisecs
) == 0:
151 for efisec
in efisecs
:
152 for section
in sections
:
153 if section
[0].strip() == efisec
[0].strip() and section
[0].strip() == '.text':
154 redirection
= int(section
[1], 16) - efisec
[1]
157 for var
in varoffset
:
158 for efisec
in efisecs
:
159 if var
[1] >= efisec
[1] and var
[1] < efisec
[1]+efisec
[3]:
160 ret
.append((var
[0], hex(efisec
[2] + var
[1] - efisec
[1] - redirection
)))
163 def _parseGeneral(lines
, efifilepath
, varnames
):
164 status
= 0 #0 - beginning of file; 1 - PE section definition; 2 - symbol table
165 secs
= [] # key = section name
167 symRe
= re
.compile('^([\da-fA-F]+):([\da-fA-F]+) +([\.:\\\\\w\?@\$]+) +([\da-fA-F]+)', re
.UNICODE
)
171 if startPatternGeneral
.match(line
):
174 if addressPatternGeneral
.match(line
):
177 if line
.startswith("entry point at"):
180 if status
== 1 and len(line
) != 0:
181 m
= secReGeneral
.match(line
)
182 assert m
is not None, "Fail to parse the section in map file , line is %s" % line
183 sec_no
, sec_start
, sec_length
, sec_name
, sec_class
= m
.groups(0)
184 secs
.append([int(sec_no
, 16), int(sec_start
, 16), int(sec_length
, 16), sec_name
, sec_class
])
185 if status
== 2 and len(line
) != 0:
186 for varname
in varnames
:
187 m
= symRe
.match(line
)
188 assert m
is not None, "Fail to parse the symbol in map file, line is %s" % line
189 sec_no
, sym_offset
, sym_name
, vir_addr
= m
.groups(0)
190 sec_no
= int(sec_no
, 16)
191 sym_offset
= int(sym_offset
, 16)
192 vir_addr
= int(vir_addr
, 16)
193 # cannot pregenerate this RegEx since it uses varname from varnames.
194 m2
= re
.match('^[_]*(%s)' % varname
, sym_name
)
196 # fond a binary pcd entry in map file
198 if sec
[0] == sec_no
and (sym_offset
>= sec
[1] and sym_offset
< sec
[1] + sec
[2]):
199 varoffset
.append([varname
, sec
[3], sym_offset
, vir_addr
, sec_no
])
201 if not varoffset
: return []
203 # get section information from efi file
204 efisecs
= PeImageClass(efifilepath
).SectionHeaderList
205 if efisecs
is None or len(efisecs
) == 0:
209 for var
in varoffset
:
211 for efisec
in efisecs
:
213 if var
[1].strip() == efisec
[0].strip():
214 ret
.append((var
[0], hex(efisec
[2] + var
[2])))
215 elif var
[4] == index
:
216 ret
.append((var
[0], hex(efisec
[2] + var
[2])))
220 ## Routine to process duplicated INF
222 # This function is called by following two cases:
225 # Pkg/module/module.inf
226 # Pkg/module/module.inf {
228 # FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836
231 # INF Pkg/module/module.inf
232 # INF FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836 Pkg/module/module.inf
234 # This function copies Pkg/module/module.inf to
235 # Conf/.cache/0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf
237 # @param Path Original PathClass object
238 # @param BaseName New file base name
240 # @retval return the new PathClass object
242 def ProcessDuplicatedInf(Path
, BaseName
, Workspace
):
243 Filename
= os
.path
.split(Path
.File
)[1]
245 Filename
= BaseName
+ Path
.BaseName
+ Filename
[Filename
.rfind('.'):]
247 Filename
= BaseName
+ Path
.BaseName
249 DbDir
= os
.path
.split(GlobalData
.gDatabasePath
)[0]
252 # A temporary INF is copied to database path which must have write permission
253 # The temporary will be removed at the end of build
254 # In case of name conflict, the file name is
255 # FILE_GUIDBaseName (0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf)
257 TempFullPath
= os
.path
.join(DbDir
,
259 RtPath
= PathClass(Path
.File
, Workspace
)
261 # Modify the full path to temporary path, keep other unchanged
263 # To build same module more than once, the module path with FILE_GUID overridden has
264 # the file name FILE_GUIDmodule.inf, but the relative path (self.MetaFile.File) is the real path
265 # in DSC which is used as relative path by C files and other files in INF.
266 # A trick was used: all module paths are PathClass instances, after the initialization
267 # of PathClass, the PathClass.Path is overridden by the temporary INF path.
269 # The reason for creating a temporary INF is:
270 # Platform.Modules which is the base to create ModuleAutoGen objects is a dictionary,
271 # the key is the full path of INF, the value is an object to save overridden library instances, PCDs.
272 # A different key for the same module is needed to create different output directory,
273 # retrieve overridden PCDs, library instances.
275 # The BaseName is the FILE_GUID which is also the output directory name.
278 RtPath
.Path
= TempFullPath
279 RtPath
.BaseName
= BaseName
280 RtPath
.OriginalPath
= Path
282 # If file exists, compare contents
284 if os
.path
.exists(TempFullPath
):
285 with
open(str(Path
), 'rb') as f1
, open(TempFullPath
, 'rb') as f2
:
286 if f1
.read() == f2
.read():
288 _TempInfs
.append(TempFullPath
)
289 shutil
.copy2(str(Path
), TempFullPath
)
292 ## Remove temporary created INFs whose paths were saved in _TempInfs
294 def ClearDuplicatedInf():
296 File
= _TempInfs
.pop()
297 if os
.path
.exists(File
):
300 ## Convert GUID string in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx style to C structure style
302 # @param Guid The GUID string
304 # @retval string The GUID string in C structure style
306 def GuidStringToGuidStructureString(Guid
):
307 GuidList
= Guid
.split('-')
309 for Index
in range(0, 3, 1):
310 Result
= Result
+ '0x' + GuidList
[Index
] + ', '
311 Result
= Result
+ '{0x' + GuidList
[3][0:2] + ', 0x' + GuidList
[3][2:4]
312 for Index
in range(0, 12, 2):
313 Result
= Result
+ ', 0x' + GuidList
[4][Index
:Index
+ 2]
317 ## Convert GUID structure in byte array to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
319 # @param GuidValue The GUID value in byte array
321 # @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
323 def GuidStructureByteArrayToGuidString(GuidValue
):
324 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
325 guidValueList
= guidValueString
.split(",")
326 if len(guidValueList
) != 16:
328 #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
330 return "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
331 int(guidValueList
[3], 16),
332 int(guidValueList
[2], 16),
333 int(guidValueList
[1], 16),
334 int(guidValueList
[0], 16),
335 int(guidValueList
[5], 16),
336 int(guidValueList
[4], 16),
337 int(guidValueList
[7], 16),
338 int(guidValueList
[6], 16),
339 int(guidValueList
[8], 16),
340 int(guidValueList
[9], 16),
341 int(guidValueList
[10], 16),
342 int(guidValueList
[11], 16),
343 int(guidValueList
[12], 16),
344 int(guidValueList
[13], 16),
345 int(guidValueList
[14], 16),
346 int(guidValueList
[15], 16)
351 ## Convert GUID string in C structure style to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
353 # @param GuidValue The GUID value in C structure format
355 # @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
357 def GuidStructureStringToGuidString(GuidValue
):
358 if not GlobalData
.gGuidCFormatPattern
.match(GuidValue
):
360 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
361 guidValueList
= guidValueString
.split(",")
362 if len(guidValueList
) != 11:
364 #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
366 return "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
367 int(guidValueList
[0], 16),
368 int(guidValueList
[1], 16),
369 int(guidValueList
[2], 16),
370 int(guidValueList
[3], 16),
371 int(guidValueList
[4], 16),
372 int(guidValueList
[5], 16),
373 int(guidValueList
[6], 16),
374 int(guidValueList
[7], 16),
375 int(guidValueList
[8], 16),
376 int(guidValueList
[9], 16),
377 int(guidValueList
[10], 16)
382 ## Convert GUID string in C structure style to xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx
384 # @param GuidValue The GUID value in C structure format
386 # @retval string The GUID value in xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx format
388 def GuidStructureStringToGuidValueName(GuidValue
):
389 guidValueString
= GuidValue
.lower().replace("{", "").replace("}", "").replace(" ", "")
390 guidValueList
= guidValueString
.split(",")
391 if len(guidValueList
) != 11:
392 EdkLogger
.error(None, FORMAT_INVALID
, "Invalid GUID value string [%s]" % GuidValue
)
393 return "%08x_%04x_%04x_%02x%02x_%02x%02x%02x%02x%02x%02x" % (
394 int(guidValueList
[0], 16),
395 int(guidValueList
[1], 16),
396 int(guidValueList
[2], 16),
397 int(guidValueList
[3], 16),
398 int(guidValueList
[4], 16),
399 int(guidValueList
[5], 16),
400 int(guidValueList
[6], 16),
401 int(guidValueList
[7], 16),
402 int(guidValueList
[8], 16),
403 int(guidValueList
[9], 16),
404 int(guidValueList
[10], 16)
407 ## Create directories
409 # @param Directory The directory name
411 def CreateDirectory(Directory
):
412 if Directory
is None or Directory
.strip() == "":
415 if not os
.access(Directory
, os
.F_OK
):
416 os
.makedirs(Directory
)
421 ## Remove directories, including files and sub-directories in it
423 # @param Directory The directory name
425 def RemoveDirectory(Directory
, Recursively
=False):
426 if Directory
is None or Directory
.strip() == "" or not os
.path
.exists(Directory
):
429 CurrentDirectory
= os
.getcwd()
431 for File
in os
.listdir("."):
432 if os
.path
.isdir(File
):
433 RemoveDirectory(File
, Recursively
)
436 os
.chdir(CurrentDirectory
)
439 ## Store content in file
441 # This method is used to save file only when its content is changed. This is
442 # quite useful for "make" system to decide what will be re-built and what won't.
444 # @param File The path of file
445 # @param Content The new content of the file
446 # @param IsBinaryFile The flag indicating if the file is binary file or not
448 # @retval True If the file content is changed and the file is renewed
449 # @retval False If the file content is the same
451 def SaveFileOnChange(File
, Content
, IsBinaryFile
=True):
453 if os
.path
.exists(File
):
456 with
open(File
, "rb") as f
:
457 if Content
== f
.read():
460 EdkLogger
.error(None, FILE_OPEN_FAILURE
, ExtraData
=File
)
463 with
open(File
, "r") as f
:
464 if Content
== f
.read():
467 EdkLogger
.error(None, FILE_OPEN_FAILURE
, ExtraData
=File
)
469 DirName
= os
.path
.dirname(File
)
470 if not CreateDirectory(DirName
):
471 EdkLogger
.error(None, FILE_CREATE_FAILURE
, "Could not create directory %s" % DirName
)
474 DirName
= os
.getcwd()
475 if not os
.access(DirName
, os
.W_OK
):
476 EdkLogger
.error(None, PERMISSION_FAILURE
, "Do not have write permission on directory %s" % DirName
)
482 if GlobalData
.gIsWindows
and not os
.path
.exists(File
):
483 # write temp file, then rename the temp file to the real file
484 # to make sure the file be immediate saved to disk
485 with tempfile
.NamedTemporaryFile(OpenMode
, dir=os
.path
.dirname(File
), delete
=False) as tf
:
489 os
.rename(tempname
, File
)
491 EdkLogger
.error(None, FILE_CREATE_FAILURE
, ExtraData
='IOError %s' % X
)
494 with
open(File
, OpenMode
) as Fd
:
497 EdkLogger
.error(None, FILE_CREATE_FAILURE
, ExtraData
='IOError %s' % X
)
501 ## Copy source file only if it is different from the destination file
503 # This method is used to copy file only if the source file and destination
504 # file content are different. This is quite useful to avoid duplicated
507 # @param SrcFile The path of source file
508 # @param Dst The path of destination file or folder
510 # @retval True The two files content are different and the file is copied
511 # @retval False No copy really happen
513 def CopyFileOnChange(SrcFile
, Dst
):
514 if not os
.path
.exists(SrcFile
):
517 if os
.path
.isdir(Dst
):
518 DstFile
= os
.path
.join(Dst
, os
.path
.basename(SrcFile
))
522 if os
.path
.exists(DstFile
) and filecmp
.cmp(SrcFile
, DstFile
, shallow
=False):
525 DirName
= os
.path
.dirname(DstFile
)
526 if not CreateDirectory(DirName
):
527 EdkLogger
.error(None, FILE_CREATE_FAILURE
, "Could not create directory %s" % DirName
)
530 DirName
= os
.getcwd()
531 if not os
.access(DirName
, os
.W_OK
):
532 EdkLogger
.error(None, PERMISSION_FAILURE
, "Do not have write permission on directory %s" % DirName
)
534 # os.replace and os.rename are the atomic operations in python 3 and 2.
535 # we use these two atomic operations to ensure the file copy is atomic:
536 # copy the src to a temp file in the dst same folder firstly, then
537 # replace or rename the temp file to the destination file.
538 with tempfile
.NamedTemporaryFile(dir=DirName
, delete
=False) as tf
:
539 shutil
.copy(SrcFile
, tf
.name
)
542 if hasattr(os
, 'replace'):
543 os
.replace(tempname
, DstFile
)
545 # os.rename reqire to remove the dst on Windows, otherwise OSError will be raised.
546 if GlobalData
.gIsWindows
and os
.path
.exists(DstFile
):
548 os
.rename(tempname
, DstFile
)
551 EdkLogger
.error(None, FILE_COPY_FAILURE
, ExtraData
='IOError %s' % X
)
555 ## Retrieve and cache the real path name in file system
557 # @param Root The root directory of path relative to
559 # @retval str The path string if the path exists
560 # @retval None If path doesn't exist
566 def __init__(self
, Root
):
568 for F
in os
.listdir(Root
):
570 self
._UPPER
_CACHE
_[F
.upper()] = F
573 def __getitem__(self
, Path
):
574 Path
= Path
[len(os
.path
.commonprefix([Path
, self
._Root
])):]
577 if Path
and Path
[0] == os
.path
.sep
:
579 if Path
in self
._CACHE
_:
580 return os
.path
.join(self
._Root
, Path
)
581 UpperPath
= Path
.upper()
582 if UpperPath
in self
._UPPER
_CACHE
_:
583 return os
.path
.join(self
._Root
, self
._UPPER
_CACHE
_[UpperPath
])
587 SepIndex
= Path
.find(os
.path
.sep
)
589 Parent
= UpperPath
[:SepIndex
]
590 if Parent
not in self
._UPPER
_CACHE
_:
592 LastSepIndex
= SepIndex
593 SepIndex
= Path
.find(os
.path
.sep
, LastSepIndex
+ 1)
595 if LastSepIndex
== -1:
600 SepIndex
= LastSepIndex
602 Parent
= Path
[:SepIndex
]
603 ParentKey
= UpperPath
[:SepIndex
]
604 if ParentKey
not in self
._UPPER
_CACHE
_:
608 if Parent
in self
._CACHE
_:
611 ParentDir
= self
._UPPER
_CACHE
_[ParentKey
]
612 for F
in os
.listdir(ParentDir
):
613 Dir
= os
.path
.join(ParentDir
, F
)
614 self
._CACHE
_.add(Dir
)
615 self
._UPPER
_CACHE
_[Dir
.upper()] = Dir
617 SepIndex
= Path
.find(os
.path
.sep
, SepIndex
+ 1)
620 if Path
in self
._CACHE
_:
621 return os
.path
.join(self
._Root
, Path
)
622 elif UpperPath
in self
._UPPER
_CACHE
_:
623 return os
.path
.join(self
._Root
, self
._UPPER
_CACHE
_[UpperPath
])
626 def RealPath(File
, Dir
='', OverrideDir
=''):
627 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
628 NewFile
= GlobalData
.gAllFiles
[NewFile
]
629 if not NewFile
and OverrideDir
:
630 NewFile
= os
.path
.normpath(os
.path
.join(OverrideDir
, File
))
631 NewFile
= GlobalData
.gAllFiles
[NewFile
]
634 ## Get GUID value from given packages
636 # @param CName The CName of the GUID
637 # @param PackageList List of packages looking-up in
638 # @param Inffile The driver file
640 # @retval GuidValue if the CName is found in any given package
641 # @retval None if the CName is not found in all given packages
643 def GuidValue(CName
, PackageList
, Inffile
= None):
644 for P
in PackageList
:
645 GuidKeys
= list(P
.Guids
.keys())
646 if Inffile
and P
._PrivateGuids
:
647 if not Inffile
.startswith(P
.MetaFile
.Dir
):
648 GuidKeys
= [x
for x
in P
.Guids
if x
not in P
._PrivateGuids
]
649 if CName
in GuidKeys
:
650 return P
.Guids
[CName
]
653 ## A string template class
655 # This class implements a template for string replacement. A string template
656 # looks like following
658 # ${BEGIN} other_string ${placeholder_name} other_string ${END}
660 # The string between ${BEGIN} and ${END} will be repeated as many times as the
661 # length of "placeholder_name", which is a list passed through a dict. The
662 # "placeholder_name" is the key name of the dict. The ${BEGIN} and ${END} can
663 # be not used and, in this case, the "placeholder_name" must not a list and it
664 # will just be replaced once.
666 class TemplateString(object):
667 _REPEAT_START_FLAG
= "BEGIN"
668 _REPEAT_END_FLAG
= "END"
670 class Section(object):
671 _LIST_TYPES
= [type([]), type(set()), type((0,))]
673 def __init__(self
, TemplateSection
, PlaceHolderList
):
674 self
._Template
= TemplateSection
675 self
._PlaceHolderList
= []
677 # Split the section into sub-sections according to the position of placeholders
679 self
._SubSectionList
= []
682 # The placeholders passed in must be in the format of
684 # PlaceHolderName, PlaceHolderStartPoint, PlaceHolderEndPoint
686 for PlaceHolder
, Start
, End
in PlaceHolderList
:
687 self
._SubSectionList
.append(TemplateSection
[SubSectionStart
:Start
])
688 self
._SubSectionList
.append(TemplateSection
[Start
:End
])
689 self
._PlaceHolderList
.append(PlaceHolder
)
690 SubSectionStart
= End
691 if SubSectionStart
< len(TemplateSection
):
692 self
._SubSectionList
.append(TemplateSection
[SubSectionStart
:])
694 self
._SubSectionList
= [TemplateSection
]
697 return self
._Template
+ " : " + str(self
._PlaceHolderList
)
699 def Instantiate(self
, PlaceHolderValues
):
701 RepeatPlaceHolders
= {}
702 NonRepeatPlaceHolders
= {}
704 for PlaceHolder
in self
._PlaceHolderList
:
705 if PlaceHolder
not in PlaceHolderValues
:
707 Value
= PlaceHolderValues
[PlaceHolder
]
708 if type(Value
) in self
._LIST
_TYPES
:
710 RepeatTime
= len(Value
)
711 elif RepeatTime
!= len(Value
):
715 "${%s} has different repeat time from others!" % PlaceHolder
,
716 ExtraData
=str(self
._Template
)
718 RepeatPlaceHolders
["${%s}" % PlaceHolder
] = Value
720 NonRepeatPlaceHolders
["${%s}" % PlaceHolder
] = Value
722 if NonRepeatPlaceHolders
:
724 for S
in self
._SubSectionList
:
725 if S
not in NonRepeatPlaceHolders
:
728 StringList
.append(str(NonRepeatPlaceHolders
[S
]))
730 StringList
= self
._SubSectionList
732 if RepeatPlaceHolders
:
734 for Index
in range(RepeatTime
):
736 if S
not in RepeatPlaceHolders
:
737 TempStringList
.append(S
)
739 TempStringList
.append(str(RepeatPlaceHolders
[S
][Index
]))
740 StringList
= TempStringList
742 return "".join(StringList
)
745 def __init__(self
, Template
=None):
747 self
.IsBinary
= False
748 self
._Template
= Template
749 self
._TemplateSectionList
= self
._Parse
(Template
)
753 # @retval string The string replaced
756 return "".join(self
.String
)
758 ## Split the template string into fragments per the ${BEGIN} and ${END} flags
760 # @retval list A list of TemplateString.Section objects
762 def _Parse(self
, Template
):
767 TemplateSectionList
= []
769 MatchObj
= gPlaceholderPattern
.search(Template
, SearchFrom
)
771 if MatchEnd
<= len(Template
):
772 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:], PlaceHolderList
)
773 TemplateSectionList
.append(TemplateSection
)
776 MatchString
= MatchObj
.group(1)
777 MatchStart
= MatchObj
.start()
778 MatchEnd
= MatchObj
.end()
780 if MatchString
== self
._REPEAT
_START
_FLAG
:
781 if MatchStart
> SectionStart
:
782 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:MatchStart
], PlaceHolderList
)
783 TemplateSectionList
.append(TemplateSection
)
784 SectionStart
= MatchEnd
786 elif MatchString
== self
._REPEAT
_END
_FLAG
:
787 TemplateSection
= TemplateString
.Section(Template
[SectionStart
:MatchStart
], PlaceHolderList
)
788 TemplateSectionList
.append(TemplateSection
)
789 SectionStart
= MatchEnd
792 PlaceHolderList
.append((MatchString
, MatchStart
- SectionStart
, MatchEnd
- SectionStart
))
793 SearchFrom
= MatchEnd
794 return TemplateSectionList
796 ## Replace the string template with dictionary of placeholders and append it to previous one
798 # @param AppendString The string template to append
799 # @param Dictionary The placeholder dictionaries
801 def Append(self
, AppendString
, Dictionary
=None):
803 SectionList
= self
._Parse
(AppendString
)
804 self
.String
.append( "".join(S
.Instantiate(Dictionary
) for S
in SectionList
))
806 if isinstance(AppendString
,list):
807 self
.String
.extend(AppendString
)
809 self
.String
.append(AppendString
)
811 ## Replace the string template with dictionary of placeholders
813 # @param Dictionary The placeholder dictionaries
815 # @retval str The string replaced with placeholder values
817 def Replace(self
, Dictionary
=None):
818 return "".join(S
.Instantiate(Dictionary
) for S
in self
._TemplateSectionList
)
820 ## Progress indicator class
822 # This class makes use of thread to print progress on console.
825 # for avoiding deadloop
827 _ProgressThread
= None
828 _CheckInterval
= 0.25
832 # @param OpenMessage The string printed before progress characters
833 # @param CloseMessage The string printed after progress characters
834 # @param ProgressChar The character used to indicate the progress
835 # @param Interval The interval in seconds between two progress characters
837 def __init__(self
, OpenMessage
="", CloseMessage
="", ProgressChar
='.', Interval
=1.0):
838 self
.PromptMessage
= OpenMessage
839 self
.CodaMessage
= CloseMessage
840 self
.ProgressChar
= ProgressChar
841 self
.Interval
= Interval
842 if Progressor
._StopFlag
is None:
843 Progressor
._StopFlag
= threading
.Event()
845 ## Start to print progress character
847 # @param OpenMessage The string printed before progress characters
849 def Start(self
, OpenMessage
=None):
850 if OpenMessage
is not None:
851 self
.PromptMessage
= OpenMessage
852 Progressor
._StopFlag
.clear()
853 if Progressor
._ProgressThread
is None:
854 Progressor
._ProgressThread
= threading
.Thread(target
=self
._ProgressThreadEntry
)
855 Progressor
._ProgressThread
.setDaemon(False)
856 Progressor
._ProgressThread
.start()
858 ## Stop printing progress character
860 # @param CloseMessage The string printed after progress characters
862 def Stop(self
, CloseMessage
=None):
863 OriginalCodaMessage
= self
.CodaMessage
864 if CloseMessage
is not None:
865 self
.CodaMessage
= CloseMessage
867 self
.CodaMessage
= OriginalCodaMessage
869 ## Thread entry method
870 def _ProgressThreadEntry(self
):
871 sys
.stdout
.write(self
.PromptMessage
+ " ")
874 while not Progressor
._StopFlag
.isSet():
876 sys
.stdout
.write(self
.ProgressChar
)
878 TimeUp
= self
.Interval
879 time
.sleep(self
._CheckInterval
)
880 TimeUp
-= self
._CheckInterval
881 sys
.stdout
.write(" " + self
.CodaMessage
+ "\n")
884 ## Abort the progress display
887 if Progressor
._StopFlag
is not None:
888 Progressor
._StopFlag
.set()
889 if Progressor
._ProgressThread
is not None:
890 Progressor
._ProgressThread
.join()
891 Progressor
._ProgressThread
= None
894 ## Dictionary using prioritized list as key
898 _TupleType
= type(())
900 _ValidWildcardList
= ['COMMON', 'DEFAULT', 'ALL', TAB_STAR
, 'PLATFORM']
902 def __init__(self
, _Single_
=False, _Level_
=2):
903 self
._Level
_ = _Level_
905 self
._Single
_ = _Single_
908 def __getitem__(self
, key
):
911 if KeyType
== self
._ListType
or KeyType
== self
._TupleType
:
915 elif self
._Level
_ > 1:
916 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
920 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
922 if FirstKey
is None or str(FirstKey
).upper() in self
._ValidWildcardList
:
923 FirstKey
= self
._Wildcard
926 return self
._GetSingleValue
(FirstKey
, RestKeys
)
928 return self
._GetAllValues
(FirstKey
, RestKeys
)
930 def _GetSingleValue(self
, FirstKey
, RestKeys
):
932 #print "%s-%s" % (FirstKey, self._Level_) ,
934 if FirstKey
== self
._Wildcard
:
935 if FirstKey
in self
.data
:
936 Value
= self
.data
[FirstKey
][RestKeys
]
938 for Key
in self
.data
:
939 Value
= self
.data
[Key
][RestKeys
]
940 if Value
is not None: break
942 if FirstKey
in self
.data
:
943 Value
= self
.data
[FirstKey
][RestKeys
]
944 if Value
is None and self
._Wildcard
in self
.data
:
946 Value
= self
.data
[self
._Wildcard
][RestKeys
]
948 if FirstKey
== self
._Wildcard
:
949 if FirstKey
in self
.data
:
950 Value
= self
.data
[FirstKey
]
952 for Key
in self
.data
:
953 Value
= self
.data
[Key
]
954 if Value
is not None: break
956 if FirstKey
in self
.data
:
957 Value
= self
.data
[FirstKey
]
958 elif self
._Wildcard
in self
.data
:
959 Value
= self
.data
[self
._Wildcard
]
962 def _GetAllValues(self
, FirstKey
, RestKeys
):
965 if FirstKey
== self
._Wildcard
:
966 for Key
in self
.data
:
967 Value
+= self
.data
[Key
][RestKeys
]
969 if FirstKey
in self
.data
:
970 Value
+= self
.data
[FirstKey
][RestKeys
]
971 if self
._Wildcard
in self
.data
:
972 Value
+= self
.data
[self
._Wildcard
][RestKeys
]
974 if FirstKey
== self
._Wildcard
:
975 for Key
in self
.data
:
976 Value
.append(self
.data
[Key
])
978 if FirstKey
in self
.data
:
979 Value
.append(self
.data
[FirstKey
])
980 if self
._Wildcard
in self
.data
:
981 Value
.append(self
.data
[self
._Wildcard
])
985 def __setitem__(self
, key
, value
):
988 if KeyType
== self
._ListType
or KeyType
== self
._TupleType
:
993 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
997 RestKeys
= [self
._Wildcard
for i
in range(0, self
._Level
_ - 1)]
999 if FirstKey
in self
._ValidWildcardList
:
1000 FirstKey
= self
._Wildcard
1002 if FirstKey
not in self
.data
and self
._Level
_ > 0:
1003 self
.data
[FirstKey
] = tdict(self
._Single
_, self
._Level
_ - 1)
1005 if self
._Level
_ > 1:
1006 self
.data
[FirstKey
][RestKeys
] = value
1008 self
.data
[FirstKey
] = value
1010 def SetGreedyMode(self
):
1011 self
._Single
_ = False
1012 if self
._Level
_ > 1:
1013 for Key
in self
.data
:
1014 self
.data
[Key
].SetGreedyMode()
1016 def SetSingleMode(self
):
1017 self
._Single
_ = True
1018 if self
._Level
_ > 1:
1019 for Key
in self
.data
:
1020 self
.data
[Key
].SetSingleMode()
1022 def GetKeys(self
, KeyIndex
=0):
1023 assert KeyIndex
>= 0
1025 return set(self
.data
.keys())
1028 for Key
in self
.data
:
1029 keys |
= self
.data
[Key
].GetKeys(KeyIndex
- 1)
1032 def AnalyzePcdExpression(Setting
):
1033 RanStr
= ''.join(sample(string
.ascii_letters
+ string
.digits
, 8))
1034 Setting
= Setting
.replace('\\\\', RanStr
).strip()
1035 # There might be escaped quote in a string: \", \\\" , \', \\\'
1037 # There might be '|' in string and in ( ... | ... ), replace it with '-'
1039 InSingleQuoteStr
= False
1040 InDoubleQuoteStr
= False
1042 for Index
, ch
in enumerate(Data
):
1043 if ch
== '"' and not InSingleQuoteStr
:
1044 if Data
[Index
- 1] != '\\':
1045 InDoubleQuoteStr
= not InDoubleQuoteStr
1046 elif ch
== "'" and not InDoubleQuoteStr
:
1047 if Data
[Index
- 1] != '\\':
1048 InSingleQuoteStr
= not InSingleQuoteStr
1049 elif ch
== '(' and not (InSingleQuoteStr
or InDoubleQuoteStr
):
1051 elif ch
== ')' and not (InSingleQuoteStr
or InDoubleQuoteStr
):
1054 if (Pair
> 0 or InSingleQuoteStr
or InDoubleQuoteStr
) and ch
== TAB_VALUE_SPLIT
:
1061 Pos
= NewStr
.find(TAB_VALUE_SPLIT
, StartPos
)
1063 FieldList
.append(Setting
[StartPos
:].strip())
1065 FieldList
.append(Setting
[StartPos
:Pos
].strip())
1067 for i
, ch
in enumerate(FieldList
):
1069 FieldList
[i
] = ch
.replace(RanStr
,'\\\\')
1072 def ParseFieldValue (Value
):
1073 def ParseDevPathValue (Value
):
1075 Value
.replace('\\', '/').replace(' ', '')
1077 Cmd
= 'DevicePath ' + '"' + Value
+ '"'
1079 p
= subprocess
.Popen(Cmd
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, shell
=True)
1080 out
, err
= p
.communicate()
1081 except Exception as X
:
1082 raise BadExpression("DevicePath: %s" % (str(X
)) )
1084 subprocess
._cleanup
()
1088 raise BadExpression("DevicePath: %s" % str(err
))
1090 Size
= len(out
.split())
1091 out
= ','.join(out
.split())
1092 return '{' + out
+ '}', Size
1094 if "{CODE(" in Value
:
1095 return Value
, len(Value
.split(","))
1096 if isinstance(Value
, type(0)):
1097 return Value
, (Value
.bit_length() + 7) // 8
1098 if not isinstance(Value
, type('')):
1099 raise BadExpression('Type %s is %s' %(Value
, type(Value
)))
1100 Value
= Value
.strip()
1101 if Value
.startswith(TAB_UINT8
) and Value
.endswith(')'):
1102 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1104 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1106 if Value
.startswith(TAB_UINT16
) and Value
.endswith(')'):
1107 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1109 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1111 if Value
.startswith(TAB_UINT32
) and Value
.endswith(')'):
1112 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1114 raise BadExpression('Value (%s) Size larger than %d' %(Value
, Size
))
1116 if Value
.startswith(TAB_UINT64
) and Value
.endswith(')'):
1117 Value
, Size
= ParseFieldValue(Value
.split('(', 1)[1][:-1])
1119 raise BadExpression('Value (%s) Size larger than %d' % (Value
, Size
))
1121 if Value
.startswith(TAB_GUID
) and Value
.endswith(')'):
1122 Value
= Value
.split('(', 1)[1][:-1].strip()
1123 if Value
[0] == '{' and Value
[-1] == '}':
1124 TmpValue
= GuidStructureStringToGuidString(Value
)
1126 raise BadExpression("Invalid GUID value string %s" % Value
)
1128 if Value
[0] == '"' and Value
[-1] == '"':
1131 Value
= str(uuid
.UUID(Value
).bytes_le
)
1132 if Value
.startswith("b'"):
1134 Value
= "'" + Value
+ "'"
1135 except ValueError as Message
:
1136 raise BadExpression(Message
)
1137 Value
, Size
= ParseFieldValue(Value
)
1139 if Value
.startswith('L"') and Value
.endswith('"'):
1141 # translate escape character
1151 Value
= (Value
<< 16) |
ord(Char
)
1152 return Value
, (len(List
) + 1) * 2
1153 if Value
.startswith('"') and Value
.endswith('"'):
1155 # translate escape character
1164 Value
= (Value
<< 8) |
ord(Char
)
1165 return Value
, len(List
) + 1
1166 if Value
.startswith("L'") and Value
.endswith("'"):
1167 # Unicode Character Constant
1168 # translate escape character
1176 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1180 Value
= (Value
<< 16) |
ord(Char
)
1181 return Value
, len(List
) * 2
1182 if Value
.startswith("'") and Value
.endswith("'"):
1183 # Character constant
1184 # translate escape character
1191 raise BadExpression('Length %s is %s' % (Value
, len(List
)))
1195 Value
= (Value
<< 8) |
ord(Char
)
1196 return Value
, len(List
)
1197 if Value
.startswith('{') and Value
.endswith('}'):
1200 List
= [Item
.strip() for Item
in Value
.split(',')]
1205 ItemValue
, Size
= ParseFieldValue(Item
)
1207 for I
in range(Size
):
1208 Value
= (Value
<< 8) |
((ItemValue
>> 8 * I
) & 0xff)
1209 return Value
, RetSize
1210 if Value
.startswith('DEVICE_PATH(') and Value
.endswith(')'):
1211 Value
= Value
.replace("DEVICE_PATH(", '').rstrip(')')
1212 Value
= Value
.strip().strip('"')
1213 return ParseDevPathValue(Value
)
1214 if Value
.lower().startswith('0x'):
1216 Value
= int(Value
, 16)
1218 raise BadExpression("invalid hex value: %s" % Value
)
1221 return Value
, (Value
.bit_length() + 7) // 8
1222 if Value
[0].isdigit():
1223 Value
= int(Value
, 10)
1226 return Value
, (Value
.bit_length() + 7) // 8
1227 if Value
.lower() == 'true':
1229 if Value
.lower() == 'false':
1235 # Analyze DSC PCD value, since there is no data type info in DSC
1236 # This function is used to match functions (AnalyzePcdData) used for retrieving PCD value from database
1237 # 1. Feature flag: TokenSpace.PcdCName|PcdValue
1238 # 2. Fix and Patch:TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1239 # 3. Dynamic default:
1240 # TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
1241 # TokenSpace.PcdCName|PcdValue
1243 # TokenSpace.PcdCName|VpdOffset[|VpdValue]
1244 # TokenSpace.PcdCName|VpdOffset[|MaxSize[|VpdValue]]
1246 # TokenSpace.PcdCName|HiiString|VariableGuid|VariableOffset[|HiiValue]
1247 # PCD value needs to be located in such kind of string, and the PCD value might be an expression in which
1248 # there might have "|" operator, also in string value.
1250 # @param Setting: String contain information described above with "TokenSpace.PcdCName|" stripped
1251 # @param PcdType: PCD type: feature, fixed, dynamic default VPD HII
1252 # @param DataType: The datum type of PCD: VOID*, UNIT, BOOL
1254 # ValueList: A List contain fields described above
1255 # IsValid: True if conforming EBNF, otherwise False
1256 # Index: The index where PcdValue is in ValueList
1258 def AnalyzeDscPcd(Setting
, PcdType
, DataType
=''):
1259 FieldList
= AnalyzePcdExpression(Setting
)
1262 if PcdType
in (MODEL_PCD_FIXED_AT_BUILD
, MODEL_PCD_PATCHABLE_IN_MODULE
, MODEL_PCD_DYNAMIC_DEFAULT
, MODEL_PCD_DYNAMIC_EX_DEFAULT
):
1263 Value
= FieldList
[0]
1265 if len(FieldList
) > 1 and FieldList
[1]:
1266 DataType
= FieldList
[1]
1267 if FieldList
[1] != TAB_VOID
and StructPattern
.match(FieldList
[1]) is None:
1269 if len(FieldList
) > 2:
1273 IsValid
= (len(FieldList
) <= 1)
1275 IsValid
= (len(FieldList
) <= 3)
1279 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1283 return [str(Value
), DataType
, str(Size
)], IsValid
, 0
1284 elif PcdType
== MODEL_PCD_FEATURE_FLAG
:
1285 Value
= FieldList
[0]
1287 IsValid
= (len(FieldList
) <= 1)
1288 return [Value
, DataType
, str(Size
)], IsValid
, 0
1289 elif PcdType
in (MODEL_PCD_DYNAMIC_VPD
, MODEL_PCD_DYNAMIC_EX_VPD
):
1290 VpdOffset
= FieldList
[0]
1292 if not DataType
== TAB_VOID
:
1293 if len(FieldList
) > 1:
1294 Value
= FieldList
[1]
1296 if len(FieldList
) > 1:
1298 if len(FieldList
) > 2:
1299 Value
= FieldList
[2]
1301 IsValid
= (len(FieldList
) <= 1)
1303 IsValid
= (len(FieldList
) <= 3)
1306 int(Size
, 16) if Size
.upper().startswith("0X") else int(Size
)
1310 return [VpdOffset
, str(Size
), Value
], IsValid
, 2
1311 elif PcdType
in (MODEL_PCD_DYNAMIC_HII
, MODEL_PCD_DYNAMIC_EX_HII
):
1312 IsValid
= (3 <= len(FieldList
) <= 5)
1313 HiiString
= FieldList
[0]
1314 Guid
= Offset
= Value
= Attribute
= ''
1315 if len(FieldList
) > 1:
1317 if len(FieldList
) > 2:
1318 Offset
= FieldList
[2]
1319 if len(FieldList
) > 3:
1320 Value
= FieldList
[3]
1321 if len(FieldList
) > 4:
1322 Attribute
= FieldList
[4]
1323 return [HiiString
, Guid
, Offset
, Value
, Attribute
], IsValid
, 3
1328 # Analyze the pcd Value, Datum type and TokenNumber.
1329 # Used to avoid split issue while the value string contain "|" character
1331 # @param[in] Setting: A String contain value/datum type/token number information;
1333 # @retval ValueList: A List contain value, datum type and toke number.
1335 def AnalyzePcdData(Setting
):
1336 ValueList
= ['', '', '']
1338 ValueRe
= re
.compile(r
'^\s*L?\".*\|.*\"')
1339 PtrValue
= ValueRe
.findall(Setting
)
1341 ValueUpdateFlag
= False
1343 if len(PtrValue
) >= 1:
1344 Setting
= re
.sub(ValueRe
, '', Setting
)
1345 ValueUpdateFlag
= True
1347 TokenList
= Setting
.split(TAB_VALUE_SPLIT
)
1348 ValueList
[0:len(TokenList
)] = TokenList
1351 ValueList
[0] = PtrValue
[0]
1355 ## check format of PCD value against its the datum type
1357 # For PCD value setting
1359 def CheckPcdDatum(Type
, Value
):
1360 if Type
== TAB_VOID
:
1361 ValueRe
= re
.compile(r
'\s*L?\".*\"\s*$')
1362 if not (((Value
.startswith('L"') or Value
.startswith('"')) and Value
.endswith('"'))
1363 or (Value
.startswith('{') and Value
.endswith('}')) or (Value
.startswith("L'") or Value
.startswith("'") and Value
.endswith("'"))
1365 return False, "Invalid value [%s] of type [%s]; must be in the form of {...} for array"\
1366 ", \"...\" or \'...\' for string, L\"...\" or L\'...\' for unicode string" % (Value
, Type
)
1367 elif ValueRe
.match(Value
):
1368 # Check the chars in UnicodeString or CString is printable
1369 if Value
.startswith("L"):
1373 Printset
= set(string
.printable
)
1374 Printset
.remove(TAB_PRINTCHAR_VT
)
1375 Printset
.add(TAB_PRINTCHAR_BS
)
1376 Printset
.add(TAB_PRINTCHAR_NUL
)
1377 if not set(Value
).issubset(Printset
):
1378 PrintList
= sorted(Printset
)
1379 return False, "Invalid PCD string value of type [%s]; must be printable chars %s." % (Type
, PrintList
)
1380 elif Type
== 'BOOLEAN':
1381 if Value
not in ['TRUE', 'True', 'true', '0x1', '0x01', '1', 'FALSE', 'False', 'false', '0x0', '0x00', '0']:
1382 return False, "Invalid value [%s] of type [%s]; must be one of TRUE, True, true, 0x1, 0x01, 1"\
1383 ", FALSE, False, false, 0x0, 0x00, 0" % (Value
, Type
)
1384 elif Type
in [TAB_UINT8
, TAB_UINT16
, TAB_UINT32
, TAB_UINT64
]:
1385 if Value
.startswith('0') and not Value
.lower().startswith('0x') and len(Value
) > 1 and Value
.lstrip('0'):
1386 Value
= Value
.lstrip('0')
1388 if Value
and int(Value
, 0) < 0:
1389 return False, "PCD can't be set to negative value[%s] for datum type [%s]" % (Value
, Type
)
1390 Value
= int(Value
, 0)
1391 if Value
> MAX_VAL_TYPE
[Type
]:
1392 return False, "Too large PCD value[%s] for datum type [%s]" % (Value
, Type
)
1394 return False, "Invalid value [%s] of type [%s];"\
1395 " must be a hexadecimal, decimal or octal in C language format." % (Value
, Type
)
1397 return True, "StructurePcd"
1401 def CommonPath(PathList
):
1402 P1
= min(PathList
).split(os
.path
.sep
)
1403 P2
= max(PathList
).split(os
.path
.sep
)
1404 for Index
in range(min(len(P1
), len(P2
))):
1405 if P1
[Index
] != P2
[Index
]:
1406 return os
.path
.sep
.join(P1
[:Index
])
1407 return os
.path
.sep
.join(P1
)
1409 class PathClass(object):
1410 def __init__(self
, File
='', Root
='', AlterRoot
='', Type
='', IsBinary
=False,
1411 Arch
='COMMON', ToolChainFamily
='', Target
='', TagName
='', ToolCode
=''):
1413 self
.File
= str(File
)
1414 if os
.path
.isabs(self
.File
):
1418 self
.Root
= str(Root
)
1419 self
.AlterRoot
= str(AlterRoot
)
1421 # Remove any '.' and '..' in path
1423 self
.Root
= mws
.getWs(self
.Root
, self
.File
)
1424 self
.Path
= os
.path
.normpath(os
.path
.join(self
.Root
, self
.File
))
1425 self
.Root
= os
.path
.normpath(CommonPath([self
.Root
, self
.Path
]))
1426 # eliminate the side-effect of 'C:'
1427 if self
.Root
[-1] == ':':
1428 self
.Root
+= os
.path
.sep
1429 # file path should not start with path separator
1430 if self
.Root
[-1] == os
.path
.sep
:
1431 self
.File
= self
.Path
[len(self
.Root
):]
1433 self
.File
= self
.Path
[len(self
.Root
) + 1:]
1435 self
.Path
= os
.path
.normpath(self
.File
)
1437 self
.SubDir
, self
.Name
= os
.path
.split(self
.File
)
1438 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1442 self
.Dir
= os
.path
.join(self
.Root
, self
.SubDir
)
1444 self
.Dir
= self
.Root
1446 self
.Dir
= self
.SubDir
1451 self
.Type
= self
.Ext
.lower()
1453 self
.IsBinary
= IsBinary
1454 self
.Target
= Target
1455 self
.TagName
= TagName
1456 self
.ToolCode
= ToolCode
1457 self
.ToolChainFamily
= ToolChainFamily
1458 self
.OriginalPath
= self
1460 ## Convert the object of this class to a string
1462 # Convert member Path of the class to a string
1464 # @retval string Formatted String
1469 ## Override __eq__ function
1471 # Check whether PathClass are the same
1473 # @retval False The two PathClass are different
1474 # @retval True The two PathClass are the same
1476 def __eq__(self
, Other
):
1477 return self
.Path
== str(Other
)
1479 ## Override __cmp__ function
1481 # Customize the comparison operation of two PathClass
1483 # @retval 0 The two PathClass are different
1484 # @retval -1 The first PathClass is less than the second PathClass
1485 # @retval 1 The first PathClass is Bigger than the second PathClass
1486 def __cmp__(self
, Other
):
1487 OtherKey
= str(Other
)
1490 if SelfKey
== OtherKey
:
1492 elif SelfKey
> OtherKey
:
1497 ## Override __hash__ function
1499 # Use Path as key in hash table
1501 # @retval string Key for hash table
1504 return hash(self
.Path
)
1508 return self
.Path
.upper()
1511 def TimeStamp(self
):
1512 return os
.stat(self
.Path
)[8]
1514 def Validate(self
, Type
='', CaseSensitive
=True):
1515 def RealPath2(File
, Dir
='', OverrideDir
=''):
1518 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(OverrideDir
, File
))]
1520 if OverrideDir
[-1] == os
.path
.sep
:
1521 return NewFile
[len(OverrideDir
):], NewFile
[0:len(OverrideDir
)]
1523 return NewFile
[len(OverrideDir
) + 1:], NewFile
[0:len(OverrideDir
)]
1524 if GlobalData
.gAllFiles
:
1525 NewFile
= GlobalData
.gAllFiles
[os
.path
.normpath(os
.path
.join(Dir
, File
))]
1527 NewFile
= os
.path
.normpath(os
.path
.join(Dir
, File
))
1528 if not os
.path
.exists(NewFile
):
1532 if Dir
[-1] == os
.path
.sep
:
1533 return NewFile
[len(Dir
):], NewFile
[0:len(Dir
)]
1535 return NewFile
[len(Dir
) + 1:], NewFile
[0:len(Dir
)]
1541 if GlobalData
.gCaseInsensitive
:
1542 CaseSensitive
= False
1543 if Type
and Type
.lower() != self
.Type
:
1544 return FILE_TYPE_MISMATCH
, '%s (expect %s but got %s)' % (self
.File
, Type
, self
.Type
)
1546 RealFile
, RealRoot
= RealPath2(self
.File
, self
.Root
, self
.AlterRoot
)
1547 if not RealRoot
and not RealFile
:
1548 RealFile
= self
.File
1550 RealFile
= os
.path
.join(self
.AlterRoot
, self
.File
)
1552 RealFile
= os
.path
.join(self
.Root
, self
.File
)
1553 if len (mws
.getPkgPath()) == 0:
1554 return FILE_NOT_FOUND
, os
.path
.join(self
.AlterRoot
, RealFile
)
1556 return FILE_NOT_FOUND
, "%s is not found in packages path:\n\t%s" % (self
.File
, '\n\t'.join(mws
.getPkgPath()))
1560 if RealRoot
!= self
.Root
or RealFile
!= self
.File
:
1561 if CaseSensitive
and (RealFile
!= self
.File
or (RealRoot
!= self
.Root
and RealRoot
!= self
.AlterRoot
)):
1562 ErrorCode
= FILE_CASE_MISMATCH
1563 ErrorInfo
= self
.File
+ '\n\t' + RealFile
+ " [in file system]"
1565 self
.SubDir
, self
.Name
= os
.path
.split(RealFile
)
1566 self
.BaseName
, self
.Ext
= os
.path
.splitext(self
.Name
)
1568 self
.Dir
= os
.path
.join(RealRoot
, self
.SubDir
)
1571 self
.File
= RealFile
1572 self
.Root
= RealRoot
1573 self
.Path
= os
.path
.join(RealRoot
, RealFile
)
1574 return ErrorCode
, ErrorInfo
1576 ## Parse PE image to get the required PE information.
1578 class PeImageClass():
1581 # @param File FilePath of PeImage
1583 def __init__(self
, PeFile
):
1584 self
.FileName
= PeFile
1585 self
.IsValid
= False
1588 self
.SectionAlignment
= 0
1589 self
.SectionHeaderList
= []
1592 PeObject
= open(PeFile
, 'rb')
1594 self
.ErrorInfo
= self
.FileName
+ ' can not be found\n'
1597 ByteArray
= array
.array('B')
1598 ByteArray
.fromfile(PeObject
, 0x3E)
1599 ByteList
= ByteArray
.tolist()
1600 # DOS signature should be 'MZ'
1601 if self
._ByteListToStr
(ByteList
[0x0:0x2]) != 'MZ':
1602 self
.ErrorInfo
= self
.FileName
+ ' has no valid DOS signature MZ'
1605 # Read 4 byte PE Signature
1606 PeOffset
= self
._ByteListToInt
(ByteList
[0x3C:0x3E])
1607 PeObject
.seek(PeOffset
)
1608 ByteArray
= array
.array('B')
1609 ByteArray
.fromfile(PeObject
, 4)
1610 # PE signature should be 'PE\0\0'
1611 if ByteArray
.tostring() != b
'PE\0\0':
1612 self
.ErrorInfo
= self
.FileName
+ ' has no valid PE signature PE00'
1615 # Read PE file header
1616 ByteArray
= array
.array('B')
1617 ByteArray
.fromfile(PeObject
, 0x14)
1618 ByteList
= ByteArray
.tolist()
1619 SecNumber
= self
._ByteListToInt
(ByteList
[0x2:0x4])
1621 self
.ErrorInfo
= self
.FileName
+ ' has no section header'
1624 # Read PE optional header
1625 OptionalHeaderSize
= self
._ByteListToInt
(ByteArray
[0x10:0x12])
1626 ByteArray
= array
.array('B')
1627 ByteArray
.fromfile(PeObject
, OptionalHeaderSize
)
1628 ByteList
= ByteArray
.tolist()
1629 self
.EntryPoint
= self
._ByteListToInt
(ByteList
[0x10:0x14])
1630 self
.SectionAlignment
= self
._ByteListToInt
(ByteList
[0x20:0x24])
1631 self
.Size
= self
._ByteListToInt
(ByteList
[0x38:0x3C])
1633 # Read each Section Header
1634 for Index
in range(SecNumber
):
1635 ByteArray
= array
.array('B')
1636 ByteArray
.fromfile(PeObject
, 0x28)
1637 ByteList
= ByteArray
.tolist()
1638 SecName
= self
._ByteListToStr
(ByteList
[0:8])
1639 SecVirtualSize
= self
._ByteListToInt
(ByteList
[8:12])
1640 SecRawAddress
= self
._ByteListToInt
(ByteList
[20:24])
1641 SecVirtualAddress
= self
._ByteListToInt
(ByteList
[12:16])
1642 self
.SectionHeaderList
.append((SecName
, SecVirtualAddress
, SecRawAddress
, SecVirtualSize
))
1646 def _ByteListToStr(self
, ByteList
):
1648 for index
in range(len(ByteList
)):
1649 if ByteList
[index
] == 0:
1651 String
+= chr(ByteList
[index
])
1654 def _ByteListToInt(self
, ByteList
):
1656 for index
in range(len(ByteList
) - 1, -1, -1):
1657 Value
= (Value
<< 8) |
int(ByteList
[index
])
1660 class DefaultStore():
1661 def __init__(self
, DefaultStores
):
1663 self
.DefaultStores
= DefaultStores
1664 def DefaultStoreID(self
, DefaultStoreName
):
1665 for key
, value
in self
.DefaultStores
.items():
1666 if value
== DefaultStoreName
:
1669 def GetDefaultDefault(self
):
1670 if not self
.DefaultStores
or "0" in self
.DefaultStores
:
1671 return "0", TAB_DEFAULT_STORES_DEFAULT
1673 minvalue
= min(int(value_str
) for value_str
in self
.DefaultStores
)
1674 return (str(minvalue
), self
.DefaultStores
[str(minvalue
)])
1675 def GetMin(self
, DefaultSIdList
):
1676 if not DefaultSIdList
:
1677 return TAB_DEFAULT_STORES_DEFAULT
1678 storeidset
= {storeid
for storeid
, storename
in self
.DefaultStores
.values() if storename
in DefaultSIdList
}
1681 minid
= min(storeidset
)
1682 for sid
, name
in self
.DefaultStores
.values():
1691 def __init__(self
,SkuIdentifier
='', SkuIds
=None):
1695 for SkuName
in SkuIds
:
1696 SkuId
= SkuIds
[SkuName
][0]
1697 skuid_num
= int(SkuId
, 16) if SkuId
.upper().startswith("0X") else int(SkuId
)
1698 if skuid_num
> 0xFFFFFFFFFFFFFFFF:
1699 EdkLogger
.error("build", PARAMETER_INVALID
,
1700 ExtraData
= "SKU-ID [%s] value %s exceeds the max value of UINT64"
1703 self
.AvailableSkuIds
= OrderedDict()
1705 self
.SkuIdNumberSet
= []
1706 self
.SkuData
= SkuIds
1707 self
._SkuInherit
= {}
1708 self
._SkuIdentifier
= SkuIdentifier
1709 if SkuIdentifier
== '' or SkuIdentifier
is None:
1710 self
.SkuIdSet
= ['DEFAULT']
1711 self
.SkuIdNumberSet
= ['0U']
1712 elif SkuIdentifier
== 'ALL':
1713 self
.SkuIdSet
= list(SkuIds
.keys())
1714 self
.SkuIdNumberSet
= [num
[0].strip() + 'U' for num
in SkuIds
.values()]
1716 r
= SkuIdentifier
.split('|')
1717 self
.SkuIdSet
=[(r
[k
].strip()).upper() for k
in range(len(r
))]
1720 self
.SkuIdNumberSet
= [SkuIds
[k
][0].strip() + 'U' for k
in self
.SkuIdSet
]
1722 EdkLogger
.error("build", PARAMETER_INVALID
,
1723 ExtraData
= "SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1724 % (k
, " | ".join(SkuIds
.keys())))
1725 for each
in self
.SkuIdSet
:
1727 self
.AvailableSkuIds
[each
] = SkuIds
[each
][0]
1729 EdkLogger
.error("build", PARAMETER_INVALID
,
1730 ExtraData
="SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
1731 % (each
, " | ".join(SkuIds
.keys())))
1732 if self
.SkuUsageType
!= SkuClass
.SINGLE
:
1733 self
.AvailableSkuIds
.update({'DEFAULT':0, 'COMMON':0})
1735 GlobalData
.gSkuids
= (self
.SkuIdSet
)
1736 if 'COMMON' in GlobalData
.gSkuids
:
1737 GlobalData
.gSkuids
.remove('COMMON')
1738 if self
.SkuUsageType
== self
.SINGLE
:
1739 if len(GlobalData
.gSkuids
) != 1:
1740 if 'DEFAULT' in GlobalData
.gSkuids
:
1741 GlobalData
.gSkuids
.remove('DEFAULT')
1742 if GlobalData
.gSkuids
:
1743 GlobalData
.gSkuids
.sort()
1745 def GetNextSkuId(self
, skuname
):
1746 if not self
._SkuInherit
:
1747 self
._SkuInherit
= {}
1748 for item
in self
.SkuData
.values():
1749 self
._SkuInherit
[item
[1]]=item
[2] if item
[2] else "DEFAULT"
1750 return self
._SkuInherit
.get(skuname
, "DEFAULT")
1752 def GetSkuChain(self
, sku
):
1753 if sku
== "DEFAULT":
1758 nextsku
= self
.GetNextSkuId(nextsku
)
1759 skulist
.append(nextsku
)
1760 if nextsku
== "DEFAULT":
1764 def SkuOverrideOrder(self
):
1766 for skuname
in self
.SkuIdSet
:
1767 skuorderset
.append(self
.GetSkuChain(skuname
))
1770 for index
in range(max(len(item
) for item
in skuorderset
)):
1771 for subset
in skuorderset
:
1772 if index
> len(subset
)-1:
1774 if subset
[index
] in skuorder
:
1776 skuorder
.append(subset
[index
])
1781 def SkuUsageType(self
):
1782 if self
._SkuIdentifier
.upper() == "ALL":
1783 return SkuClass
.MULTIPLE
1785 if len(self
.SkuIdSet
) == 1:
1786 if self
.SkuIdSet
[0] == 'DEFAULT':
1787 return SkuClass
.DEFAULT
1788 return SkuClass
.SINGLE
1789 if len(self
.SkuIdSet
) == 2 and 'DEFAULT' in self
.SkuIdSet
:
1790 return SkuClass
.SINGLE
1791 return SkuClass
.MULTIPLE
1793 def DumpSkuIdArrary(self
):
1794 if self
.SkuUsageType
== SkuClass
.SINGLE
:
1797 for skuname
in self
.AvailableSkuIds
:
1798 if skuname
== "COMMON":
1800 while skuname
!= "DEFAULT":
1801 ArrayStrList
.append(hex(int(self
.AvailableSkuIds
[skuname
])))
1802 skuname
= self
.GetNextSkuId(skuname
)
1803 ArrayStrList
.append("0x0")
1804 return "{{{myList}}}".format(myList
=",".join(ArrayStrList
))
1807 def AvailableSkuIdSet(self
):
1808 return self
.AvailableSkuIds
1811 def SystemSkuId(self
):
1812 if self
.SkuUsageType
== SkuClass
.SINGLE
:
1813 if len(self
.SkuIdSet
) == 1:
1814 return self
.SkuIdSet
[0]
1816 return self
.SkuIdSet
[0] if self
.SkuIdSet
[0] != 'DEFAULT' else self
.SkuIdSet
[1]
1820 ## Get the integer value from string like "14U" or integer like 2
1822 # @param Input The object that may be either a integer value or a string
1824 # @retval Value The integer value that the input represents
1826 def GetIntegerValue(Input
):
1827 if not isinstance(Input
, str):
1830 if String
.endswith("U"):
1831 String
= String
[:-1]
1832 if String
.endswith("ULL"):
1833 String
= String
[:-3]
1834 if String
.endswith("LL"):
1835 String
= String
[:-2]
1837 if String
.startswith("0x") or String
.startswith("0X"):
1838 return int(String
, 16)
1845 # Pack a GUID (registry format) list into a buffer and return it
1848 return pack(PACK_PATTERN_GUID
,
1852 int(Guid
[3][-4:-2], 16),
1853 int(Guid
[3][-2:], 16),
1854 int(Guid
[4][-12:-10], 16),
1855 int(Guid
[4][-10:-8], 16),
1856 int(Guid
[4][-8:-6], 16),
1857 int(Guid
[4][-6:-4], 16),
1858 int(Guid
[4][-4:-2], 16),
1859 int(Guid
[4][-2:], 16)
1863 # Pack a GUID (byte) list into a buffer and return it
1865 def PackByteFormatGUID(Guid
):
1866 return pack(PACK_PATTERN_GUID
,
1880 ## DeepCopy dict/OrderedDict recusively
1882 # @param ori_dict a nested dict or ordereddict
1884 # @retval new dict or orderdict
1886 def CopyDict(ori_dict
):
1887 dict_type
= ori_dict
.__class
__
1888 if dict_type
not in (dict,OrderedDict
):
1890 new_dict
= dict_type()
1891 for key
in ori_dict
:
1892 if isinstance(ori_dict
[key
],(dict,OrderedDict
)):
1893 new_dict
[key
] = CopyDict(ori_dict
[key
])
1895 new_dict
[key
] = ori_dict
[key
]
1899 # Remove the c/c++ comments: // and /* */
1901 def RemoveCComments(ctext
):
1902 return re
.sub('//.*?\n|/\*.*?\*/', '\n', ctext
, flags
=re
.S
)