]>
Commit | Line | Data |
---|---|---|
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 | |
16 | CommentParsing\r | |
17 | '''\r | |
18 | \r | |
19 | ##\r | |
20 | # Import Modules\r | |
21 | #\r | |
22 | import re\r | |
23 | \r | |
24 | from Library.String import GetSplitValueList\r | |
25 | from Library.String import CleanString2\r | |
26 | from Library.DataType import HEADER_COMMENT_NOT_STARTED\r | |
27 | from Library.DataType import TAB_COMMENT_SPLIT\r | |
28 | from Library.DataType import HEADER_COMMENT_LICENSE\r | |
29 | from Library.DataType import HEADER_COMMENT_ABSTRACT\r | |
30 | from Library.DataType import HEADER_COMMENT_COPYRIGHT\r | |
31 | from Library.DataType import HEADER_COMMENT_DESCRIPTION\r | |
32 | from Library.DataType import TAB_SPACE_SPLIT\r | |
33 | from Library.DataType import TAB_COMMA_SPLIT\r | |
34 | from Library.DataType import SUP_MODULE_LIST\r | |
35 | from Object.POM.CommonObject import TextObject\r | |
36 | from Object.POM.CommonObject import PcdErrorObject\r | |
37 | import Logger.Log as Logger\r | |
38 | from Logger.ToolError import FORMAT_INVALID\r | |
39 | from Logger.ToolError import FORMAT_NOT_SUPPORTED\r | |
40 | from 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 | |
50 | def 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 | |
154 | def _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 | |
170 | def 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 | |
198 | def 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 | |
255 | def 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 | |
297 | def _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 | |
309 | def 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 | |
320 | def _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 | |
328 | def 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 | |
345 | def 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 |