summaryrefslogtreecommitdiff
path: root/BaseTools/Source/Python/Common/Misc.py
diff options
context:
space:
mode:
authorkx <kx@radix.pro>2024-06-04 06:43:08 +0300
committerkx <kx@radix.pro>2024-06-04 06:43:08 +0300
commit7e2ccccace24636f29ddc210b94606abd4c7e42b (patch)
tree545d43ea12671048956cafeb12303e84d601e571 /BaseTools/Source/Python/Common/Misc.py
parent27b044605cd5f6b33a3d231576003850b3fe305b (diff)
downloadedk2-trunk.tar.xz
Renormalized end-of-lines from master@27b044605cd5f6b33a3d231576003850b3fe305btrunk
Diffstat (limited to 'BaseTools/Source/Python/Common/Misc.py')
-rwxr-xr-xBaseTools/Source/Python/Common/Misc.py3858
1 files changed, 1929 insertions, 1929 deletions
diff --git a/BaseTools/Source/Python/Common/Misc.py b/BaseTools/Source/Python/Common/Misc.py
index 34bfc90abd..d317b49975 100755
--- a/BaseTools/Source/Python/Common/Misc.py
+++ b/BaseTools/Source/Python/Common/Misc.py
@@ -1,1929 +1,1929 @@
-## @file
-# Common routines used by all tools
-#
-# Copyright (c) 2007 - 2019, Intel Corporation. All rights reserved.<BR>
-# SPDX-License-Identifier: BSD-2-Clause-Patent
-#
-
-##
-# Import Modules
-#
-from __future__ import absolute_import
-
-import sys
-import string
-import threading
-import time
-import re
-import pickle
-import array
-import shutil
-import filecmp
-from random import sample
-from struct import pack
-import uuid
-import subprocess
-import tempfile
-from collections import OrderedDict
-
-import Common.LongFilePathOs as os
-from Common import EdkLogger as EdkLogger
-from Common import GlobalData as GlobalData
-from Common.DataType import *
-from Common.BuildToolError import *
-from CommonDataClass.DataClass import *
-from Common.Parsing import GetSplitValueList
-from Common.LongFilePathSupport import OpenLongFilePath as open
-from Common.LongFilePathSupport import CopyLongFilePath as CopyLong
-from Common.LongFilePathSupport import LongFilePath as LongFilePath
-from Common.MultipleWorkspace import MultipleWorkspace as mws
-from CommonDataClass.Exceptions import BadExpression
-from Common.caching import cached_property
-import struct
-
-ArrayIndex = re.compile(r"\[\s*[0-9a-fA-FxX]*\s*\]")
-## Regular expression used to find out place holders in string template
-gPlaceholderPattern = re.compile(r"\$\{([^$()\s]+)\}", re.MULTILINE | re.UNICODE)
-
-## regular expressions for map file processing
-startPatternGeneral = re.compile(r"^Start[' ']+Length[' ']+Name[' ']+Class")
-addressPatternGeneral = re.compile(r"^Address[' ']+Publics by Value[' ']+Rva\+Base")
-valuePatternGcc = re.compile(r'^([\w_\.]+) +([\da-fA-Fx]+) +([\da-fA-Fx]+)$')
-pcdPatternGcc = re.compile(r'^([\da-fA-Fx]+) +([\da-fA-Fx]+)')
-secReGeneral = re.compile(r'^([\da-fA-F]+):([\da-fA-F]+) +([\da-fA-F]+)[Hh]? +([.\w\$]+) +(\w+)', re.UNICODE)
-
-StructPattern = re.compile(r'[_a-zA-Z][0-9A-Za-z_]*$')
-
-## Dictionary used to store dependencies of files
-gDependencyDatabase = {} # arch : {file path : [dependent files list]}
-
-#
-# If a module is built more than once with different PCDs or library classes
-# a temporary INF file with same content is created, the temporary file is removed
-# when build exits.
-#
-_TempInfs = []
-
-def GetVariableOffset(mapfilepath, efifilepath, varnames):
- """ Parse map file to get variable offset in current EFI file
- @param mapfilepath Map file absolution path
- @param efifilepath: EFI binary file full path
- @param varnames iteratable container whose elements are variable names to be searched
-
- @return List whos elements are tuple with variable name and raw offset
- """
- lines = []
- try:
- f = open(mapfilepath, 'r')
- lines = f.readlines()
- f.close()
- except:
- return None
-
- if len(lines) == 0: return None
- firstline = lines[0].strip()
- if re.match(r'^\s*Address\s*Size\s*Align\s*Out\s*In\s*Symbol\s*$', firstline):
- return _parseForXcodeAndClang9(lines, efifilepath, varnames)
- if (firstline.startswith("Archive member included ") and
- firstline.endswith(" file (symbol)")):
- return _parseForGCC(lines, efifilepath, varnames)
- if firstline.startswith("# Path:"):
- return _parseForXcodeAndClang9(lines, efifilepath, varnames)
- return _parseGeneral(lines, efifilepath, varnames)
-
-def _parseForXcodeAndClang9(lines, efifilepath, varnames):
- status = 0
- ret = []
- for line in lines:
- line = line.strip()
- if status == 0 and (re.match(r'^\s*Address\s*Size\s*Align\s*Out\s*In\s*Symbol\s*$', line) \
- or line == "# Symbols:"):
- status = 1
- continue
- if status == 1 and len(line) != 0:
- for varname in varnames:
- if varname in line:
- # cannot pregenerate this RegEx since it uses varname from varnames.
- m = re.match(r'^([\da-fA-FxX]+)([\s\S]*)([_]*%s)$' % varname, line)
- if m is not None:
- ret.append((varname, m.group(1)))
- return ret
-
-def _parseForGCC(lines, efifilepath, varnames):
- """ Parse map file generated by GCC linker """
- status = 0
- sections = []
- varoffset = []
- for index, line in enumerate(lines):
- line = line.strip()
- # status machine transection
- if status == 0 and line == "Memory Configuration":
- status = 1
- continue
- elif status == 1 and line == 'Linker script and memory map':
- status = 2
- continue
- elif status ==2 and line == 'START GROUP':
- status = 3
- continue
-
- # status handler
- if status == 3:
- m = valuePatternGcc.match(line)
- if m is not None:
- sections.append(m.groups(0))
- for varname in varnames:
- Str = ''
- m = re.match("^.data.(%s)" % varname, line)
- if m is not None:
- m = re.match(".data.(%s)$" % varname, line)
- if m is not None:
- Str = lines[index + 1]
- else:
- Str = line[len(".data.%s" % varname):]
- if Str:
- m = pcdPatternGcc.match(Str.strip())
- if m is not None:
- varoffset.append((varname, int(m.groups(0)[0], 16), int(sections[-1][1], 16), sections[-1][0]))
-
- if not varoffset:
- return []
- # get section information from efi file
- efisecs = PeImageClass(efifilepath).SectionHeaderList
- if efisecs is None or len(efisecs) == 0:
- return []
- #redirection
- redirection = 0
- for efisec in efisecs:
- for section in sections:
- if section[0].strip() == efisec[0].strip() and section[0].strip() == '.text':
- redirection = int(section[1], 16) - efisec[1]
-
- ret = []
- for var in varoffset:
- for efisec in efisecs:
- if var[1] >= efisec[1] and var[1] < efisec[1]+efisec[3]:
- ret.append((var[0], hex(efisec[2] + var[1] - efisec[1] - redirection)))
- return ret
-
-def _parseGeneral(lines, efifilepath, varnames):
- status = 0 #0 - beginning of file; 1 - PE section definition; 2 - symbol table
- secs = [] # key = section name
- varoffset = []
- symRe = re.compile(r'^([\da-fA-F]+):([\da-fA-F]+) +([\.:\\\\\w\?@\$-]+) +([\da-fA-F]+)', re.UNICODE)
-
- for line in lines:
- line = line.strip()
- if startPatternGeneral.match(line):
- status = 1
- continue
- if addressPatternGeneral.match(line):
- status = 2
- continue
- if line.startswith("entry point at"):
- status = 3
- continue
- if status == 1 and len(line) != 0:
- m = secReGeneral.match(line)
- assert m is not None, "Fail to parse the section in map file , line is %s" % line
- sec_no, sec_start, sec_length, sec_name, sec_class = m.groups(0)
- secs.append([int(sec_no, 16), int(sec_start, 16), int(sec_length, 16), sec_name, sec_class])
- if status == 2 and len(line) != 0:
- for varname in varnames:
- m = symRe.match(line)
- assert m is not None, "Fail to parse the symbol in map file, line is %s" % line
- sec_no, sym_offset, sym_name, vir_addr = m.groups(0)
- sec_no = int(sec_no, 16)
- sym_offset = int(sym_offset, 16)
- vir_addr = int(vir_addr, 16)
- # cannot pregenerate this RegEx since it uses varname from varnames.
- m2 = re.match('^[_]*(%s)' % varname, sym_name)
- if m2 is not None:
- # fond a binary pcd entry in map file
- for sec in secs:
- if sec[0] == sec_no and (sym_offset >= sec[1] and sym_offset < sec[1] + sec[2]):
- varoffset.append([varname, sec[3], sym_offset, vir_addr, sec_no])
-
- if not varoffset: return []
-
- # get section information from efi file
- efisecs = PeImageClass(efifilepath).SectionHeaderList
- if efisecs is None or len(efisecs) == 0:
- return []
-
- ret = []
- for var in varoffset:
- index = 0
- for efisec in efisecs:
- index = index + 1
- if var[1].strip() == efisec[0].strip():
- ret.append((var[0], hex(efisec[2] + var[2])))
- elif var[4] == index:
- ret.append((var[0], hex(efisec[2] + var[2])))
-
- return ret
-
-## Routine to process duplicated INF
-#
-# This function is called by following two cases:
-# Case 1 in DSC:
-# [components.arch]
-# Pkg/module/module.inf
-# Pkg/module/module.inf {
-# <Defines>
-# FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836
-# }
-# Case 2 in FDF:
-# INF Pkg/module/module.inf
-# INF FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836 Pkg/module/module.inf
-#
-# This function copies Pkg/module/module.inf to
-# Conf/.cache/0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf
-#
-# @param Path Original PathClass object
-# @param BaseName New file base name
-#
-# @retval return the new PathClass object
-#
-def ProcessDuplicatedInf(Path, BaseName, Workspace):
- Filename = os.path.split(Path.File)[1]
- if '.' in Filename:
- Filename = BaseName + Path.BaseName + Filename[Filename.rfind('.'):]
- else:
- Filename = BaseName + Path.BaseName
-
- DbDir = os.path.split(GlobalData.gDatabasePath)[0]
-
- #
- # A temporary INF is copied to database path which must have write permission
- # The temporary will be removed at the end of build
- # In case of name conflict, the file name is
- # FILE_GUIDBaseName (0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf)
- #
- TempFullPath = os.path.join(DbDir,
- Filename)
- RtPath = PathClass(Path.File, Workspace)
- #
- # Modify the full path to temporary path, keep other unchanged
- #
- # To build same module more than once, the module path with FILE_GUID overridden has
- # the file name FILE_GUIDmodule.inf, but the relative path (self.MetaFile.File) is the real path
- # in DSC which is used as relative path by C files and other files in INF.
- # A trick was used: all module paths are PathClass instances, after the initialization
- # of PathClass, the PathClass.Path is overridden by the temporary INF path.
- #
- # The reason for creating a temporary INF is:
- # Platform.Modules which is the base to create ModuleAutoGen objects is a dictionary,
- # the key is the full path of INF, the value is an object to save overridden library instances, PCDs.
- # A different key for the same module is needed to create different output directory,
- # retrieve overridden PCDs, library instances.
- #
- # The BaseName is the FILE_GUID which is also the output directory name.
- #
- #
- RtPath.Path = TempFullPath
- RtPath.BaseName = BaseName
- RtPath.OriginalPath = Path
- #
- # If file exists, compare contents
- #
- if os.path.exists(TempFullPath):
- with open(str(Path), 'rb') as f1, open(TempFullPath, 'rb') as f2:
- if f1.read() == f2.read():
- return RtPath
- _TempInfs.append(TempFullPath)
- shutil.copy2(str(Path), TempFullPath)
- return RtPath
-
-## Remove temporary created INFs whose paths were saved in _TempInfs
-#
-def ClearDuplicatedInf():
- while _TempInfs:
- File = _TempInfs.pop()
- if os.path.exists(File):
- os.remove(File)
-
-## Convert GUID string in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx style to C structure style
-#
-# @param Guid The GUID string
-#
-# @retval string The GUID string in C structure style
-#
-def GuidStringToGuidStructureString(Guid):
- GuidList = Guid.split('-')
- Result = '{'
- for Index in range(0, 3, 1):
- Result = Result + '0x' + GuidList[Index] + ', '
- Result = Result + '{0x' + GuidList[3][0:2] + ', 0x' + GuidList[3][2:4]
- for Index in range(0, 12, 2):
- Result = Result + ', 0x' + GuidList[4][Index:Index + 2]
- Result += '}}'
- return Result
-
-## Convert GUID structure in byte array to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
-#
-# @param GuidValue The GUID value in byte array
-#
-# @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
-#
-def GuidStructureByteArrayToGuidString(GuidValue):
- guidValueString = GuidValue.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
- guidValueList = guidValueString.split(",")
- if len(guidValueList) != 16:
- return ''
- #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
- try:
- return "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
- int(guidValueList[3], 16),
- int(guidValueList[2], 16),
- int(guidValueList[1], 16),
- int(guidValueList[0], 16),
- int(guidValueList[5], 16),
- int(guidValueList[4], 16),
- int(guidValueList[7], 16),
- int(guidValueList[6], 16),
- int(guidValueList[8], 16),
- int(guidValueList[9], 16),
- int(guidValueList[10], 16),
- int(guidValueList[11], 16),
- int(guidValueList[12], 16),
- int(guidValueList[13], 16),
- int(guidValueList[14], 16),
- int(guidValueList[15], 16)
- )
- except:
- return ''
-
-## Convert GUID string in C structure style to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
-#
-# @param GuidValue The GUID value in C structure format
-#
-# @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
-#
-def GuidStructureStringToGuidString(GuidValue):
- if not GlobalData.gGuidCFormatPattern.match(GuidValue):
- return ''
- guidValueString = GuidValue.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
- guidValueList = guidValueString.split(",")
- if len(guidValueList) != 11:
- return ''
- #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
- try:
- return "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
- int(guidValueList[0], 16),
- int(guidValueList[1], 16),
- int(guidValueList[2], 16),
- int(guidValueList[3], 16),
- int(guidValueList[4], 16),
- int(guidValueList[5], 16),
- int(guidValueList[6], 16),
- int(guidValueList[7], 16),
- int(guidValueList[8], 16),
- int(guidValueList[9], 16),
- int(guidValueList[10], 16)
- )
- except:
- return ''
-
-## Convert GUID string in C structure style to xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx
-#
-# @param GuidValue The GUID value in C structure format
-#
-# @retval string The GUID value in xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx format
-#
-def GuidStructureStringToGuidValueName(GuidValue):
- guidValueString = GuidValue.lower().replace("{", "").replace("}", "").replace(" ", "")
- guidValueList = guidValueString.split(",")
- if len(guidValueList) != 11:
- EdkLogger.error(None, FORMAT_INVALID, "Invalid GUID value string [%s]" % GuidValue)
- return "%08x_%04x_%04x_%02x%02x_%02x%02x%02x%02x%02x%02x" % (
- int(guidValueList[0], 16),
- int(guidValueList[1], 16),
- int(guidValueList[2], 16),
- int(guidValueList[3], 16),
- int(guidValueList[4], 16),
- int(guidValueList[5], 16),
- int(guidValueList[6], 16),
- int(guidValueList[7], 16),
- int(guidValueList[8], 16),
- int(guidValueList[9], 16),
- int(guidValueList[10], 16)
- )
-
-## Create directories
-#
-# @param Directory The directory name
-#
-def CreateDirectory(Directory):
- if Directory is None or Directory.strip() == "":
- return True
- try:
- if not os.access(Directory, os.F_OK):
- os.makedirs(Directory)
- except:
- return False
- return True
-
-## Remove directories, including files and sub-directories in it
-#
-# @param Directory The directory name
-#
-def RemoveDirectory(Directory, Recursively=False):
- if Directory is None or Directory.strip() == "" or not os.path.exists(Directory):
- return
- if Recursively:
- CurrentDirectory = os.getcwd()
- os.chdir(Directory)
- for File in os.listdir("."):
- if os.path.isdir(File):
- RemoveDirectory(File, Recursively)
- else:
- os.remove(File)
- os.chdir(CurrentDirectory)
- os.rmdir(Directory)
-
-## Store content in file
-#
-# This method is used to save file only when its content is changed. This is
-# quite useful for "make" system to decide what will be re-built and what won't.
-#
-# @param File The path of file
-# @param Content The new content of the file
-# @param IsBinaryFile The flag indicating if the file is binary file or not
-#
-# @retval True If the file content is changed and the file is renewed
-# @retval False If the file content is the same
-#
-def SaveFileOnChange(File, Content, IsBinaryFile=True, FileLock=None):
-
- # Convert to long file path format
- File = LongFilePath(File)
-
- if os.path.exists(File):
- if IsBinaryFile:
- try:
- with open(File, "rb") as f:
- if Content == f.read():
- return False
- except:
- EdkLogger.error(None, FILE_OPEN_FAILURE, ExtraData=File)
- else:
- try:
- with open(File, "r") as f:
- if Content == f.read():
- return False
- except:
- EdkLogger.error(None, FILE_OPEN_FAILURE, ExtraData=File)
-
- DirName = os.path.dirname(File)
- if not CreateDirectory(DirName):
- EdkLogger.error(None, FILE_CREATE_FAILURE, "Could not create directory %s" % DirName)
- else:
- if DirName == '':
- DirName = os.getcwd()
- if not os.access(DirName, os.W_OK):
- EdkLogger.error(None, PERMISSION_FAILURE, "Do not have write permission on directory %s" % DirName)
-
- OpenMode = "w"
- if IsBinaryFile:
- OpenMode = "wb"
-
- # use default file_lock if no input new lock
- if not FileLock:
- FileLock = GlobalData.file_lock
- if FileLock:
- FileLock.acquire()
-
-
- if GlobalData.gIsWindows and not os.path.exists(File):
- try:
- with open(File, OpenMode) as tf:
- tf.write(Content)
- except IOError as X:
- if GlobalData.gBinCacheSource:
- EdkLogger.quiet("[cache error]:fails to save file with error: %s" % (X))
- else:
- EdkLogger.error(None, FILE_CREATE_FAILURE, ExtraData='IOError %s' % X)
- finally:
- if FileLock:
- FileLock.release()
- else:
- try:
- with open(File, OpenMode) as Fd:
- Fd.write(Content)
- except IOError as X:
- if GlobalData.gBinCacheSource:
- EdkLogger.quiet("[cache error]:fails to save file with error: %s" % (X))
- else:
- EdkLogger.error(None, FILE_CREATE_FAILURE, ExtraData='IOError %s' % X)
- finally:
- if FileLock:
- FileLock.release()
-
- return True
-
-## Copy source file only if it is different from the destination file
-#
-# This method is used to copy file only if the source file and destination
-# file content are different. This is quite useful to avoid duplicated
-# file writing.
-#
-# @param SrcFile The path of source file
-# @param Dst The path of destination file or folder
-#
-# @retval True The two files content are different and the file is copied
-# @retval False No copy really happen
-#
-def CopyFileOnChange(SrcFile, Dst, FileLock=None):
-
- # Convert to long file path format
- SrcFile = LongFilePath(SrcFile)
- Dst = LongFilePath(Dst)
-
- if os.path.isdir(SrcFile):
- EdkLogger.error(None, FILE_COPY_FAILURE, ExtraData='CopyFileOnChange SrcFile is a dir, not a file: %s' % SrcFile)
- return False
-
- if os.path.isdir(Dst):
- DstFile = os.path.join(Dst, os.path.basename(SrcFile))
- else:
- DstFile = Dst
-
- if os.path.exists(DstFile) and filecmp.cmp(SrcFile, DstFile, shallow=False):
- return False
-
- DirName = os.path.dirname(DstFile)
- if not CreateDirectory(DirName):
- EdkLogger.error(None, FILE_CREATE_FAILURE, "Could not create directory %s" % DirName)
- else:
- if DirName == '':
- DirName = os.getcwd()
- if not os.access(DirName, os.W_OK):
- EdkLogger.error(None, PERMISSION_FAILURE, "Do not have write permission on directory %s" % DirName)
-
- # use default file_lock if no input new lock
- if not FileLock:
- FileLock = GlobalData.file_lock
- if FileLock:
- FileLock.acquire()
-
- try:
- CopyLong(SrcFile, DstFile)
- except IOError as X:
- if GlobalData.gBinCacheSource:
- EdkLogger.quiet("[cache error]:fails to copy file with error: %s" % (X))
- else:
- EdkLogger.error(None, FILE_COPY_FAILURE, ExtraData='IOError %s' % X)
- finally:
- if FileLock:
- FileLock.release()
-
- return True
-
-## Retrieve and cache the real path name in file system
-#
-# @param Root The root directory of path relative to
-#
-# @retval str The path string if the path exists
-# @retval None If path doesn't exist
-#
-class DirCache:
- _CACHE_ = set()
- _UPPER_CACHE_ = {}
-
- def __init__(self, Root):
- self._Root = Root
- for F in os.listdir(Root):
- self._CACHE_.add(F)
- self._UPPER_CACHE_[F.upper()] = F
-
- # =[] operator
- def __getitem__(self, Path):
- Path = Path[len(os.path.commonprefix([Path, self._Root])):]
- if not Path:
- return self._Root
- if Path and Path[0] == os.path.sep:
- Path = Path[1:]
- if Path in self._CACHE_:
- return os.path.join(self._Root, Path)
- UpperPath = Path.upper()
- if UpperPath in self._UPPER_CACHE_:
- return os.path.join(self._Root, self._UPPER_CACHE_[UpperPath])
-
- IndexList = []
- LastSepIndex = -1
- SepIndex = Path.find(os.path.sep)
- while SepIndex > -1:
- Parent = UpperPath[:SepIndex]
- if Parent not in self._UPPER_CACHE_:
- break
- LastSepIndex = SepIndex
- SepIndex = Path.find(os.path.sep, LastSepIndex + 1)
-
- if LastSepIndex == -1:
- return None
-
- Cwd = os.getcwd()
- os.chdir(self._Root)
- SepIndex = LastSepIndex
- while SepIndex > -1:
- Parent = Path[:SepIndex]
- ParentKey = UpperPath[:SepIndex]
- if ParentKey not in self._UPPER_CACHE_:
- os.chdir(Cwd)
- return None
-
- if Parent in self._CACHE_:
- ParentDir = Parent
- else:
- ParentDir = self._UPPER_CACHE_[ParentKey]
- for F in os.listdir(ParentDir):
- Dir = os.path.join(ParentDir, F)
- self._CACHE_.add(Dir)
- self._UPPER_CACHE_[Dir.upper()] = Dir
-
- SepIndex = Path.find(os.path.sep, SepIndex + 1)
-
- os.chdir(Cwd)
- if Path in self._CACHE_:
- return os.path.join(self._Root, Path)
- elif UpperPath in self._UPPER_CACHE_:
- return os.path.join(self._Root, self._UPPER_CACHE_[UpperPath])
- return None
-
-def RealPath(File, Dir='', OverrideDir=''):
- NewFile = os.path.normpath(os.path.join(Dir, File))
- NewFile = GlobalData.gAllFiles[NewFile]
- if not NewFile and OverrideDir:
- NewFile = os.path.normpath(os.path.join(OverrideDir, File))
- NewFile = GlobalData.gAllFiles[NewFile]
- return NewFile
-
-## Get GUID value from given packages
-#
-# @param CName The CName of the GUID
-# @param PackageList List of packages looking-up in
-# @param Inffile The driver file
-#
-# @retval GuidValue if the CName is found in any given package
-# @retval None if the CName is not found in all given packages
-#
-def GuidValue(CName, PackageList, Inffile = None):
- for P in PackageList:
- GuidKeys = list(P.Guids.keys())
- if Inffile and P._PrivateGuids:
- if not Inffile.startswith(P.MetaFile.Dir):
- GuidKeys = [x for x in P.Guids if x not in P._PrivateGuids]
- if CName in GuidKeys:
- return P.Guids[CName]
- return None
-
-## A string template class
-#
-# This class implements a template for string replacement. A string template
-# looks like following
-#
-# ${BEGIN} other_string ${placeholder_name} other_string ${END}
-#
-# The string between ${BEGIN} and ${END} will be repeated as many times as the
-# length of "placeholder_name", which is a list passed through a dict. The
-# "placeholder_name" is the key name of the dict. The ${BEGIN} and ${END} can
-# be not used and, in this case, the "placeholder_name" must not a list and it
-# will just be replaced once.
-#
-class TemplateString(object):
- _REPEAT_START_FLAG = "BEGIN"
- _REPEAT_END_FLAG = "END"
-
- class Section(object):
- _LIST_TYPES = [type([]), type(set()), type((0,))]
-
- def __init__(self, TemplateSection, PlaceHolderList):
- self._Template = TemplateSection
- self._PlaceHolderList = []
-
- # Split the section into sub-sections according to the position of placeholders
- if PlaceHolderList:
- self._SubSectionList = []
- SubSectionStart = 0
- #
- # The placeholders passed in must be in the format of
- #
- # PlaceHolderName, PlaceHolderStartPoint, PlaceHolderEndPoint
- #
- for PlaceHolder, Start, End in PlaceHolderList:
- self._SubSectionList.append(TemplateSection[SubSectionStart:Start])
- self._SubSectionList.append(TemplateSection[Start:End])
- self._PlaceHolderList.append(PlaceHolder)
- SubSectionStart = End
- if SubSectionStart < len(TemplateSection):
- self._SubSectionList.append(TemplateSection[SubSectionStart:])
- else:
- self._SubSectionList = [TemplateSection]
-
- def __str__(self):
- return self._Template + " : " + str(self._PlaceHolderList)
-
- def Instantiate(self, PlaceHolderValues):
- RepeatTime = -1
- RepeatPlaceHolders = {}
- NonRepeatPlaceHolders = {}
-
- for PlaceHolder in self._PlaceHolderList:
- if PlaceHolder not in PlaceHolderValues:
- continue
- Value = PlaceHolderValues[PlaceHolder]
- if type(Value) in self._LIST_TYPES:
- if RepeatTime < 0:
- RepeatTime = len(Value)
- elif RepeatTime != len(Value):
- EdkLogger.error(
- "TemplateString",
- PARAMETER_INVALID,
- "${%s} has different repeat time from others!" % PlaceHolder,
- ExtraData=str(self._Template)
- )
- RepeatPlaceHolders["${%s}" % PlaceHolder] = Value
- else:
- NonRepeatPlaceHolders["${%s}" % PlaceHolder] = Value
-
- if NonRepeatPlaceHolders:
- StringList = []
- for S in self._SubSectionList:
- if S not in NonRepeatPlaceHolders:
- StringList.append(S)
- else:
- StringList.append(str(NonRepeatPlaceHolders[S]))
- else:
- StringList = self._SubSectionList
-
- if RepeatPlaceHolders:
- TempStringList = []
- for Index in range(RepeatTime):
- for S in StringList:
- if S not in RepeatPlaceHolders:
- TempStringList.append(S)
- else:
- TempStringList.append(str(RepeatPlaceHolders[S][Index]))
- StringList = TempStringList
-
- return "".join(StringList)
-
- ## Constructor
- def __init__(self, Template=None):
- self.String = []
- self.IsBinary = False
- self._Template = Template
- self._TemplateSectionList = self._Parse(Template)
-
- ## str() operator
- #
- # @retval string The string replaced
- #
- def __str__(self):
- return "".join(self.String)
-
- ## Split the template string into fragments per the ${BEGIN} and ${END} flags
- #
- # @retval list A list of TemplateString.Section objects
- #
- def _Parse(self, Template):
- SectionStart = 0
- SearchFrom = 0
- MatchEnd = 0
- PlaceHolderList = []
- TemplateSectionList = []
- while Template:
- MatchObj = gPlaceholderPattern.search(Template, SearchFrom)
- if not MatchObj:
- if MatchEnd <= len(Template):
- TemplateSection = TemplateString.Section(Template[SectionStart:], PlaceHolderList)
- TemplateSectionList.append(TemplateSection)
- break
-
- MatchString = MatchObj.group(1)
- MatchStart = MatchObj.start()
- MatchEnd = MatchObj.end()
-
- if MatchString == self._REPEAT_START_FLAG:
- if MatchStart > SectionStart:
- TemplateSection = TemplateString.Section(Template[SectionStart:MatchStart], PlaceHolderList)
- TemplateSectionList.append(TemplateSection)
- SectionStart = MatchEnd
- PlaceHolderList = []
- elif MatchString == self._REPEAT_END_FLAG:
- TemplateSection = TemplateString.Section(Template[SectionStart:MatchStart], PlaceHolderList)
- TemplateSectionList.append(TemplateSection)
- SectionStart = MatchEnd
- PlaceHolderList = []
- else:
- PlaceHolderList.append((MatchString, MatchStart - SectionStart, MatchEnd - SectionStart))
- SearchFrom = MatchEnd
- return TemplateSectionList
-
- ## Replace the string template with dictionary of placeholders and append it to previous one
- #
- # @param AppendString The string template to append
- # @param Dictionary The placeholder dictionaries
- #
- def Append(self, AppendString, Dictionary=None):
- if Dictionary:
- SectionList = self._Parse(AppendString)
- self.String.append( "".join(S.Instantiate(Dictionary) for S in SectionList))
- else:
- if isinstance(AppendString,list):
- self.String.extend(AppendString)
- else:
- self.String.append(AppendString)
-
- ## Replace the string template with dictionary of placeholders
- #
- # @param Dictionary The placeholder dictionaries
- #
- # @retval str The string replaced with placeholder values
- #
- def Replace(self, Dictionary=None):
- return "".join(S.Instantiate(Dictionary) for S in self._TemplateSectionList)
-
-## Progress indicator class
-#
-# This class makes use of thread to print progress on console.
-#
-class Progressor:
- # for avoiding deadloop
- _StopFlag = None
- _ProgressThread = None
- _CheckInterval = 0.25
-
- ## Constructor
- #
- # @param OpenMessage The string printed before progress characters
- # @param CloseMessage The string printed after progress characters
- # @param ProgressChar The character used to indicate the progress
- # @param Interval The interval in seconds between two progress characters
- #
- def __init__(self, OpenMessage="", CloseMessage="", ProgressChar='.', Interval=1.0):
- self.PromptMessage = OpenMessage
- self.CodaMessage = CloseMessage
- self.ProgressChar = ProgressChar
- self.Interval = Interval
- if Progressor._StopFlag is None:
- Progressor._StopFlag = threading.Event()
-
- ## Start to print progress character
- #
- # @param OpenMessage The string printed before progress characters
- #
- def Start(self, OpenMessage=None):
- if OpenMessage is not None:
- self.PromptMessage = OpenMessage
- Progressor._StopFlag.clear()
- if Progressor._ProgressThread is None:
- Progressor._ProgressThread = threading.Thread(target=self._ProgressThreadEntry)
- Progressor._ProgressThread.setDaemon(False)
- Progressor._ProgressThread.start()
-
- ## Stop printing progress character
- #
- # @param CloseMessage The string printed after progress characters
- #
- def Stop(self, CloseMessage=None):
- OriginalCodaMessage = self.CodaMessage
- if CloseMessage is not None:
- self.CodaMessage = CloseMessage
- self.Abort()
- self.CodaMessage = OriginalCodaMessage
-
- ## Thread entry method
- def _ProgressThreadEntry(self):
- sys.stdout.write(self.PromptMessage + " ")
- sys.stdout.flush()
- TimeUp = 0.0
- while not Progressor._StopFlag.isSet():
- if TimeUp <= 0.0:
- sys.stdout.write(self.ProgressChar)
- sys.stdout.flush()
- TimeUp = self.Interval
- time.sleep(self._CheckInterval)
- TimeUp -= self._CheckInterval
- sys.stdout.write(" " + self.CodaMessage + "\n")
- sys.stdout.flush()
-
- ## Abort the progress display
- @staticmethod
- def Abort():
- if Progressor._StopFlag is not None:
- Progressor._StopFlag.set()
- if Progressor._ProgressThread is not None:
- Progressor._ProgressThread.join()
- Progressor._ProgressThread = None
-
-
-## Dictionary using prioritized list as key
-#
-class tdict:
- _ListType = type([])
- _TupleType = type(())
- _Wildcard = 'COMMON'
- _ValidWildcardList = ['COMMON', 'DEFAULT', 'ALL', TAB_STAR, 'PLATFORM']
-
- def __init__(self, _Single_=False, _Level_=2):
- self._Level_ = _Level_
- self.data = {}
- self._Single_ = _Single_
-
- # =[] operator
- def __getitem__(self, key):
- KeyType = type(key)
- RestKeys = None
- if KeyType == self._ListType or KeyType == self._TupleType:
- FirstKey = key[0]
- if len(key) > 1:
- RestKeys = key[1:]
- elif self._Level_ > 1:
- RestKeys = [self._Wildcard for i in range(0, self._Level_ - 1)]
- else:
- FirstKey = key
- if self._Level_ > 1:
- RestKeys = [self._Wildcard for i in range(0, self._Level_ - 1)]
-
- if FirstKey is None or str(FirstKey).upper() in self._ValidWildcardList:
- FirstKey = self._Wildcard
-
- if self._Single_:
- return self._GetSingleValue(FirstKey, RestKeys)
- else:
- return self._GetAllValues(FirstKey, RestKeys)
-
- def _GetSingleValue(self, FirstKey, RestKeys):
- Value = None
- #print "%s-%s" % (FirstKey, self._Level_) ,
- if self._Level_ > 1:
- if FirstKey == self._Wildcard:
- if FirstKey in self.data:
- Value = self.data[FirstKey][RestKeys]
- if Value is None:
- for Key in self.data:
- Value = self.data[Key][RestKeys]
- if Value is not None: break
- else:
- if FirstKey in self.data:
- Value = self.data[FirstKey][RestKeys]
- if Value is None and self._Wildcard in self.data:
- #print "Value=None"
- Value = self.data[self._Wildcard][RestKeys]
- else:
- if FirstKey == self._Wildcard:
- if FirstKey in self.data:
- Value = self.data[FirstKey]
- if Value is None:
- for Key in self.data:
- Value = self.data[Key]
- if Value is not None: break
- else:
- if FirstKey in self.data:
- Value = self.data[FirstKey]
- elif self._Wildcard in self.data:
- Value = self.data[self._Wildcard]
- return Value
-
- def _GetAllValues(self, FirstKey, RestKeys):
- Value = []
- if self._Level_ > 1:
- if FirstKey == self._Wildcard:
- for Key in self.data:
- Value += self.data[Key][RestKeys]
- else:
- if FirstKey in self.data:
- Value += self.data[FirstKey][RestKeys]
- if self._Wildcard in self.data:
- Value += self.data[self._Wildcard][RestKeys]
- else:
- if FirstKey == self._Wildcard:
- for Key in self.data:
- Value.append(self.data[Key])
- else:
- if FirstKey in self.data:
- Value.append(self.data[FirstKey])
- if self._Wildcard in self.data:
- Value.append(self.data[self._Wildcard])
- return Value
-
- ## []= operator
- def __setitem__(self, key, value):
- KeyType = type(key)
- RestKeys = None
- if KeyType == self._ListType or KeyType == self._TupleType:
- FirstKey = key[0]
- if len(key) > 1:
- RestKeys = key[1:]
- else:
- RestKeys = [self._Wildcard for i in range(0, self._Level_ - 1)]
- else:
- FirstKey = key
- if self._Level_ > 1:
- RestKeys = [self._Wildcard for i in range(0, self._Level_ - 1)]
-
- if FirstKey in self._ValidWildcardList:
- FirstKey = self._Wildcard
-
- if FirstKey not in self.data and self._Level_ > 0:
- self.data[FirstKey] = tdict(self._Single_, self._Level_ - 1)
-
- if self._Level_ > 1:
- self.data[FirstKey][RestKeys] = value
- else:
- self.data[FirstKey] = value
-
- def SetGreedyMode(self):
- self._Single_ = False
- if self._Level_ > 1:
- for Key in self.data:
- self.data[Key].SetGreedyMode()
-
- def SetSingleMode(self):
- self._Single_ = True
- if self._Level_ > 1:
- for Key in self.data:
- self.data[Key].SetSingleMode()
-
- def GetKeys(self, KeyIndex=0):
- assert KeyIndex >= 0
- if KeyIndex == 0:
- return set(self.data.keys())
- else:
- keys = set()
- for Key in self.data:
- keys |= self.data[Key].GetKeys(KeyIndex - 1)
- return keys
-
-def AnalyzePcdExpression(Setting):
- RanStr = ''.join(sample(string.ascii_letters + string.digits, 8))
- Setting = Setting.replace('\\\\', RanStr).strip()
- # There might be escaped quote in a string: \", \\\" , \', \\\'
- Data = Setting
- # There might be '|' in string and in ( ... | ... ), replace it with '-'
- NewStr = ''
- InSingleQuoteStr = False
- InDoubleQuoteStr = False
- Pair = 0
- for Index, ch in enumerate(Data):
- if ch == '"' and not InSingleQuoteStr:
- if Data[Index - 1] != '\\':
- InDoubleQuoteStr = not InDoubleQuoteStr
- elif ch == "'" and not InDoubleQuoteStr:
- if Data[Index - 1] != '\\':
- InSingleQuoteStr = not InSingleQuoteStr
- elif ch == '(' and not (InSingleQuoteStr or InDoubleQuoteStr):
- Pair += 1
- elif ch == ')' and not (InSingleQuoteStr or InDoubleQuoteStr):
- Pair -= 1
-
- if (Pair > 0 or InSingleQuoteStr or InDoubleQuoteStr) and ch == TAB_VALUE_SPLIT:
- NewStr += '-'
- else:
- NewStr += ch
- FieldList = []
- StartPos = 0
- while True:
- Pos = NewStr.find(TAB_VALUE_SPLIT, StartPos)
- if Pos < 0:
- FieldList.append(Setting[StartPos:].strip())
- break
- FieldList.append(Setting[StartPos:Pos].strip())
- StartPos = Pos + 1
- for i, ch in enumerate(FieldList):
- if RanStr in ch:
- FieldList[i] = ch.replace(RanStr,'\\\\')
- return FieldList
-
-def ParseFieldValue (Value):
- def ParseDevPathValue (Value):
- if '\\' in Value:
- Value.replace('\\', '/').replace(' ', '')
-
- Cmd = 'DevicePath ' + '"' + Value + '"'
- try:
- p = subprocess.Popen(Cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
- out, err = p.communicate()
- except Exception as X:
- raise BadExpression("DevicePath: %s" % (str(X)) )
- finally:
- subprocess._cleanup()
- p.stdout.close()
- p.stderr.close()
- if err:
- raise BadExpression("DevicePath: %s" % str(err))
- out = out.decode()
- Size = len(out.split())
- out = ','.join(out.split())
- return '{' + out + '}', Size
-
- if "{CODE(" in Value:
- return Value, len(Value.split(","))
- if isinstance(Value, type(0)):
- return Value, (Value.bit_length() + 7) // 8
- if not isinstance(Value, type('')):
- raise BadExpression('Type %s is %s' %(Value, type(Value)))
- Value = Value.strip()
- if Value.startswith(TAB_UINT8) and Value.endswith(')'):
- Value, Size = ParseFieldValue(Value.split('(', 1)[1][:-1])
- if Size > 1:
- raise BadExpression('Value (%s) Size larger than %d' %(Value, Size))
- return Value, 1
- if Value.startswith(TAB_UINT16) and Value.endswith(')'):
- Value, Size = ParseFieldValue(Value.split('(', 1)[1][:-1])
- if Size > 2:
- raise BadExpression('Value (%s) Size larger than %d' %(Value, Size))
- return Value, 2
- if Value.startswith(TAB_UINT32) and Value.endswith(')'):
- Value, Size = ParseFieldValue(Value.split('(', 1)[1][:-1])
- if Size > 4:
- raise BadExpression('Value (%s) Size larger than %d' %(Value, Size))
- return Value, 4
- if Value.startswith(TAB_UINT64) and Value.endswith(')'):
- Value, Size = ParseFieldValue(Value.split('(', 1)[1][:-1])
- if Size > 8:
- raise BadExpression('Value (%s) Size larger than %d' % (Value, Size))
- return Value, 8
- if Value.startswith(TAB_GUID) and Value.endswith(')'):
- Value = Value.split('(', 1)[1][:-1].strip()
- if Value[0] == '{' and Value[-1] == '}':
- TmpValue = GuidStructureStringToGuidString(Value)
- if not TmpValue:
- raise BadExpression("Invalid GUID value string %s" % Value)
- Value = TmpValue
- if Value[0] == '"' and Value[-1] == '"':
- Value = Value[1:-1]
- try:
- Value = uuid.UUID(Value).bytes_le
- ValueL, ValueH = struct.unpack('2Q', Value)
- Value = (ValueH << 64 ) | ValueL
-
- except ValueError as Message:
- raise BadExpression(Message)
- return Value, 16
- if Value.startswith('L"') and Value.endswith('"'):
- # Unicode String
- # translate escape character
- Value = Value[1:]
- try:
- Value = eval(Value)
- except:
- Value = Value[1:-1]
- List = list(Value)
- List.reverse()
- Value = 0
- for Char in List:
- Value = (Value << 16) | ord(Char)
- return Value, (len(List) + 1) * 2
- if Value.startswith('"') and Value.endswith('"'):
- # ASCII String
- # translate escape character
- try:
- Value = eval(Value)
- except:
- Value = Value[1:-1]
- List = list(Value)
- List.reverse()
- Value = 0
- for Char in List:
- Value = (Value << 8) | ord(Char)
- return Value, len(List) + 1
- if Value.startswith("L'") and Value.endswith("'"):
- # Unicode Character Constant
- # translate escape character
- Value = Value[1:]
- try:
- Value = eval(Value)
- except:
- Value = Value[1:-1]
- List = list(Value)
- if len(List) == 0:
- raise BadExpression('Length %s is %s' % (Value, len(List)))
- List.reverse()
- Value = 0
- for Char in List:
- Value = (Value << 16) | ord(Char)
- return Value, len(List) * 2
- if Value.startswith("'") and Value.endswith("'"):
- # Character constant
- # translate escape character
- try:
- Value = eval(Value)
- except:
- Value = Value[1:-1]
- List = list(Value)
- if len(List) == 0:
- raise BadExpression('Length %s is %s' % (Value, len(List)))
- List.reverse()
- Value = 0
- for Char in List:
- Value = (Value << 8) | ord(Char)
- return Value, len(List)
- if Value.startswith('{') and Value.endswith('}'):
- # Byte array
- Value = Value[1:-1]
- List = [Item.strip() for Item in Value.split(',')]
- List.reverse()
- Value = 0
- RetSize = 0
- for Item in List:
- ItemValue, Size = ParseFieldValue(Item)
- RetSize += Size
- for I in range(Size):
- Value = (Value << 8) | ((ItemValue >> 8 * I) & 0xff)
- return Value, RetSize
- if Value.startswith('DEVICE_PATH(') and Value.endswith(')'):
- Value = Value.replace("DEVICE_PATH(", '').rstrip(')')
- Value = Value.strip().strip('"')
- return ParseDevPathValue(Value)
- if Value.lower().startswith('0x'):
- try:
- Value = int(Value, 16)
- except:
- raise BadExpression("invalid hex value: %s" % Value)
- if Value == 0:
- return 0, 1
- return Value, (Value.bit_length() + 7) // 8
- if Value[0].isdigit():
- Value = int(Value, 10)
- if Value == 0:
- return 0, 1
- return Value, (Value.bit_length() + 7) // 8
- if Value.lower() == 'true':
- return 1, 1
- if Value.lower() == 'false':
- return 0, 1
- return Value, 1
-
-## AnalyzeDscPcd
-#
-# Analyze DSC PCD value, since there is no data type info in DSC
-# This function is used to match functions (AnalyzePcdData) used for retrieving PCD value from database
-# 1. Feature flag: TokenSpace.PcdCName|PcdValue
-# 2. Fix and Patch:TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
-# 3. Dynamic default:
-# TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
-# TokenSpace.PcdCName|PcdValue
-# 4. Dynamic VPD:
-# TokenSpace.PcdCName|VpdOffset[|VpdValue]
-# TokenSpace.PcdCName|VpdOffset[|MaxSize[|VpdValue]]
-# 5. Dynamic HII:
-# TokenSpace.PcdCName|HiiString|VariableGuid|VariableOffset[|HiiValue]
-# PCD value needs to be located in such kind of string, and the PCD value might be an expression in which
-# there might have "|" operator, also in string value.
-#
-# @param Setting: String contain information described above with "TokenSpace.PcdCName|" stripped
-# @param PcdType: PCD type: feature, fixed, dynamic default VPD HII
-# @param DataType: The datum type of PCD: VOID*, UNIT, BOOL
-# @retval:
-# ValueList: A List contain fields described above
-# IsValid: True if conforming EBNF, otherwise False
-# Index: The index where PcdValue is in ValueList
-#
-def AnalyzeDscPcd(Setting, PcdType, DataType=''):
- FieldList = AnalyzePcdExpression(Setting)
-
- IsValid = True
- if PcdType in (MODEL_PCD_FIXED_AT_BUILD, MODEL_PCD_PATCHABLE_IN_MODULE, MODEL_PCD_DYNAMIC_DEFAULT, MODEL_PCD_DYNAMIC_EX_DEFAULT):
- Value = FieldList[0]
- Size = ''
- if len(FieldList) > 1 and FieldList[1]:
- DataType = FieldList[1]
- if FieldList[1] != TAB_VOID and StructPattern.match(FieldList[1]) is None:
- IsValid = False
- if len(FieldList) > 2:
- Size = FieldList[2]
- if IsValid:
- if DataType == "":
- IsValid = (len(FieldList) <= 1)
- else:
- IsValid = (len(FieldList) <= 3)
-
- if Size:
- try:
- int(Size, 16) if Size.upper().startswith("0X") else int(Size)
- except:
- IsValid = False
- Size = -1
- return [str(Value), DataType, str(Size)], IsValid, 0
- elif PcdType == MODEL_PCD_FEATURE_FLAG:
- Value = FieldList[0]
- Size = ''
- IsValid = (len(FieldList) <= 1)
- return [Value, DataType, str(Size)], IsValid, 0
- elif PcdType in (MODEL_PCD_DYNAMIC_VPD, MODEL_PCD_DYNAMIC_EX_VPD):
- VpdOffset = FieldList[0]
- Value = Size = ''
- if not DataType == TAB_VOID:
- if len(FieldList) > 1:
- Value = FieldList[1]
- else:
- if len(FieldList) > 1:
- Size = FieldList[1]
- if len(FieldList) > 2:
- Value = FieldList[2]
- if DataType == "":
- IsValid = (len(FieldList) <= 1)
- else:
- IsValid = (len(FieldList) <= 3)
- if Size:
- try:
- int(Size, 16) if Size.upper().startswith("0X") else int(Size)
- except:
- IsValid = False
- Size = -1
- return [VpdOffset, str(Size), Value], IsValid, 2
- elif PcdType in (MODEL_PCD_DYNAMIC_HII, MODEL_PCD_DYNAMIC_EX_HII):
- IsValid = (3 <= len(FieldList) <= 5)
- HiiString = FieldList[0]
- Guid = Offset = Value = Attribute = ''
- if len(FieldList) > 1:
- Guid = FieldList[1]
- if len(FieldList) > 2:
- Offset = FieldList[2]
- if len(FieldList) > 3:
- Value = FieldList[3]
- if len(FieldList) > 4:
- Attribute = FieldList[4]
- return [HiiString, Guid, Offset, Value, Attribute], IsValid, 3
- return [], False, 0
-
-## AnalyzePcdData
-#
-# Analyze the pcd Value, Datum type and TokenNumber.
-# Used to avoid split issue while the value string contain "|" character
-#
-# @param[in] Setting: A String contain value/datum type/token number information;
-#
-# @retval ValueList: A List contain value, datum type and toke number.
-#
-def AnalyzePcdData(Setting):
- ValueList = ['', '', '']
-
- ValueRe = re.compile(r'^\s*L?\".*\|.*\"')
- PtrValue = ValueRe.findall(Setting)
-
- ValueUpdateFlag = False
-
- if len(PtrValue) >= 1:
- Setting = re.sub(ValueRe, '', Setting)
- ValueUpdateFlag = True
-
- TokenList = Setting.split(TAB_VALUE_SPLIT)
- ValueList[0:len(TokenList)] = TokenList
-
- if ValueUpdateFlag:
- ValueList[0] = PtrValue[0]
-
- return ValueList
-
-## check format of PCD value against its the datum type
-#
-# For PCD value setting
-#
-def CheckPcdDatum(Type, Value):
- if Type == TAB_VOID:
- ValueRe = re.compile(r'\s*L?\".*\"\s*$')
- if not (((Value.startswith('L"') or Value.startswith('"')) and Value.endswith('"'))
- or (Value.startswith('{') and Value.endswith('}')) or (Value.startswith("L'") or Value.startswith("'") and Value.endswith("'"))
- ):
- return False, "Invalid value [%s] of type [%s]; must be in the form of {...} for array"\
- ", \"...\" or \'...\' for string, L\"...\" or L\'...\' for unicode string" % (Value, Type)
- elif ValueRe.match(Value):
- # Check the chars in UnicodeString or CString is printable
- if Value.startswith("L"):
- Value = Value[2:-1]
- else:
- Value = Value[1:-1]
- Printset = set(string.printable)
- Printset.remove(TAB_PRINTCHAR_VT)
- Printset.add(TAB_PRINTCHAR_BS)
- Printset.add(TAB_PRINTCHAR_NUL)
- if not set(Value).issubset(Printset):
- PrintList = sorted(Printset)
- return False, "Invalid PCD string value of type [%s]; must be printable chars %s." % (Type, PrintList)
- elif Type == 'BOOLEAN':
- if Value not in ['TRUE', 'True', 'true', '0x1', '0x01', '1', 'FALSE', 'False', 'false', '0x0', '0x00', '0']:
- return False, "Invalid value [%s] of type [%s]; must be one of TRUE, True, true, 0x1, 0x01, 1"\
- ", FALSE, False, false, 0x0, 0x00, 0" % (Value, Type)
- elif Type in [TAB_UINT8, TAB_UINT16, TAB_UINT32, TAB_UINT64]:
- if Value.startswith('0') and not Value.lower().startswith('0x') and len(Value) > 1 and Value.lstrip('0'):
- Value = Value.lstrip('0')
- try:
- if Value and int(Value, 0) < 0:
- return False, "PCD can't be set to negative value[%s] for datum type [%s]" % (Value, Type)
- Value = int(Value, 0)
- if Value > MAX_VAL_TYPE[Type]:
- return False, "Too large PCD value[%s] for datum type [%s]" % (Value, Type)
- except:
- return False, "Invalid value [%s] of type [%s];"\
- " must be a hexadecimal, decimal or octal in C language format." % (Value, Type)
- else:
- return True, "StructurePcd"
-
- return True, ""
-
-def CommonPath(PathList):
- P1 = min(PathList).split(os.path.sep)
- P2 = max(PathList).split(os.path.sep)
- for Index in range(min(len(P1), len(P2))):
- if P1[Index] != P2[Index]:
- return os.path.sep.join(P1[:Index])
- return os.path.sep.join(P1)
-
-class PathClass(object):
- def __init__(self, File='', Root='', AlterRoot='', Type='', IsBinary=False,
- Arch='COMMON', ToolChainFamily='', Target='', TagName='', ToolCode=''):
- self.Arch = Arch
- self.File = str(File)
- if os.path.isabs(self.File):
- self.Root = ''
- self.AlterRoot = ''
- else:
- self.Root = str(Root)
- self.AlterRoot = str(AlterRoot)
-
- # Remove any '.' and '..' in path
- if self.Root:
- self.Root = mws.getWs(self.Root, self.File)
- self.Path = os.path.normpath(os.path.join(self.Root, self.File))
- self.Root = os.path.normpath(CommonPath([self.Root, self.Path]))
- # eliminate the side-effect of 'C:'
- if self.Root[-1] == ':':
- self.Root += os.path.sep
- # file path should not start with path separator
- if self.Root[-1] == os.path.sep:
- self.File = self.Path[len(self.Root):]
- else:
- self.File = self.Path[len(self.Root) + 1:]
- else:
- self.Path = os.path.normpath(self.File)
-
- self.SubDir, self.Name = os.path.split(self.File)
- self.BaseName, self.Ext = os.path.splitext(self.Name)
-
- if self.Root:
- if self.SubDir:
- self.Dir = os.path.join(self.Root, self.SubDir)
- else:
- self.Dir = self.Root
- else:
- self.Dir = self.SubDir
-
- if IsBinary:
- self.Type = Type
- else:
- self.Type = self.Ext.lower()
-
- self.IsBinary = IsBinary
- self.Target = Target
- self.TagName = TagName
- self.ToolCode = ToolCode
- self.ToolChainFamily = ToolChainFamily
- self.OriginalPath = self
-
- ## Convert the object of this class to a string
- #
- # Convert member Path of the class to a string
- #
- # @retval string Formatted String
- #
- def __str__(self):
- return self.Path
-
- ## Override __eq__ function
- #
- # Check whether PathClass are the same
- #
- # @retval False The two PathClass are different
- # @retval True The two PathClass are the same
- #
- def __eq__(self, Other):
- return self.Path == str(Other)
-
- ## Override __cmp__ function
- #
- # Customize the comparison operation of two PathClass
- #
- # @retval 0 The two PathClass are different
- # @retval -1 The first PathClass is less than the second PathClass
- # @retval 1 The first PathClass is Bigger than the second PathClass
- def __cmp__(self, Other):
- OtherKey = str(Other)
-
- SelfKey = self.Path
- if SelfKey == OtherKey:
- return 0
- elif SelfKey > OtherKey:
- return 1
- else:
- return -1
-
- ## Override __hash__ function
- #
- # Use Path as key in hash table
- #
- # @retval string Key for hash table
- #
- def __hash__(self):
- return hash(self.Path)
-
- @cached_property
- def Key(self):
- return self.Path.upper()
-
- @property
- def TimeStamp(self):
- return os.stat(self.Path)[8]
-
- def Validate(self, Type='', CaseSensitive=True):
- def RealPath2(File, Dir='', OverrideDir=''):
- NewFile = None
- if OverrideDir:
- NewFile = GlobalData.gAllFiles[os.path.normpath(os.path.join(OverrideDir, File))]
- if NewFile:
- if OverrideDir[-1] == os.path.sep:
- return NewFile[len(OverrideDir):], NewFile[0:len(OverrideDir)]
- else:
- return NewFile[len(OverrideDir) + 1:], NewFile[0:len(OverrideDir)]
- if GlobalData.gAllFiles:
- NewFile = GlobalData.gAllFiles[os.path.normpath(os.path.join(Dir, File))]
- if not NewFile:
- NewFile = os.path.normpath(os.path.join(Dir, File))
- if not os.path.exists(NewFile):
- return None, None
- if NewFile:
- if Dir:
- if Dir[-1] == os.path.sep:
- return NewFile[len(Dir):], NewFile[0:len(Dir)]
- else:
- return NewFile[len(Dir) + 1:], NewFile[0:len(Dir)]
- else:
- return NewFile, ''
-
- return None, None
-
- if GlobalData.gCaseInsensitive:
- CaseSensitive = False
- if Type and Type.lower() != self.Type:
- return FILE_TYPE_MISMATCH, '%s (expect %s but got %s)' % (self.File, Type, self.Type)
-
- RealFile, RealRoot = RealPath2(self.File, self.Root, self.AlterRoot)
- if not RealRoot and not RealFile:
- RealFile = self.File
- if self.AlterRoot:
- RealFile = os.path.join(self.AlterRoot, self.File)
- elif self.Root:
- RealFile = os.path.join(self.Root, self.File)
- if len (mws.getPkgPath()) == 0:
- return FILE_NOT_FOUND, os.path.join(self.AlterRoot, RealFile)
- else:
- return FILE_NOT_FOUND, "%s is not found in packages path:\n\t%s" % (self.File, '\n\t'.join(mws.getPkgPath()))
-
- ErrorCode = 0
- ErrorInfo = ''
- if RealRoot != self.Root or RealFile != self.File:
- if CaseSensitive and (RealFile != self.File or (RealRoot != self.Root and RealRoot != self.AlterRoot)):
- ErrorCode = FILE_CASE_MISMATCH
- ErrorInfo = self.File + '\n\t' + RealFile + " [in file system]"
-
- self.SubDir, self.Name = os.path.split(RealFile)
- self.BaseName, self.Ext = os.path.splitext(self.Name)
- if self.SubDir:
- self.Dir = os.path.join(RealRoot, self.SubDir)
- else:
- self.Dir = RealRoot
- self.File = RealFile
- self.Root = RealRoot
- self.Path = os.path.join(RealRoot, RealFile)
- return ErrorCode, ErrorInfo
-
-## Parse PE image to get the required PE information.
-#
-class PeImageClass():
- ## Constructor
- #
- # @param File FilePath of PeImage
- #
- def __init__(self, PeFile):
- self.FileName = PeFile
- self.IsValid = False
- self.Size = 0
- self.EntryPoint = 0
- self.SectionAlignment = 0
- self.SectionHeaderList = []
- self.ErrorInfo = ''
- try:
- PeObject = open(PeFile, 'rb')
- except:
- self.ErrorInfo = self.FileName + ' can not be found\n'
- return
- # Read DOS header
- ByteArray = array.array('B')
- ByteArray.fromfile(PeObject, 0x3E)
- ByteList = ByteArray.tolist()
- # DOS signature should be 'MZ'
- if self._ByteListToStr (ByteList[0x0:0x2]) != 'MZ':
- self.ErrorInfo = self.FileName + ' has no valid DOS signature MZ'
- return
-
- # Read 4 byte PE Signature
- PeOffset = self._ByteListToInt(ByteList[0x3C:0x3E])
- PeObject.seek(PeOffset)
- ByteArray = array.array('B')
- ByteArray.fromfile(PeObject, 4)
- # PE signature should be 'PE\0\0'
- if ByteArray.tolist() != [ord('P'), ord('E'), 0, 0]:
- self.ErrorInfo = self.FileName + ' has no valid PE signature PE00'
- return
-
- # Read PE file header
- ByteArray = array.array('B')
- ByteArray.fromfile(PeObject, 0x14)
- ByteList = ByteArray.tolist()
- SecNumber = self._ByteListToInt(ByteList[0x2:0x4])
- if SecNumber == 0:
- self.ErrorInfo = self.FileName + ' has no section header'
- return
-
- # Read PE optional header
- OptionalHeaderSize = self._ByteListToInt(ByteArray[0x10:0x12])
- ByteArray = array.array('B')
- ByteArray.fromfile(PeObject, OptionalHeaderSize)
- ByteList = ByteArray.tolist()
- self.EntryPoint = self._ByteListToInt(ByteList[0x10:0x14])
- self.SectionAlignment = self._ByteListToInt(ByteList[0x20:0x24])
- self.Size = self._ByteListToInt(ByteList[0x38:0x3C])
-
- # Read each Section Header
- for Index in range(SecNumber):
- ByteArray = array.array('B')
- ByteArray.fromfile(PeObject, 0x28)
- ByteList = ByteArray.tolist()
- SecName = self._ByteListToStr(ByteList[0:8])
- SecVirtualSize = self._ByteListToInt(ByteList[8:12])
- SecRawAddress = self._ByteListToInt(ByteList[20:24])
- SecVirtualAddress = self._ByteListToInt(ByteList[12:16])
- self.SectionHeaderList.append((SecName, SecVirtualAddress, SecRawAddress, SecVirtualSize))
- self.IsValid = True
- PeObject.close()
-
- def _ByteListToStr(self, ByteList):
- String = ''
- for index in range(len(ByteList)):
- if ByteList[index] == 0:
- break
- String += chr(ByteList[index])
- return String
-
- def _ByteListToInt(self, ByteList):
- Value = 0
- for index in range(len(ByteList) - 1, -1, -1):
- Value = (Value << 8) | int(ByteList[index])
- return Value
-
-class DefaultStore():
- def __init__(self, DefaultStores ):
-
- self.DefaultStores = DefaultStores
- def DefaultStoreID(self, DefaultStoreName):
- for key, value in self.DefaultStores.items():
- if value == DefaultStoreName:
- return key
- return None
- def GetDefaultDefault(self):
- if not self.DefaultStores or "0" in self.DefaultStores:
- return "0", TAB_DEFAULT_STORES_DEFAULT
- else:
- minvalue = min(int(value_str) for value_str in self.DefaultStores)
- return (str(minvalue), self.DefaultStores[str(minvalue)])
- def GetMin(self, DefaultSIdList):
- if not DefaultSIdList:
- return TAB_DEFAULT_STORES_DEFAULT
- storeidset = {storeid for storeid, storename in self.DefaultStores.values() if storename in DefaultSIdList}
- if not storeidset:
- return ""
- minid = min(storeidset )
- for sid, name in self.DefaultStores.values():
- if sid == minid:
- return name
-
-class SkuClass():
- DEFAULT = 0
- SINGLE = 1
- MULTIPLE =2
-
- def __init__(self,SkuIdentifier='', SkuIds=None):
- if SkuIds is None:
- SkuIds = {}
-
- for SkuName in SkuIds:
- SkuId = SkuIds[SkuName][0]
- skuid_num = int(SkuId, 16) if SkuId.upper().startswith("0X") else int(SkuId)
- if skuid_num > 0xFFFFFFFFFFFFFFFF:
- EdkLogger.error("build", PARAMETER_INVALID,
- ExtraData = "SKU-ID [%s] value %s exceeds the max value of UINT64"
- % (SkuName, SkuId))
-
- self.AvailableSkuIds = OrderedDict()
- self.SkuIdSet = []
- self.SkuIdNumberSet = []
- self.SkuData = SkuIds
- self._SkuInherit = {}
- self._SkuIdentifier = SkuIdentifier
- if SkuIdentifier == '' or SkuIdentifier is None:
- self.SkuIdSet = ['DEFAULT']
- self.SkuIdNumberSet = ['0U']
- elif SkuIdentifier == 'ALL':
- self.SkuIdSet = list(SkuIds.keys())
- self.SkuIdNumberSet = [num[0].strip() + 'U' for num in SkuIds.values()]
- else:
- r = SkuIdentifier.split('|')
- self.SkuIdSet=[(r[k].strip()).upper() for k in range(len(r))]
- k = None
- try:
- self.SkuIdNumberSet = [SkuIds[k][0].strip() + 'U' for k in self.SkuIdSet]
- except Exception:
- EdkLogger.error("build", PARAMETER_INVALID,
- ExtraData = "SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
- % (k, " | ".join(SkuIds.keys())))
- for each in self.SkuIdSet:
- if each in SkuIds:
- self.AvailableSkuIds[each] = SkuIds[each][0]
- else:
- EdkLogger.error("build", PARAMETER_INVALID,
- ExtraData="SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
- % (each, " | ".join(SkuIds.keys())))
- if self.SkuUsageType != SkuClass.SINGLE:
- self.AvailableSkuIds.update({'DEFAULT':0, 'COMMON':0})
- if self.SkuIdSet:
- GlobalData.gSkuids = (self.SkuIdSet)
- if 'COMMON' in GlobalData.gSkuids:
- GlobalData.gSkuids.remove('COMMON')
- if self.SkuUsageType == self.SINGLE:
- if len(GlobalData.gSkuids) != 1:
- if 'DEFAULT' in GlobalData.gSkuids:
- GlobalData.gSkuids.remove('DEFAULT')
- if GlobalData.gSkuids:
- GlobalData.gSkuids.sort()
-
- def GetNextSkuId(self, skuname):
- if not self._SkuInherit:
- self._SkuInherit = {}
- for item in self.SkuData.values():
- self._SkuInherit[item[1]]=item[2] if item[2] else "DEFAULT"
- return self._SkuInherit.get(skuname, "DEFAULT")
-
- def GetSkuChain(self, sku):
- if sku == "DEFAULT":
- return ["DEFAULT"]
- skulist = [sku]
- nextsku = sku
- while True:
- nextsku = self.GetNextSkuId(nextsku)
- skulist.append(nextsku)
- if nextsku == "DEFAULT":
- break
- skulist.reverse()
- return skulist
- def SkuOverrideOrder(self):
- skuorderset = []
- for skuname in self.SkuIdSet:
- skuorderset.append(self.GetSkuChain(skuname))
-
- skuorder = []
- for index in range(max(len(item) for item in skuorderset)):
- for subset in skuorderset:
- if index > len(subset)-1:
- continue
- if subset[index] in skuorder:
- continue
- skuorder.append(subset[index])
-
- return skuorder
-
- @property
- def SkuUsageType(self):
- if self._SkuIdentifier.upper() == "ALL":
- return SkuClass.MULTIPLE
-
- if len(self.SkuIdSet) == 1:
- if self.SkuIdSet[0] == 'DEFAULT':
- return SkuClass.DEFAULT
- return SkuClass.SINGLE
- if len(self.SkuIdSet) == 2 and 'DEFAULT' in self.SkuIdSet:
- return SkuClass.SINGLE
- return SkuClass.MULTIPLE
-
- def DumpSkuIdArrary(self):
- if self.SkuUsageType == SkuClass.SINGLE:
- return "{0x0}"
- ArrayStrList = []
- for skuname in self.AvailableSkuIds:
- if skuname == "COMMON":
- continue
- while skuname != "DEFAULT":
- ArrayStrList.append(hex(int(self.AvailableSkuIds[skuname])))
- skuname = self.GetNextSkuId(skuname)
- ArrayStrList.append("0x0")
- return "{{{myList}}}".format(myList=",".join(ArrayStrList))
-
- @property
- def AvailableSkuIdSet(self):
- return self.AvailableSkuIds
-
- @property
- def SystemSkuId(self):
- if self.SkuUsageType == SkuClass.SINGLE:
- if len(self.SkuIdSet) == 1:
- return self.SkuIdSet[0]
- else:
- return self.SkuIdSet[0] if self.SkuIdSet[0] != 'DEFAULT' else self.SkuIdSet[1]
- else:
- return 'DEFAULT'
-
-## Get the integer value from string like "14U" or integer like 2
-#
-# @param Input The object that may be either a integer value or a string
-#
-# @retval Value The integer value that the input represents
-#
-def GetIntegerValue(Input):
- if not isinstance(Input, str):
- return Input
- String = Input
- if String.endswith("U"):
- String = String[:-1]
- if String.endswith("ULL"):
- String = String[:-3]
- if String.endswith("LL"):
- String = String[:-2]
-
- if String.startswith("0x") or String.startswith("0X"):
- return int(String, 16)
- elif String == '':
- return 0
- else:
- return int(String)
-
-#
-# Pack a GUID (registry format) list into a buffer and return it
-#
-def PackGUID(Guid):
- return pack(PACK_PATTERN_GUID,
- int(Guid[0], 16),
- int(Guid[1], 16),
- int(Guid[2], 16),
- int(Guid[3][-4:-2], 16),
- int(Guid[3][-2:], 16),
- int(Guid[4][-12:-10], 16),
- int(Guid[4][-10:-8], 16),
- int(Guid[4][-8:-6], 16),
- int(Guid[4][-6:-4], 16),
- int(Guid[4][-4:-2], 16),
- int(Guid[4][-2:], 16)
- )
-
-#
-# Pack a GUID (byte) list into a buffer and return it
-#
-def PackByteFormatGUID(Guid):
- return pack(PACK_PATTERN_GUID,
- Guid[0],
- Guid[1],
- Guid[2],
- Guid[3],
- Guid[4],
- Guid[5],
- Guid[6],
- Guid[7],
- Guid[8],
- Guid[9],
- Guid[10],
- )
-
-## DeepCopy dict/OrderedDict recusively
-#
-# @param ori_dict a nested dict or ordereddict
-#
-# @retval new dict or orderdict
-#
-def CopyDict(ori_dict):
- dict_type = ori_dict.__class__
- if dict_type not in (dict,OrderedDict):
- return ori_dict
- new_dict = dict_type()
- for key in ori_dict:
- if isinstance(ori_dict[key],(dict,OrderedDict)):
- new_dict[key] = CopyDict(ori_dict[key])
- else:
- new_dict[key] = ori_dict[key]
- return new_dict
-
-#
-# Remove the c/c++ comments: // and /* */
-#
-def RemoveCComments(ctext):
- return re.sub('//.*?\n|/\\*.*?\\*/', '\n', ctext, flags=re.S)
+## @file
+# Common routines used by all tools
+#
+# Copyright (c) 2007 - 2019, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+
+##
+# Import Modules
+#
+from __future__ import absolute_import
+
+import sys
+import string
+import threading
+import time
+import re
+import pickle
+import array
+import shutil
+import filecmp
+from random import sample
+from struct import pack
+import uuid
+import subprocess
+import tempfile
+from collections import OrderedDict
+
+import Common.LongFilePathOs as os
+from Common import EdkLogger as EdkLogger
+from Common import GlobalData as GlobalData
+from Common.DataType import *
+from Common.BuildToolError import *
+from CommonDataClass.DataClass import *
+from Common.Parsing import GetSplitValueList
+from Common.LongFilePathSupport import OpenLongFilePath as open
+from Common.LongFilePathSupport import CopyLongFilePath as CopyLong
+from Common.LongFilePathSupport import LongFilePath as LongFilePath
+from Common.MultipleWorkspace import MultipleWorkspace as mws
+from CommonDataClass.Exceptions import BadExpression
+from Common.caching import cached_property
+import struct
+
+ArrayIndex = re.compile(r"\[\s*[0-9a-fA-FxX]*\s*\]")
+## Regular expression used to find out place holders in string template
+gPlaceholderPattern = re.compile(r"\$\{([^$()\s]+)\}", re.MULTILINE | re.UNICODE)
+
+## regular expressions for map file processing
+startPatternGeneral = re.compile(r"^Start[' ']+Length[' ']+Name[' ']+Class")
+addressPatternGeneral = re.compile(r"^Address[' ']+Publics by Value[' ']+Rva\+Base")
+valuePatternGcc = re.compile(r'^([\w_\.]+) +([\da-fA-Fx]+) +([\da-fA-Fx]+)$')
+pcdPatternGcc = re.compile(r'^([\da-fA-Fx]+) +([\da-fA-Fx]+)')
+secReGeneral = re.compile(r'^([\da-fA-F]+):([\da-fA-F]+) +([\da-fA-F]+)[Hh]? +([.\w\$]+) +(\w+)', re.UNICODE)
+
+StructPattern = re.compile(r'[_a-zA-Z][0-9A-Za-z_]*$')
+
+## Dictionary used to store dependencies of files
+gDependencyDatabase = {} # arch : {file path : [dependent files list]}
+
+#
+# If a module is built more than once with different PCDs or library classes
+# a temporary INF file with same content is created, the temporary file is removed
+# when build exits.
+#
+_TempInfs = []
+
+def GetVariableOffset(mapfilepath, efifilepath, varnames):
+ """ Parse map file to get variable offset in current EFI file
+ @param mapfilepath Map file absolution path
+ @param efifilepath: EFI binary file full path
+ @param varnames iteratable container whose elements are variable names to be searched
+
+ @return List whos elements are tuple with variable name and raw offset
+ """
+ lines = []
+ try:
+ f = open(mapfilepath, 'r')
+ lines = f.readlines()
+ f.close()
+ except:
+ return None
+
+ if len(lines) == 0: return None
+ firstline = lines[0].strip()
+ if re.match(r'^\s*Address\s*Size\s*Align\s*Out\s*In\s*Symbol\s*$', firstline):
+ return _parseForXcodeAndClang9(lines, efifilepath, varnames)
+ if (firstline.startswith("Archive member included ") and
+ firstline.endswith(" file (symbol)")):
+ return _parseForGCC(lines, efifilepath, varnames)
+ if firstline.startswith("# Path:"):
+ return _parseForXcodeAndClang9(lines, efifilepath, varnames)
+ return _parseGeneral(lines, efifilepath, varnames)
+
+def _parseForXcodeAndClang9(lines, efifilepath, varnames):
+ status = 0
+ ret = []
+ for line in lines:
+ line = line.strip()
+ if status == 0 and (re.match(r'^\s*Address\s*Size\s*Align\s*Out\s*In\s*Symbol\s*$', line) \
+ or line == "# Symbols:"):
+ status = 1
+ continue
+ if status == 1 and len(line) != 0:
+ for varname in varnames:
+ if varname in line:
+ # cannot pregenerate this RegEx since it uses varname from varnames.
+ m = re.match(r'^([\da-fA-FxX]+)([\s\S]*)([_]*%s)$' % varname, line)
+ if m is not None:
+ ret.append((varname, m.group(1)))
+ return ret
+
+def _parseForGCC(lines, efifilepath, varnames):
+ """ Parse map file generated by GCC linker """
+ status = 0
+ sections = []
+ varoffset = []
+ for index, line in enumerate(lines):
+ line = line.strip()
+ # status machine transection
+ if status == 0 and line == "Memory Configuration":
+ status = 1
+ continue
+ elif status == 1 and line == 'Linker script and memory map':
+ status = 2
+ continue
+ elif status ==2 and line == 'START GROUP':
+ status = 3
+ continue
+
+ # status handler
+ if status == 3:
+ m = valuePatternGcc.match(line)
+ if m is not None:
+ sections.append(m.groups(0))
+ for varname in varnames:
+ Str = ''
+ m = re.match("^.data.(%s)" % varname, line)
+ if m is not None:
+ m = re.match(".data.(%s)$" % varname, line)
+ if m is not None:
+ Str = lines[index + 1]
+ else:
+ Str = line[len(".data.%s" % varname):]
+ if Str:
+ m = pcdPatternGcc.match(Str.strip())
+ if m is not None:
+ varoffset.append((varname, int(m.groups(0)[0], 16), int(sections[-1][1], 16), sections[-1][0]))
+
+ if not varoffset:
+ return []
+ # get section information from efi file
+ efisecs = PeImageClass(efifilepath).SectionHeaderList
+ if efisecs is None or len(efisecs) == 0:
+ return []
+ #redirection
+ redirection = 0
+ for efisec in efisecs:
+ for section in sections:
+ if section[0].strip() == efisec[0].strip() and section[0].strip() == '.text':
+ redirection = int(section[1], 16) - efisec[1]
+
+ ret = []
+ for var in varoffset:
+ for efisec in efisecs:
+ if var[1] >= efisec[1] and var[1] < efisec[1]+efisec[3]:
+ ret.append((var[0], hex(efisec[2] + var[1] - efisec[1] - redirection)))
+ return ret
+
+def _parseGeneral(lines, efifilepath, varnames):
+ status = 0 #0 - beginning of file; 1 - PE section definition; 2 - symbol table
+ secs = [] # key = section name
+ varoffset = []
+ symRe = re.compile(r'^([\da-fA-F]+):([\da-fA-F]+) +([\.:\\\\\w\?@\$-]+) +([\da-fA-F]+)', re.UNICODE)
+
+ for line in lines:
+ line = line.strip()
+ if startPatternGeneral.match(line):
+ status = 1
+ continue
+ if addressPatternGeneral.match(line):
+ status = 2
+ continue
+ if line.startswith("entry point at"):
+ status = 3
+ continue
+ if status == 1 and len(line) != 0:
+ m = secReGeneral.match(line)
+ assert m is not None, "Fail to parse the section in map file , line is %s" % line
+ sec_no, sec_start, sec_length, sec_name, sec_class = m.groups(0)
+ secs.append([int(sec_no, 16), int(sec_start, 16), int(sec_length, 16), sec_name, sec_class])
+ if status == 2 and len(line) != 0:
+ for varname in varnames:
+ m = symRe.match(line)
+ assert m is not None, "Fail to parse the symbol in map file, line is %s" % line
+ sec_no, sym_offset, sym_name, vir_addr = m.groups(0)
+ sec_no = int(sec_no, 16)
+ sym_offset = int(sym_offset, 16)
+ vir_addr = int(vir_addr, 16)
+ # cannot pregenerate this RegEx since it uses varname from varnames.
+ m2 = re.match('^[_]*(%s)' % varname, sym_name)
+ if m2 is not None:
+ # fond a binary pcd entry in map file
+ for sec in secs:
+ if sec[0] == sec_no and (sym_offset >= sec[1] and sym_offset < sec[1] + sec[2]):
+ varoffset.append([varname, sec[3], sym_offset, vir_addr, sec_no])
+
+ if not varoffset: return []
+
+ # get section information from efi file
+ efisecs = PeImageClass(efifilepath).SectionHeaderList
+ if efisecs is None or len(efisecs) == 0:
+ return []
+
+ ret = []
+ for var in varoffset:
+ index = 0
+ for efisec in efisecs:
+ index = index + 1
+ if var[1].strip() == efisec[0].strip():
+ ret.append((var[0], hex(efisec[2] + var[2])))
+ elif var[4] == index:
+ ret.append((var[0], hex(efisec[2] + var[2])))
+
+ return ret
+
+## Routine to process duplicated INF
+#
+# This function is called by following two cases:
+# Case 1 in DSC:
+# [components.arch]
+# Pkg/module/module.inf
+# Pkg/module/module.inf {
+# <Defines>
+# FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836
+# }
+# Case 2 in FDF:
+# INF Pkg/module/module.inf
+# INF FILE_GUID = 0D1B936F-68F3-4589-AFCC-FB8B7AEBC836 Pkg/module/module.inf
+#
+# This function copies Pkg/module/module.inf to
+# Conf/.cache/0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf
+#
+# @param Path Original PathClass object
+# @param BaseName New file base name
+#
+# @retval return the new PathClass object
+#
+def ProcessDuplicatedInf(Path, BaseName, Workspace):
+ Filename = os.path.split(Path.File)[1]
+ if '.' in Filename:
+ Filename = BaseName + Path.BaseName + Filename[Filename.rfind('.'):]
+ else:
+ Filename = BaseName + Path.BaseName
+
+ DbDir = os.path.split(GlobalData.gDatabasePath)[0]
+
+ #
+ # A temporary INF is copied to database path which must have write permission
+ # The temporary will be removed at the end of build
+ # In case of name conflict, the file name is
+ # FILE_GUIDBaseName (0D1B936F-68F3-4589-AFCC-FB8B7AEBC836module.inf)
+ #
+ TempFullPath = os.path.join(DbDir,
+ Filename)
+ RtPath = PathClass(Path.File, Workspace)
+ #
+ # Modify the full path to temporary path, keep other unchanged
+ #
+ # To build same module more than once, the module path with FILE_GUID overridden has
+ # the file name FILE_GUIDmodule.inf, but the relative path (self.MetaFile.File) is the real path
+ # in DSC which is used as relative path by C files and other files in INF.
+ # A trick was used: all module paths are PathClass instances, after the initialization
+ # of PathClass, the PathClass.Path is overridden by the temporary INF path.
+ #
+ # The reason for creating a temporary INF is:
+ # Platform.Modules which is the base to create ModuleAutoGen objects is a dictionary,
+ # the key is the full path of INF, the value is an object to save overridden library instances, PCDs.
+ # A different key for the same module is needed to create different output directory,
+ # retrieve overridden PCDs, library instances.
+ #
+ # The BaseName is the FILE_GUID which is also the output directory name.
+ #
+ #
+ RtPath.Path = TempFullPath
+ RtPath.BaseName = BaseName
+ RtPath.OriginalPath = Path
+ #
+ # If file exists, compare contents
+ #
+ if os.path.exists(TempFullPath):
+ with open(str(Path), 'rb') as f1, open(TempFullPath, 'rb') as f2:
+ if f1.read() == f2.read():
+ return RtPath
+ _TempInfs.append(TempFullPath)
+ shutil.copy2(str(Path), TempFullPath)
+ return RtPath
+
+## Remove temporary created INFs whose paths were saved in _TempInfs
+#
+def ClearDuplicatedInf():
+ while _TempInfs:
+ File = _TempInfs.pop()
+ if os.path.exists(File):
+ os.remove(File)
+
+## Convert GUID string in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx style to C structure style
+#
+# @param Guid The GUID string
+#
+# @retval string The GUID string in C structure style
+#
+def GuidStringToGuidStructureString(Guid):
+ GuidList = Guid.split('-')
+ Result = '{'
+ for Index in range(0, 3, 1):
+ Result = Result + '0x' + GuidList[Index] + ', '
+ Result = Result + '{0x' + GuidList[3][0:2] + ', 0x' + GuidList[3][2:4]
+ for Index in range(0, 12, 2):
+ Result = Result + ', 0x' + GuidList[4][Index:Index + 2]
+ Result += '}}'
+ return Result
+
+## Convert GUID structure in byte array to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+#
+# @param GuidValue The GUID value in byte array
+#
+# @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
+#
+def GuidStructureByteArrayToGuidString(GuidValue):
+ guidValueString = GuidValue.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
+ guidValueList = guidValueString.split(",")
+ if len(guidValueList) != 16:
+ return ''
+ #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
+ try:
+ return "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
+ int(guidValueList[3], 16),
+ int(guidValueList[2], 16),
+ int(guidValueList[1], 16),
+ int(guidValueList[0], 16),
+ int(guidValueList[5], 16),
+ int(guidValueList[4], 16),
+ int(guidValueList[7], 16),
+ int(guidValueList[6], 16),
+ int(guidValueList[8], 16),
+ int(guidValueList[9], 16),
+ int(guidValueList[10], 16),
+ int(guidValueList[11], 16),
+ int(guidValueList[12], 16),
+ int(guidValueList[13], 16),
+ int(guidValueList[14], 16),
+ int(guidValueList[15], 16)
+ )
+ except:
+ return ''
+
+## Convert GUID string in C structure style to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+#
+# @param GuidValue The GUID value in C structure format
+#
+# @retval string The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format
+#
+def GuidStructureStringToGuidString(GuidValue):
+ if not GlobalData.gGuidCFormatPattern.match(GuidValue):
+ return ''
+ guidValueString = GuidValue.lower().replace("{", "").replace("}", "").replace(" ", "").replace(";", "")
+ guidValueList = guidValueString.split(",")
+ if len(guidValueList) != 11:
+ return ''
+ #EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)
+ try:
+ return "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (
+ int(guidValueList[0], 16),
+ int(guidValueList[1], 16),
+ int(guidValueList[2], 16),
+ int(guidValueList[3], 16),
+ int(guidValueList[4], 16),
+ int(guidValueList[5], 16),
+ int(guidValueList[6], 16),
+ int(guidValueList[7], 16),
+ int(guidValueList[8], 16),
+ int(guidValueList[9], 16),
+ int(guidValueList[10], 16)
+ )
+ except:
+ return ''
+
+## Convert GUID string in C structure style to xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx
+#
+# @param GuidValue The GUID value in C structure format
+#
+# @retval string The GUID value in xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx format
+#
+def GuidStructureStringToGuidValueName(GuidValue):
+ guidValueString = GuidValue.lower().replace("{", "").replace("}", "").replace(" ", "")
+ guidValueList = guidValueString.split(",")
+ if len(guidValueList) != 11:
+ EdkLogger.error(None, FORMAT_INVALID, "Invalid GUID value string [%s]" % GuidValue)
+ return "%08x_%04x_%04x_%02x%02x_%02x%02x%02x%02x%02x%02x" % (
+ int(guidValueList[0], 16),
+ int(guidValueList[1], 16),
+ int(guidValueList[2], 16),
+ int(guidValueList[3], 16),
+ int(guidValueList[4], 16),
+ int(guidValueList[5], 16),
+ int(guidValueList[6], 16),
+ int(guidValueList[7], 16),
+ int(guidValueList[8], 16),
+ int(guidValueList[9], 16),
+ int(guidValueList[10], 16)
+ )
+
+## Create directories
+#
+# @param Directory The directory name
+#
+def CreateDirectory(Directory):
+ if Directory is None or Directory.strip() == "":
+ return True
+ try:
+ if not os.access(Directory, os.F_OK):
+ os.makedirs(Directory)
+ except:
+ return False
+ return True
+
+## Remove directories, including files and sub-directories in it
+#
+# @param Directory The directory name
+#
+def RemoveDirectory(Directory, Recursively=False):
+ if Directory is None or Directory.strip() == "" or not os.path.exists(Directory):
+ return
+ if Recursively:
+ CurrentDirectory = os.getcwd()
+ os.chdir(Directory)
+ for File in os.listdir("."):
+ if os.path.isdir(File):
+ RemoveDirectory(File, Recursively)
+ else:
+ os.remove(File)
+ os.chdir(CurrentDirectory)
+ os.rmdir(Directory)
+
+## Store content in file
+#
+# This method is used to save file only when its content is changed. This is
+# quite useful for "make" system to decide what will be re-built and what won't.
+#
+# @param File The path of file
+# @param Content The new content of the file
+# @param IsBinaryFile The flag indicating if the file is binary file or not
+#
+# @retval True If the file content is changed and the file is renewed
+# @retval False If the file content is the same
+#
+def SaveFileOnChange(File, Content, IsBinaryFile=True, FileLock=None):
+
+ # Convert to long file path format
+ File = LongFilePath(File)
+
+ if os.path.exists(File):
+ if IsBinaryFile:
+ try:
+ with open(File, "rb") as f:
+ if Content == f.read():
+ return False
+ except:
+ EdkLogger.error(None, FILE_OPEN_FAILURE, ExtraData=File)
+ else:
+ try:
+ with open(File, "r") as f:
+ if Content == f.read():
+ return False
+ except:
+ EdkLogger.error(None, FILE_OPEN_FAILURE, ExtraData=File)
+
+ DirName = os.path.dirname(File)
+ if not CreateDirectory(DirName):
+ EdkLogger.error(None, FILE_CREATE_FAILURE, "Could not create directory %s" % DirName)
+ else:
+ if DirName == '':
+ DirName = os.getcwd()
+ if not os.access(DirName, os.W_OK):
+ EdkLogger.error(None, PERMISSION_FAILURE, "Do not have write permission on directory %s" % DirName)
+
+ OpenMode = "w"
+ if IsBinaryFile:
+ OpenMode = "wb"
+
+ # use default file_lock if no input new lock
+ if not FileLock:
+ FileLock = GlobalData.file_lock
+ if FileLock:
+ FileLock.acquire()
+
+
+ if GlobalData.gIsWindows and not os.path.exists(File):
+ try:
+ with open(File, OpenMode) as tf:
+ tf.write(Content)
+ except IOError as X:
+ if GlobalData.gBinCacheSource:
+ EdkLogger.quiet("[cache error]:fails to save file with error: %s" % (X))
+ else:
+ EdkLogger.error(None, FILE_CREATE_FAILURE, ExtraData='IOError %s' % X)
+ finally:
+ if FileLock:
+ FileLock.release()
+ else:
+ try:
+ with open(File, OpenMode) as Fd:
+ Fd.write(Content)
+ except IOError as X:
+ if GlobalData.gBinCacheSource:
+ EdkLogger.quiet("[cache error]:fails to save file with error: %s" % (X))
+ else:
+ EdkLogger.error(None, FILE_CREATE_FAILURE, ExtraData='IOError %s' % X)
+ finally:
+ if FileLock:
+ FileLock.release()
+
+ return True
+
+## Copy source file only if it is different from the destination file
+#
+# This method is used to copy file only if the source file and destination
+# file content are different. This is quite useful to avoid duplicated
+# file writing.
+#
+# @param SrcFile The path of source file
+# @param Dst The path of destination file or folder
+#
+# @retval True The two files content are different and the file is copied
+# @retval False No copy really happen
+#
+def CopyFileOnChange(SrcFile, Dst, FileLock=None):
+
+ # Convert to long file path format
+ SrcFile = LongFilePath(SrcFile)
+ Dst = LongFilePath(Dst)
+
+ if os.path.isdir(SrcFile):
+ EdkLogger.error(None, FILE_COPY_FAILURE, ExtraData='CopyFileOnChange SrcFile is a dir, not a file: %s' % SrcFile)
+ return False
+
+ if os.path.isdir(Dst):
+ DstFile = os.path.join(Dst, os.path.basename(SrcFile))
+ else:
+ DstFile = Dst
+
+ if os.path.exists(DstFile) and filecmp.cmp(SrcFile, DstFile, shallow=False):
+ return False
+
+ DirName = os.path.dirname(DstFile)
+ if not CreateDirectory(DirName):
+ EdkLogger.error(None, FILE_CREATE_FAILURE, "Could not create directory %s" % DirName)
+ else:
+ if DirName == '':
+ DirName = os.getcwd()
+ if not os.access(DirName, os.W_OK):
+ EdkLogger.error(None, PERMISSION_FAILURE, "Do not have write permission on directory %s" % DirName)
+
+ # use default file_lock if no input new lock
+ if not FileLock:
+ FileLock = GlobalData.file_lock
+ if FileLock:
+ FileLock.acquire()
+
+ try:
+ CopyLong(SrcFile, DstFile)
+ except IOError as X:
+ if GlobalData.gBinCacheSource:
+ EdkLogger.quiet("[cache error]:fails to copy file with error: %s" % (X))
+ else:
+ EdkLogger.error(None, FILE_COPY_FAILURE, ExtraData='IOError %s' % X)
+ finally:
+ if FileLock:
+ FileLock.release()
+
+ return True
+
+## Retrieve and cache the real path name in file system
+#
+# @param Root The root directory of path relative to
+#
+# @retval str The path string if the path exists
+# @retval None If path doesn't exist
+#
+class DirCache:
+ _CACHE_ = set()
+ _UPPER_CACHE_ = {}
+
+ def __init__(self, Root):
+ self._Root = Root
+ for F in os.listdir(Root):
+ self._CACHE_.add(F)
+ self._UPPER_CACHE_[F.upper()] = F
+
+ # =[] operator
+ def __getitem__(self, Path):
+ Path = Path[len(os.path.commonprefix([Path, self._Root])):]
+ if not Path:
+ return self._Root
+ if Path and Path[0] == os.path.sep:
+ Path = Path[1:]
+ if Path in self._CACHE_:
+ return os.path.join(self._Root, Path)
+ UpperPath = Path.upper()
+ if UpperPath in self._UPPER_CACHE_:
+ return os.path.join(self._Root, self._UPPER_CACHE_[UpperPath])
+
+ IndexList = []
+ LastSepIndex = -1
+ SepIndex = Path.find(os.path.sep)
+ while SepIndex > -1:
+ Parent = UpperPath[:SepIndex]
+ if Parent not in self._UPPER_CACHE_:
+ break
+ LastSepIndex = SepIndex
+ SepIndex = Path.find(os.path.sep, LastSepIndex + 1)
+
+ if LastSepIndex == -1:
+ return None
+
+ Cwd = os.getcwd()
+ os.chdir(self._Root)
+ SepIndex = LastSepIndex
+ while SepIndex > -1:
+ Parent = Path[:SepIndex]
+ ParentKey = UpperPath[:SepIndex]
+ if ParentKey not in self._UPPER_CACHE_:
+ os.chdir(Cwd)
+ return None
+
+ if Parent in self._CACHE_:
+ ParentDir = Parent
+ else:
+ ParentDir = self._UPPER_CACHE_[ParentKey]
+ for F in os.listdir(ParentDir):
+ Dir = os.path.join(ParentDir, F)
+ self._CACHE_.add(Dir)
+ self._UPPER_CACHE_[Dir.upper()] = Dir
+
+ SepIndex = Path.find(os.path.sep, SepIndex + 1)
+
+ os.chdir(Cwd)
+ if Path in self._CACHE_:
+ return os.path.join(self._Root, Path)
+ elif UpperPath in self._UPPER_CACHE_:
+ return os.path.join(self._Root, self._UPPER_CACHE_[UpperPath])
+ return None
+
+def RealPath(File, Dir='', OverrideDir=''):
+ NewFile = os.path.normpath(os.path.join(Dir, File))
+ NewFile = GlobalData.gAllFiles[NewFile]
+ if not NewFile and OverrideDir:
+ NewFile = os.path.normpath(os.path.join(OverrideDir, File))
+ NewFile = GlobalData.gAllFiles[NewFile]
+ return NewFile
+
+## Get GUID value from given packages
+#
+# @param CName The CName of the GUID
+# @param PackageList List of packages looking-up in
+# @param Inffile The driver file
+#
+# @retval GuidValue if the CName is found in any given package
+# @retval None if the CName is not found in all given packages
+#
+def GuidValue(CName, PackageList, Inffile = None):
+ for P in PackageList:
+ GuidKeys = list(P.Guids.keys())
+ if Inffile and P._PrivateGuids:
+ if not Inffile.startswith(P.MetaFile.Dir):
+ GuidKeys = [x for x in P.Guids if x not in P._PrivateGuids]
+ if CName in GuidKeys:
+ return P.Guids[CName]
+ return None
+
+## A string template class
+#
+# This class implements a template for string replacement. A string template
+# looks like following
+#
+# ${BEGIN} other_string ${placeholder_name} other_string ${END}
+#
+# The string between ${BEGIN} and ${END} will be repeated as many times as the
+# length of "placeholder_name", which is a list passed through a dict. The
+# "placeholder_name" is the key name of the dict. The ${BEGIN} and ${END} can
+# be not used and, in this case, the "placeholder_name" must not a list and it
+# will just be replaced once.
+#
+class TemplateString(object):
+ _REPEAT_START_FLAG = "BEGIN"
+ _REPEAT_END_FLAG = "END"
+
+ class Section(object):
+ _LIST_TYPES = [type([]), type(set()), type((0,))]
+
+ def __init__(self, TemplateSection, PlaceHolderList):
+ self._Template = TemplateSection
+ self._PlaceHolderList = []
+
+ # Split the section into sub-sections according to the position of placeholders
+ if PlaceHolderList:
+ self._SubSectionList = []
+ SubSectionStart = 0
+ #
+ # The placeholders passed in must be in the format of
+ #
+ # PlaceHolderName, PlaceHolderStartPoint, PlaceHolderEndPoint
+ #
+ for PlaceHolder, Start, End in PlaceHolderList:
+ self._SubSectionList.append(TemplateSection[SubSectionStart:Start])
+ self._SubSectionList.append(TemplateSection[Start:End])
+ self._PlaceHolderList.append(PlaceHolder)
+ SubSectionStart = End
+ if SubSectionStart < len(TemplateSection):
+ self._SubSectionList.append(TemplateSection[SubSectionStart:])
+ else:
+ self._SubSectionList = [TemplateSection]
+
+ def __str__(self):
+ return self._Template + " : " + str(self._PlaceHolderList)
+
+ def Instantiate(self, PlaceHolderValues):
+ RepeatTime = -1
+ RepeatPlaceHolders = {}
+ NonRepeatPlaceHolders = {}
+
+ for PlaceHolder in self._PlaceHolderList:
+ if PlaceHolder not in PlaceHolderValues:
+ continue
+ Value = PlaceHolderValues[PlaceHolder]
+ if type(Value) in self._LIST_TYPES:
+ if RepeatTime < 0:
+ RepeatTime = len(Value)
+ elif RepeatTime != len(Value):
+ EdkLogger.error(
+ "TemplateString",
+ PARAMETER_INVALID,
+ "${%s} has different repeat time from others!" % PlaceHolder,
+ ExtraData=str(self._Template)
+ )
+ RepeatPlaceHolders["${%s}" % PlaceHolder] = Value
+ else:
+ NonRepeatPlaceHolders["${%s}" % PlaceHolder] = Value
+
+ if NonRepeatPlaceHolders:
+ StringList = []
+ for S in self._SubSectionList:
+ if S not in NonRepeatPlaceHolders:
+ StringList.append(S)
+ else:
+ StringList.append(str(NonRepeatPlaceHolders[S]))
+ else:
+ StringList = self._SubSectionList
+
+ if RepeatPlaceHolders:
+ TempStringList = []
+ for Index in range(RepeatTime):
+ for S in StringList:
+ if S not in RepeatPlaceHolders:
+ TempStringList.append(S)
+ else:
+ TempStringList.append(str(RepeatPlaceHolders[S][Index]))
+ StringList = TempStringList
+
+ return "".join(StringList)
+
+ ## Constructor
+ def __init__(self, Template=None):
+ self.String = []
+ self.IsBinary = False
+ self._Template = Template
+ self._TemplateSectionList = self._Parse(Template)
+
+ ## str() operator
+ #
+ # @retval string The string replaced
+ #
+ def __str__(self):
+ return "".join(self.String)
+
+ ## Split the template string into fragments per the ${BEGIN} and ${END} flags
+ #
+ # @retval list A list of TemplateString.Section objects
+ #
+ def _Parse(self, Template):
+ SectionStart = 0
+ SearchFrom = 0
+ MatchEnd = 0
+ PlaceHolderList = []
+ TemplateSectionList = []
+ while Template:
+ MatchObj = gPlaceholderPattern.search(Template, SearchFrom)
+ if not MatchObj:
+ if MatchEnd <= len(Template):
+ TemplateSection = TemplateString.Section(Template[SectionStart:], PlaceHolderList)
+ TemplateSectionList.append(TemplateSection)
+ break
+
+ MatchString = MatchObj.group(1)
+ MatchStart = MatchObj.start()
+ MatchEnd = MatchObj.end()
+
+ if MatchString == self._REPEAT_START_FLAG:
+ if MatchStart > SectionStart:
+ TemplateSection = TemplateString.Section(Template[SectionStart:MatchStart], PlaceHolderList)
+ TemplateSectionList.append(TemplateSection)
+ SectionStart = MatchEnd
+ PlaceHolderList = []
+ elif MatchString == self._REPEAT_END_FLAG:
+ TemplateSection = TemplateString.Section(Template[SectionStart:MatchStart], PlaceHolderList)
+ TemplateSectionList.append(TemplateSection)
+ SectionStart = MatchEnd
+ PlaceHolderList = []
+ else:
+ PlaceHolderList.append((MatchString, MatchStart - SectionStart, MatchEnd - SectionStart))
+ SearchFrom = MatchEnd
+ return TemplateSectionList
+
+ ## Replace the string template with dictionary of placeholders and append it to previous one
+ #
+ # @param AppendString The string template to append
+ # @param Dictionary The placeholder dictionaries
+ #
+ def Append(self, AppendString, Dictionary=None):
+ if Dictionary:
+ SectionList = self._Parse(AppendString)
+ self.String.append( "".join(S.Instantiate(Dictionary) for S in SectionList))
+ else:
+ if isinstance(AppendString,list):
+ self.String.extend(AppendString)
+ else:
+ self.String.append(AppendString)
+
+ ## Replace the string template with dictionary of placeholders
+ #
+ # @param Dictionary The placeholder dictionaries
+ #
+ # @retval str The string replaced with placeholder values
+ #
+ def Replace(self, Dictionary=None):
+ return "".join(S.Instantiate(Dictionary) for S in self._TemplateSectionList)
+
+## Progress indicator class
+#
+# This class makes use of thread to print progress on console.
+#
+class Progressor:
+ # for avoiding deadloop
+ _StopFlag = None
+ _ProgressThread = None
+ _CheckInterval = 0.25
+
+ ## Constructor
+ #
+ # @param OpenMessage The string printed before progress characters
+ # @param CloseMessage The string printed after progress characters
+ # @param ProgressChar The character used to indicate the progress
+ # @param Interval The interval in seconds between two progress characters
+ #
+ def __init__(self, OpenMessage="", CloseMessage="", ProgressChar='.', Interval=1.0):
+ self.PromptMessage = OpenMessage
+ self.CodaMessage = CloseMessage
+ self.ProgressChar = ProgressChar
+ self.Interval = Interval
+ if Progressor._StopFlag is None:
+ Progressor._StopFlag = threading.Event()
+
+ ## Start to print progress character
+ #
+ # @param OpenMessage The string printed before progress characters
+ #
+ def Start(self, OpenMessage=None):
+ if OpenMessage is not None:
+ self.PromptMessage = OpenMessage
+ Progressor._StopFlag.clear()
+ if Progressor._ProgressThread is None:
+ Progressor._ProgressThread = threading.Thread(target=self._ProgressThreadEntry)
+ Progressor._ProgressThread.setDaemon(False)
+ Progressor._ProgressThread.start()
+
+ ## Stop printing progress character
+ #
+ # @param CloseMessage The string printed after progress characters
+ #
+ def Stop(self, CloseMessage=None):
+ OriginalCodaMessage = self.CodaMessage
+ if CloseMessage is not None:
+ self.CodaMessage = CloseMessage
+ self.Abort()
+ self.CodaMessage = OriginalCodaMessage
+
+ ## Thread entry method
+ def _ProgressThreadEntry(self):
+ sys.stdout.write(self.PromptMessage + " ")
+ sys.stdout.flush()
+ TimeUp = 0.0
+ while not Progressor._StopFlag.isSet():
+ if TimeUp <= 0.0:
+ sys.stdout.write(self.ProgressChar)
+ sys.stdout.flush()
+ TimeUp = self.Interval
+ time.sleep(self._CheckInterval)
+ TimeUp -= self._CheckInterval
+ sys.stdout.write(" " + self.CodaMessage + "\n")
+ sys.stdout.flush()
+
+ ## Abort the progress display
+ @staticmethod
+ def Abort():
+ if Progressor._StopFlag is not None:
+ Progressor._StopFlag.set()
+ if Progressor._ProgressThread is not None:
+ Progressor._ProgressThread.join()
+ Progressor._ProgressThread = None
+
+
+## Dictionary using prioritized list as key
+#
+class tdict:
+ _ListType = type([])
+ _TupleType = type(())
+ _Wildcard = 'COMMON'
+ _ValidWildcardList = ['COMMON', 'DEFAULT', 'ALL', TAB_STAR, 'PLATFORM']
+
+ def __init__(self, _Single_=False, _Level_=2):
+ self._Level_ = _Level_
+ self.data = {}
+ self._Single_ = _Single_
+
+ # =[] operator
+ def __getitem__(self, key):
+ KeyType = type(key)
+ RestKeys = None
+ if KeyType == self._ListType or KeyType == self._TupleType:
+ FirstKey = key[0]
+ if len(key) > 1:
+ RestKeys = key[1:]
+ elif self._Level_ > 1:
+ RestKeys = [self._Wildcard for i in range(0, self._Level_ - 1)]
+ else:
+ FirstKey = key
+ if self._Level_ > 1:
+ RestKeys = [self._Wildcard for i in range(0, self._Level_ - 1)]
+
+ if FirstKey is None or str(FirstKey).upper() in self._ValidWildcardList:
+ FirstKey = self._Wildcard
+
+ if self._Single_:
+ return self._GetSingleValue(FirstKey, RestKeys)
+ else:
+ return self._GetAllValues(FirstKey, RestKeys)
+
+ def _GetSingleValue(self, FirstKey, RestKeys):
+ Value = None
+ #print "%s-%s" % (FirstKey, self._Level_) ,
+ if self._Level_ > 1:
+ if FirstKey == self._Wildcard:
+ if FirstKey in self.data:
+ Value = self.data[FirstKey][RestKeys]
+ if Value is None:
+ for Key in self.data:
+ Value = self.data[Key][RestKeys]
+ if Value is not None: break
+ else:
+ if FirstKey in self.data:
+ Value = self.data[FirstKey][RestKeys]
+ if Value is None and self._Wildcard in self.data:
+ #print "Value=None"
+ Value = self.data[self._Wildcard][RestKeys]
+ else:
+ if FirstKey == self._Wildcard:
+ if FirstKey in self.data:
+ Value = self.data[FirstKey]
+ if Value is None:
+ for Key in self.data:
+ Value = self.data[Key]
+ if Value is not None: break
+ else:
+ if FirstKey in self.data:
+ Value = self.data[FirstKey]
+ elif self._Wildcard in self.data:
+ Value = self.data[self._Wildcard]
+ return Value
+
+ def _GetAllValues(self, FirstKey, RestKeys):
+ Value = []
+ if self._Level_ > 1:
+ if FirstKey == self._Wildcard:
+ for Key in self.data:
+ Value += self.data[Key][RestKeys]
+ else:
+ if FirstKey in self.data:
+ Value += self.data[FirstKey][RestKeys]
+ if self._Wildcard in self.data:
+ Value += self.data[self._Wildcard][RestKeys]
+ else:
+ if FirstKey == self._Wildcard:
+ for Key in self.data:
+ Value.append(self.data[Key])
+ else:
+ if FirstKey in self.data:
+ Value.append(self.data[FirstKey])
+ if self._Wildcard in self.data:
+ Value.append(self.data[self._Wildcard])
+ return Value
+
+ ## []= operator
+ def __setitem__(self, key, value):
+ KeyType = type(key)
+ RestKeys = None
+ if KeyType == self._ListType or KeyType == self._TupleType:
+ FirstKey = key[0]
+ if len(key) > 1:
+ RestKeys = key[1:]
+ else:
+ RestKeys = [self._Wildcard for i in range(0, self._Level_ - 1)]
+ else:
+ FirstKey = key
+ if self._Level_ > 1:
+ RestKeys = [self._Wildcard for i in range(0, self._Level_ - 1)]
+
+ if FirstKey in self._ValidWildcardList:
+ FirstKey = self._Wildcard
+
+ if FirstKey not in self.data and self._Level_ > 0:
+ self.data[FirstKey] = tdict(self._Single_, self._Level_ - 1)
+
+ if self._Level_ > 1:
+ self.data[FirstKey][RestKeys] = value
+ else:
+ self.data[FirstKey] = value
+
+ def SetGreedyMode(self):
+ self._Single_ = False
+ if self._Level_ > 1:
+ for Key in self.data:
+ self.data[Key].SetGreedyMode()
+
+ def SetSingleMode(self):
+ self._Single_ = True
+ if self._Level_ > 1:
+ for Key in self.data:
+ self.data[Key].SetSingleMode()
+
+ def GetKeys(self, KeyIndex=0):
+ assert KeyIndex >= 0
+ if KeyIndex == 0:
+ return set(self.data.keys())
+ else:
+ keys = set()
+ for Key in self.data:
+ keys |= self.data[Key].GetKeys(KeyIndex - 1)
+ return keys
+
+def AnalyzePcdExpression(Setting):
+ RanStr = ''.join(sample(string.ascii_letters + string.digits, 8))
+ Setting = Setting.replace('\\\\', RanStr).strip()
+ # There might be escaped quote in a string: \", \\\" , \', \\\'
+ Data = Setting
+ # There might be '|' in string and in ( ... | ... ), replace it with '-'
+ NewStr = ''
+ InSingleQuoteStr = False
+ InDoubleQuoteStr = False
+ Pair = 0
+ for Index, ch in enumerate(Data):
+ if ch == '"' and not InSingleQuoteStr:
+ if Data[Index - 1] != '\\':
+ InDoubleQuoteStr = not InDoubleQuoteStr
+ elif ch == "'" and not InDoubleQuoteStr:
+ if Data[Index - 1] != '\\':
+ InSingleQuoteStr = not InSingleQuoteStr
+ elif ch == '(' and not (InSingleQuoteStr or InDoubleQuoteStr):
+ Pair += 1
+ elif ch == ')' and not (InSingleQuoteStr or InDoubleQuoteStr):
+ Pair -= 1
+
+ if (Pair > 0 or InSingleQuoteStr or InDoubleQuoteStr) and ch == TAB_VALUE_SPLIT:
+ NewStr += '-'
+ else:
+ NewStr += ch
+ FieldList = []
+ StartPos = 0
+ while True:
+ Pos = NewStr.find(TAB_VALUE_SPLIT, StartPos)
+ if Pos < 0:
+ FieldList.append(Setting[StartPos:].strip())
+ break
+ FieldList.append(Setting[StartPos:Pos].strip())
+ StartPos = Pos + 1
+ for i, ch in enumerate(FieldList):
+ if RanStr in ch:
+ FieldList[i] = ch.replace(RanStr,'\\\\')
+ return FieldList
+
+def ParseFieldValue (Value):
+ def ParseDevPathValue (Value):
+ if '\\' in Value:
+ Value.replace('\\', '/').replace(' ', '')
+
+ Cmd = 'DevicePath ' + '"' + Value + '"'
+ try:
+ p = subprocess.Popen(Cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
+ out, err = p.communicate()
+ except Exception as X:
+ raise BadExpression("DevicePath: %s" % (str(X)) )
+ finally:
+ subprocess._cleanup()
+ p.stdout.close()
+ p.stderr.close()
+ if err:
+ raise BadExpression("DevicePath: %s" % str(err))
+ out = out.decode()
+ Size = len(out.split())
+ out = ','.join(out.split())
+ return '{' + out + '}', Size
+
+ if "{CODE(" in Value:
+ return Value, len(Value.split(","))
+ if isinstance(Value, type(0)):
+ return Value, (Value.bit_length() + 7) // 8
+ if not isinstance(Value, type('')):
+ raise BadExpression('Type %s is %s' %(Value, type(Value)))
+ Value = Value.strip()
+ if Value.startswith(TAB_UINT8) and Value.endswith(')'):
+ Value, Size = ParseFieldValue(Value.split('(', 1)[1][:-1])
+ if Size > 1:
+ raise BadExpression('Value (%s) Size larger than %d' %(Value, Size))
+ return Value, 1
+ if Value.startswith(TAB_UINT16) and Value.endswith(')'):
+ Value, Size = ParseFieldValue(Value.split('(', 1)[1][:-1])
+ if Size > 2:
+ raise BadExpression('Value (%s) Size larger than %d' %(Value, Size))
+ return Value, 2
+ if Value.startswith(TAB_UINT32) and Value.endswith(')'):
+ Value, Size = ParseFieldValue(Value.split('(', 1)[1][:-1])
+ if Size > 4:
+ raise BadExpression('Value (%s) Size larger than %d' %(Value, Size))
+ return Value, 4
+ if Value.startswith(TAB_UINT64) and Value.endswith(')'):
+ Value, Size = ParseFieldValue(Value.split('(', 1)[1][:-1])
+ if Size > 8:
+ raise BadExpression('Value (%s) Size larger than %d' % (Value, Size))
+ return Value, 8
+ if Value.startswith(TAB_GUID) and Value.endswith(')'):
+ Value = Value.split('(', 1)[1][:-1].strip()
+ if Value[0] == '{' and Value[-1] == '}':
+ TmpValue = GuidStructureStringToGuidString(Value)
+ if not TmpValue:
+ raise BadExpression("Invalid GUID value string %s" % Value)
+ Value = TmpValue
+ if Value[0] == '"' and Value[-1] == '"':
+ Value = Value[1:-1]
+ try:
+ Value = uuid.UUID(Value).bytes_le
+ ValueL, ValueH = struct.unpack('2Q', Value)
+ Value = (ValueH << 64 ) | ValueL
+
+ except ValueError as Message:
+ raise BadExpression(Message)
+ return Value, 16
+ if Value.startswith('L"') and Value.endswith('"'):
+ # Unicode String
+ # translate escape character
+ Value = Value[1:]
+ try:
+ Value = eval(Value)
+ except:
+ Value = Value[1:-1]
+ List = list(Value)
+ List.reverse()
+ Value = 0
+ for Char in List:
+ Value = (Value << 16) | ord(Char)
+ return Value, (len(List) + 1) * 2
+ if Value.startswith('"') and Value.endswith('"'):
+ # ASCII String
+ # translate escape character
+ try:
+ Value = eval(Value)
+ except:
+ Value = Value[1:-1]
+ List = list(Value)
+ List.reverse()
+ Value = 0
+ for Char in List:
+ Value = (Value << 8) | ord(Char)
+ return Value, len(List) + 1
+ if Value.startswith("L'") and Value.endswith("'"):
+ # Unicode Character Constant
+ # translate escape character
+ Value = Value[1:]
+ try:
+ Value = eval(Value)
+ except:
+ Value = Value[1:-1]
+ List = list(Value)
+ if len(List) == 0:
+ raise BadExpression('Length %s is %s' % (Value, len(List)))
+ List.reverse()
+ Value = 0
+ for Char in List:
+ Value = (Value << 16) | ord(Char)
+ return Value, len(List) * 2
+ if Value.startswith("'") and Value.endswith("'"):
+ # Character constant
+ # translate escape character
+ try:
+ Value = eval(Value)
+ except:
+ Value = Value[1:-1]
+ List = list(Value)
+ if len(List) == 0:
+ raise BadExpression('Length %s is %s' % (Value, len(List)))
+ List.reverse()
+ Value = 0
+ for Char in List:
+ Value = (Value << 8) | ord(Char)
+ return Value, len(List)
+ if Value.startswith('{') and Value.endswith('}'):
+ # Byte array
+ Value = Value[1:-1]
+ List = [Item.strip() for Item in Value.split(',')]
+ List.reverse()
+ Value = 0
+ RetSize = 0
+ for Item in List:
+ ItemValue, Size = ParseFieldValue(Item)
+ RetSize += Size
+ for I in range(Size):
+ Value = (Value << 8) | ((ItemValue >> 8 * I) & 0xff)
+ return Value, RetSize
+ if Value.startswith('DEVICE_PATH(') and Value.endswith(')'):
+ Value = Value.replace("DEVICE_PATH(", '').rstrip(')')
+ Value = Value.strip().strip('"')
+ return ParseDevPathValue(Value)
+ if Value.lower().startswith('0x'):
+ try:
+ Value = int(Value, 16)
+ except:
+ raise BadExpression("invalid hex value: %s" % Value)
+ if Value == 0:
+ return 0, 1
+ return Value, (Value.bit_length() + 7) // 8
+ if Value[0].isdigit():
+ Value = int(Value, 10)
+ if Value == 0:
+ return 0, 1
+ return Value, (Value.bit_length() + 7) // 8
+ if Value.lower() == 'true':
+ return 1, 1
+ if Value.lower() == 'false':
+ return 0, 1
+ return Value, 1
+
+## AnalyzeDscPcd
+#
+# Analyze DSC PCD value, since there is no data type info in DSC
+# This function is used to match functions (AnalyzePcdData) used for retrieving PCD value from database
+# 1. Feature flag: TokenSpace.PcdCName|PcdValue
+# 2. Fix and Patch:TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
+# 3. Dynamic default:
+# TokenSpace.PcdCName|PcdValue[|VOID*[|MaxSize]]
+# TokenSpace.PcdCName|PcdValue
+# 4. Dynamic VPD:
+# TokenSpace.PcdCName|VpdOffset[|VpdValue]
+# TokenSpace.PcdCName|VpdOffset[|MaxSize[|VpdValue]]
+# 5. Dynamic HII:
+# TokenSpace.PcdCName|HiiString|VariableGuid|VariableOffset[|HiiValue]
+# PCD value needs to be located in such kind of string, and the PCD value might be an expression in which
+# there might have "|" operator, also in string value.
+#
+# @param Setting: String contain information described above with "TokenSpace.PcdCName|" stripped
+# @param PcdType: PCD type: feature, fixed, dynamic default VPD HII
+# @param DataType: The datum type of PCD: VOID*, UNIT, BOOL
+# @retval:
+# ValueList: A List contain fields described above
+# IsValid: True if conforming EBNF, otherwise False
+# Index: The index where PcdValue is in ValueList
+#
+def AnalyzeDscPcd(Setting, PcdType, DataType=''):
+ FieldList = AnalyzePcdExpression(Setting)
+
+ IsValid = True
+ if PcdType in (MODEL_PCD_FIXED_AT_BUILD, MODEL_PCD_PATCHABLE_IN_MODULE, MODEL_PCD_DYNAMIC_DEFAULT, MODEL_PCD_DYNAMIC_EX_DEFAULT):
+ Value = FieldList[0]
+ Size = ''
+ if len(FieldList) > 1 and FieldList[1]:
+ DataType = FieldList[1]
+ if FieldList[1] != TAB_VOID and StructPattern.match(FieldList[1]) is None:
+ IsValid = False
+ if len(FieldList) > 2:
+ Size = FieldList[2]
+ if IsValid:
+ if DataType == "":
+ IsValid = (len(FieldList) <= 1)
+ else:
+ IsValid = (len(FieldList) <= 3)
+
+ if Size:
+ try:
+ int(Size, 16) if Size.upper().startswith("0X") else int(Size)
+ except:
+ IsValid = False
+ Size = -1
+ return [str(Value), DataType, str(Size)], IsValid, 0
+ elif PcdType == MODEL_PCD_FEATURE_FLAG:
+ Value = FieldList[0]
+ Size = ''
+ IsValid = (len(FieldList) <= 1)
+ return [Value, DataType, str(Size)], IsValid, 0
+ elif PcdType in (MODEL_PCD_DYNAMIC_VPD, MODEL_PCD_DYNAMIC_EX_VPD):
+ VpdOffset = FieldList[0]
+ Value = Size = ''
+ if not DataType == TAB_VOID:
+ if len(FieldList) > 1:
+ Value = FieldList[1]
+ else:
+ if len(FieldList) > 1:
+ Size = FieldList[1]
+ if len(FieldList) > 2:
+ Value = FieldList[2]
+ if DataType == "":
+ IsValid = (len(FieldList) <= 1)
+ else:
+ IsValid = (len(FieldList) <= 3)
+ if Size:
+ try:
+ int(Size, 16) if Size.upper().startswith("0X") else int(Size)
+ except:
+ IsValid = False
+ Size = -1
+ return [VpdOffset, str(Size), Value], IsValid, 2
+ elif PcdType in (MODEL_PCD_DYNAMIC_HII, MODEL_PCD_DYNAMIC_EX_HII):
+ IsValid = (3 <= len(FieldList) <= 5)
+ HiiString = FieldList[0]
+ Guid = Offset = Value = Attribute = ''
+ if len(FieldList) > 1:
+ Guid = FieldList[1]
+ if len(FieldList) > 2:
+ Offset = FieldList[2]
+ if len(FieldList) > 3:
+ Value = FieldList[3]
+ if len(FieldList) > 4:
+ Attribute = FieldList[4]
+ return [HiiString, Guid, Offset, Value, Attribute], IsValid, 3
+ return [], False, 0
+
+## AnalyzePcdData
+#
+# Analyze the pcd Value, Datum type and TokenNumber.
+# Used to avoid split issue while the value string contain "|" character
+#
+# @param[in] Setting: A String contain value/datum type/token number information;
+#
+# @retval ValueList: A List contain value, datum type and toke number.
+#
+def AnalyzePcdData(Setting):
+ ValueList = ['', '', '']
+
+ ValueRe = re.compile(r'^\s*L?\".*\|.*\"')
+ PtrValue = ValueRe.findall(Setting)
+
+ ValueUpdateFlag = False
+
+ if len(PtrValue) >= 1:
+ Setting = re.sub(ValueRe, '', Setting)
+ ValueUpdateFlag = True
+
+ TokenList = Setting.split(TAB_VALUE_SPLIT)
+ ValueList[0:len(TokenList)] = TokenList
+
+ if ValueUpdateFlag:
+ ValueList[0] = PtrValue[0]
+
+ return ValueList
+
+## check format of PCD value against its the datum type
+#
+# For PCD value setting
+#
+def CheckPcdDatum(Type, Value):
+ if Type == TAB_VOID:
+ ValueRe = re.compile(r'\s*L?\".*\"\s*$')
+ if not (((Value.startswith('L"') or Value.startswith('"')) and Value.endswith('"'))
+ or (Value.startswith('{') and Value.endswith('}')) or (Value.startswith("L'") or Value.startswith("'") and Value.endswith("'"))
+ ):
+ return False, "Invalid value [%s] of type [%s]; must be in the form of {...} for array"\
+ ", \"...\" or \'...\' for string, L\"...\" or L\'...\' for unicode string" % (Value, Type)
+ elif ValueRe.match(Value):
+ # Check the chars in UnicodeString or CString is printable
+ if Value.startswith("L"):
+ Value = Value[2:-1]
+ else:
+ Value = Value[1:-1]
+ Printset = set(string.printable)
+ Printset.remove(TAB_PRINTCHAR_VT)
+ Printset.add(TAB_PRINTCHAR_BS)
+ Printset.add(TAB_PRINTCHAR_NUL)
+ if not set(Value).issubset(Printset):
+ PrintList = sorted(Printset)
+ return False, "Invalid PCD string value of type [%s]; must be printable chars %s." % (Type, PrintList)
+ elif Type == 'BOOLEAN':
+ if Value not in ['TRUE', 'True', 'true', '0x1', '0x01', '1', 'FALSE', 'False', 'false', '0x0', '0x00', '0']:
+ return False, "Invalid value [%s] of type [%s]; must be one of TRUE, True, true, 0x1, 0x01, 1"\
+ ", FALSE, False, false, 0x0, 0x00, 0" % (Value, Type)
+ elif Type in [TAB_UINT8, TAB_UINT16, TAB_UINT32, TAB_UINT64]:
+ if Value.startswith('0') and not Value.lower().startswith('0x') and len(Value) > 1 and Value.lstrip('0'):
+ Value = Value.lstrip('0')
+ try:
+ if Value and int(Value, 0) < 0:
+ return False, "PCD can't be set to negative value[%s] for datum type [%s]" % (Value, Type)
+ Value = int(Value, 0)
+ if Value > MAX_VAL_TYPE[Type]:
+ return False, "Too large PCD value[%s] for datum type [%s]" % (Value, Type)
+ except:
+ return False, "Invalid value [%s] of type [%s];"\
+ " must be a hexadecimal, decimal or octal in C language format." % (Value, Type)
+ else:
+ return True, "StructurePcd"
+
+ return True, ""
+
+def CommonPath(PathList):
+ P1 = min(PathList).split(os.path.sep)
+ P2 = max(PathList).split(os.path.sep)
+ for Index in range(min(len(P1), len(P2))):
+ if P1[Index] != P2[Index]:
+ return os.path.sep.join(P1[:Index])
+ return os.path.sep.join(P1)
+
+class PathClass(object):
+ def __init__(self, File='', Root='', AlterRoot='', Type='', IsBinary=False,
+ Arch='COMMON', ToolChainFamily='', Target='', TagName='', ToolCode=''):
+ self.Arch = Arch
+ self.File = str(File)
+ if os.path.isabs(self.File):
+ self.Root = ''
+ self.AlterRoot = ''
+ else:
+ self.Root = str(Root)
+ self.AlterRoot = str(AlterRoot)
+
+ # Remove any '.' and '..' in path
+ if self.Root:
+ self.Root = mws.getWs(self.Root, self.File)
+ self.Path = os.path.normpath(os.path.join(self.Root, self.File))
+ self.Root = os.path.normpath(CommonPath([self.Root, self.Path]))
+ # eliminate the side-effect of 'C:'
+ if self.Root[-1] == ':':
+ self.Root += os.path.sep
+ # file path should not start with path separator
+ if self.Root[-1] == os.path.sep:
+ self.File = self.Path[len(self.Root):]
+ else:
+ self.File = self.Path[len(self.Root) + 1:]
+ else:
+ self.Path = os.path.normpath(self.File)
+
+ self.SubDir, self.Name = os.path.split(self.File)
+ self.BaseName, self.Ext = os.path.splitext(self.Name)
+
+ if self.Root:
+ if self.SubDir:
+ self.Dir = os.path.join(self.Root, self.SubDir)
+ else:
+ self.Dir = self.Root
+ else:
+ self.Dir = self.SubDir
+
+ if IsBinary:
+ self.Type = Type
+ else:
+ self.Type = self.Ext.lower()
+
+ self.IsBinary = IsBinary
+ self.Target = Target
+ self.TagName = TagName
+ self.ToolCode = ToolCode
+ self.ToolChainFamily = ToolChainFamily
+ self.OriginalPath = self
+
+ ## Convert the object of this class to a string
+ #
+ # Convert member Path of the class to a string
+ #
+ # @retval string Formatted String
+ #
+ def __str__(self):
+ return self.Path
+
+ ## Override __eq__ function
+ #
+ # Check whether PathClass are the same
+ #
+ # @retval False The two PathClass are different
+ # @retval True The two PathClass are the same
+ #
+ def __eq__(self, Other):
+ return self.Path == str(Other)
+
+ ## Override __cmp__ function
+ #
+ # Customize the comparison operation of two PathClass
+ #
+ # @retval 0 The two PathClass are different
+ # @retval -1 The first PathClass is less than the second PathClass
+ # @retval 1 The first PathClass is Bigger than the second PathClass
+ def __cmp__(self, Other):
+ OtherKey = str(Other)
+
+ SelfKey = self.Path
+ if SelfKey == OtherKey:
+ return 0
+ elif SelfKey > OtherKey:
+ return 1
+ else:
+ return -1
+
+ ## Override __hash__ function
+ #
+ # Use Path as key in hash table
+ #
+ # @retval string Key for hash table
+ #
+ def __hash__(self):
+ return hash(self.Path)
+
+ @cached_property
+ def Key(self):
+ return self.Path.upper()
+
+ @property
+ def TimeStamp(self):
+ return os.stat(self.Path)[8]
+
+ def Validate(self, Type='', CaseSensitive=True):
+ def RealPath2(File, Dir='', OverrideDir=''):
+ NewFile = None
+ if OverrideDir:
+ NewFile = GlobalData.gAllFiles[os.path.normpath(os.path.join(OverrideDir, File))]
+ if NewFile:
+ if OverrideDir[-1] == os.path.sep:
+ return NewFile[len(OverrideDir):], NewFile[0:len(OverrideDir)]
+ else:
+ return NewFile[len(OverrideDir) + 1:], NewFile[0:len(OverrideDir)]
+ if GlobalData.gAllFiles:
+ NewFile = GlobalData.gAllFiles[os.path.normpath(os.path.join(Dir, File))]
+ if not NewFile:
+ NewFile = os.path.normpath(os.path.join(Dir, File))
+ if not os.path.exists(NewFile):
+ return None, None
+ if NewFile:
+ if Dir:
+ if Dir[-1] == os.path.sep:
+ return NewFile[len(Dir):], NewFile[0:len(Dir)]
+ else:
+ return NewFile[len(Dir) + 1:], NewFile[0:len(Dir)]
+ else:
+ return NewFile, ''
+
+ return None, None
+
+ if GlobalData.gCaseInsensitive:
+ CaseSensitive = False
+ if Type and Type.lower() != self.Type:
+ return FILE_TYPE_MISMATCH, '%s (expect %s but got %s)' % (self.File, Type, self.Type)
+
+ RealFile, RealRoot = RealPath2(self.File, self.Root, self.AlterRoot)
+ if not RealRoot and not RealFile:
+ RealFile = self.File
+ if self.AlterRoot:
+ RealFile = os.path.join(self.AlterRoot, self.File)
+ elif self.Root:
+ RealFile = os.path.join(self.Root, self.File)
+ if len (mws.getPkgPath()) == 0:
+ return FILE_NOT_FOUND, os.path.join(self.AlterRoot, RealFile)
+ else:
+ return FILE_NOT_FOUND, "%s is not found in packages path:\n\t%s" % (self.File, '\n\t'.join(mws.getPkgPath()))
+
+ ErrorCode = 0
+ ErrorInfo = ''
+ if RealRoot != self.Root or RealFile != self.File:
+ if CaseSensitive and (RealFile != self.File or (RealRoot != self.Root and RealRoot != self.AlterRoot)):
+ ErrorCode = FILE_CASE_MISMATCH
+ ErrorInfo = self.File + '\n\t' + RealFile + " [in file system]"
+
+ self.SubDir, self.Name = os.path.split(RealFile)
+ self.BaseName, self.Ext = os.path.splitext(self.Name)
+ if self.SubDir:
+ self.Dir = os.path.join(RealRoot, self.SubDir)
+ else:
+ self.Dir = RealRoot
+ self.File = RealFile
+ self.Root = RealRoot
+ self.Path = os.path.join(RealRoot, RealFile)
+ return ErrorCode, ErrorInfo
+
+## Parse PE image to get the required PE information.
+#
+class PeImageClass():
+ ## Constructor
+ #
+ # @param File FilePath of PeImage
+ #
+ def __init__(self, PeFile):
+ self.FileName = PeFile
+ self.IsValid = False
+ self.Size = 0
+ self.EntryPoint = 0
+ self.SectionAlignment = 0
+ self.SectionHeaderList = []
+ self.ErrorInfo = ''
+ try:
+ PeObject = open(PeFile, 'rb')
+ except:
+ self.ErrorInfo = self.FileName + ' can not be found\n'
+ return
+ # Read DOS header
+ ByteArray = array.array('B')
+ ByteArray.fromfile(PeObject, 0x3E)
+ ByteList = ByteArray.tolist()
+ # DOS signature should be 'MZ'
+ if self._ByteListToStr (ByteList[0x0:0x2]) != 'MZ':
+ self.ErrorInfo = self.FileName + ' has no valid DOS signature MZ'
+ return
+
+ # Read 4 byte PE Signature
+ PeOffset = self._ByteListToInt(ByteList[0x3C:0x3E])
+ PeObject.seek(PeOffset)
+ ByteArray = array.array('B')
+ ByteArray.fromfile(PeObject, 4)
+ # PE signature should be 'PE\0\0'
+ if ByteArray.tolist() != [ord('P'), ord('E'), 0, 0]:
+ self.ErrorInfo = self.FileName + ' has no valid PE signature PE00'
+ return
+
+ # Read PE file header
+ ByteArray = array.array('B')
+ ByteArray.fromfile(PeObject, 0x14)
+ ByteList = ByteArray.tolist()
+ SecNumber = self._ByteListToInt(ByteList[0x2:0x4])
+ if SecNumber == 0:
+ self.ErrorInfo = self.FileName + ' has no section header'
+ return
+
+ # Read PE optional header
+ OptionalHeaderSize = self._ByteListToInt(ByteArray[0x10:0x12])
+ ByteArray = array.array('B')
+ ByteArray.fromfile(PeObject, OptionalHeaderSize)
+ ByteList = ByteArray.tolist()
+ self.EntryPoint = self._ByteListToInt(ByteList[0x10:0x14])
+ self.SectionAlignment = self._ByteListToInt(ByteList[0x20:0x24])
+ self.Size = self._ByteListToInt(ByteList[0x38:0x3C])
+
+ # Read each Section Header
+ for Index in range(SecNumber):
+ ByteArray = array.array('B')
+ ByteArray.fromfile(PeObject, 0x28)
+ ByteList = ByteArray.tolist()
+ SecName = self._ByteListToStr(ByteList[0:8])
+ SecVirtualSize = self._ByteListToInt(ByteList[8:12])
+ SecRawAddress = self._ByteListToInt(ByteList[20:24])
+ SecVirtualAddress = self._ByteListToInt(ByteList[12:16])
+ self.SectionHeaderList.append((SecName, SecVirtualAddress, SecRawAddress, SecVirtualSize))
+ self.IsValid = True
+ PeObject.close()
+
+ def _ByteListToStr(self, ByteList):
+ String = ''
+ for index in range(len(ByteList)):
+ if ByteList[index] == 0:
+ break
+ String += chr(ByteList[index])
+ return String
+
+ def _ByteListToInt(self, ByteList):
+ Value = 0
+ for index in range(len(ByteList) - 1, -1, -1):
+ Value = (Value << 8) | int(ByteList[index])
+ return Value
+
+class DefaultStore():
+ def __init__(self, DefaultStores ):
+
+ self.DefaultStores = DefaultStores
+ def DefaultStoreID(self, DefaultStoreName):
+ for key, value in self.DefaultStores.items():
+ if value == DefaultStoreName:
+ return key
+ return None
+ def GetDefaultDefault(self):
+ if not self.DefaultStores or "0" in self.DefaultStores:
+ return "0", TAB_DEFAULT_STORES_DEFAULT
+ else:
+ minvalue = min(int(value_str) for value_str in self.DefaultStores)
+ return (str(minvalue), self.DefaultStores[str(minvalue)])
+ def GetMin(self, DefaultSIdList):
+ if not DefaultSIdList:
+ return TAB_DEFAULT_STORES_DEFAULT
+ storeidset = {storeid for storeid, storename in self.DefaultStores.values() if storename in DefaultSIdList}
+ if not storeidset:
+ return ""
+ minid = min(storeidset )
+ for sid, name in self.DefaultStores.values():
+ if sid == minid:
+ return name
+
+class SkuClass():
+ DEFAULT = 0
+ SINGLE = 1
+ MULTIPLE =2
+
+ def __init__(self,SkuIdentifier='', SkuIds=None):
+ if SkuIds is None:
+ SkuIds = {}
+
+ for SkuName in SkuIds:
+ SkuId = SkuIds[SkuName][0]
+ skuid_num = int(SkuId, 16) if SkuId.upper().startswith("0X") else int(SkuId)
+ if skuid_num > 0xFFFFFFFFFFFFFFFF:
+ EdkLogger.error("build", PARAMETER_INVALID,
+ ExtraData = "SKU-ID [%s] value %s exceeds the max value of UINT64"
+ % (SkuName, SkuId))
+
+ self.AvailableSkuIds = OrderedDict()
+ self.SkuIdSet = []
+ self.SkuIdNumberSet = []
+ self.SkuData = SkuIds
+ self._SkuInherit = {}
+ self._SkuIdentifier = SkuIdentifier
+ if SkuIdentifier == '' or SkuIdentifier is None:
+ self.SkuIdSet = ['DEFAULT']
+ self.SkuIdNumberSet = ['0U']
+ elif SkuIdentifier == 'ALL':
+ self.SkuIdSet = list(SkuIds.keys())
+ self.SkuIdNumberSet = [num[0].strip() + 'U' for num in SkuIds.values()]
+ else:
+ r = SkuIdentifier.split('|')
+ self.SkuIdSet=[(r[k].strip()).upper() for k in range(len(r))]
+ k = None
+ try:
+ self.SkuIdNumberSet = [SkuIds[k][0].strip() + 'U' for k in self.SkuIdSet]
+ except Exception:
+ EdkLogger.error("build", PARAMETER_INVALID,
+ ExtraData = "SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
+ % (k, " | ".join(SkuIds.keys())))
+ for each in self.SkuIdSet:
+ if each in SkuIds:
+ self.AvailableSkuIds[each] = SkuIds[each][0]
+ else:
+ EdkLogger.error("build", PARAMETER_INVALID,
+ ExtraData="SKU-ID [%s] is not supported by the platform. [Valid SKU-ID: %s]"
+ % (each, " | ".join(SkuIds.keys())))
+ if self.SkuUsageType != SkuClass.SINGLE:
+ self.AvailableSkuIds.update({'DEFAULT':0, 'COMMON':0})
+ if self.SkuIdSet:
+ GlobalData.gSkuids = (self.SkuIdSet)
+ if 'COMMON' in GlobalData.gSkuids:
+ GlobalData.gSkuids.remove('COMMON')
+ if self.SkuUsageType == self.SINGLE:
+ if len(GlobalData.gSkuids) != 1:
+ if 'DEFAULT' in GlobalData.gSkuids:
+ GlobalData.gSkuids.remove('DEFAULT')
+ if GlobalData.gSkuids:
+ GlobalData.gSkuids.sort()
+
+ def GetNextSkuId(self, skuname):
+ if not self._SkuInherit:
+ self._SkuInherit = {}
+ for item in self.SkuData.values():
+ self._SkuInherit[item[1]]=item[2] if item[2] else "DEFAULT"
+ return self._SkuInherit.get(skuname, "DEFAULT")
+
+ def GetSkuChain(self, sku):
+ if sku == "DEFAULT":
+ return ["DEFAULT"]
+ skulist = [sku]
+ nextsku = sku
+ while True:
+ nextsku = self.GetNextSkuId(nextsku)
+ skulist.append(nextsku)
+ if nextsku == "DEFAULT":
+ break
+ skulist.reverse()
+ return skulist
+ def SkuOverrideOrder(self):
+ skuorderset = []
+ for skuname in self.SkuIdSet:
+ skuorderset.append(self.GetSkuChain(skuname))
+
+ skuorder = []
+ for index in range(max(len(item) for item in skuorderset)):
+ for subset in skuorderset:
+ if index > len(subset)-1:
+ continue
+ if subset[index] in skuorder:
+ continue
+ skuorder.append(subset[index])
+
+ return skuorder
+
+ @property
+ def SkuUsageType(self):
+ if self._SkuIdentifier.upper() == "ALL":
+ return SkuClass.MULTIPLE
+
+ if len(self.SkuIdSet) == 1:
+ if self.SkuIdSet[0] == 'DEFAULT':
+ return SkuClass.DEFAULT
+ return SkuClass.SINGLE
+ if len(self.SkuIdSet) == 2 and 'DEFAULT' in self.SkuIdSet:
+ return SkuClass.SINGLE
+ return SkuClass.MULTIPLE
+
+ def DumpSkuIdArrary(self):
+ if self.SkuUsageType == SkuClass.SINGLE:
+ return "{0x0}"
+ ArrayStrList = []
+ for skuname in self.AvailableSkuIds:
+ if skuname == "COMMON":
+ continue
+ while skuname != "DEFAULT":
+ ArrayStrList.append(hex(int(self.AvailableSkuIds[skuname])))
+ skuname = self.GetNextSkuId(skuname)
+ ArrayStrList.append("0x0")
+ return "{{{myList}}}".format(myList=",".join(ArrayStrList))
+
+ @property
+ def AvailableSkuIdSet(self):
+ return self.AvailableSkuIds
+
+ @property
+ def SystemSkuId(self):
+ if self.SkuUsageType == SkuClass.SINGLE:
+ if len(self.SkuIdSet) == 1:
+ return self.SkuIdSet[0]
+ else:
+ return self.SkuIdSet[0] if self.SkuIdSet[0] != 'DEFAULT' else self.SkuIdSet[1]
+ else:
+ return 'DEFAULT'
+
+## Get the integer value from string like "14U" or integer like 2
+#
+# @param Input The object that may be either a integer value or a string
+#
+# @retval Value The integer value that the input represents
+#
+def GetIntegerValue(Input):
+ if not isinstance(Input, str):
+ return Input
+ String = Input
+ if String.endswith("U"):
+ String = String[:-1]
+ if String.endswith("ULL"):
+ String = String[:-3]
+ if String.endswith("LL"):
+ String = String[:-2]
+
+ if String.startswith("0x") or String.startswith("0X"):
+ return int(String, 16)
+ elif String == '':
+ return 0
+ else:
+ return int(String)
+
+#
+# Pack a GUID (registry format) list into a buffer and return it
+#
+def PackGUID(Guid):
+ return pack(PACK_PATTERN_GUID,
+ int(Guid[0], 16),
+ int(Guid[1], 16),
+ int(Guid[2], 16),
+ int(Guid[3][-4:-2], 16),
+ int(Guid[3][-2:], 16),
+ int(Guid[4][-12:-10], 16),
+ int(Guid[4][-10:-8], 16),
+ int(Guid[4][-8:-6], 16),
+ int(Guid[4][-6:-4], 16),
+ int(Guid[4][-4:-2], 16),
+ int(Guid[4][-2:], 16)
+ )
+
+#
+# Pack a GUID (byte) list into a buffer and return it
+#
+def PackByteFormatGUID(Guid):
+ return pack(PACK_PATTERN_GUID,
+ Guid[0],
+ Guid[1],
+ Guid[2],
+ Guid[3],
+ Guid[4],
+ Guid[5],
+ Guid[6],
+ Guid[7],
+ Guid[8],
+ Guid[9],
+ Guid[10],
+ )
+
+## DeepCopy dict/OrderedDict recusively
+#
+# @param ori_dict a nested dict or ordereddict
+#
+# @retval new dict or orderdict
+#
+def CopyDict(ori_dict):
+ dict_type = ori_dict.__class__
+ if dict_type not in (dict,OrderedDict):
+ return ori_dict
+ new_dict = dict_type()
+ for key in ori_dict:
+ if isinstance(ori_dict[key],(dict,OrderedDict)):
+ new_dict[key] = CopyDict(ori_dict[key])
+ else:
+ new_dict[key] = ori_dict[key]
+ return new_dict
+
+#
+# Remove the c/c++ comments: // and /* */
+#
+def RemoveCComments(ctext):
+ return re.sub('//.*?\n|/\\*.*?\\*/', '\n', ctext, flags=re.S)