From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- .../BaseTools/Source/Python/Common/Misc.py | 1929 ++++++++++++++++++++ 1 file changed, 1929 insertions(+) create mode 100755 src/VBox/Devices/EFI/Firmware/BaseTools/Source/Python/Common/Misc.py (limited to 'src/VBox/Devices/EFI/Firmware/BaseTools/Source/Python/Common/Misc.py') diff --git a/src/VBox/Devices/EFI/Firmware/BaseTools/Source/Python/Common/Misc.py b/src/VBox/Devices/EFI/Firmware/BaseTools/Source/Python/Common/Misc.py new file mode 100755 index 00000000..980fa839 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/BaseTools/Source/Python/Common/Misc.py @@ -0,0 +1,1929 @@ +## @file +# Common routines used by all tools +# +# Copyright (c) 2007 - 2019, Intel Corporation. All rights reserved.
+# 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("\[\s*[0-9a-fA-FxX]*\s*\]") +## Regular expression used to find out place holders in string template +gPlaceholderPattern = re.compile("\$\{([^$()\s]+)\}", re.MULTILINE | re.UNICODE) + +## regular expressions for map file processing +startPatternGeneral = re.compile("^Start[' ']+Length[' ']+Name[' ']+Class") +addressPatternGeneral = re.compile("^Address[' ']+Publics by Value[' ']+Rva\+Base") +valuePatternGcc = re.compile('^([\w_\.]+) +([\da-fA-Fx]+) +([\da-fA-Fx]+)$') +pcdPatternGcc = re.compile('^([\da-fA-Fx]+) +([\da-fA-Fx]+)') +secReGeneral = re.compile('^([\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('^\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('^\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('^([\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('^([\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 { +# +# 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) -- cgit v1.2.3