]> git.proxmox.com Git - mirror_edk2.git/blame - BaseTools/Source/Python/UPT/Library/CommentParsing.py
Add PCI PMC and PMCSR register definitions from PCI Power Management Interface Specif...
[mirror_edk2.git] / BaseTools / Source / Python / UPT / Library / CommentParsing.py
CommitLineData
4234283c
LG
1## @file\r
2# This file is used to define comment parsing interface\r
3#\r
4# Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>\r
5#\r
6# This program and the accompanying materials are licensed and made available \r
7# under the terms and conditions of the BSD License which accompanies this \r
8# distribution. The full text of the license may be found at \r
9# http://opensource.org/licenses/bsd-license.php\r
10#\r
11# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13#\r
14\r
15'''\r
16CommentParsing\r
17'''\r
18\r
19##\r
20# Import Modules\r
21#\r
22import re\r
23\r
24from Library.String import GetSplitValueList\r
25from Library.String import CleanString2\r
26from Library.DataType import HEADER_COMMENT_NOT_STARTED\r
27from Library.DataType import TAB_COMMENT_SPLIT\r
28from Library.DataType import HEADER_COMMENT_LICENSE\r
29from Library.DataType import HEADER_COMMENT_ABSTRACT\r
30from Library.DataType import HEADER_COMMENT_COPYRIGHT\r
31from Library.DataType import HEADER_COMMENT_DESCRIPTION\r
32from Library.DataType import TAB_SPACE_SPLIT\r
33from Library.DataType import TAB_COMMA_SPLIT\r
34from Library.DataType import SUP_MODULE_LIST\r
35from Object.POM.CommonObject import TextObject\r
36from Object.POM.CommonObject import PcdErrorObject\r
37import Logger.Log as Logger\r
38from Logger.ToolError import FORMAT_INVALID\r
39from Logger.ToolError import FORMAT_NOT_SUPPORTED\r
40from Logger import StringTable as ST\r
41\r
42## ParseHeaderCommentSection\r
43#\r
44# Parse Header comment section lines, extract Abstract, Description, Copyright\r
45# , License lines\r
46#\r
47# @param CommentList: List of (Comment, LineNumber)\r
48# @param FileName: FileName of the comment\r
49#\r
50def ParseHeaderCommentSection(CommentList, FileName = None):\r
51 Abstract = ''\r
52 Description = ''\r
53 Copyright = ''\r
54 License = ''\r
55 EndOfLine = "\n"\r
56 STR_HEADER_COMMENT_START = "@file"\r
57 HeaderCommentStage = HEADER_COMMENT_NOT_STARTED\r
58 \r
59 #\r
60 # first find the last copyright line\r
61 #\r
62 Last = 0\r
63 for Index in xrange(len(CommentList)-1, 0, -1):\r
64 Line = CommentList[Index][0]\r
65 if _IsCopyrightLine(Line):\r
66 Last = Index\r
67 break\r
68 \r
69 for Item in CommentList:\r
70 Line = Item[0]\r
71 LineNo = Item[1]\r
72 \r
73 if not Line.startswith(TAB_COMMENT_SPLIT) and Line:\r
74 Logger.Error("\nUPT", FORMAT_INVALID, ST.ERR_INVALID_COMMENT_FORMAT, FileName, Item[1])\r
75 Comment = CleanString2(Line)[1]\r
76 Comment = Comment.strip()\r
77 #\r
78 # if there are blank lines between License or Description, keep them as they would be \r
79 # indication of different block; or in the position that Abstract should be, also keep it\r
80 # as it indicates that no abstract\r
81 #\r
82 if not Comment and HeaderCommentStage not in [HEADER_COMMENT_LICENSE, \\r
83 HEADER_COMMENT_DESCRIPTION, HEADER_COMMENT_ABSTRACT]:\r
84 continue\r
85 \r
86 if HeaderCommentStage == HEADER_COMMENT_NOT_STARTED:\r
87 if Comment.startswith(STR_HEADER_COMMENT_START):\r
88 HeaderCommentStage = HEADER_COMMENT_ABSTRACT\r
89 else:\r
90 License += Comment + EndOfLine\r
91 else:\r
92 if HeaderCommentStage == HEADER_COMMENT_ABSTRACT:\r
93 #\r
94 # in case there is no abstract and description\r
95 #\r
96 if not Comment:\r
97 Abstract = ''\r
98 HeaderCommentStage = HEADER_COMMENT_DESCRIPTION\r
99 elif _IsCopyrightLine(Comment):\r
100 Result, ErrMsg = _ValidateCopyright(Comment)\r
101 ValidateCopyright(Result, ST.WRN_INVALID_COPYRIGHT, FileName, LineNo, ErrMsg)\r
102 Copyright += Comment + EndOfLine\r
103 HeaderCommentStage = HEADER_COMMENT_COPYRIGHT\r
104 else: \r
105 Abstract += Comment + EndOfLine\r
106 HeaderCommentStage = HEADER_COMMENT_DESCRIPTION\r
107 elif HeaderCommentStage == HEADER_COMMENT_DESCRIPTION:\r
108 #\r
109 # in case there is no description\r
110 # \r
111 if _IsCopyrightLine(Comment):\r
112 Result, ErrMsg = _ValidateCopyright(Comment)\r
113 ValidateCopyright(Result, ST.WRN_INVALID_COPYRIGHT, FileName, LineNo, ErrMsg)\r
114 Copyright += Comment + EndOfLine\r
115 HeaderCommentStage = HEADER_COMMENT_COPYRIGHT\r
116 else:\r
117 Description += Comment + EndOfLine \r
118 elif HeaderCommentStage == HEADER_COMMENT_COPYRIGHT:\r
119 if _IsCopyrightLine(Comment):\r
120 Result, ErrMsg = _ValidateCopyright(Comment)\r
121 ValidateCopyright(Result, ST.WRN_INVALID_COPYRIGHT, FileName, LineNo, ErrMsg)\r
122 Copyright += Comment + EndOfLine\r
123 else:\r
124 #\r
125 # Contents after copyright line are license, those non-copyright lines in between\r
126 # copyright line will be discarded \r
127 #\r
128 if LineNo > Last:\r
129 if License:\r
130 License += EndOfLine\r
131 License += Comment + EndOfLine\r
132 HeaderCommentStage = HEADER_COMMENT_LICENSE \r
133 else:\r
134 if not Comment and not License:\r
135 continue\r
136 License += Comment + EndOfLine\r
137 \r
138 if not Copyright:\r
139 Logger.Error("\nUPT", FORMAT_INVALID, ST.ERR_COPYRIGHT_MISSING, \\r
140 FileName)\r
141\r
142 if not License:\r
143 Logger.Error("\nUPT", FORMAT_INVALID, ST.ERR_LICENSE_MISSING, FileName)\r
144 \r
145 return Abstract.strip(), Description.strip(), Copyright.strip(), License.strip()\r
146\r
147## _IsCopyrightLine\r
148# check whether current line is copyright line, the criteria is whether there is case insensitive keyword "Copyright" \r
149# followed by zero or more white space characters followed by a "(" character \r
150#\r
151# @param LineContent: the line need to be checked\r
152# @return: True if current line is copyright line, False else\r
153#\r
154def _IsCopyrightLine (LineContent):\r
155 LineContent = LineContent.upper()\r
156 Result = False\r
157 \r
158 ReIsCopyrightRe = re.compile(r"""(^|\s)COPYRIGHT *\(""", re.DOTALL)\r
159 if ReIsCopyrightRe.search(LineContent):\r
160 Result = True\r
161 \r
162 return Result\r
163\r
164## ParseGenericComment\r
165#\r
166# @param GenericComment: Generic comment list, element of \r
167# (CommentLine, LineNum)\r
168# @param ContainerFile: Input value for filename of Dec file\r
169# \r
170def ParseGenericComment (GenericComment, ContainerFile=None, SkipTag=None):\r
171 if ContainerFile:\r
172 pass\r
173 HelpTxt = None \r
174 HelpStr = '' \r
175 \r
176 for Item in GenericComment:\r
177 CommentLine = Item[0]\r
178 Comment = CleanString2(CommentLine)[1]\r
179 if SkipTag is not None and Comment.startswith(SkipTag):\r
180 Comment = Comment.replace(SkipTag, '', 1)\r
181 HelpStr += Comment + '\n'\r
182 \r
183 if HelpStr:\r
184 HelpTxt = TextObject()\r
185 if HelpStr.endswith('\n') and not HelpStr.endswith('\n\n') and HelpStr != '\n':\r
186 HelpStr = HelpStr[:-1]\r
187 HelpTxt.SetString(HelpStr)\r
188\r
189 return HelpTxt\r
190\r
191 \r
192## ParseDecPcdGenericComment\r
193#\r
194# @param GenericComment: Generic comment list, element of (CommentLine, \r
195# LineNum)\r
196# @param ContainerFile: Input value for filename of Dec file\r
197# \r
198def ParseDecPcdGenericComment (GenericComment, ContainerFile): \r
199 HelpStr = '' \r
200 PcdErr = None\r
201 \r
202 for (CommentLine, LineNum) in GenericComment:\r
203 Comment = CleanString2(CommentLine)[1]\r
204 if Comment.startswith("@ValidRange"):\r
205 if PcdErr:\r
206 Logger.Error('Parser', \r
207 FORMAT_NOT_SUPPORTED,\r
208 ST.WRN_MULTI_PCD_RANGES,\r
209 File = ContainerFile, \r
210 Line = LineNum)\r
211 ValidRange = Comment.replace("@ValidRange", "", 1)\r
212 if _CheckRangeExpression(ValidRange):\r
213 PcdErr = PcdErrorObject()\r
214 PcdErr.SetValidValueRange(ValidRange)\r
215 elif Comment.startswith("@ValidList"):\r
216 if PcdErr:\r
217 Logger.Error('Parser', \r
218 FORMAT_NOT_SUPPORTED,\r
219 ST.WRN_MULTI_PCD_RANGES,\r
220 File = ContainerFile, \r
221 Line = LineNum)\r
222 ValidValue = Comment.replace("@ValidList", "", 1).replace(TAB_COMMA_SPLIT, TAB_SPACE_SPLIT)\r
223 PcdErr = PcdErrorObject()\r
224 PcdErr.SetValidValue(ValidValue)\r
225 elif Comment.startswith("@Expression"):\r
226 if PcdErr:\r
227 Logger.Error('Parser', \r
228 FORMAT_NOT_SUPPORTED,\r
229 ST.WRN_MULTI_PCD_RANGES,\r
230 File = ContainerFile, \r
231 Line = LineNum)\r
232 Expression = Comment.replace("@Expression", "", 1)\r
233 if _CheckRangeExpression(Expression):\r
234 PcdErr = PcdErrorObject()\r
235 PcdErr.SetExpression(Expression)\r
236 else:\r
237 HelpStr += Comment + '\n'\r
238 \r
239 #\r
240 # remove the last EOL if the comment is of format 'FOO\n'\r
241 #\r
242 if HelpStr.endswith('\n'):\r
243 if HelpStr != '\n' and not HelpStr.endswith('\n\n'):\r
244 HelpStr = HelpStr[:-1]\r
245\r
246 return HelpStr, PcdErr\r
247\r
248## ParseDecPcdTailComment\r
249#\r
250# @param TailCommentList: Tail comment list of Pcd, item of format (Comment, LineNum)\r
251# @param ContainerFile: Input value for filename of Dec file\r
252# @retVal SupModuleList: The supported module type list detected\r
253# @retVal HelpStr: The generic help text string detected\r
254#\r
255def ParseDecPcdTailComment (TailCommentList, ContainerFile):\r
256 assert(len(TailCommentList) == 1)\r
257 TailComment = TailCommentList[0][0]\r
258 LineNum = TailCommentList[0][1]\r
259\r
260 Comment = TailComment.lstrip(" #")\r
261 \r
262 ReFindFirstWordRe = re.compile(r"""^([^ #]*)""", re.DOTALL)\r
263 \r
264 #\r
265 # get first word and compare with SUP_MODULE_LIST\r
266 #\r
267 MatchObject = ReFindFirstWordRe.match(Comment)\r
268 if not (MatchObject and MatchObject.group(1) in SUP_MODULE_LIST):\r
269 return None, Comment\r
270\r
271 #\r
272 # parse line, it must have supported module type specified\r
273 #\r
274 if Comment.find(TAB_COMMENT_SPLIT) == -1:\r
275 Comment += TAB_COMMENT_SPLIT \r
276 SupMode, HelpStr = GetSplitValueList(Comment, TAB_COMMENT_SPLIT, 1)\r
277 SupModuleList = []\r
278 for Mod in GetSplitValueList(SupMode, TAB_SPACE_SPLIT):\r
279 if not Mod:\r
280 continue\r
281 elif Mod not in SUP_MODULE_LIST:\r
282 Logger.Error("UPT",\r
283 FORMAT_INVALID,\r
284 ST.WRN_INVALID_MODULE_TYPE%Mod, \r
285 ContainerFile, \r
286 LineNum)\r
287 else:\r
288 SupModuleList.append(Mod)\r
289\r
290 return SupModuleList, HelpStr\r
291\r
292\r
293## _CheckRangeExpression\r
294#\r
295# @param Expression: Pcd range expression\r
296# \r
297def _CheckRangeExpression(Expression):\r
298 #\r
299 # check grammar for Pcd range expression is not required yet\r
300 #\r
301 if Expression:\r
302 pass\r
303 return True\r
304\r
305## ValidateCopyright\r
306#\r
307#\r
308#\r
309def ValidateCopyright(Result, ErrType, FileName, LineNo, ErrMsg):\r
310 if not Result:\r
311 Logger.Warn("\nUPT", ErrType, FileName, LineNo, ErrMsg) \r
312\r
313## _ValidateCopyright\r
314#\r
315# @param Line: Line that contains copyright information, # stripped\r
316# \r
317# @retval Result: True if line is conformed to Spec format, False else\r
318# @retval ErrMsg: the detailed error description\r
319# \r
320def _ValidateCopyright(Line):\r
321 if Line:\r
322 pass\r
323 Result = True\r
324 ErrMsg = ''\r
325 \r
326 return Result, ErrMsg\r
327\r
328def GenerateTokenList (Comment):\r
329 #\r
330 # Tokenize Comment using '#' and ' ' as token seperators\r
331 #\r
332 RelplacedComment = None \r
333 while Comment != RelplacedComment:\r
334 RelplacedComment = Comment\r
335 Comment = Comment.replace('##', '#').replace(' ', ' ').replace(' ', '#').strip('# ')\r
336 return Comment.split('#')\r
337\r
338\r
339#\r
340# Comment - Comment to parse\r
341# TypeTokens - A dictionary of type token synonyms\r
342# RemoveTokens - A list of tokens to remove from help text\r
343# ParseVariable - True for parsing [Guids]. Otherwise False\r
344#\r
345def ParseComment (Comment, UsageTokens, TypeTokens, RemoveTokens, ParseVariable):\r
346 #\r
347 # Initialize return values\r
348 #\r
349 Usage = None\r
350 Type = None\r
351 String = None\r
352 HelpText = None\r
353 \r
354 Comment = Comment[0]\r
355 \r
356 NumTokens = 2 \r
357 if ParseVariable:\r
358 # \r
359 # Remove white space around first instance of ':' from Comment if 'Variable' \r
360 # is in front of ':' and Variable is the 1st or 2nd token in Comment.\r
361 #\r
362 List = Comment.split(':', 1) \r
363 if len(List) > 1:\r
364 SubList = GenerateTokenList (List[0].strip())\r
365 if len(SubList) in [1, 2] and SubList[-1] == 'Variable':\r
366 if List[1].strip().find('L"') == 0: \r
367 Comment = List[0].strip() + ':' + List[1].strip()\r
368 \r
369 # \r
370 # Remove first instance of L"<VariableName> from Comment and put into String\r
371 # if and only if L"<VariableName>" is the 1st token, the 2nd token. Or \r
372 # L"<VariableName>" is the third token immediately following 'Variable:'.\r
373 #\r
374 End = -1\r
375 Start = Comment.find('Variable:L"')\r
376 if Start >= 0:\r
377 String = Comment[Start + 9:]\r
378 End = String[2:].find('"')\r
379 else:\r
380 Start = Comment.find('L"')\r
381 if Start >= 0:\r
382 String = Comment[Start:]\r
383 End = String[2:].find('"')\r
384 if End >= 0:\r
385 SubList = GenerateTokenList (Comment[:Start])\r
386 if len(SubList) < 2: \r
387 Comment = Comment[:Start] + String[End + 3:]\r
388 String = String[:End + 3]\r
389 Type = 'Variable'\r
390 NumTokens = 1 \r
391 \r
392 #\r
393 # Initialze HelpText to Comment. \r
394 # Content will be remove from HelpText as matching tokens are found\r
395 # \r
396 HelpText = Comment\r
397 \r
398 #\r
399 # Tokenize Comment using '#' and ' ' as token seperators\r
400 #\r
401 List = GenerateTokenList (Comment)\r
402 \r
403 #\r
404 # Search first two tokens for Usage and Type and remove any matching tokens \r
405 # from HelpText\r
406 #\r
407 for Token in List[0:NumTokens]:\r
408 if Usage == None and Token in UsageTokens:\r
409 Usage = UsageTokens[Token]\r
410 HelpText = HelpText.replace(Token, '')\r
411 if Usage != None or not ParseVariable:\r
412 for Token in List[0:NumTokens]:\r
413 if Type == None and Token in TypeTokens:\r
414 Type = TypeTokens[Token]\r
415 HelpText = HelpText.replace(Token, '')\r
416 if Usage != None: \r
417 for Token in List[0:NumTokens]:\r
418 if Token in RemoveTokens:\r
419 HelpText = HelpText.replace(Token, '')\r
420 \r
421 #\r
422 # If no Usage token is present and set Usage to UNDEFINED\r
423 # \r
424 if Usage == None:\r
425 Usage = 'UNDEFINED'\r
426 \r
427 #\r
428 # If no Type token is present and set Type to UNDEFINED\r
429 # \r
430 if Type == None:\r
431 Type = 'UNDEFINED'\r
432 \r
433 #\r
434 # If Type is not 'Variable:', then set String to None\r
435 # \r
436 if Type != 'Variable':\r
437 String = None \r
438 \r
439 #\r
440 # Strip ' ' and '#' from the beginning of HelpText\r
441 # If HelpText is an empty string after all parsing is \r
442 # complete then set HelpText to None\r
443 # \r
444 HelpText = HelpText.lstrip('# ')\r
445 if HelpText == '':\r
446 HelpText = None\r
447 \r
448 #\r
449 # Return parsing results\r
450 # \r
451 return Usage, Type, String, HelpText \r