92684e5f22234a79d1563295e14359d654057621
[people/mcb30/basetools.git] / Source / Python / Common / String.py
1 # Copyright (c) 2007, Intel Corporation\r
2 # All rights reserved. This program and the accompanying materials\r
3 # are licensed and made available under the terms and conditions of the BSD License\r
4 # which accompanies this distribution.    The full text of the license may be found at\r
5 # http://opensource.org/licenses/bsd-license.php\r
6 #\r
7 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
8 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
9 \r
10 #\r
11 #This file is used to define some common useful string functions\r
12 #\r
13 \r
14 import DataType\r
15 import os.path\r
16 import string\r
17 from BuildToolError import *\r
18 \r
19 #\r
20 # Get a value list from a string with multiple values splited with SplitTag\r
21 # The default SplitTag is DataType.TAB_VALUE_SPLIT\r
22 # 'AAA|BBB|CCC' -> ['AAA', 'BBB', 'CCC']\r
23 #\r
24 def GetSplitValueList(String, SplitTag = DataType.TAB_VALUE_SPLIT, MaxSplit = -1):\r
25     return map(lambda l: l.strip(), String.split(SplitTag, MaxSplit))\r
26 \r
27 #\r
28 # Find a key's all arches in dict, add the new arch to the list\r
29 # If not exist any arch, set the arch directly\r
30 #\r
31 def MergeArches(Dict, Key, Arch):\r
32     if Key in Dict.keys():\r
33         Dict[Key].append(Arch)\r
34     else:\r
35         Dict[Key] = Arch.split()\r
36 \r
37 #\r
38 # Parse a string with format "DEFINE <VarName> = <PATH>"\r
39 # Generate a map Defines[VarName] = PATH\r
40 # Return False if invalid format\r
41 #\r
42 def GenDefines(String, Arch, Defines):\r
43     if String.find(DataType.TAB_DEFINE + ' ') > -1:\r
44         List = String.replace(DataType.TAB_DEFINE + ' ', '').split(DataType.TAB_EQUAL_SPLIT)\r
45         if len(List) == 2:\r
46             Defines[(CleanString(List[0]), Arch)] = CleanString(List[1])\r
47             return 0\r
48         else:\r
49             return -1\r
50     \r
51     return 1\r
52 \r
53 #\r
54 # Parse a string with format "!include <Filename>"\r
55 # Return the file path\r
56 # Return False if invalid format or NOT FOUND\r
57 #\r
58 def GenInclude(String, IncludeFiles, Arch):\r
59     if String.upper().find(DataType.TAB_INCLUDE.upper() + ' ') > -1:\r
60         IncludeFile = CleanString(String[String.upper().find(DataType.TAB_INCLUDE.upper() + ' ') + len(DataType.TAB_INCLUDE + ' ') : ])\r
61         MergeArches(IncludeFiles, IncludeFile, Arch)\r
62         return True\r
63     else:\r
64         return False\r
65     \r
66 #\r
67 # Parse a string with format "InfFilename [EXEC = ExecFilename]"\r
68 # Return (InfFilename, ExecFilename)\r
69 #\r
70 def GetExec(String):\r
71     InfFilename = ''\r
72     ExecFilename = '' \r
73     if String.find('EXEC') > -1:\r
74         InfFilename = String[ : String.find('EXEC')].strip()\r
75         ExecFilename = String[String.find('EXEC') + len('EXEC') : ].strip()\r
76     else:\r
77         InfFilename = String.strip()\r
78     \r
79     return (InfFilename, ExecFilename)\r
80 \r
81 #\r
82 # Parse a string with format "[<Family>:]<ToolFlag>=Flag"\r
83 # Return (Family, ToolFlag, Flag)\r
84 #\r
85 def GetBuildOption(String, File):\r
86     if String.find(DataType.TAB_EQUAL_SPLIT) < 0:\r
87         RaiseParserError(String, 'BuildOptions', File, '[<Family>:]<ToolFlag>=Flag')\r
88     (Family, ToolChain, Flag) = ('', '', '')\r
89     List = GetSplitValueList(String, DataType.TAB_EQUAL_SPLIT, MaxSplit = 1)\r
90     if List[0].find(':') > -1:\r
91         Family = CleanString(List[0][ : List[0].find(':')])\r
92         ToolChain = CleanString(List[0][List[0].find(':') + 1 : ])\r
93     else:\r
94         ToolChain = CleanString(List[0])                    \r
95     Flag = CleanString(List[1])\r
96     \r
97     return (Family, ToolChain, Flag)\r
98 \r
99 #\r
100 # Parse block of the components defined in dsc file\r
101 # Return KeyValues [ ['component name', [lib1, lib2, lib3], [bo1, bo2, bo3], [pcd1, pcd2, pcd3]], ...]\r
102 #\r
103 def GetComponents(Lines, Key, KeyValues, CommentCharacter):\r
104     #KeyValues [ ['component name', [lib1, lib2, lib3], [bo1, bo2, bo3], [pcd1, pcd2, pcd3]], ...]\r
105     if Lines.find(DataType.TAB_SECTION_END) > -1:\r
106         Lines = Lines.split(DataType.TAB_SECTION_END, 1)[1]\r
107     (findBlock, findLibraryClass, findBuildOption, findPcdsFeatureFlag, findPcdsPatchableInModule, findPcdsFixedAtBuild, findPcdsDynamic, findPcdsDynamicEx) = (False, False, False, False, False, False, False, False)\r
108     ListItem = None\r
109     LibraryClassItem = []\r
110     BuildOption = []\r
111     Pcd = []\r
112     \r
113     LineList = Lines.split('\n')\r
114     for Line in LineList:\r
115         Line = CleanString(Line, CommentCharacter)\r
116         if Line == None or Line == '':\r
117             continue\r
118         \r
119         if findBlock == False:\r
120             ListItem = Line\r
121             #find '{' at line tail\r
122             if Line.endswith('{'):\r
123                 findBlock = True\r
124                 ListItem = CleanString(Line.rsplit('{', 1)[0], CommentCharacter)\r
125 \r
126         if findBlock:    \r
127             if Line.find('<LibraryClasses>') != -1:\r
128                 (findLibraryClass, findBuildOption, findPcdsFeatureFlag, findPcdsPatchableInModule, findPcdsFixedAtBuild, findPcdsDynamic, findPcdsDynamicEx) = (True, False, False, False, False, False, False)\r
129                 continue\r
130             if Line.find('<BuildOptions>') != -1:\r
131                 (findLibraryClass, findBuildOption, findPcdsFeatureFlag, findPcdsPatchableInModule, findPcdsFixedAtBuild, findPcdsDynamic, findPcdsDynamicEx) = (False, True, False, False, False, False, False)\r
132                 continue\r
133             if Line.find('<PcdsFeatureFlag>') != -1:\r
134                 (findLibraryClass, findBuildOption, findPcdsFeatureFlag, findPcdsPatchableInModule, findPcdsFixedAtBuild, findPcdsDynamic, findPcdsDynamicEx) = (False, False, True, False, False, False, False)\r
135                 continue\r
136             if Line.find('<PcdsPatchableInModule>') != -1:\r
137                 (findLibraryClass, findBuildOption, findPcdsFeatureFlag, findPcdsPatchableInModule, findPcdsFixedAtBuild, findPcdsDynamic, findPcdsDynamicEx) = (False, False, False, True, False, False, False)\r
138                 continue\r
139             if Line.find('<PcdsFixedAtBuild>') != -1:\r
140                 (findLibraryClass, findBuildOption, findPcdsFeatureFlag, findPcdsPatchableInModule, findPcdsFixedAtBuild, findPcdsDynamic, findPcdsDynamicEx) = (False, False, False, False, True, False, False)\r
141                 continue\r
142             if Line.find('<PcdsDynamic>') != -1:\r
143                 (findLibraryClass, findBuildOption, findPcdsFeatureFlag, findPcdsPatchableInModule, findPcdsFixedAtBuild, findPcdsDynamic, findPcdsDynamicEx) = (False, False, False, False, False, True, False)\r
144                 continue\r
145             if Line.find('<PcdsDynamicEx>') != -1:\r
146                 (findLibraryClass, findBuildOption, findPcdsFeatureFlag, findPcdsPatchableInModule, findPcdsFixedAtBuild, findPcdsDynamic, findPcdsDynamicEx) = (False, False, False, False, False, False, True)\r
147                 continue\r
148             if Line.endswith('}'):\r
149                 #find '}' at line tail\r
150                 KeyValues.append([ListItem, LibraryClassItem, BuildOption, Pcd])\r
151                 findBlock = False\r
152                 findLibraryClass = False\r
153                 findBuildOption = False\r
154                 findPcdsFeatureFlag = False\r
155                 findPcdsPatchableInModule = False\r
156                 findPcdsFixedAtBuild = False\r
157                 findPcdsDynamic = False\r
158                 findPcdsDynamicEx = False\r
159                 LibraryClassItem = []\r
160                 BuildOption = []\r
161                 Pcd = []\r
162                 continue\r
163 \r
164         if findBlock:\r
165             if findLibraryClass:\r
166                 LibraryClassItem.append(Line)\r
167             elif findBuildOption:\r
168                 BuildOption.append(Line)\r
169             elif findPcdsFeatureFlag:\r
170                 Pcd.append((DataType.TAB_PCDS_FEATURE_FLAG, Line))\r
171             elif findPcdsPatchableInModule:\r
172                 Pcd.append((DataType.TAB_PCDS_PATCHABLE_IN_MODULE, Line))\r
173             elif findPcdsFixedAtBuild:\r
174                 Pcd.append((DataType.TAB_PCDS_FIXED_AT_BUILD, Line))\r
175             elif findPcdsDynamic:\r
176                 Pcd.append((DataType.TAB_PCDS_DYNAMIC, Line))\r
177             elif findPcdsDynamicEx:\r
178                 Pcd.append((DataType.TAB_PCDS_DYNAMIC_EX, Line))\r
179         else:\r
180             KeyValues.append([ListItem, [], [], []])\r
181         \r
182     return True\r
183 \r
184 #\r
185 # Get Library Class definition when no module type defined\r
186 #\r
187 def GetLibraryClassesWithModuleType(Lines, Key, KeyValues, CommentCharacter):\r
188     newKey = SplitModuleType(Key)\r
189     Lines = Lines.split(DataType.TAB_SECTION_END, 1)[1]\r
190     LineList = Lines.splitlines()\r
191     for Line in LineList:\r
192         Line = CleanString(Line, CommentCharacter)\r
193         if Line != '' and Line[0] != CommentCharacter:\r
194             KeyValues.append([CleanString(Line, CommentCharacter), newKey[1]])\r
195 \r
196     return True\r
197 \r
198 #\r
199 # Get Dynamic Pcds\r
200 #\r
201 def GetDynamics(Lines, Key, KeyValues, CommentCharacter):\r
202     #\r
203     # Get SkuId Name List\r
204     #\r
205     SkuIdNameList = SplitModuleType(Key)\r
206     \r
207     Lines = Lines.split(DataType.TAB_SECTION_END, 1)[1]\r
208     LineList = Lines.splitlines()\r
209     for Line in LineList:\r
210         Line = CleanString(Line, CommentCharacter)\r
211         if Line != '' and Line[0] != CommentCharacter:\r
212             KeyValues.append([CleanString(Line, CommentCharacter), SkuIdNameList[1]])\r
213 \r
214     return True\r
215 \r
216 #\r
217 # Split ModuleType out of section defien to get key\r
218 # [LibraryClass.Arch.ModuleType|ModuleType|ModuleType] -> [ 'LibraryClass.Arch', ['ModuleType', 'ModuleType', 'ModuleType'] ]\r
219 #\r
220 def SplitModuleType(Key):\r
221     KeyList = Key.split(DataType.TAB_SPLIT)\r
222     KeyList.append('')                    # Fill in for arch\r
223     KeyList.append('')                    # Fill in for moduletype\r
224     ReturnValue = []\r
225     KeyValue = KeyList[0]\r
226     if KeyList[1] != '':\r
227         KeyValue = KeyValue + DataType.TAB_SPLIT + KeyList[1]\r
228     ReturnValue.append(KeyValue)\r
229     ReturnValue.append(GetSplitValueList(KeyList[2]))\r
230     \r
231     return ReturnValue\r
232     \r
233 #\r
234 # Create a normal path\r
235 # And replace DFEINE in the path\r
236 #\r
237 def NormPath(Path, Defines = {}):\r
238     if Path != '':\r
239         # Replace with Define\r
240         for Key in Defines.keys():\r
241             Path = Path.replace(Key, Defines[Key])\r
242 \r
243         # Remove ${WORKSPACE}\r
244         Path = Path.replace(DataType.TAB_WORKSPACE, '')\r
245 \r
246         # To local path format\r
247         Path = os.path.normpath(Path)\r
248         if Path[0] == os.path.sep:\r
249             Path = Path[1:]\r
250 \r
251     return Path\r
252 \r
253 #\r
254 # Remove comments in a string\r
255 # Remove spaces\r
256 #\r
257 def CleanString(Line, CommentCharacter = DataType.TAB_COMMENT_SPLIT):\r
258     #remove whitespace\r
259     Line = Line.strip();\r
260     #remove comments\r
261     Line = Line.split(CommentCharacter, 1)[0];\r
262     #remove whitespace again\r
263     Line = Line.strip();\r
264     \r
265     return Line\r
266 \r
267 def GetMultipleValuesOfKeyFromLines(Lines, Key, KeyValues, CommentCharacter):\r
268     Lines = Lines.split(DataType.TAB_SECTION_END, 1)[1]\r
269     LineList = Lines.split('\n')\r
270     for Line in LineList:\r
271         Line = CleanString(Line, CommentCharacter)\r
272         if Line != '' and Line[0] != CommentCharacter:\r
273             KeyValues += [Line]\r
274 \r
275     return True\r
276 \r
277 def GetDefineValue(String, Key, CommentCharacter):\r
278     String = CleanString(String)\r
279     return String[String.find(Key + ' ') + len(Key + ' ') : ]\r
280 \r
281 def GetSingleValueOfKeyFromLines(Lines, Dictionary, CommentCharacter, KeySplitCharacter, ValueSplitFlag, ValueSplitCharacter):\r
282     Lines = Lines.split('\n')\r
283     Keys = []\r
284     Value = ''\r
285     DefineValues = ['']\r
286     SpecValues = ['']\r
287     \r
288     for Line in Lines:\r
289         #\r
290         # Handle DEFINE and SPEC\r
291         #\r
292         if Line.find(DataType.TAB_INF_DEFINES_DEFINE + ' ') > -1:\r
293             if '' in DefineValues:\r
294                 DefineValues.remove('')\r
295             DefineValues.append(GetDefineValue(Line, DataType.TAB_INF_DEFINES_DEFINE, CommentCharacter))\r
296             continue\r
297         if Line.find(DataType.TAB_INF_DEFINES_SPEC + ' ') > -1:\r
298             if '' in SpecValues:\r
299                 SpecValues.remove('')\r
300             SpecValues.append(GetDefineValue(Line, DataType.TAB_INF_DEFINES_SPEC, CommentCharacter))\r
301             continue\r
302                 \r
303         #\r
304         # Handle Others\r
305         #\r
306         LineList = Line.split(KeySplitCharacter, 1)\r
307         if len(LineList) >= 2:\r
308             Key = LineList[0].split()\r
309             if len(Key) == 1 and Key[0][0] != CommentCharacter:\r
310                 #Remove comments and white spaces\r
311                 LineList[1] = CleanString(LineList[1], CommentCharacter)\r
312                 if ValueSplitFlag:\r
313                     Value = map(string.strip, LineList[1].split(ValueSplitCharacter))\r
314                 else:\r
315                     Value = CleanString(LineList[1], CommentCharacter).splitlines()\r
316                 \r
317                 if Key[0] not in Keys:\r
318                     Dictionary[Key[0]] = Value\r
319                     Keys.append(Key[0])\r
320                 else:\r
321                     Dictionary[Key[0]].extend(Value)                \r
322     \r
323     if DefineValues == []:\r
324         DefineValues == ['']\r
325     if SpecValues == []:\r
326         SpecValues == ['']\r
327     Dictionary[DataType.TAB_INF_DEFINES_DEFINE] = DefineValues\r
328     Dictionary[DataType.TAB_INF_DEFINES_SPEC] = SpecValues\r
329     \r
330     return True\r
331 \r
332 #\r
333 # Do pre-check for a file before it is parsed\r
334 # Check $()\r
335 # Check []\r
336 #\r
337 def PreCheck(FileName, FileContent, SupSectionTag):\r
338     LineNo = 0\r
339     IsFailed = False\r
340     for Line in FileContent.splitlines():\r
341         LineNo = LineNo + 1\r
342         Line = CleanString(Line)\r
343         #\r
344         # Check $()\r
345         #\r
346         if Line.find('$') > -1:\r
347             if Line.find('$(') < 0 or Line.find(')') < 0:\r
348                 raise ParserError(FORMAT_INVALID, lineno = LineNo, name = FileName)\r
349 \r
350         #\r
351         # Check []\r
352         #\r
353         if Line.find('[') > -1 or Line.find(']') > -1:\r
354             #\r
355             # Only get one '[' or one ']'\r
356             #\r
357             if not (Line.find('[') > -1 and Line.find(']') > -1):\r
358                 raise ParserError(FORMAT_INVALID, lineno = LineNo, name = FileName)\r
359 \r
360             #\r
361             # Tag not in defined value\r
362             #\r
363             TagList = GetSplitValueList(Line, DataType.TAB_COMMA_SPLIT)\r
364             for Tag in TagList:\r
365                 Tag = Tag.split(DataType.TAB_SPLIT, 1)[0].replace('[', '').replace(']', '').strip()\r
366                 if Tag.upper() == DataType.TAB_COMMON_DEFINES.upper():\r
367                     break\r
368                 if Tag.upper() == DataType.TAB_USER_EXTENSIONS.upper():\r
369                     break\r
370                 if Tag.upper() not in map(lambda s: s.upper(), SupSectionTag):\r
371                     ErrorMsg = "'%s' is not a supportted section name found at line %s in file '%s'" % (Tag, LineNo, FileName)\r
372                     raise ParserError(PARSER_ERROR, msg = ErrorMsg)\r
373     \r
374     if IsFailed:\r
375        raise ParserError(FORMAT_INVALID, lineno = LineNo, name = FileName)\r
376 \r
377 #\r
378 # Check if the Filename is including ExtName\r
379 # Pass if it exists\r
380 # Raise a error message if it not exists\r
381 #\r
382 def CheckFileType(CheckFilename, ExtName, ContainerFilename, SectionName, Line):\r
383     if CheckFilename != '' and CheckFilename != None:\r
384         (Root, Ext) = os.path.splitext(CheckFilename)\r
385         if Ext.upper() != ExtName.upper():\r
386             ContainerFile = open(ContainerFilename, 'r').read()\r
387             LineNo = GetLineNo(ContainerFile, Line)\r
388             ErrorMsg = "Invalid %s '%s' is defined at line %s in file '%s', it is NOT a valid '%s' file" % (SectionName, CheckFilename, LineNo, ContainerFilename, ExtName) \r
389             raise ParserError(PARSER_ERROR, msg = ErrorMsg)\r
390     \r
391     return True\r
392 \r
393 #\r
394 # Check if the file exists\r
395 # Pass if it exists\r
396 # Raise a error message if it not exists\r
397 #\r
398 def CheckFileExist(WorkspaceDir, CheckFilename, ContainerFilename, SectionName, Line):\r
399     if CheckFilename != '' and CheckFilename != None:\r
400         CheckFile = WorkspaceFile(WorkspaceDir, NormPath(CheckFilename))\r
401         if os.path.exists(CheckFile) and os.path.isfile(CheckFile):\r
402             pass\r
403         else:\r
404             ContainerFile = open(ContainerFilename, 'r').read()\r
405             LineNo = GetLineNo(ContainerFile, Line)\r
406             ErrorMsg = "Can't find file '%s' defined in section '%s' at line %s in file '%s'" % (CheckFile, SectionName, LineNo, ContainerFilename) \r
407             raise ParserError(PARSER_ERROR, msg = ErrorMsg)\r
408     \r
409     return True\r
410 \r
411 #\r
412 # Check if PcdTokenInfo is following <TokenSpaceGuidCName>.<PcdCName>\r
413 #\r
414 def CheckPcdTokenInfo(TokenInfoString, Section, File):\r
415     if TokenInfoString != '' and TokenInfoString != None:\r
416         Format = '<TokenSpaceGuidCName>.<PcdCName>'\r
417         TokenInfoList = GetSplitValueList(TokenInfoString, DataType.TAB_SPLIT)\r
418         if len(TokenInfoList) != 2:\r
419             RaiseParserError(TokenInfoString, Section, File, Format)\r
420     \r
421     return True\r
422 \r
423 #\r
424 # Find the index of a line in a file\r
425 #\r
426 def GetLineNo(FileContent, Line):\r
427     LineList = FileContent.splitlines()\r
428     for Index in range(len(LineList)):\r
429         if LineList[Index].find(Line) > -1:\r
430             return Index + 1\r
431     \r
432     return -1\r
433 \r
434 #\r
435 # Raise a parser error\r
436 #\r
437 def RaiseParserError(Line, Section, File, Format):\r
438     LineNo = GetLineNo(open(os.path.normpath(File), 'r').read(), Line)\r
439     ErrorMsg = "Invalid statement '%s' is found in section '%s' at line %s in file '%s', correct format is '%s'" % (Line, Section, LineNo, File, Format) \r
440     raise ParserError(PARSER_ERROR, msg = ErrorMsg)\r
441 \r
442 #\r
443 # Return a full path with workspace dir\r
444 #\r
445 def WorkspaceFile(WorkspaceDir, Filename):\r
446     return os.path.join(NormPath(WorkspaceDir), NormPath(Filename))\r
447 \r
448 if __name__ == '__main__':\r
449     print SplitModuleType('LibraryClasses.common.DXE_RUNTIME_DRIVER')\r
450     print SplitModuleType('Library.common')\r
451     print SplitModuleType('Librarsdsfwe')\r
452     print NormPath('sdfas//dsfsadf//dsfsd')\r
453     print NormPath('\\dsfsdf\\\\sd\\fsd\\dsfsdfsdf\\\\')\r