2 # This file is used to be the main entrance of ECC tool
4 # Copyright (c) 2009 - 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
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 Common
.LongFilePathOs
as 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 Common
.Misc
import DirCache
32 from MetaFileWorkspace
.MetaFileParser
import DscParser
33 from MetaFileWorkspace
.MetaFileParser
import DecParser
34 from MetaFileWorkspace
.MetaFileParser
import InfParser
35 from MetaFileWorkspace
.MetaFileParser
import Fdf
36 from MetaFileWorkspace
.MetaFileTable
import MetaFileStorage
39 from Exception import *
40 from Common
.LongFilePathSupport
import OpenLongFilePath
as open
44 # This class is used to define Ecc main entrance
46 # @param object: Inherited from object class
50 # Version and Copyright
51 self
.VersionNumber
= ("0.01" + " " + gBUILD_VERSION
)
52 self
.Version
= "%prog Version " + self
.VersionNumber
53 self
.Copyright
= "Copyright (c) 2009 - 2010, Intel Corporation All rights reserved."
55 self
.InitDefaultConfigIni()
56 self
.OutputFile
= 'output.txt'
57 self
.ReportFile
= 'Report.csv'
58 self
.ExceptionFile
= 'exception.xml'
60 self
.ScanSourceCode
= True
61 self
.ScanMetaData
= True
65 # Parse the options and args
69 # Check EFI_SOURCE (Edk build convention). EDK_SOURCE will always point to ECP
71 WorkspaceDir
= os
.path
.normcase(os
.path
.normpath(os
.environ
["WORKSPACE"]))
72 os
.environ
["WORKSPACE"] = WorkspaceDir
73 if "ECP_SOURCE" not in os
.environ
:
74 os
.environ
["ECP_SOURCE"] = os
.path
.join(WorkspaceDir
, GlobalData
.gEdkCompatibilityPkg
)
75 if "EFI_SOURCE" not in os
.environ
:
76 os
.environ
["EFI_SOURCE"] = os
.environ
["ECP_SOURCE"]
77 if "EDK_SOURCE" not in os
.environ
:
78 os
.environ
["EDK_SOURCE"] = os
.environ
["ECP_SOURCE"]
81 # Unify case of characters on case-insensitive systems
83 EfiSourceDir
= os
.path
.normcase(os
.path
.normpath(os
.environ
["EFI_SOURCE"]))
84 EdkSourceDir
= os
.path
.normcase(os
.path
.normpath(os
.environ
["EDK_SOURCE"]))
85 EcpSourceDir
= os
.path
.normcase(os
.path
.normpath(os
.environ
["ECP_SOURCE"]))
87 os
.environ
["EFI_SOURCE"] = EfiSourceDir
88 os
.environ
["EDK_SOURCE"] = EdkSourceDir
89 os
.environ
["ECP_SOURCE"] = EcpSourceDir
91 GlobalData
.gWorkspace
= WorkspaceDir
92 GlobalData
.gEfiSource
= EfiSourceDir
93 GlobalData
.gEdkSource
= EdkSourceDir
94 GlobalData
.gEcpSource
= EcpSourceDir
96 GlobalData
.gGlobalDefines
["WORKSPACE"] = WorkspaceDir
97 GlobalData
.gGlobalDefines
["EFI_SOURCE"] = EfiSourceDir
98 GlobalData
.gGlobalDefines
["EDK_SOURCE"] = EdkSourceDir
99 GlobalData
.gGlobalDefines
["ECP_SOURCE"] = EcpSourceDir
102 # Generate checkpoints list
103 EccGlobalData
.gConfig
= Configuration(self
.ConfigFile
)
105 # Generate exception list
106 EccGlobalData
.gException
= ExceptionCheck(self
.ExceptionFile
)
109 EccGlobalData
.gDb
= Database
.Database(Database
.DATABASE_PATH
)
110 EccGlobalData
.gDb
.InitDatabase(self
.IsInit
)
113 # Get files real name in workspace dir
115 GlobalData
.gAllFiles
= DirCache(GlobalData
.gWorkspace
)
118 # self.BuildDatabase()
119 self
.DetectOnlyScanDirs()
128 EccGlobalData
.gDb
.Close()
130 def InitDefaultConfigIni(self
):
131 paths
= map(lambda p
: os
.path
.join(p
, 'Ecc', 'config.ini'), sys
.path
)
132 paths
= (os
.path
.realpath('config.ini'),) + tuple(paths
)
134 if os
.path
.exists(path
):
135 self
.ConfigFile
= path
137 self
.ConfigFile
= 'config.ini'
142 # Detect whether only scanned folders have been enabled
144 def DetectOnlyScanDirs(self
):
145 if self
.OnlyScan
== True:
147 # Use regex here if multiple spaces or TAB exists in ScanOnlyDirList in config.ini file
148 for folder
in re
.finditer(r
'\S+', EccGlobalData
.gConfig
.ScanOnlyDirList
):
149 OnlyScanDirs
.append(folder
.group())
150 if len(OnlyScanDirs
) != 0:
151 self
.BuildDatabase(OnlyScanDirs
)
153 EdkLogger
.error("ECC", BuildToolError
.OPTION_VALUE_INVALID
, ExtraData
="Use -f option need to fill specific folders in config.ini file")
160 # Build the database for target
162 def BuildDatabase(self
, SpeciDirs
= None):
164 EccGlobalData
.gDb
.TblReport
.Drop()
165 EccGlobalData
.gDb
.TblReport
.Create()
169 if self
.ScanMetaData
:
170 EdkLogger
.quiet("Building database for Meta Data File ...")
171 self
.BuildMetaDataFileDatabase(SpeciDirs
)
172 if self
.ScanSourceCode
:
173 EdkLogger
.quiet("Building database for Meta Data File Done!")
174 if SpeciDirs
== None:
175 c
.CollectSourceCodeDataIntoDB(EccGlobalData
.gTarget
)
177 for specificDir
in SpeciDirs
:
178 c
.CollectSourceCodeDataIntoDB(os
.path
.join(EccGlobalData
.gTarget
, specificDir
))
180 EccGlobalData
.gIdentifierTableList
= GetTableList((MODEL_FILE_C
, MODEL_FILE_H
), 'Identifier', EccGlobalData
.gDb
)
181 EccGlobalData
.gCFileList
= GetFileList(MODEL_FILE_C
, EccGlobalData
.gDb
)
182 EccGlobalData
.gHFileList
= GetFileList(MODEL_FILE_H
, EccGlobalData
.gDb
)
184 ## BuildMetaDataFileDatabase
186 # Build the database for meta data files
188 def BuildMetaDataFileDatabase(self
, SpecificDirs
= None):
190 if SpecificDirs
== None:
191 ScanFolders
.append(EccGlobalData
.gTarget
)
193 for specificDir
in SpecificDirs
:
194 ScanFolders
.append(os
.path
.join(EccGlobalData
.gTarget
, specificDir
))
195 EdkLogger
.quiet("Building database for meta data files ...")
196 Op
= open(EccGlobalData
.gConfig
.MetaDataFileCheckPathOfGenerateFileList
, 'w+')
197 #SkipDirs = Read from config file
198 SkipDirs
= EccGlobalData
.gConfig
.SkipDirList
199 SkipDirString
= string
.join(SkipDirs
, '|')
200 # p = re.compile(r'.*[\\/](?:%s)[\\/]?.*' % SkipDirString)
201 p
= re
.compile(r
'.*[\\/](?:%s^\S)[\\/]?.*' % SkipDirString
)
202 for scanFolder
in ScanFolders
:
203 for Root
, Dirs
, Files
in os
.walk(scanFolder
):
204 if p
.match(Root
.upper()):
207 Dirname
= os
.path
.join(Root
, Dir
)
208 if os
.path
.islink(Dirname
):
209 Dirname
= os
.path
.realpath(Dirname
)
210 if os
.path
.isdir(Dirname
):
211 # symlinks to directories are treated as directories
216 if len(File
) > 4 and File
[-4:].upper() == ".DEC":
217 Filename
= os
.path
.normpath(os
.path
.join(Root
, File
))
218 EdkLogger
.quiet("Parsing %s" % Filename
)
219 Op
.write("%s\r" % Filename
)
220 #Dec(Filename, True, True, EccGlobalData.gWorkspace, EccGlobalData.gDb)
221 self
.MetaFile
= DecParser(Filename
, MODEL_FILE_DEC
, EccGlobalData
.gDb
.TblDec
)
222 self
.MetaFile
.Start()
224 if len(File
) > 4 and File
[-4:].upper() == ".DSC":
225 Filename
= os
.path
.normpath(os
.path
.join(Root
, File
))
226 EdkLogger
.quiet("Parsing %s" % Filename
)
227 Op
.write("%s\r" % Filename
)
228 #Dsc(Filename, True, True, EccGlobalData.gWorkspace, EccGlobalData.gDb)
229 self
.MetaFile
= DscParser(PathClass(Filename
, Root
), MODEL_FILE_DSC
, MetaFileStorage(EccGlobalData
.gDb
.TblDsc
.Cur
, Filename
, MODEL_FILE_DSC
, True))
230 # alwasy do post-process, in case of macros change
231 self
.MetaFile
.DoPostProcess()
232 self
.MetaFile
.Start()
233 self
.MetaFile
._PostProcess
()
235 if len(File
) > 4 and File
[-4:].upper() == ".INF":
236 Filename
= os
.path
.normpath(os
.path
.join(Root
, File
))
237 EdkLogger
.quiet("Parsing %s" % Filename
)
238 Op
.write("%s\r" % Filename
)
239 #Inf(Filename, True, True, EccGlobalData.gWorkspace, EccGlobalData.gDb)
240 self
.MetaFile
= InfParser(Filename
, MODEL_FILE_INF
, EccGlobalData
.gDb
.TblInf
)
241 self
.MetaFile
.Start()
243 if len(File
) > 4 and File
[-4:].upper() == ".FDF":
244 Filename
= os
.path
.normpath(os
.path
.join(Root
, File
))
245 EdkLogger
.quiet("Parsing %s" % Filename
)
246 Op
.write("%s\r" % Filename
)
247 Fdf(Filename
, True, EccGlobalData
.gWorkspace
, EccGlobalData
.gDb
)
252 EccGlobalData
.gDb
.Conn
.commit()
254 EdkLogger
.quiet("Building database for meta data files done!")
258 # Check each checkpoint
261 EdkLogger
.quiet("Checking ...")
264 EdkLogger
.quiet("Checking done!")
268 # Generate the scan report
271 EdkLogger
.quiet("Generating report ...")
272 EccGlobalData
.gDb
.TblReport
.ToCSV(self
.ReportFile
)
273 EdkLogger
.quiet("Generating report done!")
275 def GetRealPathCase(self
, path
):
276 TmpPath
= path
.rstrip(os
.sep
)
277 PathParts
= TmpPath
.split(os
.sep
)
278 if len(PathParts
) == 0:
280 if len(PathParts
) == 1:
281 if PathParts
[0].strip().endswith(':'):
282 return PathParts
[0].upper()
283 # Relative dir, list . current dir
284 Dirs
= os
.listdir('.')
286 if Dir
.upper() == PathParts
[0].upper():
289 if PathParts
[0].strip().endswith(':'):
290 PathParts
[0] = PathParts
[0].upper()
291 ParentDir
= PathParts
[0]
293 if PathParts
[0] == '':
297 PathParts
.remove(PathParts
[0]) # need to remove the parent
298 for Part
in PathParts
:
299 Dirs
= os
.listdir(ParentDir
+ os
.sep
)
301 if Dir
.upper() == Part
.upper():
314 def ParseOption(self
):
315 EdkLogger
.quiet("Loading ECC configuration ... done")
316 (Options
, Target
) = self
.EccOptionParser()
318 if Options
.Workspace
:
319 os
.environ
["WORKSPACE"] = Options
.Workspace
321 # Check workspace envirnoment
322 if "WORKSPACE" not in os
.environ
:
323 EdkLogger
.error("ECC", BuildToolError
.ATTRIBUTE_NOT_AVAILABLE
, "Environment variable not found",
324 ExtraData
="WORKSPACE")
326 EccGlobalData
.gWorkspace
= os
.path
.normpath(os
.getenv("WORKSPACE"))
327 if not os
.path
.exists(EccGlobalData
.gWorkspace
):
328 EdkLogger
.error("ECC", BuildToolError
.FILE_NOT_FOUND
, ExtraData
="WORKSPACE = %s" % EccGlobalData
.gWorkspace
)
329 os
.environ
["WORKSPACE"] = EccGlobalData
.gWorkspace
331 self
.SetLogLevel(Options
)
334 if Options
.ConfigFile
!= None:
335 self
.ConfigFile
= Options
.ConfigFile
336 if Options
.OutputFile
!= None:
337 self
.OutputFile
= Options
.OutputFile
338 if Options
.ReportFile
!= None:
339 self
.ReportFile
= Options
.ReportFile
340 if Options
.ExceptionFile
!= None:
341 self
.ExceptionFile
= Options
.ExceptionFile
342 if Options
.Target
!= None:
343 if not os
.path
.isdir(Options
.Target
):
344 EdkLogger
.error("ECC", BuildToolError
.OPTION_VALUE_INVALID
, ExtraData
="Target [%s] does NOT exist" % Options
.Target
)
346 EccGlobalData
.gTarget
= self
.GetRealPathCase(os
.path
.normpath(Options
.Target
))
348 EdkLogger
.warn("Ecc", EdkLogger
.ECC_ERROR
, "The target source tree was not specified, using current WORKSPACE instead!")
349 EccGlobalData
.gTarget
= os
.path
.normpath(os
.getenv("WORKSPACE"))
350 if Options
.keepdatabase
!= None:
352 if Options
.metadata
!= None and Options
.sourcecode
!= None:
353 EdkLogger
.error("ECC", BuildToolError
.OPTION_CONFLICT
, ExtraData
="-m and -s can't be specified at one time")
354 if Options
.metadata
!= None:
355 self
.ScanSourceCode
= False
356 if Options
.sourcecode
!= None:
357 self
.ScanMetaData
= False
358 if Options
.folders
!= None:
363 # Set current log level of the tool based on args
365 # @param Option: The option list including log level setting
367 def SetLogLevel(self
, Option
):
368 if Option
.verbose
!= None:
369 EdkLogger
.SetLevel(EdkLogger
.VERBOSE
)
370 elif Option
.quiet
!= None:
371 EdkLogger
.SetLevel(EdkLogger
.QUIET
)
372 elif Option
.debug
!= None:
373 EdkLogger
.SetLevel(Option
.debug
+ 1)
375 EdkLogger
.SetLevel(EdkLogger
.INFO
)
377 ## Parse command line options
379 # Using standard Python module optparse to parse command line option of this tool.
381 # @retval Opt A optparse.Values object containing the parsed options
382 # @retval Args Target of build command
384 def EccOptionParser(self
):
385 Parser
= OptionParser(description
= self
.Copyright
, version
= self
.Version
, prog
= "Ecc.exe", usage
= "%prog [options]")
386 Parser
.add_option("-t", "--target sourcepath", action
="store", type="string", dest
='Target',
387 help="Check all files under the target workspace.")
388 Parser
.add_option("-c", "--config filename", action
="store", type="string", dest
="ConfigFile",
389 help="Specify a configuration file. Defaultly use config.ini under ECC tool directory.")
390 Parser
.add_option("-o", "--outfile filename", action
="store", type="string", dest
="OutputFile",
391 help="Specify the name of an output file, if and only if one filename was specified.")
392 Parser
.add_option("-r", "--reportfile filename", action
="store", type="string", dest
="ReportFile",
393 help="Specify the name of an report file, if and only if one filename was specified.")
394 Parser
.add_option("-e", "--exceptionfile filename", action
="store", type="string", dest
="ExceptionFile",
395 help="Specify the name of an exception file, if and only if one filename was specified.")
396 Parser
.add_option("-m", "--metadata", action
="store_true", type=None, help="Only scan meta-data files information if this option is specified.")
397 Parser
.add_option("-s", "--sourcecode", action
="store_true", type=None, help="Only scan source code files information if this option is specified.")
398 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.")
399 Parser
.add_option("-l", "--log filename", action
="store", dest
="LogFile", help="""If specified, the tool should emit the changes that
400 were made by the tool after printing the result message.
401 If filename, the emit to the file, otherwise emit to
402 standard output. If no modifications were made, then do not
403 create a log file, or output a log message.""")
404 Parser
.add_option("-q", "--quiet", action
="store_true", type=None, help="Disable all messages except FATAL ERRORS.")
405 Parser
.add_option("-v", "--verbose", action
="store_true", type=None, help="Turn on verbose output with informational messages printed, "\
406 "including library instances selected, final dependency expression, "\
407 "and warning messages, etc.")
408 Parser
.add_option("-d", "--debug", action
="store", type="int", help="Enable debug messages at specified level.")
409 Parser
.add_option("-w", "--workspace", action
="store", type="string", dest
='Workspace', help="Specify workspace.")
410 Parser
.add_option("-f", "--folders", action
="store_true", type=None, help="Only scanning specified folders which are recorded in config.ini file.")
412 (Opt
, Args
)=Parser
.parse_args()
418 # This acts like the main() function for the script, unless it is 'import'ed into another
421 if __name__
== '__main__':
422 # Initialize log system
423 EdkLogger
.Initialize()
424 EdkLogger
.IsRaiseError
= False
425 EdkLogger
.quiet(time
.strftime("%H:%M:%S, %b.%d %Y ", time
.localtime()) + "[00:00]" + "\n")
427 StartTime
= time
.clock()
429 FinishTime
= time
.clock()
431 BuildDuration
= time
.strftime("%M:%S", time
.gmtime(int(round(FinishTime
- StartTime
))))
432 EdkLogger
.quiet("\n%s [%s]" % (time
.strftime("%H:%M:%S, %b.%d %Y", time
.localtime()), BuildDuration
))