2 # This file is used to define comment parsing interface
4 # Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>
6 # This program and the accompanying materials are licensed and made available
7 # under the terms and conditions of the BSD License which accompanies this
8 # distribution. The full text of the license may be found at
9 # http://opensource.org/licenses/bsd-license.php
11 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
24 from Library
.String
import GetSplitValueList
25 from Library
.String
import CleanString2
26 from Library
.DataType
import HEADER_COMMENT_NOT_STARTED
27 from Library
.DataType
import TAB_COMMENT_SPLIT
28 from Library
.DataType
import HEADER_COMMENT_LICENSE
29 from Library
.DataType
import HEADER_COMMENT_ABSTRACT
30 from Library
.DataType
import HEADER_COMMENT_COPYRIGHT
31 from Library
.DataType
import HEADER_COMMENT_DESCRIPTION
32 from Library
.DataType
import TAB_SPACE_SPLIT
33 from Library
.DataType
import TAB_COMMA_SPLIT
34 from Library
.DataType
import SUP_MODULE_LIST
35 from Object
.POM
.CommonObject
import TextObject
36 from Object
.POM
.CommonObject
import PcdErrorObject
37 import Logger
.Log
as Logger
38 from Logger
.ToolError
import FORMAT_INVALID
39 from Logger
.ToolError
import FORMAT_NOT_SUPPORTED
40 from Logger
import StringTable
as ST
42 ## ParseHeaderCommentSection
44 # Parse Header comment section lines, extract Abstract, Description, Copyright
47 # @param CommentList: List of (Comment, LineNumber)
48 # @param FileName: FileName of the comment
50 def ParseHeaderCommentSection(CommentList
, FileName
= None):
56 STR_HEADER_COMMENT_START
= "@file"
57 HeaderCommentStage
= HEADER_COMMENT_NOT_STARTED
60 # first find the last copyright line
63 for Index
in xrange(len(CommentList
)-1, 0, -1):
64 Line
= CommentList
[Index
][0]
65 if _IsCopyrightLine(Line
):
69 for Item
in CommentList
:
73 if not Line
.startswith(TAB_COMMENT_SPLIT
) and Line
:
74 Logger
.Error("\nUPT", FORMAT_INVALID
, ST
.ERR_INVALID_COMMENT_FORMAT
, FileName
, Item
[1])
75 Comment
= CleanString2(Line
)[1]
76 Comment
= Comment
.strip()
78 # if there are blank lines between License or Description, keep them as they would be
79 # indication of different block; or in the position that Abstract should be, also keep it
80 # as it indicates that no abstract
82 if not Comment
and HeaderCommentStage
not in [HEADER_COMMENT_LICENSE
, \
83 HEADER_COMMENT_DESCRIPTION
, HEADER_COMMENT_ABSTRACT
]:
86 if HeaderCommentStage
== HEADER_COMMENT_NOT_STARTED
:
87 if Comment
.startswith(STR_HEADER_COMMENT_START
):
88 HeaderCommentStage
= HEADER_COMMENT_ABSTRACT
90 License
+= Comment
+ EndOfLine
92 if HeaderCommentStage
== HEADER_COMMENT_ABSTRACT
:
94 # in case there is no abstract and description
98 HeaderCommentStage
= HEADER_COMMENT_DESCRIPTION
99 elif _IsCopyrightLine(Comment
):
100 Result
, ErrMsg
= _ValidateCopyright(Comment
)
101 ValidateCopyright(Result
, ST
.WRN_INVALID_COPYRIGHT
, FileName
, LineNo
, ErrMsg
)
102 Copyright
+= Comment
+ EndOfLine
103 HeaderCommentStage
= HEADER_COMMENT_COPYRIGHT
105 Abstract
+= Comment
+ EndOfLine
106 HeaderCommentStage
= HEADER_COMMENT_DESCRIPTION
107 elif HeaderCommentStage
== HEADER_COMMENT_DESCRIPTION
:
109 # in case there is no description
111 if _IsCopyrightLine(Comment
):
112 Result
, ErrMsg
= _ValidateCopyright(Comment
)
113 ValidateCopyright(Result
, ST
.WRN_INVALID_COPYRIGHT
, FileName
, LineNo
, ErrMsg
)
114 Copyright
+= Comment
+ EndOfLine
115 HeaderCommentStage
= HEADER_COMMENT_COPYRIGHT
117 Description
+= Comment
+ EndOfLine
118 elif HeaderCommentStage
== HEADER_COMMENT_COPYRIGHT
:
119 if _IsCopyrightLine(Comment
):
120 Result
, ErrMsg
= _ValidateCopyright(Comment
)
121 ValidateCopyright(Result
, ST
.WRN_INVALID_COPYRIGHT
, FileName
, LineNo
, ErrMsg
)
122 Copyright
+= Comment
+ EndOfLine
125 # Contents after copyright line are license, those non-copyright lines in between
126 # copyright line will be discarded
131 License
+= Comment
+ EndOfLine
132 HeaderCommentStage
= HEADER_COMMENT_LICENSE
134 if not Comment
and not License
:
136 License
+= Comment
+ EndOfLine
139 Logger
.Error("\nUPT", FORMAT_INVALID
, ST
.ERR_COPYRIGHT_MISSING
, \
143 Logger
.Error("\nUPT", FORMAT_INVALID
, ST
.ERR_LICENSE_MISSING
, FileName
)
145 return Abstract
.strip(), Description
.strip(), Copyright
.strip(), License
.strip()
148 # check whether current line is copyright line, the criteria is whether there is case insensitive keyword "Copyright"
149 # followed by zero or more white space characters followed by a "(" character
151 # @param LineContent: the line need to be checked
152 # @return: True if current line is copyright line, False else
154 def _IsCopyrightLine (LineContent
):
155 LineContent
= LineContent
.upper()
158 ReIsCopyrightRe
= re
.compile(r
"""(^|\s)COPYRIGHT *\(""", re
.DOTALL
)
159 if ReIsCopyrightRe
.search(LineContent
):
164 ## ParseGenericComment
166 # @param GenericComment: Generic comment list, element of
167 # (CommentLine, LineNum)
168 # @param ContainerFile: Input value for filename of Dec file
170 def ParseGenericComment (GenericComment
, ContainerFile
=None, SkipTag
=None):
176 for Item
in GenericComment
:
177 CommentLine
= Item
[0]
178 Comment
= CleanString2(CommentLine
)[1]
179 if SkipTag
is not None and Comment
.startswith(SkipTag
):
180 Comment
= Comment
.replace(SkipTag
, '', 1)
181 HelpStr
+= Comment
+ '\n'
184 HelpTxt
= TextObject()
185 if HelpStr
.endswith('\n') and not HelpStr
.endswith('\n\n') and HelpStr
!= '\n':
186 HelpStr
= HelpStr
[:-1]
187 HelpTxt
.SetString(HelpStr
)
192 ## ParseDecPcdGenericComment
194 # @param GenericComment: Generic comment list, element of (CommentLine,
196 # @param ContainerFile: Input value for filename of Dec file
198 def ParseDecPcdGenericComment (GenericComment
, ContainerFile
):
202 for (CommentLine
, LineNum
) in GenericComment
:
203 Comment
= CleanString2(CommentLine
)[1]
204 if Comment
.startswith("@ValidRange"):
206 Logger
.Error('Parser',
207 FORMAT_NOT_SUPPORTED
,
208 ST
.WRN_MULTI_PCD_RANGES
,
209 File
= ContainerFile
,
211 ValidRange
= Comment
.replace("@ValidRange", "", 1)
212 if _CheckRangeExpression(ValidRange
):
213 PcdErr
= PcdErrorObject()
214 PcdErr
.SetValidValueRange(ValidRange
)
215 elif Comment
.startswith("@ValidList"):
217 Logger
.Error('Parser',
218 FORMAT_NOT_SUPPORTED
,
219 ST
.WRN_MULTI_PCD_RANGES
,
220 File
= ContainerFile
,
222 ValidValue
= Comment
.replace("@ValidList", "", 1).replace(TAB_COMMA_SPLIT
, TAB_SPACE_SPLIT
)
223 PcdErr
= PcdErrorObject()
224 PcdErr
.SetValidValue(ValidValue
)
225 elif Comment
.startswith("@Expression"):
227 Logger
.Error('Parser',
228 FORMAT_NOT_SUPPORTED
,
229 ST
.WRN_MULTI_PCD_RANGES
,
230 File
= ContainerFile
,
232 Expression
= Comment
.replace("@Expression", "", 1)
233 if _CheckRangeExpression(Expression
):
234 PcdErr
= PcdErrorObject()
235 PcdErr
.SetExpression(Expression
)
237 HelpStr
+= Comment
+ '\n'
240 # remove the last EOL if the comment is of format 'FOO\n'
242 if HelpStr
.endswith('\n'):
243 if HelpStr
!= '\n' and not HelpStr
.endswith('\n\n'):
244 HelpStr
= HelpStr
[:-1]
246 return HelpStr
, PcdErr
248 ## ParseDecPcdTailComment
250 # @param TailCommentList: Tail comment list of Pcd, item of format (Comment, LineNum)
251 # @param ContainerFile: Input value for filename of Dec file
252 # @retVal SupModuleList: The supported module type list detected
253 # @retVal HelpStr: The generic help text string detected
255 def ParseDecPcdTailComment (TailCommentList
, ContainerFile
):
256 assert(len(TailCommentList
) == 1)
257 TailComment
= TailCommentList
[0][0]
258 LineNum
= TailCommentList
[0][1]
260 Comment
= TailComment
.lstrip(" #")
262 ReFindFirstWordRe
= re
.compile(r
"""^([^ #]*)""", re
.DOTALL
)
265 # get first word and compare with SUP_MODULE_LIST
267 MatchObject
= ReFindFirstWordRe
.match(Comment
)
268 if not (MatchObject
and MatchObject
.group(1) in SUP_MODULE_LIST
):
272 # parse line, it must have supported module type specified
274 if Comment
.find(TAB_COMMENT_SPLIT
) == -1:
275 Comment
+= TAB_COMMENT_SPLIT
276 SupMode
, HelpStr
= GetSplitValueList(Comment
, TAB_COMMENT_SPLIT
, 1)
278 for Mod
in GetSplitValueList(SupMode
, TAB_SPACE_SPLIT
):
281 elif Mod
not in SUP_MODULE_LIST
:
284 ST
.WRN_INVALID_MODULE_TYPE
%Mod
,
288 SupModuleList
.append(Mod
)
290 return SupModuleList
, HelpStr
293 ## _CheckRangeExpression
295 # @param Expression: Pcd range expression
297 def _CheckRangeExpression(Expression
):
299 # check grammar for Pcd range expression is not required yet
309 def ValidateCopyright(Result
, ErrType
, FileName
, LineNo
, ErrMsg
):
311 Logger
.Warn("\nUPT", ErrType
, FileName
, LineNo
, ErrMsg
)
313 ## _ValidateCopyright
315 # @param Line: Line that contains copyright information, # stripped
317 # @retval Result: True if line is conformed to Spec format, False else
318 # @retval ErrMsg: the detailed error description
320 def _ValidateCopyright(Line
):
326 return Result
, ErrMsg
328 def GenerateTokenList (Comment
):
330 # Tokenize Comment using '#' and ' ' as token seperators
332 RelplacedComment
= None
333 while Comment
!= RelplacedComment
:
334 RelplacedComment
= Comment
335 Comment
= Comment
.replace('##', '#').replace(' ', ' ').replace(' ', '#').strip('# ')
336 return Comment
.split('#')
340 # Comment - Comment to parse
341 # TypeTokens - A dictionary of type token synonyms
342 # RemoveTokens - A list of tokens to remove from help text
343 # ParseVariable - True for parsing [Guids]. Otherwise False
345 def ParseComment (Comment
, UsageTokens
, TypeTokens
, RemoveTokens
, ParseVariable
):
347 # Initialize return values
359 # Remove white space around first instance of ':' from Comment if 'Variable'
360 # is in front of ':' and Variable is the 1st or 2nd token in Comment.
362 List
= Comment
.split(':', 1)
364 SubList
= GenerateTokenList (List
[0].strip())
365 if len(SubList
) in [1, 2] and SubList
[-1] == 'Variable':
366 if List
[1].strip().find('L"') == 0:
367 Comment
= List
[0].strip() + ':' + List
[1].strip()
370 # Remove first instance of L"<VariableName> from Comment and put into String
371 # if and only if L"<VariableName>" is the 1st token, the 2nd token. Or
372 # L"<VariableName>" is the third token immediately following 'Variable:'.
375 Start
= Comment
.find('Variable:L"')
377 String
= Comment
[Start
+ 9:]
378 End
= String
[2:].find('"')
380 Start
= Comment
.find('L"')
382 String
= Comment
[Start
:]
383 End
= String
[2:].find('"')
385 SubList
= GenerateTokenList (Comment
[:Start
])
387 Comment
= Comment
[:Start
] + String
[End
+ 3:]
388 String
= String
[:End
+ 3]
393 # Initialze HelpText to Comment.
394 # Content will be remove from HelpText as matching tokens are found
399 # Tokenize Comment using '#' and ' ' as token seperators
401 List
= GenerateTokenList (Comment
)
404 # Search first two tokens for Usage and Type and remove any matching tokens
407 for Token
in List
[0:NumTokens
]:
408 if Usage
== None and Token
in UsageTokens
:
409 Usage
= UsageTokens
[Token
]
410 HelpText
= HelpText
.replace(Token
, '')
411 if Usage
!= None or not ParseVariable
:
412 for Token
in List
[0:NumTokens
]:
413 if Type
== None and Token
in TypeTokens
:
414 Type
= TypeTokens
[Token
]
415 HelpText
= HelpText
.replace(Token
, '')
417 for Token
in List
[0:NumTokens
]:
418 if Token
in RemoveTokens
:
419 HelpText
= HelpText
.replace(Token
, '')
422 # If no Usage token is present and set Usage to UNDEFINED
428 # If no Type token is present and set Type to UNDEFINED
434 # If Type is not 'Variable:', then set String to None
436 if Type
!= 'Variable':
440 # Strip ' ' and '#' from the beginning of HelpText
441 # If HelpText is an empty string after all parsing is
442 # complete then set HelpText to None
444 HelpText
= HelpText
.lstrip('# ')
449 # Return parsing results
451 return Usage
, Type
, String
, HelpText