1## @file
2# This file is used to define common parser functions for meta-data
3#
4# Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.<BR>
5# This program and the accompanying materials
6# are licensed and made available under the terms and conditions of the BSD License
7# which accompanies this distribution.  The full text of the license may be found at
8# http://opensource.org/licenses/bsd-license.php
9#
10# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12#
13
14import Common.LongFilePathOs as os
15from CommonDataClass.DataClass import *
16from EccToolError import *
17from Common.MultipleWorkspace import MultipleWorkspace as mws
18import EccGlobalData
19import re
20## Get the inlcude path list for a source file
21#
22# 1. Find the source file belongs to which inf file
23# 2. Find the inf's package
24# 3. Return the include path list of the package
25#
26def GetIncludeListOfFile(WorkSpace, Filepath, Db):
27    IncludeList = []
28    Filepath = os.path.normpath(Filepath)
29    SqlCommand = """
30                select Value1, FullPath from Inf, File where Inf.Model = %s and Inf.BelongsToFile in(
31                    select distinct B.BelongsToFile from File as A left join Inf as B
32                        where A.ID = B.BelongsToFile and B.Model = %s and (A.Path || '%s' || B.Value1) = '%s')
33                        and Inf.BelongsToFile = File.ID""" \
34                % (MODEL_META_DATA_PACKAGE, MODEL_EFI_SOURCE_FILE, '\\', Filepath)
35    RecordSet = Db.TblFile.Exec(SqlCommand)
36    for Record in RecordSet:
37        DecFullPath = os.path.normpath(mws.join(WorkSpace, Record[0]))
38        InfFullPath = os.path.normpath(mws.join(WorkSpace, Record[1]))
39        (DecPath, DecName) = os.path.split(DecFullPath)
40        (InfPath, InfName) = os.path.split(InfFullPath)
41        SqlCommand = """select Value1 from Dec where BelongsToFile =
42                           (select ID from File where FullPath = '%s') and Model = %s""" \
43                    % (DecFullPath, MODEL_EFI_INCLUDE)
44        NewRecordSet = Db.TblDec.Exec(SqlCommand)
45        if InfPath not in IncludeList:
46            IncludeList.append(InfPath)
47        for NewRecord in NewRecordSet:
48            IncludePath = os.path.normpath(os.path.join(DecPath, NewRecord[0]))
49            if IncludePath not in IncludeList:
50                IncludeList.append(IncludePath)
51
52    return IncludeList
53
54## Get the file list
55#
56# Search table file and find all specific type files
57#
58def GetFileList(FileModel, Db):
59    FileList = []
60    SqlCommand = """select FullPath from File where Model = %s""" % str(FileModel)
61    RecordSet = Db.TblFile.Exec(SqlCommand)
62    for Record in RecordSet:
63        FileList.append(Record[0])
64
65    return FileList
66
67## Get the table list
68#
69# Search table file and find all small tables
70#
71def GetTableList(FileModelList, Table, Db):
72    TableList = []
73    SqlCommand = """select ID from File where Model in %s""" % str(FileModelList)
74    RecordSet = Db.TblFile.Exec(SqlCommand)
75    for Record in RecordSet:
76        TableName = Table + str(Record[0])
77        TableList.append(TableName)
78
79    return TableList
80
81## ParseHeaderCommentSection
82#
83# Parse Header comment section lines, extract Abstract, Description, Copyright
84# , License lines
85#
86# @param CommentList:   List of (Comment, LineNumber)
87# @param FileName:      FileName of the comment
88#
89def ParseHeaderCommentSection(CommentList, FileName = None):
90
91    Abstract = ''
92    Description = ''
93    Copyright = ''
94    License = ''
95    EndOfLine = "\n"
96    STR_HEADER_COMMENT_START = "@file"
97
98    #
99    # used to indicate the state of processing header comment section of dec,
100    # inf files
101    #
102    HEADER_COMMENT_NOT_STARTED = -1
103    HEADER_COMMENT_STARTED     = 0
104    HEADER_COMMENT_FILE        = 1
105    HEADER_COMMENT_ABSTRACT    = 2
106    HEADER_COMMENT_DESCRIPTION = 3
107    HEADER_COMMENT_COPYRIGHT   = 4
108    HEADER_COMMENT_LICENSE     = 5
109    HEADER_COMMENT_END         = 6
110    #
111    # first find the last copyright line
112    #
113    Last = 0
114    HeaderCommentStage = HEADER_COMMENT_NOT_STARTED
115    for Index in xrange(len(CommentList)-1, 0, -1):
116        Line = CommentList[Index][0]
117        if _IsCopyrightLine(Line):
118            Last = Index
119            break
120
121    for Item in CommentList:
122        Line = Item[0]
123        LineNo = Item[1]
124
125        if not Line.startswith('#') and Line:
126            SqlStatement = """ select ID from File where FullPath like '%s'""" % FileName
127            ResultSet = EccGlobalData.gDb.TblFile.Exec(SqlStatement)
128            for Result in ResultSet:
129                Msg = 'Comment must start with #'
130                EccGlobalData.gDb.TblReport.Insert(ERROR_DOXYGEN_CHECK_FILE_HEADER, Msg, "File", Result[0])
131        Comment = CleanString2(Line)[1]
132        Comment = Comment.strip()
133        #
134        # if there are blank lines between License or Description, keep them as they would be
135        # indication of different block; or in the position that Abstract should be, also keep it
136        # as it indicates that no abstract
137        #
138        if not Comment and HeaderCommentStage not in [HEADER_COMMENT_LICENSE, \
139                                                      HEADER_COMMENT_DESCRIPTION, HEADER_COMMENT_ABSTRACT]:
140            continue
141
142        if HeaderCommentStage == HEADER_COMMENT_NOT_STARTED:
143            if Comment.startswith(STR_HEADER_COMMENT_START):
144                HeaderCommentStage = HEADER_COMMENT_ABSTRACT
145            else:
146                License += Comment + EndOfLine
147        else:
148            if HeaderCommentStage == HEADER_COMMENT_ABSTRACT:
149                #
150                # in case there is no abstract and description
151                #
152                if not Comment:
153                    Abstract = ''
154                    HeaderCommentStage = HEADER_COMMENT_DESCRIPTION
155                elif _IsCopyrightLine(Comment):
156                    Copyright += Comment + EndOfLine
157                    HeaderCommentStage = HEADER_COMMENT_COPYRIGHT
158                else:
159                    Abstract += Comment + EndOfLine
160                    HeaderCommentStage = HEADER_COMMENT_DESCRIPTION
161            elif HeaderCommentStage == HEADER_COMMENT_DESCRIPTION:
162                #
163                # in case there is no description
164                #
165                if _IsCopyrightLine(Comment):
166                    Copyright += Comment + EndOfLine
167                    HeaderCommentStage = HEADER_COMMENT_COPYRIGHT
168                else:
169                    Description += Comment + EndOfLine
170            elif HeaderCommentStage == HEADER_COMMENT_COPYRIGHT:
171                if _IsCopyrightLine(Comment):
172                    Copyright += Comment + EndOfLine
173                else:
174                    #
175                    # Contents after copyright line are license, those non-copyright lines in between
176                    # copyright line will be discarded
177                    #
178                    if LineNo > Last:
179                        if License:
180                            License += EndOfLine
181                        License += Comment + EndOfLine
182                        HeaderCommentStage = HEADER_COMMENT_LICENSE
183            else:
184                if not Comment and not License:
185                    continue
186                License += Comment + EndOfLine
187
188    if not Copyright.strip():
189        SqlStatement = """ select ID from File where FullPath like '%s'""" % FileName
190        ResultSet = EccGlobalData.gDb.TblFile.Exec(SqlStatement)
191        for Result in ResultSet:
192            Msg = 'Header comment section must have copyright information'
193            EccGlobalData.gDb.TblReport.Insert(ERROR_DOXYGEN_CHECK_FILE_HEADER, Msg, "File", Result[0])
194
195    if not License.strip():
196        SqlStatement = """ select ID from File where FullPath like '%s'""" % FileName
197        ResultSet = EccGlobalData.gDb.TblFile.Exec(SqlStatement)
198        for Result in ResultSet:
199            Msg = 'Header comment section must have license information'
200            EccGlobalData.gDb.TblReport.Insert(ERROR_DOXYGEN_CHECK_FILE_HEADER, Msg, "File", Result[0])
201
202    if not Abstract.strip() or Abstract.find('Component description file') > -1:
203        SqlStatement = """ select ID from File where FullPath like '%s'""" % FileName
204        ResultSet = EccGlobalData.gDb.TblFile.Exec(SqlStatement)
205        for Result in ResultSet:
206            Msg = 'Header comment section must have Abstract information.'
207            EccGlobalData.gDb.TblReport.Insert(ERROR_DOXYGEN_CHECK_FILE_HEADER, Msg, "File", Result[0])
208
209    return Abstract.strip(), Description.strip(), Copyright.strip(), License.strip()
210
211## _IsCopyrightLine
212# check whether current line is copyright line, the criteria is whether there is case insensitive keyword "Copyright"
213# followed by zero or more white space characters followed by a "(" character
214#
215# @param LineContent:  the line need to be checked
216# @return: True if current line is copyright line, False else
217#
218def _IsCopyrightLine (LineContent):
219    LineContent = LineContent.upper()
220    Result = False
221
222    ReIsCopyrightRe = re.compile(r"""(^|\s)COPYRIGHT *\(""", re.DOTALL)
223    if ReIsCopyrightRe.search(LineContent):
224        Result = True
225
226    return Result
227
228
229## CleanString2
230#
231# Split comments in a string
232# Remove spaces
233#
234# @param Line:              The string to be cleaned
235# @param CommentCharacter:  Comment char, used to ignore comment content,
236#                           default is DataType.TAB_COMMENT_SPLIT
237#
238def CleanString2(Line, CommentCharacter='#', AllowCppStyleComment=False):
239    #
240    # remove whitespace
241    #
242    Line = Line.strip()
243    #
244    # Replace EDK1's comment character
245    #
246    if AllowCppStyleComment:
247        Line = Line.replace('//', CommentCharacter)
248    #
249    # separate comments and statements
250    #
251    LineParts = Line.split(CommentCharacter, 1)
252    #
253    # remove whitespace again
254    #
255    Line = LineParts[0].strip()
256    if len(LineParts) > 1:
257        Comment = LineParts[1].strip()
258        #
259        # Remove prefixed and trailing comment characters
260        #
261        Start = 0
262        End = len(Comment)
263        while Start < End and Comment.startswith(CommentCharacter, Start, End):
264            Start += 1
265        while End >= 0 and Comment.endswith(CommentCharacter, Start, End):
266            End -= 1
267        Comment = Comment[Start:End]
268        Comment = Comment.strip()
269    else:
270        Comment = ''
271
272    return Line, Comment
273