Enhance ECC tools:
[efi/basetools/.git] / Source / Python / Ecc / Ecc.py
1 ## @file\r
2 # This file is used to be the main entrance of ECC tool\r
3 #\r
4 # Copyright (c) 2009 - 2010, Intel Corporation\r
5 # All rights reserved. This program and the accompanying materials\r
6 # are licensed and made available under the terms and conditions of the BSD License\r
7 # which accompanies this distribution.  The full text of the license may be found at\r
8 # http://opensource.org/licenses/bsd-license.php\r
9 #\r
10 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12 #\r
13 \r
14 ##\r
15 # Import Modules\r
16 #\r
17 import os, time, glob, sys\r
18 import Common.EdkLogger as EdkLogger\r
19 import Database\r
20 import EccGlobalData\r
21 from MetaDataParser import *\r
22 from optparse import OptionParser\r
23 from Configuration import Configuration\r
24 from Check import Check\r
25 from Common.InfClassObject import Inf\r
26 from Common.DecClassObject import Dec\r
27 from Common.DscClassObject import Dsc\r
28 from Common.FdfClassObject import Fdf\r
29 from Common.String import NormPath\r
30 from Common import BuildToolError\r
31 import c\r
32 import re, string\r
33 from Exception import *\r
34 \r
35 ## Ecc\r
36 #\r
37 # This class is used to define Ecc main entrance\r
38 #\r
39 # @param object:          Inherited from object class\r
40 #\r
41 class Ecc(object):\r
42     def __init__(self):\r
43         # Version and Copyright\r
44         self.VersionNumber = "0.01"\r
45         self.Version = "%prog Version " + self.VersionNumber\r
46         self.Copyright = "Copyright (c) 2009, Intel Corporation  All rights reserved."\r
47 \r
48         self.InitDefaultConfigIni()\r
49         self.OutputFile = 'output.txt'\r
50         self.ReportFile = 'Report.csv'\r
51         self.ExceptionFile = 'exception.xml'\r
52         self.IsInit = True\r
53         self.ScanSourceCode = True\r
54         self.ScanMetaData = True\r
55 \r
56         # Parse the options and args\r
57         self.ParseOption()\r
58 \r
59         # Generate checkpoints list\r
60         EccGlobalData.gConfig = Configuration(self.ConfigFile)\r
61 \r
62         # Generate exception list\r
63         EccGlobalData.gException = ExceptionCheck(self.ExceptionFile)\r
64 \r
65         # Init Ecc database\r
66         EccGlobalData.gDb = Database.Database(Database.DATABASE_PATH)\r
67         EccGlobalData.gDb.InitDatabase(self.IsInit)\r
68 \r
69         # Build ECC database\r
70         self.BuildDatabase()\r
71 \r
72         # Start to check\r
73         self.Check()\r
74 \r
75         # Show report\r
76         self.GenReport()\r
77 \r
78         # Close Database\r
79         EccGlobalData.gDb.Close()\r
80 \r
81     def InitDefaultConfigIni(self):\r
82         paths = map(lambda p: os.path.join(p, 'Ecc', 'config.ini'), sys.path)\r
83         paths = (os.path.realpath('config.ini'),) + tuple(paths)\r
84         for path in paths:\r
85             if os.path.exists(path):\r
86                 self.ConfigFile = path\r
87                 return\r
88         self.ConfigFile = 'config.ini'\r
89 \r
90     ## BuildDatabase\r
91     #\r
92     # Build the database for target\r
93     #\r
94     def BuildDatabase(self):\r
95         # Clean report table\r
96         EccGlobalData.gDb.TblReport.Drop()\r
97         EccGlobalData.gDb.TblReport.Create()\r
98 \r
99         # Build database\r
100         if self.IsInit:\r
101             if self.ScanSourceCode:\r
102                 EdkLogger.quiet("Building database for source code ...")\r
103                 c.CollectSourceCodeDataIntoDB(EccGlobalData.gTarget)\r
104             if self.ScanMetaData:\r
105                 EdkLogger.quiet("Building database for source code done!")\r
106                 self.BuildMetaDataFileDatabase()\r
107 \r
108         EccGlobalData.gIdentifierTableList = GetTableList((MODEL_FILE_C, MODEL_FILE_H), 'Identifier', EccGlobalData.gDb)\r
109 \r
110     ## BuildMetaDataFileDatabase\r
111     #\r
112     # Build the database for meta data files\r
113     #\r
114     def BuildMetaDataFileDatabase(self):\r
115         EdkLogger.quiet("Building database for meta data files ...")\r
116         Op = open(EccGlobalData.gConfig.MetaDataFileCheckPathOfGenerateFileList, 'w+')\r
117         #SkipDirs = Read from config file\r
118         SkipDirs = EccGlobalData.gConfig.SkipDirList\r
119         SkipDirString = string.join(SkipDirs, '|')\r
120         p = re.compile(r'.*[\\/](?:%s)[\\/]?.*' % SkipDirString)\r
121         for Root, Dirs, Files in os.walk(EccGlobalData.gTarget):\r
122             if p.match(Root.upper()):\r
123                 continue\r
124 \r
125             for Dir in Dirs:\r
126                 Dirname = os.path.join(Root, Dir)\r
127                 if os.path.islink(Dirname):\r
128                     Dirname = os.path.realpath(Dirname)\r
129                     if os.path.isdir(Dirname):\r
130                         # symlinks to directories are treated as directories\r
131                         Dirs.remove(Dir)\r
132                         Dirs.append(Dirname)\r
133 \r
134             for File in Files:\r
135                 if len(File) > 4 and File[-4:].upper() == ".DEC":\r
136                     Filename = os.path.normpath(os.path.join(Root, File))\r
137                     EdkLogger.quiet("Parsing %s" % Filename)\r
138                     Op.write("%s\r" % Filename)\r
139                     Dec(Filename, True, True, EccGlobalData.gWorkspace, EccGlobalData.gDb)\r
140                     continue\r
141                 if len(File) > 4 and File[-4:].upper() == ".DSC":\r
142                     Filename = os.path.normpath(os.path.join(Root, File))\r
143                     EdkLogger.quiet("Parsing %s" % Filename)\r
144                     Op.write("%s\r" % Filename)\r
145                     Dsc(Filename, True, True, EccGlobalData.gWorkspace, EccGlobalData.gDb)\r
146                     continue\r
147                 if len(File) > 4 and File[-4:].upper() == ".INF":\r
148                     Filename = os.path.normpath(os.path.join(Root, File))\r
149                     EdkLogger.quiet("Parsing %s" % Filename)\r
150                     Op.write("%s\r" % Filename)\r
151                     Inf(Filename, True, True, EccGlobalData.gWorkspace, EccGlobalData.gDb)\r
152                     continue\r
153                 if len(File) > 4 and File[-4:].upper() == ".FDF":\r
154                     Filename = os.path.normpath(os.path.join(Root, File))\r
155                     EdkLogger.quiet("Parsing %s" % Filename)\r
156                     Op.write("%s\r" % Filename)\r
157                     Fdf(Filename, True, EccGlobalData.gWorkspace, EccGlobalData.gDb)\r
158                     continue\r
159         Op.close()\r
160 \r
161         # Commit to database\r
162         EccGlobalData.gDb.Conn.commit()\r
163 \r
164         EdkLogger.quiet("Building database for meta data files done!")\r
165 \r
166     ##\r
167     #\r
168     # Check each checkpoint\r
169     #\r
170     def Check(self):\r
171         EdkLogger.quiet("Checking ...")\r
172         EccCheck = Check()\r
173         EccCheck.Check()\r
174         EdkLogger.quiet("Checking  done!")\r
175 \r
176     ##\r
177     #\r
178     # Generate the scan report\r
179     #\r
180     def GenReport(self):\r
181         EdkLogger.quiet("Generating report ...")\r
182         EccGlobalData.gDb.TblReport.ToCSV(self.ReportFile)\r
183         EdkLogger.quiet("Generating report done!")\r
184 \r
185     def GetRealPathCase(self, path):\r
186         TmpPath = path.rstrip(os.sep)\r
187         PathParts = TmpPath.split(os.sep)\r
188         if len(PathParts) == 0:\r
189             return path\r
190         if len(PathParts) == 1:\r
191             if PathParts[0].strip().endswith(':'):\r
192                 return PathParts[0].upper()\r
193             # Relative dir, list . current dir\r
194             Dirs = os.listdir('.')\r
195             for Dir in Dirs:\r
196                 if Dir.upper() == PathParts[0].upper():\r
197                     return Dir\r
198 \r
199         if PathParts[0].strip().endswith(':'):\r
200             PathParts[0] = PathParts[0].upper()\r
201         ParentDir = PathParts[0]\r
202         RealPath = ParentDir\r
203         if PathParts[0] == '':\r
204             RealPath = os.sep\r
205             ParentDir = os.sep\r
206 \r
207         PathParts.remove(PathParts[0])    # need to remove the parent\r
208         for Part in PathParts:\r
209             Dirs = os.listdir(ParentDir + os.sep)\r
210             for Dir in Dirs:\r
211                 if Dir.upper() == Part.upper():\r
212                     RealPath += os.sep\r
213                     RealPath += Dir\r
214                     break\r
215             ParentDir += os.sep\r
216             ParentDir += Dir\r
217 \r
218         return RealPath\r
219 \r
220     ## ParseOption\r
221     #\r
222     # Parse options\r
223     #\r
224     def ParseOption(self):\r
225         EdkLogger.quiet("Loading ECC configuration ... done")\r
226         (Options, Target) = self.EccOptionParser()\r
227 \r
228         if Options.Workspace:\r
229             os.environ["WORKSPACE"] = Options.Workspace\r
230             \r
231         # Check workspace envirnoment\r
232         if "WORKSPACE" not in os.environ:\r
233             EdkLogger.error("ECC", BuildToolError.ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",\r
234                             ExtraData="WORKSPACE")\r
235         else:\r
236             EccGlobalData.gWorkspace = os.path.normpath(os.getenv("WORKSPACE"))\r
237             if not os.path.exists(EccGlobalData.gWorkspace):\r
238                 EdkLogger.error("ECC", BuildToolError.FILE_NOT_FOUND, ExtraData="WORKSPACE = %s" % EccGlobalData.gWorkspace)\r
239             os.environ["WORKSPACE"] = EccGlobalData.gWorkspace\r
240         # Set log level\r
241         self.SetLogLevel(Options)\r
242 \r
243         # Set other options\r
244         if Options.ConfigFile != None:\r
245             self.ConfigFile = Options.ConfigFile\r
246         if Options.OutputFile != None:\r
247             self.OutputFile = Options.OutputFile\r
248         if Options.ReportFile != None:\r
249             self.ReportFile = Options.ReportFile\r
250         if Options.ExceptionFile != None:\r
251             self.ExceptionFile = Options.ExceptionFile\r
252         if Options.Target != None:\r
253             if not os.path.isdir(Options.Target):\r
254                 EdkLogger.error("ECC", BuildToolError.OPTION_VALUE_INVALID, ExtraData="Target [%s] does NOT exist" % Options.Target)\r
255             else:\r
256                 EccGlobalData.gTarget = self.GetRealPathCase(os.path.normpath(Options.Target))\r
257         else:\r
258             EdkLogger.warn("Ecc", EdkLogger.ECC_ERROR, "The target source tree was not specified, using current WORKSPACE instead!")\r
259             EccGlobalData.gTarget = os.path.normpath(os.getenv("WORKSPACE"))\r
260         if Options.keepdatabase != None:\r
261             self.IsInit = False\r
262         if Options.metadata != None and Options.sourcecode != None:\r
263             EdkLogger.error("ECC", BuildToolError.OPTION_CONFLICT, ExtraData="-m and -s can't be specified at one time")\r
264         if Options.metadata != None:\r
265             self.ScanSourceCode = False\r
266         if Options.sourcecode != None:\r
267             self.ScanMetaData = False\r
268 \r
269     ## SetLogLevel\r
270     #\r
271     # Set current log level of the tool based on args\r
272     #\r
273     # @param Option:  The option list including log level setting\r
274     #\r
275     def SetLogLevel(self, Option):\r
276         if Option.verbose != None:\r
277             EdkLogger.SetLevel(EdkLogger.VERBOSE)\r
278         elif Option.quiet != None:\r
279             EdkLogger.SetLevel(EdkLogger.QUIET)\r
280         elif Option.debug != None:\r
281             EdkLogger.SetLevel(Option.debug + 1)\r
282         else:\r
283             EdkLogger.SetLevel(EdkLogger.INFO)\r
284 \r
285     ## Parse command line options\r
286     #\r
287     # Using standard Python module optparse to parse command line option of this tool.\r
288     #\r
289     # @retval Opt   A optparse.Values object containing the parsed options\r
290     # @retval Args  Target of build command\r
291     #\r
292     def EccOptionParser(self):\r
293         Parser = OptionParser(description = self.Copyright, version = self.Version, prog = "Ecc.exe", usage = "%prog [options]")\r
294         Parser.add_option("-t", "--target sourcepath", action="store", type="string", dest='Target',\r
295             help="Check all files under the target workspace.")\r
296         Parser.add_option("-c", "--config filename", action="store", type="string", dest="ConfigFile",\r
297             help="Specify a configuration file. Defaultly use config.ini under ECC tool directory.")\r
298         Parser.add_option("-o", "--outfile filename", action="store", type="string", dest="OutputFile",\r
299             help="Specify the name of an output file, if and only if one filename was specified.")\r
300         Parser.add_option("-r", "--reportfile filename", action="store", type="string", dest="ReportFile",\r
301             help="Specify the name of an report file, if and only if one filename was specified.")\r
302         Parser.add_option("-e", "--exceptionfile filename", action="store", type="string", dest="ExceptionFile",\r
303             help="Specify the name of an exception file, if and only if one filename was specified.")\r
304         Parser.add_option("-m", "--metadata", action="store_true", type=None, help="Only scan meta-data files information if this option is specified.")\r
305         Parser.add_option("-s", "--sourcecode", action="store_true", type=None, help="Only scan source code files information if this option is specified.")\r
306         Parser.add_option("-k", "--keepdatabase", action="store_true", type=None, help="The existing Ecc database will not be cleaned except report information if this option is specified.")\r
307         Parser.add_option("-l", "--log filename", action="store", dest="LogFile", help="""If specified, the tool should emit the changes that\r
308                                                                                           were made by the tool after printing the result message.\r
309                                                                                           If filename, the emit to the file, otherwise emit to\r
310                                                                                           standard output. If no modifications were made, then do not\r
311                                                                                           create a log file, or output a log message.""")\r
312         Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")\r
313         Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\\r
314                                                                                    "including library instances selected, final dependency expression, "\\r
315                                                                                    "and warning messages, etc.")\r
316         Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")\r
317         Parser.add_option("-w", "--workspace", action="store", type="string", dest='Workspace', help="Specify workspace.")\r
318 \r
319         (Opt, Args)=Parser.parse_args()\r
320 \r
321         return (Opt, Args)\r
322 \r
323 ##\r
324 #\r
325 # This acts like the main() function for the script, unless it is 'import'ed into another\r
326 # script.\r
327 #\r
328 if __name__ == '__main__':\r
329     # Initialize log system\r
330     EdkLogger.Initialize()\r
331     EdkLogger.IsRaiseError = False\r
332     EdkLogger.quiet(time.strftime("%H:%M:%S, %b.%d %Y ", time.localtime()) + "[00:00]" + "\n")\r
333 \r
334     StartTime = time.clock()\r
335     Ecc = Ecc()\r
336     FinishTime = time.clock()\r
337 \r
338     BuildDuration = time.strftime("%M:%S", time.gmtime(int(round(FinishTime - StartTime))))\r
339     EdkLogger.quiet("\n%s [%s]" % (time.strftime("%H:%M:%S, %b.%d %Y", time.localtime()), BuildDuration))\r