## @file\r
# This file is used to define common string related functions used in parsing process\r
#\r
-# Copyright (c) 2007 ~ 2008, Intel Corporation\r
-# All rights reserved. This program and the accompanying materials\r
+# Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.<BR>\r
+# This program and the accompanying materials\r
# are licensed and made available under the terms and conditions of the BSD License\r
# which accompanies this distribution. The full text of the license may be found at\r
# http://opensource.org/licenses/bsd-license.php\r
#\r
import re\r
import DataType\r
-import os.path\r
+import Common.LongFilePathOs as os\r
import string\r
import EdkLogger as EdkLogger\r
\r
-from GlobalData import *\r
+import GlobalData\r
from BuildToolError import *\r
+from CommonDataClass.Exceptions import *\r
+from Common.LongFilePathSupport import OpenLongFilePath as open\r
+from Common.MultipleWorkspace import MultipleWorkspace as mws\r
+\r
+gHexVerPatt = re.compile('0x[a-f0-9]{4}[a-f0-9]{4}$', re.IGNORECASE)\r
+gHumanReadableVerPatt = re.compile(r'([1-9][0-9]*|0)\.[0-9]{1,2}$')\r
\r
## GetSplitValueList\r
#\r
#\r
# @retval list() A list for splitted string\r
#\r
-def GetSplitValueList(String, SplitTag = DataType.TAB_VALUE_SPLIT, MaxSplit = -1):\r
- return map(lambda l: l.strip(), String.split(SplitTag, MaxSplit))\r
+def GetSplitValueList(String, SplitTag=DataType.TAB_VALUE_SPLIT, MaxSplit= -1):\r
+ ValueList = []\r
+ Last = 0\r
+ Escaped = False\r
+ InSingleQuoteString = False\r
+ InDoubleQuoteString = False\r
+ InParenthesis = 0\r
+ for Index in range(0, len(String)):\r
+ Char = String[Index]\r
+\r
+ if not Escaped:\r
+ # Found a splitter not in a string, split it\r
+ if (not InSingleQuoteString or not InDoubleQuoteString) and InParenthesis == 0 and Char == SplitTag:\r
+ ValueList.append(String[Last:Index].strip())\r
+ Last = Index + 1\r
+ if MaxSplit > 0 and len(ValueList) >= MaxSplit:\r
+ break\r
+\r
+ if Char == '\\' and (InSingleQuoteString or InDoubleQuoteString):\r
+ Escaped = True\r
+ elif Char == '"' and not InSingleQuoteString:\r
+ if not InDoubleQuoteString:\r
+ InDoubleQuoteString = True\r
+ else:\r
+ InDoubleQuoteString = False\r
+ elif Char == "'" and not InDoubleQuoteString:\r
+ if not InSingleQuoteString:\r
+ InSingleQuoteString = True\r
+ else:\r
+ InSingleQuoteString = False\r
+ elif Char == '(':\r
+ InParenthesis = InParenthesis + 1\r
+ elif Char == ')':\r
+ InParenthesis = InParenthesis - 1\r
+ else:\r
+ Escaped = False\r
+\r
+ if Last < len(String):\r
+ ValueList.append(String[Last:].strip())\r
+ elif Last == len(String):\r
+ ValueList.append('')\r
+\r
+ return ValueList\r
+\r
+## GetSplitList\r
+#\r
+# Get a value list from a string with multiple values splited with SplitString\r
+# The default SplitTag is DataType.TAB_VALUE_SPLIT\r
+# 'AAA|BBB|CCC' -> ['AAA', 'BBB', 'CCC']\r
+#\r
+# @param String: The input string to be splitted\r
+# @param SplitStr: The split key, default is DataType.TAB_VALUE_SPLIT\r
+# @param MaxSplit: The max number of split values, default is -1\r
+#\r
+# @retval list() A list for splitted string\r
+#\r
+def GetSplitList(String, SplitStr=DataType.TAB_VALUE_SPLIT, MaxSplit= -1):\r
+ return map(lambda l: l.strip(), String.split(SplitStr, MaxSplit))\r
\r
## MergeArches\r
#\r
#\r
# @retval NewList A new string list whose macros are replaced\r
#\r
-def ReplaceMacros(StringList, MacroDefinitions={}, SelfReplacement = False):\r
+def ReplaceMacros(StringList, MacroDefinitions={}, SelfReplacement=False):\r
NewList = []\r
for String in StringList:\r
if type(String) == type(''):\r
#\r
# @retval string The string whose macros are replaced\r
#\r
-def ReplaceMacro(String, MacroDefinitions={}, SelfReplacement = False):\r
+def ReplaceMacro(String, MacroDefinitions={}, SelfReplacement=False, RaiseError=False):\r
LastString = String\r
- while MacroDefinitions:\r
- MacroUsed = gMacroPattern.findall(String)\r
+ while String and MacroDefinitions:\r
+ MacroUsed = GlobalData.gMacroRefPattern.findall(String)\r
# no macro found in String, stop replacing\r
if len(MacroUsed) == 0:\r
break\r
\r
for Macro in MacroUsed:\r
if Macro not in MacroDefinitions:\r
+ if RaiseError:\r
+ raise SymbolNotFound("%s not defined" % Macro)\r
if SelfReplacement:\r
String = String.replace("$(%s)" % Macro, '')\r
continue\r
- String = String.replace("$(%s)" % Macro, MacroDefinitions[Macro])\r
+ if "$(%s)" % Macro not in MacroDefinitions[Macro]:\r
+ String = String.replace("$(%s)" % Macro, MacroDefinitions[Macro])\r
# in case there's macro not defined\r
if String == LastString:\r
break\r
#\r
# @retval Path Formatted path\r
#\r
-def NormPath(Path, Defines = {}):\r
+def NormPath(Path, Defines={}):\r
IsRelativePath = False\r
if Path:\r
if Path[0] == '.':\r
# To local path format\r
#\r
Path = os.path.normpath(Path)\r
+ if Path.startswith(GlobalData.gWorkspace) and not Path.startswith(GlobalData.gBuildDirectory) and not os.path.exists(Path):\r
+ Path = Path[len (GlobalData.gWorkspace):]\r
+ if Path[0] == os.path.sep:\r
+ Path = Path[1:]\r
+ Path = mws.join(GlobalData.gWorkspace, Path)\r
\r
if IsRelativePath and Path[0] != '.':\r
Path = os.path.join('.', Path)\r
#\r
# @retval Path Formatted path\r
#\r
-def CleanString(Line, CommentCharacter = DataType.TAB_COMMENT_SPLIT, AllowCppStyleComment=False):\r
+def CleanString(Line, CommentCharacter=DataType.TAB_COMMENT_SPLIT, AllowCppStyleComment=False, BuildOption=False):\r
#\r
# remove whitespace\r
#\r
Line = Line.strip();\r
#\r
- # Replace R8's comment character\r
+ # Replace Edk's comment character\r
#\r
if AllowCppStyleComment:\r
- Line = Line.replace(DataType.TAB_COMMENT_R8_SPLIT, CommentCharacter)\r
+ Line = Line.replace(DataType.TAB_COMMENT_EDK_SPLIT, CommentCharacter)\r
#\r
- # remove comments\r
+ # remove comments, but we should escape comment character in string\r
#\r
- Line = Line.split(CommentCharacter, 1)[0];\r
+ InDoubleQuoteString = False\r
+ InSingleQuoteString = False\r
+ CommentInString = False\r
+ for Index in range(0, len(Line)):\r
+ if Line[Index] == '"' and not InSingleQuoteString:\r
+ InDoubleQuoteString = not InDoubleQuoteString\r
+ elif Line[Index] == "'" and not InDoubleQuoteString:\r
+ InSingleQuoteString = not InSingleQuoteString\r
+ elif Line[Index] == CommentCharacter and (InSingleQuoteString or InDoubleQuoteString):\r
+ CommentInString = True\r
+ elif Line[Index] == CommentCharacter and not (InSingleQuoteString or InDoubleQuoteString):\r
+ Line = Line[0: Index]\r
+ break\r
+\r
+ if CommentInString and BuildOption:\r
+ Line = Line.replace('"', '')\r
+ ChIndex = Line.find('#')\r
+ while ChIndex >= 0:\r
+ if GlobalData.gIsWindows:\r
+ if ChIndex == 0 or Line[ChIndex - 1] != '^':\r
+ Line = Line[0:ChIndex] + '^' + Line[ChIndex:]\r
+ ChIndex = Line.find('#', ChIndex + 2)\r
+ else:\r
+ ChIndex = Line.find('#', ChIndex + 1)\r
+ else:\r
+ if ChIndex == 0 or Line[ChIndex - 1] != '\\':\r
+ Line = Line[0:ChIndex] + '\\' + Line[ChIndex:]\r
+ ChIndex = Line.find('#', ChIndex + 2)\r
+ else:\r
+ ChIndex = Line.find('#', ChIndex + 1)\r
#\r
# remove whitespace again\r
#\r
\r
return Line\r
\r
+## CleanString2\r
+#\r
+# Split statement with comments in a string\r
+# Remove spaces\r
+#\r
+# @param Line: The string to be cleaned\r
+# @param CommentCharacter: Comment char, used to ignore comment content, default is DataType.TAB_COMMENT_SPLIT\r
+#\r
+# @retval Path Formatted path\r
+#\r
+def CleanString2(Line, CommentCharacter=DataType.TAB_COMMENT_SPLIT, AllowCppStyleComment=False):\r
+ #\r
+ # remove whitespace\r
+ #\r
+ Line = Line.strip();\r
+ #\r
+ # Replace Edk's comment character\r
+ #\r
+ if AllowCppStyleComment:\r
+ Line = Line.replace(DataType.TAB_COMMENT_EDK_SPLIT, CommentCharacter)\r
+ #\r
+ # separate comments and statements, but we should escape comment character in string\r
+ #\r
+ InDoubleQuoteString = False\r
+ InSingleQuoteString = False\r
+ CommentInString = False\r
+ Comment = ''\r
+ for Index in range(0, len(Line)):\r
+ if Line[Index] == '"' and not InSingleQuoteString:\r
+ InDoubleQuoteString = not InDoubleQuoteString\r
+ elif Line[Index] == "'" and not InDoubleQuoteString:\r
+ InSingleQuoteString = not InSingleQuoteString\r
+ elif Line[Index] == CommentCharacter and (InDoubleQuoteString or InSingleQuoteString):\r
+ CommentInString = True\r
+ elif Line[Index] == CommentCharacter and not (InDoubleQuoteString or InSingleQuoteString):\r
+ Comment = Line[Index:].strip()\r
+ Line = Line[0:Index].strip()\r
+ break\r
+\r
+ return Line, Comment\r
+\r
## GetMultipleValuesOfKeyFromLines\r
#\r
# Parse multiple strings to clean comment and spaces\r
String = CleanString(String)\r
return String[String.find(Key + ' ') + len(Key + ' ') : ]\r
\r
+## GetHexVerValue\r
+#\r
+# Get a Hex Version Value\r
+#\r
+# @param VerString: The version string to be parsed\r
+#\r
+#\r
+# @retval: If VerString is incorrectly formatted, return "None" which will break the build.\r
+# If VerString is correctly formatted, return a Hex value of the Version Number (0xmmmmnnnn)\r
+# where mmmm is the major number and nnnn is the adjusted minor number.\r
+#\r
+def GetHexVerValue(VerString):\r
+ VerString = CleanString(VerString)\r
+\r
+ if gHumanReadableVerPatt.match(VerString):\r
+ ValueList = VerString.split('.')\r
+ Major = ValueList[0]\r
+ Minor = ValueList[1]\r
+ if len(Minor) == 1:\r
+ Minor += '0'\r
+ DeciValue = (int(Major) << 16) + int(Minor);\r
+ return "0x%08x" % DeciValue\r
+ elif gHexVerPatt.match(VerString):\r
+ return VerString\r
+ else:\r
+ return None\r
+\r
+\r
## GetSingleValueOfKeyFromLines\r
#\r
# Parse multiple strings as below to get value of each definition line\r
#\r
if Line.find('$') > -1:\r
if Line.find('$(') < 0 or Line.find(')') < 0:\r
- EdkLogger.error("Parser", FORMAT_INVALID, Line=LineNo, File=FileName, RaiseError = EdkLogger.IsRaiseError)\r
+ EdkLogger.error("Parser", FORMAT_INVALID, Line=LineNo, File=FileName, RaiseError=EdkLogger.IsRaiseError)\r
\r
#\r
# Check []\r
# Only get one '[' or one ']'\r
#\r
if not (Line.find('[') > -1 and Line.find(']') > -1):\r
- EdkLogger.error("Parser", FORMAT_INVALID, Line=LineNo, File=FileName, RaiseError = EdkLogger.IsRaiseError)\r
+ EdkLogger.error("Parser", FORMAT_INVALID, Line=LineNo, File=FileName, RaiseError=EdkLogger.IsRaiseError)\r
\r
#\r
# Regenerate FileContent\r
NewFileContent = NewFileContent + Line + '\r\n'\r
\r
if IsFailed:\r
- EdkLogger.error("Parser", FORMAT_INVALID, Line=LineNo, File=FileName, RaiseError = EdkLogger.IsRaiseError)\r
+ EdkLogger.error("Parser", FORMAT_INVALID, Line=LineNo, File=FileName, RaiseError=EdkLogger.IsRaiseError)\r
\r
return NewFileContent\r
\r
#\r
# @retval True The file type is correct\r
#\r
-def CheckFileType(CheckFilename, ExtName, ContainerFilename, SectionName, Line, LineNo = -1):\r
+def CheckFileType(CheckFilename, ExtName, ContainerFilename, SectionName, Line, LineNo= -1):\r
if CheckFilename != '' and CheckFilename != None:\r
(Root, Ext) = os.path.splitext(CheckFilename)\r
if Ext.upper() != ExtName.upper():\r
LineNo = GetLineNo(ContainerFile, Line)\r
ErrorMsg = "Invalid %s. '%s' is found, but '%s' file is needed" % (SectionName, CheckFilename, ExtName)\r
EdkLogger.error("Parser", PARSER_ERROR, ErrorMsg, Line=LineNo,\r
- File=ContainerFilename, RaiseError = EdkLogger.IsRaiseError)\r
+ File=ContainerFilename, RaiseError=EdkLogger.IsRaiseError)\r
\r
return True\r
\r
#\r
# @retval The file full path if the file exists\r
#\r
-def CheckFileExist(WorkspaceDir, CheckFilename, ContainerFilename, SectionName, Line, LineNo = -1):\r
+def CheckFileExist(WorkspaceDir, CheckFilename, ContainerFilename, SectionName, Line, LineNo= -1):\r
CheckFile = ''\r
if CheckFilename != '' and CheckFilename != None:\r
CheckFile = WorkspaceFile(WorkspaceDir, CheckFilename)\r
LineNo = GetLineNo(ContainerFile, Line)\r
ErrorMsg = "Can't find file '%s' defined in section '%s'" % (CheckFile, SectionName)\r
EdkLogger.error("Parser", PARSER_ERROR, ErrorMsg,\r
- File=ContainerFilename, Line = LineNo, RaiseError = EdkLogger.IsRaiseError)\r
+ File=ContainerFilename, Line=LineNo, RaiseError=EdkLogger.IsRaiseError)\r
\r
return CheckFile\r
\r
# @retval int Index of the line\r
# @retval -1 The line is not found\r
#\r
-def GetLineNo(FileContent, Line, IsIgnoreComment = True):\r
+def GetLineNo(FileContent, Line, IsIgnoreComment=True):\r
LineList = FileContent.splitlines()\r
for Index in range(len(LineList)):\r
if LineList[Index].find(Line) > -1:\r
# @param File: File which has the string\r
# @param Format: Correct format\r
#\r
-def RaiseParserError(Line, Section, File, Format = '', LineNo = -1):\r
+def RaiseParserError(Line, Section, File, Format='', LineNo= -1):\r
if LineNo == -1:\r
LineNo = GetLineNo(open(os.path.normpath(File), 'r').read(), Line)\r
ErrorMsg = "Invalid statement '%s' is found in section '%s'" % (Line, Section)\r
if Format != '':\r
Format = "Correct format is " + Format\r
- EdkLogger.error("Parser", PARSER_ERROR, ErrorMsg, File=File, Line=LineNo, ExtraData=Format, RaiseError = EdkLogger.IsRaiseError)\r
+ EdkLogger.error("Parser", PARSER_ERROR, ErrorMsg, File=File, Line=LineNo, ExtraData=Format, RaiseError=EdkLogger.IsRaiseError)\r
\r
## WorkspaceFile\r
#\r
# @retval string A full path\r
#\r
def WorkspaceFile(WorkspaceDir, Filename):\r
- return os.path.join(NormPath(WorkspaceDir), NormPath(Filename))\r
+ return mws.join(NormPath(WorkspaceDir), NormPath(Filename))\r
\r
## Split string\r
#\r
#\r
# Remove comment block\r
#\r
- if Line.find(DataType.TAB_COMMENT_R8_START) > -1:\r
- ReservedLine = GetSplitValueList(Line, DataType.TAB_COMMENT_R8_START, 1)[0]\r
+ if Line.find(DataType.TAB_COMMENT_EDK_START) > -1:\r
+ ReservedLine = GetSplitList(Line, DataType.TAB_COMMENT_EDK_START, 1)[0]\r
IsFindBlockComment = True\r
- if Line.find(DataType.TAB_COMMENT_R8_END) > -1:\r
- Line = ReservedLine + GetSplitValueList(Line, DataType.TAB_COMMENT_R8_END, 1)[1]\r
+ if Line.find(DataType.TAB_COMMENT_EDK_END) > -1:\r
+ Line = ReservedLine + GetSplitList(Line, DataType.TAB_COMMENT_EDK_END, 1)[1]\r
ReservedLine = ''\r
IsFindBlockComment = False\r
if IsFindBlockComment:\r
#\r
# Get String of a List\r
#\r
-def GetStringOfList(List, Split = ' '):\r
+def GetStringOfList(List, Split=' '):\r
if type(List) != type([]):\r
return List\r
Str = ''\r
\r
def StringToArray(String):\r
if isinstance(String, unicode):\r
- if len(unicode) ==0:\r
- return "{0x00, 0x00}"\r
- return "{%s, 0x00, 0x00}" % ", ".join(["0x%02x, 0x00" % ord(C) for C in String])\r
+ if len(unicode) == 0:\r
+ return "{0x00,0x00}"\r
+ return "{%s,0x00,0x00}" % ",".join(["0x%02x,0x00" % ord(C) for C in String])\r
elif String.startswith('L"'):\r
if String == "L\"\"":\r
- return "{0x00, 0x00}"\r
+ return "{0x00,0x00}"\r
else:\r
- return "{%s, 0x00, 0x00}" % ", ".join(["0x%02x, 0x00" % ord(C) for C in String[2:-1]])\r
+ return "{%s,0x00,0x00}" % ",".join(["0x%02x,0x00" % ord(C) for C in String[2:-1]])\r
elif String.startswith('"'):\r
if String == "\"\"":\r
- return "{0x00}";\r
+ return "{0x00,0x00}"\r
else:\r
- return "{%s, 0x00}" % ", ".join(["0x%02x" % ord(C) for C in String[1:-1]])\r
+ StringLen = len(String[1:-1])\r
+ if StringLen % 2:\r
+ return "{%s,0x00}" % ",".join(["0x%02x" % ord(C) for C in String[1:-1]])\r
+ else:\r
+ return "{%s,0x00,0x00}" % ",".join(["0x%02x" % ord(C) for C in String[1:-1]])\r
+ elif String.startswith('{'):\r
+ StringLen = len(String.split(","))\r
+ if StringLen % 2:\r
+ return "{%s,0x00}" % ",".join([ C.strip() for C in String[1:-1].split(',')])\r
+ else:\r
+ return "{%s}" % ",".join([ C.strip() for C in String[1:-1].split(',')])\r
+ \r
else:\r
- return '{%s, 0}' % ', '.join(String.split())\r
+ if len(String.split()) % 2:\r
+ return '{%s,0}' % ','.join(String.split())\r
+ else:\r
+ return '{%s,0,0}' % ','.join(String.split())\r
\r
def StringArrayLength(String):\r
if isinstance(String, unicode):\r
return (len(String) - 2 + 1)\r
else:\r
return len(String.split()) + 1\r
- \r
+\r
def RemoveDupOption(OptionString, Which="/I", Against=None):\r
OptionList = OptionString.split()\r
ValueList = []\r