2 # This file is used to be the main entrance of ECC tool
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
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.
17 import os
, time
, glob
, sys
18 import Common
.EdkLogger
as EdkLogger
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
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
38 from Exception import *
42 # This class is used to define Ecc main entrance
44 # @param object: Inherited from object class
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."
53 self
.InitDefaultConfigIni()
54 self
.OutputFile
= 'output.txt'
55 self
.ReportFile
= 'Report.csv'
56 self
.ExceptionFile
= 'exception.xml'
58 self
.ScanSourceCode
= True
59 self
.ScanMetaData
= True
62 # Parse the options and args
66 # Check EFI_SOURCE (Edk build convention). EDK_SOURCE will always point to ECP
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"]
78 # Unify case of characters on case-insensitive systems
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"]))
84 os
.environ
["EFI_SOURCE"] = EfiSourceDir
85 os
.environ
["EDK_SOURCE"] = EdkSourceDir
86 os
.environ
["ECP_SOURCE"] = EcpSourceDir
88 GlobalData
.gWorkspace
= WorkspaceDir
89 GlobalData
.gEfiSource
= EfiSourceDir
90 GlobalData
.gEdkSource
= EdkSourceDir
91 GlobalData
.gEcpSource
= EcpSourceDir
93 GlobalData
.gGlobalDefines
["WORKSPACE"] = WorkspaceDir
94 GlobalData
.gGlobalDefines
["EFI_SOURCE"] = EfiSourceDir
95 GlobalData
.gGlobalDefines
["EDK_SOURCE"] = EdkSourceDir
96 GlobalData
.gGlobalDefines
["ECP_SOURCE"] = EcpSourceDir
99 # Generate checkpoints list
100 EccGlobalData
.gConfig
= Configuration(self
.ConfigFile
)
102 # Generate exception list
103 EccGlobalData
.gException
= ExceptionCheck(self
.ExceptionFile
)
106 EccGlobalData
.gDb
= Database
.Database(Database
.DATABASE_PATH
)
107 EccGlobalData
.gDb
.InitDatabase(self
.IsInit
)
119 EccGlobalData
.gDb
.Close()
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
)
125 if os
.path
.exists(path
):
126 self
.ConfigFile
= path
128 self
.ConfigFile
= 'config.ini'
132 # Build the database for target
134 def BuildDatabase(self
):
136 EccGlobalData
.gDb
.TblReport
.Drop()
137 EccGlobalData
.gDb
.TblReport
.Create()
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()
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
)
152 ## BuildMetaDataFileDatabase
154 # Build the database for meta data files
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()):
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
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()
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
()
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()
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
)
212 EccGlobalData
.gDb
.Conn
.commit()
214 EdkLogger
.quiet("Building database for meta data files done!")
218 # Check each checkpoint
221 EdkLogger
.quiet("Checking ...")
224 EdkLogger
.quiet("Checking done!")
228 # Generate the scan report
231 EdkLogger
.quiet("Generating report ...")
232 EccGlobalData
.gDb
.TblReport
.ToCSV(self
.ReportFile
)
233 EdkLogger
.quiet("Generating report done!")
235 def GetRealPathCase(self
, path
):
236 TmpPath
= path
.rstrip(os
.sep
)
237 PathParts
= TmpPath
.split(os
.sep
)
238 if len(PathParts
) == 0:
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('.')
246 if Dir
.upper() == PathParts
[0].upper():
249 if PathParts
[0].strip().endswith(':'):
250 PathParts
[0] = PathParts
[0].upper()
251 ParentDir
= PathParts
[0]
253 if PathParts
[0] == '':
257 PathParts
.remove(PathParts
[0]) # need to remove the parent
258 for Part
in PathParts
:
259 Dirs
= os
.listdir(ParentDir
+ os
.sep
)
261 if Dir
.upper() == Part
.upper():
274 def ParseOption(self
):
275 EdkLogger
.quiet("Loading ECC configuration ... done")
276 (Options
, Target
) = self
.EccOptionParser()
278 if Options
.Workspace
:
279 os
.environ
["WORKSPACE"] = Options
.Workspace
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")
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
291 self
.SetLogLevel(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
)
306 EccGlobalData
.gTarget
= self
.GetRealPathCase(os
.path
.normpath(Options
.Target
))
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:
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
321 # Set current log level of the tool based on args
323 # @param Option: The option list including log level setting
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)
333 EdkLogger
.SetLevel(EdkLogger
.INFO
)
335 ## Parse command line options
337 # Using standard Python module optparse to parse command line option of this tool.
339 # @retval Opt A optparse.Values object containing the parsed options
340 # @retval Args Target of build command
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.")
369 (Opt
, Args
)=Parser
.parse_args()
375 # This acts like the main() function for the script, unless it is 'import'ed into another
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")
384 StartTime
= time
.clock()
386 FinishTime
= time
.clock()
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
))