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