ea9d0b343c955080add93ce9789e9c4d25acc26e
[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, 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         # Check workspace envirnoment\r
229         if "WORKSPACE" not in os.environ:\r
230             EdkLogger.error("ECC", BuildToolError.ATTRIBUTE_NOT_AVAILABLE, "Environment variable not found",\r
231                             ExtraData="WORKSPACE")\r
232         else:\r
233             EccGlobalData.gWorkspace = os.path.normpath(os.getenv("WORKSPACE"))\r
234             if not os.path.exists(EccGlobalData.gWorkspace):\r
235                 EdkLogger.error("ECC", BuildToolError.FILE_NOT_FOUND, ExtraData="WORKSPACE = %s" % EccGlobalData.gWorkspace)\r
236             os.environ["WORKSPACE"] = EccGlobalData.gWorkspace\r
237         # Set log level\r
238         self.SetLogLevel(Options)\r
239 \r
240         # Set other options\r
241         if Options.ConfigFile != None:\r
242             self.ConfigFile = Options.ConfigFile\r
243         if Options.OutputFile != None:\r
244             self.OutputFile = Options.OutputFile\r
245         if Options.ReportFile != None:\r
246             self.ReportFile = Options.ReportFile\r
247         if Options.Target != None:\r
248             if not os.path.isdir(Options.Target):\r
249                 EdkLogger.error("ECC", BuildToolError.OPTION_VALUE_INVALID, ExtraData="Target [%s] does NOT exist" % Options.Target)\r
250             else:\r
251                 EccGlobalData.gTarget = self.GetRealPathCase(os.path.normpath(Options.Target))\r
252         else:\r
253             EdkLogger.warn("Ecc", EdkLogger.ECC_ERROR, "The target source tree was not specified, using current WORKSPACE instead!")\r
254             EccGlobalData.gTarget = os.path.normpath(os.getenv("WORKSPACE"))\r
255         if Options.keepdatabase != None:\r
256             self.IsInit = False\r
257         if Options.metadata != None and Options.sourcecode != None:\r
258             EdkLogger.error("ECC", BuildToolError.OPTION_CONFLICT, ExtraData="-m and -s can't be specified at one time")\r
259         if Options.metadata != None:\r
260             self.ScanSourceCode = False\r
261         if Options.sourcecode != None:\r
262             self.ScanMetaData = False\r
263 \r
264     ## SetLogLevel\r
265     #\r
266     # Set current log level of the tool based on args\r
267     #\r
268     # @param Option:  The option list including log level setting\r
269     #\r
270     def SetLogLevel(self, Option):\r
271         if Option.verbose != None:\r
272             EdkLogger.SetLevel(EdkLogger.VERBOSE)\r
273         elif Option.quiet != None:\r
274             EdkLogger.SetLevel(EdkLogger.QUIET)\r
275         elif Option.debug != None:\r
276             EdkLogger.SetLevel(Option.debug + 1)\r
277         else:\r
278             EdkLogger.SetLevel(EdkLogger.INFO)\r
279 \r
280     ## Parse command line options\r
281     #\r
282     # Using standard Python module optparse to parse command line option of this tool.\r
283     #\r
284     # @retval Opt   A optparse.Values object containing the parsed options\r
285     # @retval Args  Target of build command\r
286     #\r
287     def EccOptionParser(self):\r
288         Parser = OptionParser(description = self.Copyright, version = self.Version, prog = "Ecc.exe", usage = "%prog [options]")\r
289         Parser.add_option("-t", "--target sourcepath", action="store", type="string", dest='Target',\r
290             help="Check all files under the target workspace.")\r
291         Parser.add_option("-c", "--config filename", action="store", type="string", dest="ConfigFile",\r
292             help="Specify a configuration file. Defaultly use config.ini under ECC tool directory.")\r
293         Parser.add_option("-o", "--outfile filename", action="store", type="string", dest="OutputFile",\r
294             help="Specify the name of an output file, if and only if one filename was specified.")\r
295         Parser.add_option("-r", "--reportfile filename", action="store", type="string", dest="ReportFile",\r
296             help="Specify the name of an report file, if and only if one filename was specified.")\r
297         Parser.add_option("-m", "--metadata", action="store_true", type=None, help="Only scan meta-data files information if this option is specified.")\r
298         Parser.add_option("-s", "--sourcecode", action="store_true", type=None, help="Only scan source code files information if this option is specified.")\r
299         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
300         Parser.add_option("-l", "--log filename", action="store", dest="LogFile", help="""If specified, the tool should emit the changes that\r
301                                                                                           were made by the tool after printing the result message.\r
302                                                                                           If filename, the emit to the file, otherwise emit to\r
303                                                                                           standard output. If no modifications were made, then do not\r
304                                                                                           create a log file, or output a log message.""")\r
305         Parser.add_option("-q", "--quiet", action="store_true", type=None, help="Disable all messages except FATAL ERRORS.")\r
306         Parser.add_option("-v", "--verbose", action="store_true", type=None, help="Turn on verbose output with informational messages printed, "\\r
307                                                                                    "including library instances selected, final dependency expression, "\\r
308                                                                                    "and warning messages, etc.")\r
309         Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")\r
310 \r
311         (Opt, Args)=Parser.parse_args()\r
312 \r
313         return (Opt, Args)\r
314 \r
315 ##\r
316 #\r
317 # This acts like the main() function for the script, unless it is 'import'ed into another\r
318 # script.\r
319 #\r
320 if __name__ == '__main__':\r
321     # Initialize log system\r
322     EdkLogger.Initialize()\r
323     EdkLogger.IsRaiseError = False\r
324     EdkLogger.quiet(time.strftime("%H:%M:%S, %b.%d %Y ", time.localtime()) + "[00:00]" + "\n")\r
325 \r
326     StartTime = time.clock()\r
327     Ecc = Ecc()\r
328     FinishTime = time.clock()\r
329 \r
330     BuildDuration = time.strftime("%M:%S", time.gmtime(int(round(FinishTime - StartTime))))\r
331     EdkLogger.quiet("\n%s [%s]" % (time.strftime("%H:%M:%S, %b.%d %Y", time.localtime()), BuildDuration))\r